code:
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social.  If not, see <http://www.gnu.org/licenses/>.

/*
 * @copyright 2008, 2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

defined('GNUSOCIAL') || die();

define('LISTENER', 1);
define('LISTENEE', -1);
define('BOTH', 0);

// @todo XXX: Documentation missing.
class FoafAction extends ManagedAction
{
    public function isReadOnly($args)
    {
        return true;
    }

    protected function doPreparation()
    {
        $nickname_arg = $this->arg('nickname');

        if (empty($nickname_arg)) {
            // TRANS: Client error displayed when requesting Friends of a Friend feed without providing a user nickname.
            $this->clientError(_('No such user.'), 404);
        }

        $this->nickname = common_canonical_nickname($nickname_arg);

        // Permanent redirect on non-canonical nickname

        if ($nickname_arg != $this->nickname) {
            common_redirect(
                common_local_url(
                    'foaf',
                    ['nickname' => $this->nickname]
                ),
                301
            );
        }

        $this->user = User::getKV('nickname', $this->nickname);

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

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

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

        return true;
    }

    public function showPage()
    {
        header('Content-Type: application/rdf+xml');

        $this->startXML();
        $this->elementStart('rdf:RDF', array('xmlns:rdf' =>
                                              'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
                                              'xmlns:rdfs' =>
                                              'http://www.w3.org/2000/01/rdf-schema#',
                                              'xmlns:geo' =>
                                              'http://www.w3.org/2003/01/geo/wgs84_pos#',
                                              'xmlns:bio' =>
                                              'http://purl.org/vocab/bio/0.1/',
                                              'xmlns:sioc' =>
                                              'http://rdfs.org/sioc/ns#',
                                              'xmlns' => 'http://xmlns.com/foaf/0.1/'));

        // This is the document about the user

        $this->showPpd('', $this->user->getUri());

        // Would be nice to tell if they were a Person or not (e.g. a #person usertag?)
        $this->elementStart('Agent', array('rdf:about' => $this->user->getUri()));
        if (common_config('foaf', 'mbox_sha1sum') && $this->user->email) {
            $this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email));
        }
        if ($this->profile->fullname) {
            $this->element('name', null, $this->profile->fullname);
        }
        if ($this->profile->homepage) {
            $this->element('homepage', array('rdf:resource' => $this->profile->homepage));
        }
        if ($this->profile->profileurl) {
            $this->element('weblog', array('rdf:resource' => $this->profile->profileurl));
        }
        if ($this->profile->bio) {
            $this->element('bio:olb', null, $this->profile->bio);
        }

        $location = $this->profile->getLocation();
        if ($location) {
            $attr = array();
            if ($location->getRdfURL()) {
                $attr['rdf:about'] = $location->getRdfURL();
            }
            $location_name = $location->getName();

            $this->elementStart('based_near');
            $this->elementStart('geo:SpatialThing', $attr);
            if ($location_name) {
                $this->element('name', null, $location_name);
            }
            if ($location->lat) {
                $this->element('geo:lat', null, $location->lat);
            }
            if ($location->lon) {
                $this->element('geo:long', null, $location->lon);
            }
            if ($location->getURL()) {
                $this->element('page', array('rdf:resource'=>$location->getURL()));
            }
            $this->elementEnd('geo:SpatialThing');
            $this->elementEnd('based_near');
        }

        try {
            $avatar = Avatar::getUploaded($this->profile);
            $this->elementStart('img');
            $this->elementStart('Image', array('rdf:about' => $avatar->displayUrl()));
            foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) {
                try {
                    $scaled = $this->profile->getAvatar($size);
                    $this->elementStart('thumbnail');
                    $this->element('Image', array('rdf:about' => $scaled->displayUrl()));
                    $this->elementEnd('thumbnail');
                } catch (Exception $e) {
                    // This avatar did not exist
                }
            }
            $this->elementEnd('Image');
            $this->elementEnd('img');
        } catch (NoAvatarException $e) {
            // No avatar for this user!
        }

        $person = $this->showMicrobloggingAccount(
            $this->profile,
            common_root_url(),
            $this->user->getUri(),
            // $fetchSubscriptions
            true,
            // $isSubscriber
            false
        );

        // Get people who subscribe to user

        $sub = new Subscription();
        $sub->subscribed = $this->profile->id;
        $sub->whereAdd('subscriber <> subscribed');

        if ($sub->find()) {
            while ($sub->fetch()) {
                $profile = Profile::getKV('id', $sub->subscriber);
                if (!$profile instanceof Profile) {
                    common_debug('Got a bad subscription: ' . print_r($sub, true));
                    continue;
                }
                $other_uri = $profile->getUri();
                if (array_key_exists($other_uri, $person)) {
                    $person[$other_uri][0] = BOTH;
                } else {
                    $person[$other_uri] = array(LISTENER,
                                                $profile->id,
                                                $profile->nickname,
                                                $profile->isLocal() ? 'local' : 'remote');
                }
                unset($profile);
            }
        }

        unset($sub);

        foreach ($person as $uri => $p) {
            list($type, $id, $nickname, $local) = $p;
            if ($type == BOTH) {
                $this->element('knows', array('rdf:resource' => $uri));
            }
        }

        $this->elementEnd('Agent');

        foreach ($person as $uri => $p) {
            $foaf_url = null;
            list($type, $id, $nickname, $local) = $p;
            if ($local == 'local') {
                $foaf_url = common_local_url('foaf', array('nickname' => $nickname));
            }
            $profile = Profile::getKV($id);
            $this->elementStart('Agent', array('rdf:about' => $uri));
            if ($type == BOTH) {
                $this->element('knows', array('rdf:resource' => $this->user->getUri()));
            }
            $this->showMicrobloggingAccount(
                $profile,
                ($local === 'local') ? common_root_url() : null,
                $uri,
                // $fetchSubscriptions
                false,
                // $isSubscriber
                ($type == LISTENER || $type == BOTH)
            );
            if ($foaf_url) {
                $this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url));
            }
            $this->elementEnd('Agent');
            if ($foaf_url) {
                $this->showPpd($foaf_url, $uri);
            }
            $profile->free();
            $profile = null;
            unset($profile);
        }

        $this->elementEnd('rdf:RDF');
        $this->endXML();
    }

    public function showPpd($foaf_url, $person_uri)
    {
        $this->elementStart('PersonalProfileDocument', array('rdf:about' => $foaf_url));
        $this->element('maker', array('rdf:resource' => $person_uri));
        $this->element('primaryTopic', array('rdf:resource' => $person_uri));
        $this->elementEnd('PersonalProfileDocument');
    }

    /**
     * Output FOAF <account> bit for the given profile.
     *
     * @param Profile $profile
     * @param mixed $service Root URL of this StatusNet instance for a local
     *                       user, otherwise null.
     * @param mixed $useruri URI string for the referenced profile..
     * @param boolean $fetchSubscriptions Should we load and list all their subscriptions?
     * @param boolean $isSubscriber if not fetching subs, we can still mark the user as following the current page.
     *
     * @return array if $fetchSubscribers is set, return a list of info on those
     *               subscriptions.
     */
    public function showMicrobloggingAccount($profile, $service = null, $useruri = null, $fetchSubscriptions = false, $isSubscriber = false)
    {
        $attr = array();
        if ($useruri) {
            $attr['rdf:about'] = $useruri . '#acct';
        }

        // Their account
        $this->elementStart('account');
        $this->elementStart('OnlineAccount', $attr);
        if ($service) {
            $this->element('accountServiceHomepage', array('rdf:resource' =>
                                                           $service));
        }
        $this->element('accountName', null, $profile->nickname);
        $this->element('accountProfilePage', array('rdf:resource' => $profile->profileurl));
        if ($useruri) {
            $this->element('sioc:account_of', array('rdf:resource'=>$useruri));
        }

        $person = array();

        if ($fetchSubscriptions) {
            // Get people user is subscribed to
            $sub = new Subscription();
            $sub->subscriber = $profile->id;
            $sub->whereAdd('subscriber <> subscribed');

            if ($sub->find()) {
                while ($sub->fetch()) {
                    $profile = Profile::getKV('id', $sub->subscribed);
                    if (empty($profile)) {
                        common_debug('Got a bad subscription: ' . print_r($sub, true));
                        continue;
                    }
                    $other_uri = $profile->getUri();
                    $this->element('sioc:follows', array('rdf:resource' => $other_uri.'#acct'));
                    $person[$other_uri] = array(LISTENEE,
                                                $profile->id,
                                                $profile->nickname,
                                                $profile->isLocal() ? 'local' : 'remote');
                    unset($profile);
                }
            }

            unset($sub);
        } elseif ($isSubscriber) {
            // Just declare that they follow the user whose FOAF we're showing.
            $this->element('sioc:follows', array('rdf:resource' => $this->user->getUri() . '#acct'));
        }

        $this->elementEnd('OnlineAccount');
        $this->elementEnd('account');

        return $person;
    }
}


