code:
<?php
/**
 * Block a user from a group action class.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Block a user from a group
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class GroupblockAction extends RedirectingAction
{
    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()) {
            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
            $this->clientError(_('Not logged in.'));
        }
        $token = $this->trimmed('token');
        if (empty($token) || $token != common_session_token()) {
            // TRANS: Client error displayed when the session token does not match or is not given.
            $this->clientError(_('There was a problem with your session token. Try again, please.'));
        }
        $id = $this->trimmed('blockto');
        if (empty($id)) {
            // TRANS: Client error displayed trying to block a user from a group while not specifying a to be blocked user profile.
            $this->clientError(_('No profile specified.'));
        }
        $this->profile = Profile::getKV('id', $id);
        if (empty($this->profile)) {
            // TRANS: Client error displayed trying to block a user from a group while specifying a non-existing profile.
            $this->clientError(_('No profile with that ID.'));
        }
        $group_id = $this->trimmed('blockgroup');
        if (empty($group_id)) {
            // TRANS: Client error displayed trying to block a user from a group while not specifying a group to block a profile from.
            $this->clientError(_('No group specified.'));
        }
        $this->group = User_group::getKV('id', $group_id);
        if (empty($this->group)) {
            // TRANS: Client error displayed trying to block a user from a group while specifying a non-existing group.
            $this->clientError(_('No such group.'));
        }
        $user = common_current_user();
        if (!$user->isAdmin($this->group)) {
            // TRANS: Client error displayed trying to block a user from a group while not being an admin user.
            $this->clientError(_('Only an admin can block group members.'), 401);
        }
        if (Group_block::isBlocked($this->group, $this->profile)) {
            // TRANS: Client error displayed trying to block a user from a group while user is already blocked from the given group.
            $this->clientError(_('User is already blocked from group.'));
        }
        // XXX: could have proactive blocks, but we don't have UI for it.
        if (!$this->profile->isMember($this->group)) {
            // TRANS: Client error displayed trying to block a user from a group while user is not a member of given group.
            $this->clientError(_('User is not a member of group.'));
        }
        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')) {
                $this->returnToPrevious();
            } elseif ($this->arg('yes')) {
                $this->blockProfile();
            } elseif ($this->arg('blockto')) {
                $this->showPage();
            }
        }
    }

    function showContent() {
        $this->areYouSureForm();
    }

    function title() {
        // TRANS: Title for block user from group page.
        return _('Block user from group');
    }

    function showNoticeForm() {
        // nop
    }

    /**
     * Confirm with user.
     *
     * Shows a confirmation form.
     *
     * @return void
     */
    function areYouSureForm()
    {
        $id = $this->profile->id;
        $this->elementStart('form', array('id' => 'block-' . $id,
                                           'method' => 'post',
                                           'class' => 'form_settings form_entity_block',
                                           'action' => common_local_url('groupblock')));
        $this->elementStart('fieldset');
        $this->hidden('token', common_session_token());
        // TRANS: Fieldset legend for block user from group form.
        $this->element('legend', _('Block user'));
        $this->element('p', null,
                       // TRANS: Explanatory text for block user from group form before setting the block.
                       // TRANS: %1$s is that to be blocked user, %2$s is the group the user will be blocked from.
                       sprintf(_('Are you sure you want to block user "%1$s" from the group "%2$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->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('form_action-no',
                      // TRANS: Button label on the form to block a user from a group.
                      _m('BUTTON','No'),
                      'submit form_action-primary',
                      'no',
                      // TRANS: Submit button title for 'No' when blocking a user from a group.
                      _('Do not block this user from this group.'));
        $this->submit('form_action-yes',
                      // TRANS: Button label on the form to block a user from a group.
                      _m('BUTTON','Yes'),
                      'submit form_action-secondary',
                      'yes',
                      // TRANS: Submit button title for 'Yes' when blocking a user from a group.
                      _('Block this user from this group.'));
        $this->elementEnd('fieldset');
        $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)) {
            // TRANS: Server error displayed when trying to block a user from a group fails because of an application error.
            $this->serverError(_("Database error blocking user from group."));
        }

        $this->returnToPrevious();
    }

    /**
     * If we reached this form without returnto arguments, default to
     * the top of the group's member list.
     *
     * @return string URL
     */
    function defaultReturnTo()
    {
        return common_local_url('groupmembers',
                                array('nickname' => $this->group->nickname));
    }

    function showScripts()
    {
        parent::showScripts();
        $this->autofocus('form_action-yes');
    }
}


code:
<?php
/**
 * StatusNet, 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   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/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  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupbyidAction extends Action
{
    /** group we're viewing. */
    var $group = null;

    /**
     * Is this page read-only?
     *
     * @return boolean true
     */
    function isReadOnly($args)
    {
        return true;
    }

    function prepare($args)
    {
        parent::prepare($args);

        $id = $this->arg('id');

        if (!$id) {
            // TRANS: Client error displayed referring to a group's permalink without providing a group ID.
            $this->clientError(_('No ID.'));
        }

        common_debug("Got ID $id");

        $this->group = User_group::getKV('id', $id);

        if (!$this->group) {
            // TRANS: Client error displayed referring to a group's permalink for a non-existing group ID.
            $this->clientError(_('No such group.'), 404);
        }

        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);
    }
}

license: mit


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Upload an avatar
 *
 * 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  Settings
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Zach Copley <zach@status.net>
 * @copyright 2008-2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

define('MAX_ORIGINAL', 480);