code:
<?php
/*
 * 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/>.
 *
 * @category  Mail
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Toby Inkster <mail@tobyinkster.co.uk>
 * @copyright 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); }

// @todo XXX: Documentation missing.
class FoafGroupAction extends Action
{
    function isReadOnly($args)
    {
        return true;
    }

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

        $nickname_arg = $this->arg('nickname');

        if (empty($nickname_arg)) {
            // TRANS: Client error displayed when requesting Friends of a Friend feed without providing a group nickname.
            $this->clientError(_('No such group.'), 404);
        }

        $this->nickname = common_canonical_nickname($nickname_arg);

        // Permanent redirect on non-canonical nickname

        if ($nickname_arg != $this->nickname) {
            common_redirect(common_local_url('foafgroup',
                                             array('nickname' => $this->nickname)),
                            301);
            return false;
        }

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

        if (!$local) {
            // TRANS: Client error displayed when requesting Friends of a Friend feed for a non-local group.
            $this->clientError(_('No such group.'), 404);
        }

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

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

        common_set_returnto($this->selfUrl());

        return true;
    }

    function handle()
    {
        parent::handle();

        header('Content-Type: application/rdf+xml');

        $this->startXML();
        $this->elementStart('rdf:RDF', array('xmlns:rdf' =>
                                              'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
                                              'xmlns:dcterms' =>
                                              'http://purl.org/dc/terms/',
                                              'xmlns:sioc' =>
                                              'http://rdfs.org/sioc/ns#',
                                              'xmlns:foaf' =>
                                              'http://xmlns.com/foaf/0.1/',
                                              'xmlns:statusnet' =>
                                              'http://status.net/ont/',
                                              'xmlns' => 'http://xmlns.com/foaf/0.1/'));

        $this->showPpd(common_local_url('foafgroup', array('nickname' => $this->nickname)), $this->group->permalink());

        $this->elementStart('Group', array('rdf:about' =>
                                             $this->group->permalink()));
        if ($this->group->fullname) {
            $this->element('name', null, $this->group->fullname);
        }
        if ($this->group->description) {
            $this->element('dcterms:description', null, $this->group->description);
        }
        if ($this->group->nickname) {
            $this->element('dcterms:identifier', null, $this->group->nickname);
            $this->element('nick', null, $this->group->nickname);
        }
        foreach ($this->group->getAliases() as $alias) {
            $this->element('nick', null, $alias);
        }
        if ($this->group->homeUrl()) {
            $this->element('weblog', array('rdf:resource' => $this->group->homeUrl()));
        }
        if ($this->group->homepage) {
            $this->element('page', array('rdf:resource' => $this->group->homepage));
        }
        if ($this->group->homepage_logo) {
            $this->element('depiction', array('rdf:resource' => $this->group->homepage_logo));
        }

        $members = $this->group->getMembers();
        $member_details = array();
        while ($members->fetch()) {
            $member_uri = common_local_url('userbyid', array('id'=>$members->id));
            $member_details[$member_uri] = array(
                                        'nickname' => $members->nickname,
                                        'is_admin' => false,
                                        );
            $this->element('member', array('rdf:resource' => $member_uri));
        }

        $admins = $this->group->getAdmins();
        while ($admins->fetch()) {
            $admin_uri = common_local_url('userbyid', array('id'=>$admins->id));
            $member_details[$admin_uri]['is_admin'] = true;
            $this->element('statusnet:groupAdmin', array('rdf:resource' => $admin_uri));
        }

        $this->elementEnd('Group');

        ksort($member_details);
        foreach ($member_details as $uri => $details) {
            if ($details['is_admin'])
            {
                $this->elementStart('Agent', array('rdf:about' => $uri));
                $this->element('nick', null, $details['nickname']);
                $this->elementStart('account');
                $this->elementStart('sioc:User', array('rdf:about'=>$uri.'#acct'));
                $this->elementStart('sioc:has_function');
                $this->elementStart('statusnet:GroupAdminRole');
                $this->element('sioc:scope', array('rdf:resource' => $this->group->permalink()));
                $this->elementEnd('statusnet:GroupAdminRole');
                $this->elementEnd('sioc:has_function');
                $this->elementEnd('sioc:User');
                $this->elementEnd('account');
                $this->elementEnd('Agent');
            }
            else
            {
                $this->element('Agent', array(
                                        'foaf:nick' => $details['nickname'],
                                        'rdf:about' => $uri,
                                        ));
            }
        }

        $this->elementEnd('rdf:RDF');
        $this->endXML();
    }

    function showPpd($foaf_url, $person_uri)
    {
        $this->elementStart('Document', array('rdf:about' => $foaf_url));
        $this->element('primaryTopic', array('rdf:resource' => $person_uri));
        $this->elementEnd('Document');
    }

}

repo_name: ksuudilla/gnu

license: mit


code:
<?php
/**
 * Geocode action class
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Craig Andrews <candrews@integralblue.com>
 * @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);
}

/**
 * Geocode action class
 *
 * @category Action
 * @package  StatusNet
 * @author   Craig Andrews <candrews@integralblue.com>
 * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class GeocodeAction extends Action
{
    var $lat = null;
    var $lon = null;
    var $location = null;

    function prepare(array $args = array())
    {
        parent::prepare($args);
        $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->clientError(_('There was a problem with your session token. '.
                                 'Try again, please.'));
        }
        $this->lat = $this->trimmed('lat');
        $this->lon = $this->trimmed('lon');
        $this->location = Location::fromLatLon($this->lat, $this->lon);
        return true;
    }

    /**
     * Class handler
     *
     * @param array $args query arguments
     *
     * @return nothing
     *
     */
    function handle()
    {
        header('Content-Type: application/json; charset=utf-8');
        $location_object = array();
        $location_object['lat']=$this->lat;
        $location_object['lon']=$this->lon;
        if($this->location) {
            $location_object['location_id']=$this->location->location_id;
            $location_object['location_ns']=$this->location->location_ns;
            $location_object['name']=$this->location->getName();
            $location_object['url']=$this->location->getUrl();
        }
        print(json_encode($location_object));
    }

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


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Action class to grant user roles.
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Action
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 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')) {
    exit(1);
}