/**
 * Upload an avatar
 *
 * We use jCrop plugin for jQuery to crop the image after upload.
 *
 * @category Settings
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Zach Copley <zach@status.net>
 * @author   Sarven Capadisli <csarven@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GrouplogoAction extends GroupAction
{
    var $mode = null;
    var $imagefile = null;
    var $filename = null;
    var $msg = null;
    var $success = null;

    /**
     * Prepare to run
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        if (!common_logged_in()) {
            // TRANS: Client error displayed when trying to create a group while not logged in.
            $this->clientError(_('You must be logged in to create a group.'));
        }

        $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('grouplogo', $args), 301);
        }

        if (!$nickname) {
            // TRANS: Client error displayed when trying to change group logo settings without providing a nickname.
            $this->clientError(_('No nickname.'), 404);
        }

        $groupid = $this->trimmed('groupid');

        if ($groupid) {
            $this->group = User_group::getKV('id', $groupid);
        } else {
            $local = Local_group::getKV('nickname', $nickname);
            if ($local) {
                $this->group = User_group::getKV('id', $local->group_id);
            }
        }

        if (!$this->group) {
            // TRANS: Client error displayed when trying to update logo settings for a non-existing group.
            $this->clientError(_('No such group.'), 404);
        }

        $cur = common_current_user();

        if (!$cur->isAdmin($this->group)) {
            // TRANS: Client error displayed when trying to change group logo settings while not being a group admin.
            $this->clientError(_('You must be an admin to edit the group.'), 403);
        }

        return true;
    }

    protected function handle()
    {
        parent::handle();
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
        } else {
            $this->showForm();
        }
    }

    function showForm($msg = null, $success = false)
    {
        $this->msg     = $msg;
        $this->success = $success;

        $this->showPage();
    }

    /**
     * Title of the page
     *
     * @return string Title of the page
     */
    function title()
    {
        // TRANS: Title for group logo settings page.
        return _('Group logo');
    }

    /**
     * Instructions for use
     *
     * @return instructions for use
     */
    function getInstructions()
    {
        // TRANS: Instructions for group logo page.
        // TRANS: %s is the maximum file size for that site.
        return sprintf(_('You can upload a logo image for your group. The maximum file size is %s.'), ImageFile::maxFileSize());
    }

    /**
     * Content area of the page
     *
     * Shows a form for uploading an avatar.
     *
     * @return void
     */
    function showContent()
    {
        if ($this->mode == 'crop') {
            $this->showCropForm();
        } else {
            $this->showUploadForm();
        }
    }

    function showUploadForm()
    {
        $user = common_current_user();

        $profile = $user->getProfile();

        if (!$profile) {
            common_log_db_error($user, 'SELECT', __FILE__);
            // TRANS: Error message displayed when referring to a user without a profile.
            $this->serverError(_('User has no profile.'));
        }

        $original = $this->group->original_logo;

        $this->elementStart('form', array('enctype' => 'multipart/form-data',
                                          'method' => 'post',
                                          'id' => 'form_settings_avatar',
                                          'class' => 'form_settings',
                                          'action' =>
                                          common_local_url('grouplogo',
                                                           array('nickname' => $this->group->nickname))));
        $this->elementStart('fieldset');
        // TRANS: Group logo form legend.
        $this->element('legend', null, _('Group logo'));
        $this->hidden('token', common_session_token());

        $this->elementStart('ul', 'form_data');
        if ($original) {
            $this->elementStart('li', array('id' => 'avatar_original',
                                            'class' => 'avatar_view'));
            // TRANS: Uploaded original file in group logo form.
            $this->element('h2', null, _('Original'));
            $this->elementStart('div', array('id'=>'avatar_original_view'));
            $this->element('img', array('src' => $this->group->original_logo,
                                        'alt' => $this->group->nickname));
            $this->elementEnd('div');
            $this->elementEnd('li');
        }

        if ($this->group->homepage_logo) {
            $this->elementStart('li', array('id' => 'avatar_preview',
                                            'class' => 'avatar_view'));
            // TRANS: Header for preview of to be displayed group logo.
            $this->element('h2', null, _('Preview'));
            $this->elementStart('div', array('id'=>'avatar_preview_view'));
            $this->element('img', array('src' => $this->group->homepage_logo,
                                        'width' => AVATAR_PROFILE_SIZE,
                                        'height' => AVATAR_PROFILE_SIZE,
                                        'alt' => $this->group->nickname));
            $this->elementEnd('div');
            $this->elementEnd('li');
        }

        $this->elementStart('li', array ('id' => 'settings_attach'));
        $this->element('input', array('name' => 'MAX_FILE_SIZE',
                                      'type' => 'hidden',
                                      'id' => 'MAX_FILE_SIZE',
                                      'value' => ImageFile::maxFileSizeInt()));
        $this->element('input', array('name' => 'avatarfile',
                                      'type' => 'file',
                                      'id' => 'avatarfile'));
        $this->elementEnd('li');
        $this->elementEnd('ul');

        $this->elementStart('ul', 'form_actions');
        $this->elementStart('li');
        // TRANS: Submit button for uploading a group logo.
        $this->submit('upload', _('Upload'));
        $this->elementEnd('li');
        $this->elementEnd('ul');

        $this->elementEnd('fieldset');
        $this->elementEnd('form');
    }

    function showCropForm()
    {
        $this->elementStart('form', array('method' => 'post',
                                          'id' => 'form_settings_avatar',
                                          'class' => 'form_settings',
                                          'action' =>
                                          common_local_url('grouplogo',
                                                           array('nickname' => $this->group->nickname))));
        $this->elementStart('fieldset');
        // TRANS: Legend for group logo settings fieldset.
        $this->element('legend', null, _('Avatar settings'));
        $this->hidden('token', common_session_token());

        $this->elementStart('ul', 'form_data');

        $this->elementStart('li',
                            array('id' => 'avatar_original',
                                  'class' => 'avatar_view'));
        // TRANS: Header for originally uploaded file before a crop on the group logo page.
        $this->element('h2', null, _('Original'));
        $this->elementStart('div', array('id'=>'avatar_original_view'));
        $this->element('img', array('src' => Avatar::url($this->filedata['filename']),
                                    'width' => $this->filedata['width'],
                                    'height' => $this->filedata['height'],
                                    'alt' => $this->group->nickname));
        $this->elementEnd('div');
        $this->elementEnd('li');

        $this->elementStart('li',
                            array('id' => 'avatar_preview',
                                  'class' => 'avatar_view'));
        // TRANS: Header for the cropped group logo on the group logo page.
        $this->element('h2', null, _('Preview'));
        $this->elementStart('div', array('id'=>'avatar_preview_view'));
        $this->element('img', array('src' => Avatar::url($this->filedata['filename']),
                                    'width' => AVATAR_PROFILE_SIZE,
                                    'height' => AVATAR_PROFILE_SIZE,
                                    'alt' => $this->group->nickname));
        $this->elementEnd('div');

        foreach (array('avatar_crop_x', 'avatar_crop_y',
                       'avatar_crop_w', 'avatar_crop_h') as $crop_info) {
            $this->element('input', array('name' => $crop_info,
                                          'type' => 'hidden',
                                          'id' => $crop_info));
        }

        // TRANS: Button text for cropping an uploaded group logo.
        $this->submit('crop', _('Crop'));

        $this->elementEnd('li');
        $this->elementEnd('ul');
        $this->elementEnd('fieldset');
        $this->elementEnd('form');
    }

    /**
     * Handle a post
     *
     * We mux on the button name to figure out what the user actually wanted.
     *
     * @return void
     */
    function handlePost()
    {
        // CSRF protection

        $token = $this->trimmed('token');
        if (!$token || $token != common_session_token()) {
            // TRANS: Form validation error message.
            $this->show_form(_('There was a problem with your session token. '.
                               'Try again, please.'));
            return;
        }

        if ($this->arg('upload')) {
            $this->uploadLogo();
        } else if ($this->arg('crop')) {
            $this->cropLogo();
        } else {
            // TRANS: Form validation error message when an unsupported argument is used.
            $this->showForm(_('Unexpected form submission.'));
        }
    }

    /**
     * Handle an image upload
     *
     * Does all the magic for handling an image upload, and crops the
     * image by default.
     *
     * @return void
     */
    function uploadLogo()
    {
        try {
            $imagefile = ImageFile::fromUpload('avatarfile');
        } catch (Exception $e) {
            $this->showForm($e->getMessage());
            return;
        }

        $type = $imagefile->preferredType();
        $filename = Avatar::filename($this->group->id,
                                     image_type_to_extension($type),
                                     null,
                                     'group-temp-'.common_timestamp());

        $filepath = Avatar::path($filename);

        $imagefile->copyTo($filepath);

        $filedata = array('filename' => $filename,
                          'filepath' => $filepath,
                          'width' => $imagefile->width,
                          'height' => $imagefile->height,
                          'type' => $type);

        $_SESSION['FILEDATA'] = $filedata;

        $this->filedata = $filedata;

        $this->mode = 'crop';

        // TRANS: Form instructions on the group logo page.
        $this->showForm(_('Pick a square area of the image to be the logo.'),
                        true);
    }

    /**
     * Handle the results of jcrop.
     *
     * @return void
     */
    function cropLogo()
    {
        $filedata = $_SESSION['FILEDATA'];

        if (!$filedata) {
            // TRANS: Server error displayed trying to crop an uploaded group logo that is no longer present.
            $this->serverError(_('Lost our file data.'));
        }

        // If image is not being cropped assume pos & dimentions of original
        $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
        $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
        $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width'];
        $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height'];
        $size = min($dest_w, $dest_h);
        $size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size;

        $imagefile = new ImageFile($this->group->id, $filedata['filepath']);
        $filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);

        if ($this->group->setOriginal($filename)) {
            @unlink($filedata['filepath']);
            unset($_SESSION['FILEDATA']);
            $this->mode = 'upload';
            // TRANS: Form success message after updating a group logo.
            $this->showForm(_('Logo updated.'), true);
        } else {
            // TRANS: Form failure message after failing to update a group logo.
            $this->showForm(_('Failed updating logo.'));
        }
    }

    function showPageNotice()
    {
        if ($this->msg) {
            $this->element('div', ($this->success) ? 'success' : 'error',
                           $this->msg);
        } else {
            $inst   = $this->getInstructions();
            $output = common_markup_to_html($inst);

            $this->elementStart('div', 'instructions');
            $this->raw($output);
            $this->elementEnd('div');
        }
    }

    /**
     * Add the jCrop stylesheet
     *
     * @return void
     */
    function showStylesheets()
    {
        parent::showStylesheets();
        $this->cssLink('js/extlib/jquery-jcrop/css/jcrop.css','base','screen, projection, tv');
    }

    /**
     * Add the jCrop scripts
     *
     * @return void
     */
    function showScripts()
    {
        parent::showScripts();

        if ($this->mode == 'crop') {
            $this->script('extlib/jquery-jcrop/jcrop.js');
            $this->script('jcrop.go.js');
        }

        $this->autofocus('avatarfile');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * List of group members
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Group
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once(INSTALLDIR.'/lib/profilelist.php');
require_once INSTALLDIR.'/lib/publicgroupnav.php';

/**
 * List of group members
 *
 * @category Group
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupmembersAction extends GroupAction
{
    var $page = null;

    function isReadOnly($args)
    {
        return true;
    }

    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;

        return true;
    }

    function title()
    {
        if ($this->page == 1) {
            // TRANS: Title of the page showing group members.
            // TRANS: %s is the name of the group.
            return sprintf(_('%s group members'),
                           $this->group->nickname);
        } else {
            // TRANS: Title of the page showing group members.
            // TRANS: %1$s is the name of the group, %2$d is the page number of the members list.
            return sprintf(_('%1$s group members, page %2$d'),
                           $this->group->nickname,
                           $this->page);
        }
    }

    protected function handle()
    {
        parent::handle();
        $this->showPage();
    }

    function showPageNotice()
    {
        $this->element('p', 'instructions',
                       // TRANS: Page notice for group members page.
                       _('A list of the users in this group.'));
    }

    function showContent()
    {
        $offset = ($this->page-1) * PROFILES_PER_PAGE;
        $limit =  PROFILES_PER_PAGE + 1;

        $cnt = 0;

        $members = $this->group->getMembers($offset, $limit);

        if ($members) {
            $member_list = new GroupMemberList($members, $this->group, $this);
            $cnt = $member_list->show();
        }

        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
                          $this->page, 'groupmembers',
                          array('nickname' => $this->group->nickname));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * List of group members
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Group
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once(INSTALLDIR.'/lib/profilelist.php');
require_once INSTALLDIR.'/lib/publicgroupnav.php';

/**
 * List of group members
 *
 * @category Group
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupqueueAction extends GroupAction
{
    var $page = null;

    function isReadOnly($args)
    {
        return true;
    }

    // @todo FIXME: most of this belongs in a base class, sounds common to most group actions?
    protected function prepare(array $args=array())
    {
        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('groupqueue', $args), 301);
        }

        if (!$nickname) {
            // TRANS: Client error displayed when trying to view group members without providing a group nickname.
            $this->clientError(_('No nickname.'), 404);
        }

        $local = Local_group::getKV('nickname', $nickname);

        if (!$local) {
            // TRANS: Client error displayed when trying to view group members for a non-existing group.
            $this->clientError(_('No such group.'), 404);
        }

        $this->group = User_group::getKV('id', $local->group_id);

        if (!$this->group) {
            // TRANS: Client error displayed when trying to view group members for an object that is not a group.
            $this->clientError(_('No such group.'), 404);
        }

        $cur = common_current_user();
        if (!$cur || !$cur->isAdmin($this->group)) {
            // TRANS: Client error displayed when trying to approve group applicants without being a group administrator.
            $this->clientError(_('Only the group admin may approve users.'));
        }
        return true;
    }

    function title()
    {
        if ($this->page == 1) {
            // TRANS: Title of the first page showing pending group members still awaiting approval to join the group.
            // TRANS: %s is the name of the group.
            return sprintf(_('%s group members awaiting approval'),
                           $this->group->nickname);
        } else {
            // TRANS: Title of all but the first page showing pending group members still awaiting approval to join the group.
            // TRANS: %1$s is the name of the group, %2$d is the page number of the members list.
            return sprintf(_('%1$s group members awaiting approval, page %2$d'),
                           $this->group->nickname,
                           $this->page);
        }
    }

    protected function handle()
    {
        parent::handle();
        $this->showPage();
    }

    function showPageNotice()
    {
        $this->element('p', 'instructions',
                       // TRANS: Page notice for group members page.
                       _('A list of users awaiting approval to join this group.'));
    }

    function showContent()
    {
        $offset = ($this->page-1) * PROFILES_PER_PAGE;
        $limit =  PROFILES_PER_PAGE + 1;

        $cnt = 0;

        $members = $this->group->getRequests($offset, $limit);

        if ($members) {
            // @fixme change!
            $member_list = new GroupQueueList($members, $this->group, $this);
            $cnt = $member_list->show();
        }

        $members->free();

        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
                          $this->page, 'groupqueue',
                          array('nickname' => $this->group->nickname));
    }
}

// @todo FIXME: documentation missing.
class GroupQueueList extends GroupMemberList
{
    function newListItem($profile)
    {
        return new GroupQueueListItem($profile, $this->group, $this->action);
    }
}

// @todo FIXME: documentation missing.
class GroupQueueListItem extends GroupMemberListItem
{
    function showActions()
    {
        $this->startActions();
        if (Event::handle('StartProfileListItemActionElements', array($this))) {
            $this->showApproveButtons();
            Event::handle('EndProfileListItemActionElements', array($this));
        }
        $this->endActions();
    }

    function showApproveButtons()
    {
        $this->out->elementStart('li', 'entity_approval');
        $form = new ApproveGroupForm($this->out, $this->group, $this->profile);
        $form->show();
        $this->out->elementEnd('li');
    }
}


code:
<?php
/**
 * StatusNet, 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   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/rssaction.php';

define('MEMBERS_PER_SECTION', 27);

/**
 * Group RSS feed
 *
 * @category Group
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class groupRssAction extends Rss10Action
{
    /** group we're viewing. */
    var $group = null;

    /**
     * Is this page read-only?
     *
     * @return boolean true
     */
    function isReadOnly($args)
    {
        return true;
    }

    /**
     * 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);

        $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);
            common_redirect(common_local_url('showgroup', $args), 301);
        }

        if (!$nickname) {
            // TRANS: Client error displayed when requesting a group RSS feed without providing a group nickname.
            $this->clientError(_('No nickname.'), 404);
        }

        $local = Local_group::getKV('nickname', $nickname);

        if (!$local) {
            // TRANS: Client error displayed when requesting a group RSS feed for group that does not exist.
            $this->clientError(_('No such group.'), 404);
        }

        $this->group = User_group::getKV('id', $local->group_id);

        if (!$this->group) {
            // TRANS: Client error displayed when requesting a group RSS feed for an object that is not a group.
            $this->clientError(_('No such group.'), 404);
        }

        $this->notices = $this->getNotices($this->limit);
        return true;
    }

    function getNotices($limit=0)
    {
        $group = $this->group;

        if (is_null($group)) {
            return null;
        }

        $notices = array();
        $notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);

        while ($notice->fetch()) {
            $notices[] = clone($notice);
        }

        return $notices;
    }

    function getChannel()
    {
        $group = $this->group;
        $c = array('url' => common_local_url('grouprss',
                                             array('nickname' =>
                                                   $group->nickname)),
                   // TRANS: Message is used as link title. %s is a user nickname.
                   'title' => sprintf(_('%s timeline'), $group->nickname),
                   'link' => common_local_url('showgroup', array('nickname' => $group->nickname)),
                   // TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name.
                   'description' => sprintf(_('Updates from members of %1$s on %2$s!'),
                                            $group->nickname, common_config('site', 'name')));
        return $c;
    }

    function getImage()
    {
        return $this->group->homepage_logo;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Latest groups information
 *
 * 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  Personal
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/grouplist.php';

/**
 * Latest groups
 *
 * Show the latest groups on the site
 *
 * @category Personal
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class GroupsAction extends Action
{
    var $page = null;
    var $profile = null;

    function isReadOnly($args)
    {
        return true;
    }

    function title()
    {
        if ($this->page == 1) {
            // TRANS: Title for first page of the groups list.
            return _m('TITLE',"Groups");
        } else {
            // TRANS: Title for all but the first page of the groups list.
            // TRANS: %d is the page number.
            return sprintf(_m('TITLE',"Groups, page %d"), $this->page);
        }
    }

    function prepare($args)
    {
        parent::prepare($args);
        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
        return true;
    }

    function handle($args)
    {
        parent::handle($args);
        $this->showPage();
    }

    function showPageNotice()
    {
        $notice =
          // TRANS: Page notice of group list. %%%%site.name%%%% is the StatusNet site name,
          // TRANS: %%%%action.groupsearch%%%% and %%%%action.newgroup%%%% are URLs. Do not change them.
          // TRANS: This message contains Markdown links in the form [link text](link).
          sprintf(_('%%%%site.name%%%% groups let you find and talk with ' .
                    'people of similar interests. After you join a group ' .
                    'you can send messages to all other members using the ' .
                    'syntax "!groupname". Don\'t see a group you like? Try ' .
                    '[searching for one](%%%%action.groupsearch%%%%) or ' .
                    '[start your own](%%%%action.newgroup%%%%)!'));
        $this->elementStart('div', 'instructions');
        $this->raw(common_markup_to_html($notice));
        $this->elementEnd('div');
    }

    function showContent()
    {
        if (common_logged_in()) {
            $this->elementStart('p', array('id' => 'new_group'));
            $this->element('a', array('href' => common_local_url('newgroup'),
                                      'class' => 'more'),
                           // TRANS: Link to create a new group on the group list page.
                           _('Create a new group'));
            $this->elementEnd('p');
        }

        $offset = ($this->page-1) * GROUPS_PER_PAGE;
        $limit  = GROUPS_PER_PAGE + 1;

        $qry = 'SELECT user_group.* '.
          'from user_group join local_group on user_group.id = local_group.group_id '.
          'order by user_group.created desc '.
          'limit ' . $limit . ' offset ' . $offset;

        $groups = new User_group();

        $cnt = 0;

        $groups->query($qry);

        $gl = new GroupList($groups, null, $this);
        $cnt = $gl->show();

        $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
                          $this->page, 'groups');
    }

    function showSections()
    {
        $gbp = new GroupsByPostsSection($this);
        $gbp->show();
        $gbm = new GroupsByMembersSection($this);
        $gbm->show();
    }
}


code:
<?php
/**
 * Group search action class.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <millette@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

//require_once INSTALLDIR.'/lib/searchaction.php';
//require_once INSTALLDIR.'/lib/profilelist.php';

/**
 * Group search action class.
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <millette@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class GroupsearchAction extends SearchAction
{
    function getInstructions()
    {
        // TRANS: Instructions for page where groups can be searched. %%site.name%% is the name of the StatusNet site.
        return _('Search for groups on %%site.name%% by their name, location, or description. ' .
                  'Separate the terms by spaces; they must be 3 characters or more.');
    }

    function title()
    {
        // TRANS: Title for page where groups can be searched.
        return _('Group search');
    }

    function showResults($q, $page)
    {
        $user_group = new User_group;
        $user_group->limit((($page-1)*GROUPS_PER_PAGE), GROUPS_PER_PAGE + 1);
        $wheres = array('nickname', 'fullname', 'homepage', 'description', 'location');
        foreach ($wheres as $where) {
            $where_q = "$where like '%" . trim($user_group->escape($q), '\'') . '%\'';
            $user_group->whereAdd($where_q, 'OR');
        }
        $cnt = $user_group->find();
        if ($cnt > 0) {
            $terms = preg_split('/[\s,]+/', $q);
            $results = new GroupSearchResults($user_group, $terms, $this);
            $results->show();
            $user_group->free();
            $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE,
                          $page, 'groupsearch', array('q' => $q));
        } else {
            // TRANS: Text on page where groups can be searched if no results were found for a query.
            $this->element('p', 'error', _('No results.'));
            $this->searchSuggestions($q);
            if (common_logged_in()) {
                // TRANS: Additional text on page where groups can be searched if no results were found for a query for a logged in user.
                // TRANS: This message contains Markdown links in the form [link text](link).
                $message = _('If you cannot find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.');
            }
            else {
                // TRANS: Additional text on page where groups can be searched if no results were found for a query for a not logged in user.
                // TRANS: This message contains Markdown links in the form [link text](link).
                $message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!');
            }
            $this->elementStart('div', 'guide');
            $this->raw(common_markup_to_html($message));
            $this->elementEnd('div');
            $user_group->free();
        }
    }

    function showScripts()
    {
        parent::showScripts();
        $this->autofocus('q');
    }
}

class GroupSearchResults extends GroupList
{
    var $terms = null;
    var $pattern = null;

    function __construct($user_group, $terms, $action)
    {
        parent::__construct($user_group, null, $action);
        $this->terms = array_map('preg_quote',
                                 array_map('htmlspecialchars', $terms));
        $this->pattern = '/('.implode('|',$terms).')/i';
    }

    function highlight($text)
    {
        return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
    }
}


code:
<?php
/**
 * Block a user from a group action class.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008, 2009, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Unblock a user from a group
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
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()) {
            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
            $this->clientError(_('Not logged in.'));
        }
        $token = $this->trimmed('token');
        if (empty($token) || $token != common_session_token()) {
            // TRANS: Client error displayed when the session token does not match or is not given.
            $this->clientError(_('There was a problem with your session token. Try again, please.'));
        }
        $id = $this->trimmed('unblockto');
        if (empty($id)) {
            // TRANS: Client error displayed when trying to unblock a user from a group without providing a profile.
            $this->clientError(_('No profile specified.'));
        }
        $this->profile = Profile::getKV('id', $id);
        if (empty($this->profile)) {
            // TRANS: Client error displayed when trying to unblock a user from a group without providing an existing profile.
            $this->clientError(_('No profile with that ID.'));
        }
        $group_id = $this->trimmed('unblockgroup');
        if (empty($group_id)) {
            // TRANS: Client error displayed when trying to unblock a user from a group without providing a group.
            $this->clientError(_('No group specified.'));
        }
        $this->group = User_group::getKV('id', $group_id);
        if (empty($this->group)) {
            // TRANS: Client error displayed when trying to unblock a user from a non-existing group.
            $this->clientError(_('No such group.'));
        }
        $user = common_current_user();
        if (!$user->isAdmin($this->group)) {
            // TRANS: Client error displayed when trying to unblock a user from a group without being an administrator for the group.
            $this->clientError(_('Only an admin can unblock group members.'), 401);
        }
        if (!Group_block::isBlocked($this->group, $this->profile)) {
            // TRANS: Client error displayed when trying to unblock a non-blocked user from a group.
            $this->clientError(_('User is not blocked from group.'));
        }
        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) {
            // TRANS: Server error displayed when unblocking a user from a group fails because of an unknown error.
            $this->serverError(_('Error removing the block.'));
        }

        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);
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Settings for Jabber/XMPP integration
 *
 * 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  Settings
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Settings for Jabber/XMPP integration
 *
 * @category Settings
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      SettingsAction
 */

class ImsettingsAction extends SettingsAction
{
    /**
     * Title of the page
     *
     * @return string Title of the page
     */
    function title()
    {
        // TRANS: Title for Instant Messaging settings.
        return _('IM settings');
    }

    /**
     * Instructions for use
     *
     * @return instructions for use
     */
    function getInstructions()
    {
        // TRANS: Instant messaging settings page instructions.
        // TRANS: [instant messages] is link text, "(%%doc.im%%)" is the link.
        // TRANS: the order and formatting of link text and link should remain unchanged.
        return _('You can send and receive notices through '.
                 '[instant messaging](%%doc.im%%). '.
                 'Configure your addresses and settings below.');
    }

    /**
     * Content area of the page
     *
     * We make different sections of the form for the different kinds of
     * functions, and have submit buttons with different names. These
     * are muxed by handlePost() to see what the user really wants to do.
     *
     * @return void
     */
    function showContent()
    {
        $transports = array();
        Event::handle('GetImTransports', array(&$transports));
        if (! $transports) {
            $this->element('div', array('class' => 'error'),
                           // TRANS: Message given in the IM settings if IM is not enabled on the site.
                           _('IM is not available.'));
            return;
        }

        $user = common_current_user();

        $user_im_prefs_by_transport = array();

        foreach($transports as $transport=>$transport_info)
        {
            $this->elementStart('form', array('method' => 'post',
                                              'id' => 'form_settings_im',
                                              'class' => 'form_settings',
                                              'action' =>
                                              common_local_url('imsettings')));
            $this->elementStart('fieldset', array('id' => 'settings_im_address'));
            // TRANS: Form legend for IM settings form.
            $this->element('legend', null, $transport_info['display']);
            $this->hidden('token', common_session_token());
            $this->hidden('transport', $transport);

            if ($user_im_prefs = User_im_prefs::pkeyGet( array('transport' => $transport, 'user_id' => $user->id) )) {
                $user_im_prefs_by_transport[$transport] = $user_im_prefs;
                $this->element('p', 'form_confirmed', $user_im_prefs->screenname);
                $this->element('p', 'form_note',
                               // TRANS: Form note in IM settings form. %s is the type of IM address that was confirmed.
                               sprintf(_('Current confirmed %s address.'),$transport_info['display']));
                $this->hidden('screenname', $user_im_prefs->screenname);
                // TRANS: Button label to remove a confirmed IM address.
                $this->submit('remove', _m('BUTTON','Remove'));
            } else {
                $confirm = $this->getConfirmation($transport);
                if ($confirm) {
                    $this->element('p', 'form_unconfirmed', $confirm->address);
                    // TRANS: Form note in IM settings form.
                    $this->element('p', 'form_note',
                                   // TRANS: Form note in IM settings form.
                                   // TRANS: %s is the IM service name, %2$s is the IM address set.
                                   sprintf(_('Awaiting confirmation on this address. '.
                                             'Check your %1$s account for a '.
                                             'message with further instructions. '.
                                             '(Did you add %2$s to your buddy list?)'),
                                             $transport_info['display'],
                                             $transport_info['daemonScreenname']));
                    $this->hidden('screenname', $confirm->address);
                    // TRANS: Button label to cancel an IM address confirmation procedure.
                    $this->submit('cancel', _m('BUTTON','Cancel'));
                } else {
                    $this->elementStart('ul', 'form_data');
                    $this->elementStart('li');
                    // TRANS: Field label for IM address.
                    $this->input('screenname', _('IM address'),
                                 ($this->arg('screenname')) ? $this->arg('screenname') : null,
                                 // TRANS: Field title for IM address. %s is the IM service name.
                                 sprintf(_('%s screenname.'),
                                         $transport_info['display']));
                    $this->elementEnd('li');
                    $this->elementEnd('ul');
                    // TRANS: Button label for adding an IM address in IM settings form.
                    $this->submit('add', _m('BUTTON','Add'));
                }
            }
            $this->elementEnd('fieldset');
            $this->elementEnd('form');
        }

        if($user_im_prefs_by_transport)
        {
            $this->elementStart('form', array('method' => 'post',
                                              'id' => 'form_settings_im',
                                              'class' => 'form_settings',
                                              'action' =>
                                              common_local_url('imsettings')));
            $this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
            // TRANS: Header for IM preferences form.
            $this->element('legend', null, _('IM Preferences'));
            $this->hidden('token', common_session_token());
            $this->elementStart('table');
            $this->elementStart('tr');
            foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
            {
                $this->element('th', null, $transports[$transport]['display']);
            }
            $this->elementEnd('tr');
            $preferences = array(
                // TRANS: Checkbox label in IM preferences form.
                array('name'=>'notify', 'description'=>_('Send me notices')),
                // TRANS: Checkbox label in IM preferences form.
                array('name'=>'updatefrompresence', 'description'=>_('Post a notice when my status changes.')),
                // TRANS: Checkbox label in IM preferences form.
                array('name'=>'replies', 'description'=>_('Send me replies '.
                              'from people I\'m not subscribed to.')),
                // TRANS: Checkbox label in IM preferences form.
                array('name'=>'microid', 'description'=>_('Publish a MicroID'))
            );
            foreach($preferences as $preference)
            {
                $this->elementStart('tr');
                foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
                {
                    $preference_name = $preference['name'];
                    $this->elementStart('td');
                    $this->checkbox($transport . '_' . $preference['name'],
                                $preference['description'],
                                $user_im_prefs->$preference_name);
                    $this->elementEnd('td');
                }
                $this->elementEnd('tr');
            }
            $this->elementEnd('table');
            // TRANS: Button label to save IM preferences.
            $this->submit('save', _m('BUTTON','Save'));
            $this->elementEnd('fieldset');
            $this->elementEnd('form');
        }
    }

    /**
     * Get a confirmation code for this user
     *
     * @return Confirm_address address object for this user
     */
    function getConfirmation($transport)
    {
        $user = common_current_user();

        $confirm = new Confirm_address();

        $confirm->user_id      = $user->id;
        $confirm->address_type = $transport;

        if ($confirm->find(true)) {
            return $confirm;
        } else {
            return null;
        }
    }

    /**
     * Handle posts to this form
     *
     * Based on the button that was pressed, muxes out to other functions
     * to do the actual task requested.
     *
     * All sub-functions reload the form with a message -- success or failure.
     *
     * @return void
     */
    function handlePost()
    {
        // CSRF protection
        $token = $this->trimmed('token');
        if (!$token || $token != common_session_token()) {
            // TRANS: Client error displayed when the session token does not match or is not given.
            $this->showForm(_('There was a problem with your session token. '.
                              'Try again, please.'));
            return;
        }

        if ($this->arg('save')) {
            $this->savePreferences();
        } else if ($this->arg('add')) {
            $this->addAddress();
        } else if ($this->arg('cancel')) {
            $this->cancelConfirmation();
        } else if ($this->arg('remove')) {
            $this->removeAddress();
        } else {
            // TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
            $this->showForm(_('Unexpected form submission.'));
        }
    }

    /**
     * Save user's Jabber preferences
     *
     * These are the checkboxes at the bottom of the page. They're used to
     * set different settings
     *
     * @return void
     */
    function savePreferences()
    {
        $user = common_current_user();

        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->query('BEGIN');
        $user_im_prefs->user_id = $user->id;
        if($user_im_prefs->find() && $user_im_prefs->fetch())
        {
            $preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
            do
            {
                $original = clone($user_im_prefs);
                $new = clone($user_im_prefs);
                foreach($preferences as $preference)
                {
                    $new->$preference = $this->boolean($new->transport . '_' . $preference);
                }
                $result = $new->update($original);

                if ($result === false) {
                    common_log_db_error($user, 'UPDATE', __FILE__);
                    // TRANS: Server error thrown on database error updating IM preferences.
                    $this->serverError(_('Could not update IM preferences.'));
                }
            }while($user_im_prefs->fetch());
        }
        $user_im_prefs->query('COMMIT');
        // TRANS: Confirmation message for successful IM preferences save.
        $this->showForm(_('Preferences saved.'), true);
    }

    /**
     * Sends a confirmation to the address given
     *
     * Stores a confirmation record and sends out a
     * message with the confirmation info.
     *
     * @return void
     */
    function addAddress()
    {
        $user = common_current_user();

        $screenname = $this->trimmed('screenname');
        $transport = $this->trimmed('transport');

        // Some validation

        if (!$screenname) {
            // TRANS: Message given saving IM address without having provided one.
            $this->showForm(_('No screenname.'));
            return;
        }

        if (!$transport) {
            // TRANS: Form validation error when no transport is available setting an IM address.
            $this->showForm(_('No transport.'));
            return;
        }

        Event::handle('NormalizeImScreenname', array($transport, &$screenname));

        if (!$screenname) {
            // TRANS: Message given saving IM address that cannot be normalised.
            $this->showForm(_('Cannot normalize that screenname.'));
            return;
        }
        $valid = false;
        Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid));
        if (!$valid) {
            // TRANS: Message given saving IM address that not valid.
            $this->showForm(_('Not a valid screenname.'));
            return;
        } else if ($this->screennameExists($transport, $screenname)) {
            // TRANS: Message given saving IM address that is already set for another user.
            $this->showForm(_('Screenname already belongs to another user.'));
            return;
        }

        $confirm = new Confirm_address();

        $confirm->address      = $screenname;
        $confirm->address_type = $transport;
        $confirm->user_id      = $user->id;
        $confirm->code         = common_confirmation_code(64);
        $confirm->sent         = common_sql_now();
        $confirm->claimed      = common_sql_now();

        $result = $confirm->insert();

        if ($result === false) {
            common_log_db_error($confirm, 'INSERT', __FILE__);
            // TRANS: Server error thrown on database error adding Instant Messaging confirmation code.
            $this->serverError(_('Could not insert confirmation code.'));
        }

        Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $user));

        // TRANS: Message given saving valid IM address that is to be confirmed.
        $msg = _('A confirmation code was sent '.
                         'to the IM address you added.');

        $this->showForm($msg, true);
    }

    /**
     * Cancel a confirmation
     *
     * If a confirmation exists, cancel it.
     *
     * @return void
     */
    function cancelConfirmation()
    {
        $screenname = $this->trimmed('screenname');
        $transport = $this->trimmed('transport');

        $confirm = $this->getConfirmation($transport);

        if (!$confirm) {
            // TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
            $this->showForm(_('No pending confirmation to cancel.'));
            return;
        }
        if ($confirm->address != $screenname) {
            // TRANS: Message given canceling IM address confirmation for the wrong IM address.
            $this->showForm(_('That is the wrong IM address.'));
            return;
        }

        $result = $confirm->delete();

        if (!$result) {
            common_log_db_error($confirm, 'DELETE', __FILE__);
            // TRANS: Server error thrown on database error canceling IM address confirmation.
            $this->serverError(_('Could not delete confirmation.'));
        }

        // TRANS: Message given after successfully canceling IM address confirmation.
        $this->showForm(_('IM confirmation cancelled.'), true);
    }

    /**
     * Remove an address
     *
     * If the user has a confirmed address, remove it.
     *
     * @return void
     */
    function removeAddress()
    {
        $user = common_current_user();

        $screenname = $this->trimmed('screenname');
        $transport = $this->trimmed('transport');

        // Maybe an old tab open...?

        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->user_id = $user->id;
        if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) {
            // TRANS: Message given trying to remove an IM address that is not
            // TRANS: registered for the active user.
            $this->showForm(_('That is not your screenname.'));
            return;
        }

        $result = $user_im_prefs->delete();

        if (!$result) {
            common_log_db_error($user, 'UPDATE', __FILE__);
            // TRANS: Server error thrown on database error removing a registered IM address.
            $this->serverError(_('Could not update user IM preferences.'));
        }

        // XXX: unsubscribe to the old address

        // TRANS: Message given after successfully removing a registered Instant Messaging address.
        $this->showForm(_('The IM address was removed.'), true);
    }

    /**
     * Does this screenname exist?
     *
     * Checks if we already have another user with this address.
     *
     * @param string $transport Transport to check
     * @param string $screenname Screenname to check
     *
     * @return boolean whether the screenname exists
     */

    function screennameExists($transport, $screenname)
    {
        $user = common_current_user();

        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->transport = $transport;
        $user_im_prefs->screenname = $screenname;
        if($user_im_prefs->find() && $user_im_prefs->fetch()){
            return true;
        }else{
            return false;
        }
    }
}


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008-2011, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('GNUSOCIAL')) { exit(1); }

// @todo XXX: Add documentation.
class InviteAction extends Action
{
    var $mode = null;
    var $error = null;
    var $already = null;
    var $subbed = null;
    var $sent = null;

    function showNoticeForm()
    {
        return;
    }

    function isReadOnly($args)
    {
        return false;
    }

    function handle($args)
    {
        parent::handle($args);
        if (!common_config('invite', 'enabled')) {
            // TRANS: Client error displayed when trying to sent invites while they have been disabled.
            $this->clientError(_('Invites have been disabled.'));
        } else if (!common_logged_in()) {
            // TRANS: Client error displayed when trying to sent invites while not logged in.
            // TRANS: %s is the StatusNet site name.
            $this->clientError(sprintf(_('You must be logged in to invite other users to use %s.'),
                                        common_config('site', 'name')));
            return;
        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->sendInvitations();
        } else {
            $this->showForm();
        }
    }

    function sendInvitations()
    {
        if (Event::handle('StartSendInvitations', array(&$this))) {
            // CSRF protection
            $token = $this->trimmed('token');
            if (!$token || $token != common_session_token()) {
                // TRANS: Client error displayed when the session token does not match or is not given.
                $this->showForm(_('There was a problem with your session token. Try again, please.'));
                return;
            }

            $user = common_current_user();
            $profile = $user->getProfile();

            $bestname = $profile->getBestName();
            $sitename = common_config('site', 'name');
            $personal = $this->trimmed('personal');

            $addresses = explode("\n", $this->trimmed('addresses'));
            foreach ($addresses as $email) {
                $email = trim($email);
                $valid = null;

                try {

                    if (Event::handle('StartValidateUserEmail', array(null, $email, &$valid))) {
                        $valid = Validate::email($email, common_config('email', 'check_domain'));
                        Event::handle('EndValidateUserEmail', array(null, $email, &$valid));
                    }

                    if ($valid) {
                        if (Event::handle('StartValidateEmailInvite', array($user, $email, &$valid))) {
                            $valid = true;
                            Event::handle('EndValidateEmailInvite', array($user, $email, &$valid));
                        }
                    }

                    if (!$valid) {
                        // TRANS: Form validation message when providing an e-mail address that does not validate.
                        // TRANS: %s is an invalid e-mail address.
                        $this->showForm(sprintf(_('Invalid email address: %s.'), $email));
                        return;
                    }
                } catch (ClientException $e) {
                    $this->showForm($e->getMessage());
                    return;
                }
            }

            $this->already = array();
            $this->subbed = array();

            foreach ($addresses as $email) {
                $email = common_canonical_email($email);
                try {
                    // If this user is already registered, subscribe to it!
                    $other = Profile::getByEmail($email);
                    if ($user->isSubscribed($other)) {
                        $this->already[] = $other;
                    } else {
                        try {
                            Subscription::start($profile, $other);
                            $this->subbed[] = $other;
                        } catch (Exception $e) {
                            // subscription failed, but keep working
                            common_debug('Invitation-based subscription failed: '.$e->getMessage());
                        }
                    }
                } catch (NoSuchUserException $e) {
                    // If email was not known, let's send an invite!
                    $this->sent[] = $email;
                    $this->sendInvitation($email, $user, $personal);
                }
            }

            $this->mode = 'sent';

            $this->showPage();
            Event::handle('EndSendInvitations', array($this));
        }
    }