/**
 * Assign role to user.
 *
 * @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 GrantRoleAction extends ProfileFormAction
{
    /**
     * Check parameters
     *
     * @param array $args action arguments (URL, GET, POST)
     *
     * @return boolean success flag
     */
    function prepare(array $args = array())
    {
        if (!parent::prepare($args)) {
            return false;
        }

        $this->role = $this->arg('role');
        if (!Profile_role::isValid($this->role)) {
            // TRANS: Client error displayed when trying to assign an invalid role to a user.
            $this->clientError(_('Invalid role.'));
        }
        if (!Profile_role::isSettable($this->role)) {
            // TRANS: Client error displayed when trying to assign an reserved role to a user.
            $this->clientError(_('This role is reserved and cannot be set.'));
        }

        $cur = common_current_user();

        assert(!empty($cur)); // checked by parent

        if (!$cur->hasRight(Right::GRANTROLE)) {
            // TRANS: Client error displayed when trying to assign a role to a user while not being allowed to set roles.
            $this->clientError(_('You cannot grant user roles on this site.'));
        }

        assert(!empty($this->profile)); // checked by parent

        if ($this->profile->hasRole($this->role)) {
            // TRANS: Client error displayed when trying to assign a role to a user that already has that role.
            $this->clientError(_('User already has this role.'));
        }

        return true;
    }

    /**
     * Sandbox a user.
     *
     * @return void
     */
    function handlePost()
    {
        $this->profile->grantRole($this->role);
    }
}


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(array $args = array())
    {
        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()
    {
        parent::handle();
        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('GNUSOCIAL')) { exit(1); }

/**
 * 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 ShowgroupAction
{
    /** group we're viewing. */
    protected $group = null;

    function isReadOnly($args)
    {
        return true;
    }

    protected function doPreparation()
    {
        $this->group = User_group::getByID($this->arg('id'));
        $this->target = $this->group->getProfile();

        if ($this->target->isLocal()) {
            common_redirect($this->target->getUrl());
        }
    }
}


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('GNUSOCIAL')) {
    exit(1);
}