    function showScripts()
    {
        parent::showScripts();
        $this->autofocus('addresses');
    }

    function title()
    {
        if ($this->mode == 'sent') {
            // TRANS: Page title when invitations have been sent.
            return _('Invitations sent');
        } else {
            // TRANS: Page title when inviting potential users.
            return _('Invite new users');
        }
    }

    function showContent()
    {
        if ($this->mode == 'sent') {
            $this->showInvitationSuccess();
        } else {
            $this->showInviteForm();
        }
    }

    function showInvitationSuccess()
    {
        if (Event::handle('StartShowInvitationSuccess', array($this))) {
            if ($this->already) {
                // TRANS: Message displayed inviting users to use a StatusNet site while the inviting user
                // TRANS: is already subscribed to one or more users with the given e-mail address(es).
                // TRANS: Plural form is based on the number of reported already subscribed e-mail addresses.
                // TRANS: Followed by a bullet list.
                $this->element('p', null, _m('You are already subscribed to this user:',
                                             'You are already subscribed to these users:',
                                             count($this->already)));
                $this->elementStart('ul');
                foreach ($this->already as $other) {
                    // TRANS: Used as list item for already subscribed users (%1$s is nickname, %2$s is e-mail address).
                    $this->element('li', null, sprintf(_m('INVITE','%1$s (%2$s)'), $other->nickname, $other->email));
                }
                $this->elementEnd('ul');
            }
            if ($this->subbed) {
                // TRANS: Message displayed inviting users to use a StatusNet site while the invited user
                // TRANS: already uses a this StatusNet site. Plural form is based on the number of
                // TRANS: reported already present people. Followed by a bullet list.
                $this->element('p', null, _m('This person is already a user and you were automatically subscribed:',
                                             'These people are already users and you were automatically subscribed to them:',
                                             count($this->subbed)));
                $this->elementStart('ul');
                foreach ($this->subbed as $other) {
                    // TRANS: Used as list item for already registered people (%1$s is nickname, %2$s is e-mail address).
                    $this->element('li', null, sprintf(_m('INVITE','%1$s (%2$s)'), $other->nickname, $other->email));
                }
                $this->elementEnd('ul');
            }
            if ($this->sent) {
                // TRANS: Message displayed inviting users to use a StatusNet site. Plural form is
                // TRANS: based on the number of invitations sent. Followed by a bullet list of
                // TRANS: e-mail addresses to which invitations were sent.
                $this->element('p', null, _m('Invitation sent to the following person:',
                                             'Invitations sent to the following people:',
                                             count($this->sent)));
                $this->elementStart('ul');
                foreach ($this->sent as $other) {
                    $this->element('li', null, $other);
                }
                $this->elementEnd('ul');
                // TRANS: Generic message displayed after sending out one or more invitations to
                // TRANS: people to join a StatusNet site.
                $this->element('p', null, _('You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!'));
            }
            Event::handle('EndShowInvitationSuccess', array($this));
        }
    }