/**
 * 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>
 * @author   Alexei Sorokin <sor.alexei@meowr.ru>
 * @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
{
    public $mode = null;
    public $imagefile = null;
    public $filename = null;
    public $message = null;
    public $success = null;
    protected $canPost = true;

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

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

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

    public 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');
            if (!empty($this->group->homepage_logo)) {
                // TRANS: Button on group logo upload page to delete current group logo.
                $this->submit('delete', _('Delete'));
            }
            $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');
    }

    public function showPageNoticeBlock()
    {
        parent::showPageNoticeBlock();

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

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

    /**
     * Instructions for use
     *
     * @return string instructions for use
     */
    public 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());
    }

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

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

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

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

    /**
     * Prepare to run
     * @param array $args
     * @return bool
     * @throws ClientException
     * @throws NicknameException
     */
    protected function prepare(array $args = [])
    {
        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;
    }

    /**
     * Handle a post
     *
     * We mux on the button name to figure out what the user actually wanted.
     *
     * @return void
     * @throws ClientException
     * @throws NoResultException
     * @throws UnsupportedMediaException
     * @throws UseFileAsThumbnailException
     */
    protected function handlePost()
    {
        parent::handlePost();

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

    /**
     * Handle an image upload
     *
     * Does all the magic for handling an image upload, and crops the
     * image by default.
     *
     * @return void
     */
    public function uploadLogo()
    {
        try {
            $imagefile = ImageFile::fromUpload('avatarfile');
        } catch (Exception $e) {
            $this->setMessage($e->getMessage(), true);
            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->setMessage(_('Pick a square area of the image to be the logo.'));
    }

    public function setMessage($msg, $error = false)
    {
        $this->message = $msg;
        $this->success = !$error;
    }

    /**
     * Handle the results of jcrop.
     *
     * @return void
     * @throws NoResultException
     * @throws UnsupportedMediaException
     * @throws UseFileAsThumbnailException
     */
    public 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, common_config('avatar', 'maxsize'));
        $box = array('width' => $size, 'height' => $size,
            'x' => $dest_x, 'y' => $dest_y,
            'w' => $dest_w, 'h' => $dest_h);

        $profile = $this->group->getProfile();

        $imagefile = new ImageFile(null, $filedata['filepath']);
        $filename = Avatar::filename(
            $profile->getID(),
            image_type_to_extension($imagefile->preferredType()),
            $size,
            common_timestamp()
        );

        $imagefile->resizeTo(Avatar::path($filename), $box);

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

    /**
     * Get rid of the current group logo.
     *
     * @return void
     */
    public function deleteLogo()
    {
        $orig = clone($this->group);
        Avatar::deleteFromProfile($this->group->getProfile());
        @unlink(Avatar::path(basename($this->group->original_logo)));
        @unlink(Avatar::path(basename($this->group->homepage_logo)));
        @unlink(Avatar::path(basename($this->group->stream_logo)));
        @unlink(Avatar::path(basename($this->group->mini_logo)));
        $this->group->original_logo = User_group::defaultLogo(AVATAR_PROFILE_SIZE);
        $this->group->homepage_logo = User_group::defaultLogo(AVATAR_PROFILE_SIZE);
        $this->group->stream_logo = User_group::defaultLogo(AVATAR_STREAM_SIZE);
        $this->group->mini_logo = User_group::defaultLogo(AVATAR_MINI_SIZE);
        $this->group->update($orig);

        // TRANS: Success message for deleting the group logo.
        $this->setMessage(_('Logo deleted.'));
    }
}


code:
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social.  If not, see <http://www.gnu.org/licenses/>.

/**
 * List of group members
 *
 * @category  Group
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

defined('GNUSOCIAL') || die();

/**
 * List of group members
 *
 * @category  Group
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GroupmembersAction extends GroupAction
{
    public $page = null;

    public function isReadOnly($args)
    {
        return true;
    }

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

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

    public 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',
            ['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/profile/profilelist.php';
require_once INSTALLDIR . '/lib/groups/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 $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('GNUSOCIAL')) { exit(1); }

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 TargetedRss10Action
{
    /** group we're viewing. */
    protected $group = null;

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

    protected function doStreamPreparation()
    {

        $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 instanceof Local_group) {
            // TRANS: Client error displayed when requesting a group RSS feed for group that does not exist.
            $this->clientError(_('No such group.'), 404);
        }

        $this->group = $local->getGroup();
        $this->target = $this->group->getProfile();
    }

    protected function getNotices()
    {
        $stream = $this->group->getNotices(0, $this->limit);
        return $stream->fetchAll();
    }

    function getChannel()
    {
        $c = array('url' => common_local_url('grouprss',
                                             array('nickname' =>
                                                   $this->target->getNickname())),
                   // TRANS: Message is used as link title. %s is a user nickname.
                   'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
                   'link' => common_local_url('showgroup', array('nickname' => $this->target->getNickname())),
                   // 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!'),
                                            $this->target->getNickname(), common_config('site', 'name')));
        return $c;
    }

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