    function showPageNotice()
    {
        if ($this->mode != 'sent') {
            if ($this->error) {
                $this->element('p', 'error', $this->error);
            } else {
                $this->elementStart('div', 'instructions');
                $this->element('p', null,
                               // TRANS: Form instructions.
                               _('Use this form to invite your friends and colleagues to use this service.'));
                $this->elementEnd('div');
            }
        }
    }

    function showForm($error=null)
    {
        $this->mode = 'form';
        $this->error = $error;
        $this->showPage();
    }

    function showInviteForm()
    {
        if (Event::handle('StartShowInviteForm', array($this))) {
            $form = new InviteForm($this);
            $form->show();
            Event::handle('EndShowInviteForm', array($this));
        }
    }

    function sendInvitation($email, $user, $personal)
    {
        $profile = $user->getProfile();
        $bestname = $profile->getBestName();

        $sitename = common_config('site', 'name');

        $invite = new Invitation();

        $invite->address = $email;
        $invite->address_type = 'email';
        $invite->code = common_confirmation_code(128);
        $invite->user_id = $user->id;
        $invite->created = common_sql_now();

        if (!$invite->insert()) {
            common_log_db_error($invite, 'INSERT', __FILE__);
            return false;
        }

        $confirmUrl = common_local_url('register', array('code' => $invite->code));

        $recipients = array($email);

        $headers['From'] = mail_notify_from();
        $headers['To'] = trim($email);
        $headers['Content-Type'] = 'text/html; charset=UTF-8';

        // TRANS: Subject for invitation email. Note that 'them' is correct as a gender-neutral
        // TRANS: singular 3rd-person pronoun in English. %1$s is the inviting user, $2$s is
        // TRANS: the StatusNet sitename.
        $headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename);

        $title = (empty($personal)) ? 'invite' : 'invitepersonal';

        // @todo FIXME: i18n issue.
        $inviteTemplate = DocFile::forTitle($title, DocFile::mailPaths());

        $body = $inviteTemplate->toHTML(array('inviter' => $bestname,
                                              'inviterurl' => $profile->profileurl,
                                              'confirmurl' => $confirmUrl,
                                              'personal' => $personal));

        common_debug('Confirm URL is ' . common_local_url('register', array('code' => $invite->code)));

        mail_send($recipients, $headers, $body);
    }
}


code:
<?php
/**
 * StatusNet, 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   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) { 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  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class JoingroupAction extends Action
{
    var $group = null;

    /**
     * Prepare to run
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        if (!common_logged_in()) {
            // TRANS: Client error displayed when trying to join a group while not logged in.
            $this->clientError(_('You must be logged in to join a group.'));
        }

        $nickname_arg = $this->trimmed('nickname');
        $id = intval($this->arg('id'));
        if ($id) {
            $this->group = User_group::getKV('id', $id);
        } else if ($nickname_arg) {
            $nickname = common_canonical_nickname($nickname_arg);

            // Permanent redirect on non-canonical nickname

            if ($nickname_arg != $nickname) {
                $args = array('nickname' => $nickname);
                common_redirect(common_local_url('leavegroup', $args), 301);
            }

            $local = Local_group::getKV('nickname', $nickname);

            if (!$local) {
                // TRANS: Client error displayed when trying to join a non-local group.
                $this->clientError(_('No such group.'), 404);
            }

            $this->group = User_group::getKV('id', $local->group_id);
        } else {
            // TRANS: Client error displayed when trying to join a group without providing a group name or group ID.
            $this->clientError(_('No nickname or ID.'), 404);
        }

        if (!$this->group) {
            // TRANS: Client error displayed when trying to join a non-existing group.
            $this->clientError(_('No such group.'), 404);
        }

        if ($this->scoped->isMember($this->group)) {
            // TRANS: Client error displayed when trying to join a group while already a member.
            $this->clientError(_('You are already a member of that group.'), 403);
        }

        if (Group_block::isBlocked($this->group, $this->scoped)) {
            // TRANS: Client error displayed when trying to join a group while being blocked form joining it.
            $this->clientError(_('You have been blocked from that group by the admin.'), 403);
        }

        return true;
    }

    /**
     * Handle the request
     *
     * On POST, add the current user to the group
     *
     * @return void
     */
    protected function handle()
    {
        parent::handle();

        try {
            $result = $this->scoped->joinGroup($this->group);
        } catch (Exception $e) {
        	common_log(LOG_ERR, sprintf("Couldn't join user %s to group %s: '%s'",
        								$this->scoped->nickname,
        								$this->group->nickname,
        								$e->getMessage()));
            // TRANS: Server error displayed when joining a group failed in the database.
            // TRANS: %1$s is the joining user's nickname, $2$s is the group nickname for which the join failed.
            $this->serverError(sprintf(_('Could not join user %1$s to group %2$s.'),
                                       $this->scoped->nickname, $this->group->nickname));
            return;
        }

        if ($this->boolean('ajax')) {
            $this->startHTML('text/xml;charset=utf-8');
            $this->elementStart('head');
            // TRANS: Title for join group page after joining.
            $this->element('title', null, sprintf(_m('TITLE','%1$s joined group %2$s'),
                                                  $this->scoped->nickname,
                                                  $this->group->nickname));
            $this->elementEnd('head');
            $this->elementStart('body');

            if ($result instanceof Group_member) {
                $form = new LeaveForm($this, $this->group);
            } else if ($result instanceof Group_join_queue) {
                $form = new CancelGroupForm($this, $this->group);
            } else {
                // wtf?
                // TRANS: Exception thrown when there is an unknown error joining a group.
                throw new Exception(_("Unknown error joining group."));
            }
            $form->show();
            $this->elementEnd('body');
            $this->endHTML();
        } else {
            common_redirect(common_local_url('groupmembers', array('nickname' => $this->group->nickname)), 303);
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Leave a group
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Group
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) { exit(1); }

/**
 * Leave a group
 *
 * This is the action for leaving a group. It works more or less like the subscribe action
 * for users.
 *
 * @category Group
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class LeavegroupAction extends Action
{
    var $group = null;

    /**
     * Prepare to run
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        if (!common_logged_in()) {
            // TRANS: Client error displayed when trying to leave a group while not logged in.
            $this->clientError(_('You must be logged in to leave a group.'));
        }

        $nickname_arg = $this->trimmed('nickname');
        $id = intval($this->arg('id'));
        if ($id) {
            $this->group = User_group::getKV('id', $id);
        } else if ($nickname_arg) {
            $nickname = common_canonical_nickname($nickname_arg);

            // Permanent redirect on non-canonical nickname

            if ($nickname_arg != $nickname) {
                $args = array('nickname' => $nickname);
                common_redirect(common_local_url('leavegroup', $args), 301);
            }

            $local = Local_group::getKV('nickname', $nickname);

            if (!$local) {
                // TRANS: Client error displayed when trying to leave a non-local group.
                $this->clientError(_('No such group.'), 404);
            }

            $this->group = User_group::getKV('id', $local->group_id);
        } else {
            // TRANS: Client error displayed when trying to leave a group without providing a group name or group ID.
            $this->clientError(_('No nickname or ID.'), 404);
        }

        if (!$this->group) {
            // TRANS: Client error displayed when trying to leave a non-existing group.
            $this->clientError(_('No such group.'), 404);
        }

        if (!$this->scoped->isMember($this->group)) {
            // TRANS: Client error displayed when trying to join a group while already a member.
            $this->clientError(_('You are not a member of that group.'), 403);
        }

        return true;
    }

    /**
     * Handle the request
     *
     * On POST, add the current user to the group
     *
     * @return void
     */
    protected function handle()
    {
        parent::handle();

        try {
            $this->scoped->leaveGroup($this->group);
        } catch (Exception $e) {
            common_log(LOG_ERR, "Error when {$this->scoped->nickname} tried to leave {$this->group->nickname}: " . $e->getMessage());
            // TRANS: Server error displayed when leaving a group failed in the database.
            // TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
            $this->serverError(sprintf(_('Could not remove user %1$s from group %2$s.'),
                                       $this->scoped->nickname, $this->group->nickname));
            return;
        }

        if ($this->boolean('ajax')) {
            $this->startHTML('text/xml;charset=utf-8');
            $this->elementStart('head');
            // TRANS: Title for leave group page after leaving.
            $this->element('title', null, sprintf(_m('TITLE','%1$s left group %2$s'),
                                                  $this->scoped->nickname,
                                                  $this->group->nickname));
            $this->elementEnd('head');
            $this->elementStart('body');
            $jf = new JoinForm($this, $this->group);
            $jf->show();
            $this->elementEnd('body');
            $this->endHTML();
        } else {
            common_redirect(common_local_url('groupmembers', array('nickname' => $this->group->nickname)), 303);
        }
    }
}