code:
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Latest groups information
 *
 * @category  Personal
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

defined('GNUSOCIAL') || die();

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

/**
 * Latest groups
 *
 * Show the latest groups on the site
 *
 * @category  Personal
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GroupsAction extends Action
{
    public $page = null;
    public $profile = null;

    public function isReadOnly($args)
    {
        return true;
    }

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

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

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

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

    public function showContent()
    {
        if (common_logged_in()) {
            $this->elementStart('p', ['id' => 'new_group']);
            $this->element(
                'a',
                [
                    '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;

        $groups = new User_group();
        $groups->selectAdd();
        $groups->selectAdd('user_group.*');
        $groups->joinAdd(['id', 'local_group:group_id']);
        $groups->orderBy('user_group.created DESC, user_group.id DESC');
        $groups->limit($offset, $limit);
        $groups->find();

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

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

    public 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/search/searchaction.php';
require_once INSTALLDIR . '/lib/profile/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(array $args = array())
    {
        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()
    {
        parent::handle();
        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
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Settings for Jabber/XMPP integration
 *
 * @category  Settings
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

defined('GNUSOCIAL') || die();

/**
 * Settings for Jabber/XMPP integration
 *
 * @category  Settings
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 *
 * @see       SettingsAction
 */

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

    /**
     * Instructions for use
     *
     * @return instructions for use
     */
    public 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
     */
    public function showContent()
    {
        $transports = array();
        Event::handle('GetImTransports', array(&$transports));
        if (! $transports) {
            $this->element(
                'div',
                ['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',
                [
                    '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 (!empty($user_im_prefs = User_im_prefs::pkeyGet([
                '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 {
                try {
                    $confirm = $this->getConfirmation($transport);
                    $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'));
                } catch (NoResultException $e) {
                    $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',
                [
                    '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.')),
            );
            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
     */
    public function getConfirmation($transport)
    {
        $confirm = new Confirm_address();

        $confirm->user_id      = $this->scoped->getID();
        $confirm->address_type = $transport;

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

        throw new NoResultException($confirm);
    }

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

    /**
     * Save user's XMPP preferences
     *
     * These are the checkboxes at the bottom of the page. They're used to
     * set different settings
     *
     * @return void
     */
    public function savePreferences()
    {
        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->query('START TRANSACTION');
        $user_im_prefs->user_id = $this->scoped->getID();
        if ($user_im_prefs->find() && $user_im_prefs->fetch()) {
            $preferences = array('notify', 'updatefrompresence', 'replies');
            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_im_prefs, 'UPDATE', __FILE__);
                    // TRANS: Server error thrown on database error updating IM preferences.
                    throw new ServerException(_('Could not update IM preferences.'));
                }
            } while ($user_im_prefs->fetch());
        }
        $user_im_prefs->query('COMMIT');
        // TRANS: Confirmation message for successful IM preferences save.
        return _('Preferences saved.');
    }

    /**
     * Sends a confirmation to the address given
     *
     * Stores a confirmation record and sends out a
     * message with the confirmation info.
     *
     * @return void
     */
    public function addAddress()
    {
        $screenname = $this->trimmed('screenname');
        $transport = $this->trimmed('transport');

        // Some validation

        if (empty($screenname)) {
            // TRANS: Message given saving IM address without having provided one.
            throw new ClientException(_('No screenname.'));
        }

        if (empty($transport)) {
            // TRANS: Form validation error when no transport is available setting an IM address.
            throw new ClientException(_('No transport.'));
        }

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

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

        $confirm = new Confirm_address();

        $confirm->address      = $screenname;
        $confirm->address_type = $transport;
        $confirm->user_id      = $this->scoped->getID();
        $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, $this->scoped));

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

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

        try {
            $confirm = $this->getConfirmation($transport);
            if ($confirm->address != $screenname) {
                // TRANS: Message given canceling IM address confirmation for the wrong IM address.
                throw new ClientException(_('That is the wrong IM address.'));
            }
        } catch (NoResultException $e) {
            // TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
            throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
        }

        $confirm->delete();

        // TRANS: Message given after successfully canceling IM address confirmation.
        return _('IM confirmation cancelled.');
    }

    /**
     * Remove an address
     *
     * If the user has a confirmed address, remove it.
     *
     * @return void
     */
    public function removeAddress()
    {
        $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 = $this->scoped->getID();
        $user_im_prefs->transport = $transport;
        if (!$user_im_prefs->find(true)) {
            // TRANS: Message given trying to remove an IM address that is not
            // TRANS: registered for the active user.
            throw new AlreadyFulfilledException(_('There were no preferences stored for this transport.'));
        }

        $result = $user_im_prefs->delete();

        if ($result === false) {
            common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
            // TRANS: Server error thrown on database error removing a registered IM address.
            throw new ServerException(_('Could not update user IM preferences.'));
        }

        // XXX: unsubscribe to the old address

        // TRANS: Message given after successfully removing a registered Instant Messaging address.
        return _('The IM address was removed.');
    }

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

    public function screennameExists($transport, $screenname)
    {
        $user_im_prefs = new User_im_prefs();
        $user_im_prefs->transport = $transport;
        $user_im_prefs->screenname = $screenname;
        return $user_im_prefs->find(true) ? true : false;
    }
}