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

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

/**
 * Table Definition for user_group
 */
class User_group extends Managed_DataObject
{
    const JOIN_POLICY_OPEN = 0;
    const JOIN_POLICY_MODERATE = 1;
    const CACHE_WINDOW = 201;

    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_group';                      // table name
    public $id;                              // int(4)  primary_key not_null
    public $profile_id;                      // int(4)  primary_key not_null
    public $nickname;                        // varchar(64)
    public $fullname;                        // varchar(191)   not 255 because utf8mb4 takes more space
    public $homepage;                        // varchar(191)   not 255 because utf8mb4 takes more space
    public $description;                     // text
    public $location;                        // varchar(191)   not 255 because utf8mb4 takes more space
    public $original_logo;                   // varchar(191)   not 255 because utf8mb4 takes more space
    public $homepage_logo;                   // varchar(191)   not 255 because utf8mb4 takes more space
    public $stream_logo;                     // varchar(191)   not 255 because utf8mb4 takes more space
    public $mini_logo;                       // varchar(191)   not 255 because utf8mb4 takes more space
    public $created;                         // datetime()
    public $modified;                        // timestamp()    not_null default_CURRENT_TIMESTAMP
    public $uri;                             // varchar(191)  unique_key   not 255 because utf8mb4 takes more space
    public $mainpage;                        // varchar(191)   not 255 because utf8mb4 takes more space
    public $join_policy;                     // tinyint
    public $force_scope;                     // tinyint

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE

    public function getObjectType()
    {
        return ActivityObject::GROUP;
    }

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),

                'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'),
                'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name'),
                'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
                'description' => array('type' => 'text', 'description' => 'group description'),
                'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'related physical location, if any'),

                'original_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'original size logo'),
                'homepage_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'),
                'stream_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'),
                'mini_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'mini logo'),

                'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

                'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
                'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page for group info to link to'),
                'join_policy' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=open; 1=requires admin approval'),
                'force_scope' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=never,1=sometimes,-1=always'),
            ),
            'primary key' => array('id'),
            'unique keys' => array(
                'user_group_uri_key' => array('uri'),
// when it's safe and everyone's run upgrade.php                'user_profile_id_key' => array('profile_id'),
            ),
            'foreign keys' => array(
                'user_group_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
            ),
            'indexes' => array(
                'user_group_nickname_idx' => array('nickname'),
                'user_group_created_id_idx' => array('created', 'id'),
                'user_group_profile_id_idx' => array('profile_id'), //make this unique in future
            ),
        );
    }

    protected $_profile = array();

    /**
     * @return Profile
     *
     * @throws GroupNoProfileException if user has no profile
     */
    public function getProfile()
    {
        if (!isset($this->_profile[$this->profile_id])) {
            $profile = Profile::getKV('id', $this->profile_id);
            if (!$profile instanceof Profile) {
                throw new GroupNoProfileException($this);
            }
            $this->_profile[$this->profile_id] = $profile;
        }
        return $this->_profile[$this->profile_id];
    }

    public function getNickname()
    {
        return $this->getProfile()->getNickname();
    }

    public function getFullname()
    {
        return $this->getProfile()->getFullname();
    }

    public static function defaultLogo($size)
    {
        static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile',
                                  AVATAR_STREAM_SIZE => 'stream',
                                  AVATAR_MINI_SIZE => 'mini');
        return Theme::path('default-avatar-'.$sizenames[$size].'.png');
    }

    public function homeUrl()
    {
        return $this->getProfile()->getUrl();
    }

    public function getUri()
    {
        $uri = null;
        if (Event::handle('StartUserGroupGetUri', array($this, &$uri))) {
            if (!empty($this->uri)) {
                $uri = $this->uri;
            } elseif ($this->isLocal()) {
                $uri = common_local_url('groupbyid', ['id' => $this->id]);
            }
        }
        Event::handle('EndUserGroupGetUri', array($this, &$uri));
        return $uri;
    }

    public function permalink()
    {
        $url = null;
        if (Event::handle('StartUserGroupPermalink', array($this, &$url))) {
            if ($this->isLocal()) {
                $url = common_local_url('groupbyid', ['id' => $this->id]);
            }
        }
        Event::handle('EndUserGroupPermalink', array($this, &$url));
        return $url;
    }

    public function getNotices($offset, $limit, $since_id = null, $max_id = null)
    {
        // FIXME: Get the Profile::current() some other way, to avoid
        // possible confusion between current session and queue process.
        $stream = new GroupNoticeStream($this, Profile::current());

        return $stream->getNotices($offset, $limit, $since_id, $max_id);
    }

    public function getMembers($offset = 0, $limit = null)
    {
        $ids = null;
        if (is_null($limit) || $offset + $limit > User_group::CACHE_WINDOW) {
            $ids = $this->getMemberIDs($offset, $limit);
        } else {
            $key = sprintf('group:member_ids:%d', $this->id);
            $window = self::cacheGet($key);
            if ($window === false) {
                $window = $this->getMemberIDs(0, User_group::CACHE_WINDOW);
                self::cacheSet($key, $window);
            }

            $ids = array_slice($window, $offset, $limit);
        }

        return Profile::multiGet('id', $ids);
    }

    public function getMemberIDs($offset = 0, $limit = null)
    {
        $gm = new Group_member();

        $gm->selectAdd();
        $gm->selectAdd('profile_id');

        $gm->group_id = $this->id;

        $gm->orderBy('created DESC, profile_id DESC');

        if (!is_null($limit)) {
            $gm->limit($offset, $limit);
        }

        $ids = array();

        if ($gm->find()) {
            while ($gm->fetch()) {
                $ids[] = $gm->profile_id;
            }
        }

        return $ids;
    }

    /**
     * Get pending members, who have not yet been approved.
     *
     * @param int $offset
     * @param int $limit
     * @return Profile
     */
    public function getRequests($offset = 0, $limit = null)
    {
        $rq = new Group_join_queue();
        $rq->group_id = $this->id;

        $members = new Profile();

        $members->joinAdd(['id', $rq, 'profile_id']);

        if ($limit != null) {
            $members->limit($offset, $limit);
        }

        $members->find();

        return $members;
    }

    public function getAdminCount()
    {
        $block = new Group_member();
        $block->group_id = $this->id;
        $block->is_admin = true;

        return $block->count();
    }

    public function getMemberCount()
    {
        $key = sprintf("group:member_count:%d", $this->id);

        $cnt = self::cacheGet($key);

        if (is_integer($cnt)) {
            return (int) $cnt;
        }

        $mem = new Group_member();
        $mem->group_id = $this->id;

        // XXX: why 'distinct'?

        $cnt = (int) $mem->count('distinct profile_id');

        self::cacheSet($key, $cnt);

        return $cnt;
    }

    public function getBlockedCount()
    {
        // XXX: WORM cache this

        $block = new Group_block();
        $block->group_id = $this->id;

        return $block->count();
    }

    public function getQueueCount()
    {
        // XXX: WORM cache this

        $queue = new Group_join_queue();
        $queue->group_id = $this->id;

        return $queue->count();
    }

    // offset is null because DataObject wants it, 0 would mean no results
    public function getAdmins($offset = null, $limit = null)
    {
        $admins = new Profile();
        $admins->joinAdd(['id', 'group_member:profile_id']);
        $admins->whereAdd(sprintf(
            'group_member.group_id = %d AND group_member.is_admin IS TRUE',
            $this->getID()
        ));
        $admins->orderBy('group_member.modified, group_member.profile_id');
        $admins->limit($offset, $limit);
        $admins->find();

        return $admins;
    }

    // offset is null because DataObject wants it, 0 would mean no results
    public function getBlocked($offset = null, $limit = null)
    {
        $blocked = new Profile();
        $blocked->joinAdd(array('id', 'group_block:blocked'));
        $blocked->whereAdd(sprintf('group_block.group_id = %u', $this->id));
        $blocked->orderBy('group_block.modified DESC, group_block.blocked DESC');
        $blocked->limit($offset, $limit);
        $blocked->find();

        return $blocked;
    }

    public function setOriginal($filename)
    {
        // This should be handled by the Profile->setOriginal function so user and group avatars are handled the same
        $imagefile = new ImageFile(null, Avatar::path($filename));

        $sizes = array('homepage_logo' => AVATAR_PROFILE_SIZE,
                       'stream_logo' => AVATAR_STREAM_SIZE,
                       'mini_logo' => AVATAR_MINI_SIZE);

        $orig = clone($this);
        $this->original_logo = Avatar::url($filename);
        foreach ($sizes as $name=>$size) {
            $filename = Avatar::filename(
                $this->profile_id,
                image_type_to_extension($imagefile->preferredType()),
                $size,
                common_timestamp()
            );
            $imagefile->resizeTo(Avatar::path($filename), array('width'=>$size, 'height'=>$size));
            $this->$name = Avatar::url($filename);
        }
        common_debug(common_log_objstring($this));
        return $this->update($orig);
    }

    public function getBestName()
    {
        return ($this->fullname) ? $this->fullname : $this->nickname;
    }

    /**
     * Gets the full name (if filled) with nickname as a parenthetical, or the nickname alone
     * if no fullname is provided.
     *
     * @return string
     */
    public function getFancyName()
    {
        if ($this->fullname) {
            // TRANS: Full name of a profile or group followed by nickname in parens
            return sprintf(_m('FANCYNAME', '%1$s (%2$s)'), $this->fullname, $this->nickname);
        } else {
            return $this->nickname;
        }
    }

    public function getAliases()
    {
        $aliases = array();

        // XXX: cache this

        $alias = new Group_alias();

        $alias->group_id = $this->id;

        if ($alias->find()) {
            while ($alias->fetch()) {
                $aliases[] = $alias->alias;
            }
        }

        $alias->free();

        return $aliases;
    }

    public function setAliases($newaliases)
    {
        $newaliases = array_unique($newaliases);

        $oldaliases = $this->getAliases();

        // Delete stuff that's old that not in new

        $to_delete = array_diff($oldaliases, $newaliases);

        // Insert stuff that's in new and not in old

        $to_insert = array_diff($newaliases, $oldaliases);

        $alias = new Group_alias();

        $alias->group_id = $this->id;

        foreach ($to_delete as $delalias) {
            $alias->alias = $delalias;
            $result = $alias->delete();
            if (!$result) {
                common_log_db_error($alias, 'DELETE', __FILE__);
                return false;
            }
        }

        foreach ($to_insert as $insalias) {
            if ($insalias === $this->nickname) {
                continue;
            }
            $alias->alias = Nickname::normalize($insalias, true);
            $result = $alias->insert();
            if (!$result) {
                common_log_db_error($alias, 'INSERT', __FILE__);
                return false;
            }
        }

        return true;
    }

    public static function getForNickname($nickname, Profile $profile = null)
    {
        $nickname = Nickname::normalize($nickname);

        // Are there any matching remote groups this profile's in?
        if ($profile instanceof Profile) {
            $group = $profile->getGroups(0, null);
            while ($group instanceof User_group && $group->fetch()) {
                if ($group->nickname == $nickname) {
                    // @fixme is this the best way?
                    return clone($group);
                }
            }
        }

        // If not, check local groups.
        $group = Local_group::getKV('nickname', $nickname);
        if ($group instanceof Local_group) {
            return User_group::getKV('id', $group->group_id);
        }
        $alias = Group_alias::getKV('alias', $nickname);
        if ($alias instanceof Group_alias) {
            return User_group::getKV('id', $alias->group_id);
        }
        return null;
    }

    public function getUserMembers()
    {
        // XXX: cache this

        $user = new User();

        $user->query(sprintf(
            'SELECT id FROM %1$s INNER JOIN group_member ' .
            'ON %1$s.id = group_member.profile_id ' .
            'WHERE group_member.group_id = %2$d ',
            $user->escapedTableName(),
            $this->id
        ));

        $ids = [];

        while ($user->fetch()) {
            $ids[] = $user->id;
        }

        $user->free();

        return $ids;
    }

    public static function maxDescription()
    {
        $desclimit = common_config('group', 'desclimit');
        // null => use global limit (distinct from 0!)
        if (is_null($desclimit)) {
            $desclimit = common_config('site', 'textlimit');
        }
        return $desclimit;
    }

    public static function descriptionTooLong($desc)
    {
        $desclimit = self::maxDescription();
        return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
    }

    public function asAtomEntry($namespace = false, $source = false)
    {
        $xs = new XMLStringer(true);

        if ($namespace) {
            $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom',
                           'xmlns:thr' => 'http://purl.org/syndication/thread/1.0');
        } else {
            $attrs = array();
        }

        $xs->elementStart('entry', $attrs);

        if ($source) {
            $xs->elementStart('source');
            $xs->element('id', null, $this->permalink());
            $xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
            $xs->element('link', array('href' => $this->permalink()));
            $xs->element('updated', null, $this->modified);
            $xs->elementEnd('source');
        }

        $xs->element('title', null, $this->nickname);
        $xs->element('summary', null, common_xml_safe_str($this->description));

        $xs->element('link', array('rel' => 'alternate',
                                   'href' => $this->permalink()));

        $xs->element('id', null, $this->permalink());

        $xs->element('published', null, common_date_w3dtf($this->created));
        $xs->element('updated', null, common_date_w3dtf($this->modified));

        $xs->element(
            'content',
            array('type' => 'html'),
            common_xml_safe_str($this->description)
        );

        $xs->elementEnd('entry');

        return $xs->getString();
    }

    public function asAtomAuthor()
    {
        $xs = new XMLStringer(true);

        $xs->elementStart('author');
        $xs->element('name', null, $this->nickname);
        $xs->element('uri', null, $this->permalink());
        $xs->elementEnd('author');

        return $xs->getString();
    }

    /**
     * Returns an XML string fragment with group information as an
     * Activity Streams noun object with the given element type.
     *
     * Assumes that 'activity', 'georss', and 'poco' namespace has been
     * previously defined.
     *
     * @param string $element one of 'actor', 'subject', 'object', 'target'
     *
     * @return string
     */
    public function asActivityNoun($element)
    {
        $noun = ActivityObject::fromGroup($this);
        return $noun->asString('activity:' . $element);
    }

    public function getAvatar()
    {
        return empty($this->homepage_logo)
            ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
            : $this->homepage_logo;
    }

    public static function register($fields)
    {
        if (!empty($fields['userid'])) {
            $profile = Profile::getKV('id', $fields['userid']);
            if ($profile && !$profile->hasRight(Right::CREATEGROUP)) {
                common_log(LOG_WARNING, "Attempted group creation from banned user: " . $profile->nickname);

                // TRANS: Client exception thrown when a user tries to create a group while banned.
                throw new ClientException(_('You are not allowed to create groups on this site.'), 403);
            }
        }

        $fields['nickname'] = Nickname::normalize($fields['nickname']);

        // MAGICALLY put fields into current scope
        // @fixme kill extract(); it makes debugging absurdly hard

        $defaults = [
            'nickname'    => null,
            'fullname'    => null,
            'homepage'    => null,
            'description' => null,
            'location'    => null,
            'uri'         => null,
            'mainpage'    => null,
            'aliases'     => [],
            'userid'      => null,
        ];

        $fields = array_merge($defaults, $fields);

        extract($fields);

        $group = new User_group();

        if (empty($uri)) {
            // fill in later...
            $uri = null;
        }
        if (empty($mainpage)) {
            $mainpage = common_local_url('showgroup', array('nickname' => $nickname));
        }

        // We must create a new, incrementally assigned profile_id
        $profile = new Profile();
        $profile->nickname   = $nickname;
        $profile->fullname   = $fullname;
        $profile->profileurl = $mainpage;
        $profile->homepage   = $homepage;
        $profile->bio        = $description;
        $profile->location   = $location;
        $profile->created    = common_sql_now();

        $group->nickname    = $profile->nickname;
        $group->fullname    = $profile->fullname;
        $group->homepage    = $profile->homepage;
        $group->description = $profile->bio;
        $group->location    = $profile->location;
        $group->mainpage    = $profile->profileurl;
        $group->created     = $profile->created;

        $profile->query('START TRANSACTION');
        $id = $profile->insert();
        if ($id === false) {
            $profile->query('ROLLBACK');
            throw new ServerException(_('Profile insertion failed'));
        }

        $group->profile_id = $id;
        $group->uri        = $uri;

        if (isset($fields['join_policy'])) {
            $group->join_policy = intval($fields['join_policy']);
        } else {
            $group->join_policy = 0;
        }

        if (isset($fields['force_scope'])) {
            $group->force_scope = intval($fields['force_scope']);
        } else {
            $group->force_scope = 0;
        }

        if (Event::handle('StartGroupSave', array(&$group))) {
            $result = $group->insert();

            if ($result === false) {
                common_log_db_error($group, 'INSERT', __FILE__);
                // TRANS: Server exception thrown when creating a group failed.
                throw new ServerException(_('Could not create group.'));
            }

            if (!isset($uri) || empty($uri)) {
                $orig = clone($group);
                $group->uri = common_local_url('groupbyid', array('id' => $group->id));
                $result = $group->update($orig);
                if (!$result) {
                    common_log_db_error($group, 'UPDATE', __FILE__);
                    // TRANS: Server exception thrown when updating a group URI failed.
                    throw new ServerException(_('Could not set group URI.'));
                }
            }

            $result = $group->setAliases($aliases);

            if (!$result) {
                // TRANS: Server exception thrown when creating group aliases failed.
                throw new ServerException(_('Could not create aliases.'));
            }

            $member = new Group_member();

            $member->group_id   = $group->id;
            $member->profile_id = $userid;
            $member->is_admin   = true;
            $member->created    = $group->created;

            $result = $member->insert();

            if (!$result) {
                common_log_db_error($member, 'INSERT', __FILE__);
                // TRANS: Server exception thrown when setting group membership failed.
                throw new ServerException(_('Could not set group membership.'));
            }

            self::blow('profile:groups:%d', $userid);

            if ($local) {
                $local_group = new Local_group();

                $local_group->group_id = $group->id;
                $local_group->nickname = $nickname;
                $local_group->created  = common_sql_now();

                $result = $local_group->insert();

                if (!$result) {
                    common_log_db_error($local_group, 'INSERT', __FILE__);
                    // TRANS: Server exception thrown when saving local group information failed.
                    throw new ServerException(_('Could not save local group info.'));
                }
            }

            Event::handle('EndGroupSave', array($group));
        }

        $profile->query('COMMIT');

        return $group;
    }

    /**
     * Handle cascading deletion, on the model of notice and profile.
     *
     * This should handle freeing up cached entries for the group's
     * id, nickname, URI, and aliases. There may be other areas that
     * are not de-cached in the UI, including the sidebar lists on
     * GroupsAction
     */
    public function delete($useWhere = false)
    {
        if (empty($this->id)) {
            common_log(LOG_WARNING, "Ambiguous User_group->delete(); skipping related tables.");
            return parent::delete($useWhere);
        }

        // Safe to delete in bulk for now

        $related = array('Group_inbox',
                         'Group_block',
                         'Group_member',
                         'Related_group');

        Event::handle('UserGroupDeleteRelated', array($this, &$related));

        foreach ($related as $cls) {
            $inst = new $cls();
            $inst->group_id = $this->id;

            if ($inst->find()) {
                while ($inst->fetch()) {
                    $dup = clone($inst);
                    $dup->delete();
                }
            }
        }

        // And related groups in the other direction...
        $inst = new Related_group();
        $inst->related_group_id = $this->id;
        $inst->delete();

        // Aliases and the local_group entry need to be cleared explicitly
        // or we'll miss clearing some cache keys; that can make it hard
        // to create a new group with one of those names or aliases.
        $this->setAliases(array());

        // $this->isLocal() but we're using the resulting object
        $local = Local_group::getKV('group_id', $this->id);
        if ($local instanceof Local_group) {
            $local->delete();
        }

        $result = parent::delete($useWhere);

        try {
            $profile = $this->getProfile();
            $profile->delete();
        } catch (GroupNoProfileException $unp) {
            common_log(
                LOG_INFO,
                "Group {$this->nickname} has no profile; continuing deletion."
            );
        }

        // blow the cached ids
        self::blow('user_group:notice_ids:%d', $this->id);

        return $result;
    }

    public function update($dataObject=false)
    {
        // Whenever the User_group is updated, find the Local_group
        // and update its nickname too.
        if ($this->nickname != $dataObject->nickname) {
            $local = Local_group::getKV('group_id', $this->id);
            if ($local instanceof Local_group) {
                common_debug("Updating Local_group ({$this->id}) nickname from {$dataObject->nickname} to {$this->nickname}");
                $local->setNickname($this->nickname);
            }
        }

        // Also make sure the Profile table is up to date!
        $fields = array(/*group field => profile field*/
                    'nickname'      => 'nickname',
                    'fullname'      => 'fullname',
                    'mainpage'      => 'profileurl',
                    'homepage'      => 'homepage',
                    'description'   => 'bio',
                    'location'      => 'location',
                    'created'       => 'created',
                    'modified'      => 'modified',
                    );
        $profile = $this->getProfile();
        $origpro = clone($profile);
        foreach ($fields as $gf=>$pf) {
            $profile->$pf = $this->$gf;
        }
        if ($profile->update($origpro) === false) {
            throw new ServerException(_('Unable to update profile'));
        }

        return parent::update($dataObject);
    }

    public function isPrivate()
    {
        return ($this->join_policy == self::JOIN_POLICY_MODERATE &&
                intval($this->force_scope) === 1);
    }

    public function isLocal()
    {
        $local = Local_group::getKV('group_id', $this->id);
        return ($local instanceof Local_group);
    }

    public static function groupsFromText($text, Profile $profile)
    {
        $groups = array();

        /* extract all !group */
        $count = preg_match_all(
            '/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
            strtolower($text),
            $match
        );

        if (!$count) {
            return $groups;
        }

        foreach (array_unique($match[1]) as $nickname) {
            $group = self::getForNickname($nickname, $profile);
            if ($group instanceof User_group && $profile->isMember($group)) {
                $groups[] = clone($group);
            }
        }

        return $groups;
    }

    public static function idsFromText($text, Profile $profile)
    {
        $ids = array();
        $groups = self::groupsFromText($text, $profile);
        foreach ($groups as $group) {
            $ids[$group->id] = true;
        }
        return array_keys($ids);
    }
}


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

/**
 * Data class for user IM preferences
 *
 * @category  Data
 * @package   GNUsocial
 * @author    Craig Andrews <candrews@integralblue.com>
 * @copyright 2009 StatusNet Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

class User_im_prefs extends Managed_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_im_prefs';       // table name
    public $user_id;                         // int(4)  primary_key not_null
    public $screenname;                      // varchar(191)  not_null   not 255 because utf8mb4 takes more space
    public $transport;                       // varchar(191)  not_null   not 255 because utf8mb4 takes more space
    public $notify;                          // bool    not_null default_false
    public $replies;                         // bool    not_null default_false
    public $updatefrompresence;              // bool    not_null default_false
    public $created;                         // datetime()
    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
                'screenname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'),
                'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
                'notify' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Notify when a new notice is sent'),
                'replies' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Send replies from people not subscribed to'),
                'updatefrompresence' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Update from presence.'),
                'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('user_id', 'transport'),
            'unique keys' => array(
                'user_im_prefs_transport_screenname_key' => array('transport', 'screenname'),
            ),
            'foreign keys' => array(
                'user_im_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
            ),
        );
    }
}

repo_name: ksuudilla/gnu

license: mit


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

/**
 * Data class for user location preferences
 *
 * @category  Data
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

class User_location_prefs extends Managed_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_location_prefs';             // table name
    public $user_id;                         // int(4)  primary_key not_null
    public $share_location;                  // bool    default_true
    public $created;                         // datetime()
    public $modified;                        // timestamp()  not_null default_CURRENT_TIMESTAMP

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who has the preference'),
                'share_location' => array('type' => 'bool', 'default' => true, 'description' => 'Whether to share location data'),
                'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('user_id'),
            'foreign keys' => array(
                'user_location_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
            ),
        );
    }
}


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 2010 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

class User_urlshortener_prefs extends Managed_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_urlshortener_prefs';         // table name
    public $user_id;                         // int(4)  primary_key not_null
    public $urlshorteningservice;            // varchar(50)   default_ur1.ca
    public $maxurllength;                    // int(4)   not_null
    public $maxnoticelength;                 // int(4)   not_null
    public $created;                         // datetime()
    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
                'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
                'maxurllength' => array('type' => 'int', 'not null' => true, 'description' => 'urls greater than this length will be shortened, 0 = always, null = never'),
                'maxnoticelength' => array('type' => 'int', 'not null' => true, 'description' => 'notices with content greater than this value will have all urls shortened, 0 = always, -1 = only if notice text is longer than max allowed'),
                'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('user_id'),
            'foreign keys' => array(
                'user_urlshortener_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
            ),
        );
    }

    public static function maxUrlLength($user)
    {
        $def = common_config('url', 'maxurllength');

        $prefs = self::getPrefs($user);

        if (empty($prefs)) {
            return $def;
        } else {
            return $prefs->maxurllength;
        }
    }

    public static function maxNoticeLength($user)
    {
        $def = common_config('url', 'maxnoticelength');

        if ($def == -1) {
            /*
             * maxContent==0 means infinite length,
             * but maxNoticeLength==0 means "always shorten"
             * so if maxContent==0 we must set this to -1
             */
            $def = Notice::maxContent() ?: -1;
        }

        $prefs = self::getPrefs($user);

        if (empty($prefs)) {
            return $def;
        } else {
            return $prefs->maxnoticelength;
        }
    }

    public static function urlShorteningService($user)
    {
        $def = common_config('url', 'shortener');

        $prefs = self::getPrefs($user);

        if (empty($prefs)) {
            if (!empty($user)) {
                return $user->urlshorteningservice;
            } else {
                return $def;
            }
        } else {
            return $prefs->urlshorteningservice;
        }
    }

    public static function getPrefs($user)
    {
        if (empty($user)) {
            return null;
        }

        $prefs = User_urlshortener_prefs::getKV('user_id', $user->id);

        return $prefs;
    }
}


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

/**
 * Table Definition for user_username
 */

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

class User_username extends Managed_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_username';                     // table name
    public $user_id;                        // int(4)  not_null
    public $provider_name;                  // varchar(191)  primary_key not_null   not 255 because utf8mb4 takes more space
    public $username;                       // varchar(191)  primary_key not_null   not 255 because utf8mb4 takes more space
    public $created;                        // datetime()
    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'provider_name' => array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'provider name'),
                'username' => array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'username'),
                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
                'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('provider_name', 'username'),
            'foreign keys' => array(
                'user_username_user_id_fkey' => array('user', array('user_id' => 'id')),
            ),
            'indexes' => array(
                'user_username_user_id_idx' => array('user_id'),
            ),
        );
    }

    /**
    * Register a user with a username on a given provider
    * @param User User object
    * @param string username on the given provider
    * @param provider_name string name of the provider
    * @return mixed User_username instance if the registration succeeded, false if it did not
    */
    public static function register($user, $username, $provider_name)
    {
        $user_username = new User_username();
        $user_username->user_id = $user->id;
        $user_username->provider_name = $provider_name;
        $user_username->username = $username;
        $user_username->created = common_sql_now();

        if ($user_username->insert()) {
            return $user_username;
        } else {
            return false;
        }
    }
}


code:
[status_network]
site_id = 129
nickname = 130
hostname = 2
pathname = 2
dbhost = 2
dbuser = 2
dbpass = 2
dbname = 2
sitename = 2
theme = 2
logo = 2
created = 142
modified = 384

[status_network__keys]
site_id  = K
nickname = U
hostname = U
pathname = U

[status_network_tag]
site_id = 129
tag = 130
created = 142

[status_network_tag__keys]
site_id = K
tag = K


gnusocial/gnusocial

description: Free software social networking platform.

type: project

minimum-stability: dev

preferred-install: dist


_readme: This file locks the dependencies of your project to a known state

_readme: Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies

alchemy/binary-driver

type: git

type: zip

type: library

Alchemy: src

Jens Hausdorf

role: Maintainer

description: A set of tools to build binary drivers

keywords: binary driver

apereo/phpcas

description: Provides a simple API for authenticating users against a CAS server

keywords: apereo cas jasig

composer/ca-bundle

Composer\CaBundle\: src

description: Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.

keywords: cabundle cacert certificate ssl tls

type: custom

type: tidelift

diogocomposer/xmpphp

XMPPHP\: XMPPHP

description: XMPP PHP Library

keywords: jabber xmpp xmpphp

doctrine/cache

alcaeus/mongo-php-adapter: Required to use legacy MongoDB driver

Doctrine\Common\Cache\: lib/Doctrine/Common/Cache

description: PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.

keywords: abstraction apcu cache caching couchdb memcached php redis xcache

type: patreon

embed/embed

Embed\: src

Oscar Otero

role: Developer

description: PHP library to retrieve page info using oembed, opengraph, etc

keywords:
embed
embedly
oembed
opengraph
twitter cards

evenement/evenement

Evenement: src

description: Événement is a very simple event dispatching library for PHP

keywords: event-dispatcher event-emitter

ezyang/htmlpurifier

HTMLPurifier: library/

description: Standards compliant HTML filter written in PHP

zendframework/zend-httphandlerrunner: Emit PSR-7 responses

GuzzleHttp\Psr7\: src/

description: PSR-7 message implementation that also provides common utility methods

keywords: http message psr-7 request response stream uri url

hoa/consistency

description: The Hoa\Consistency library.

keywords: autoloader callable consistency entity flex keyword library

hoa/event

description: The Hoa\Event library.

keywords: event library listener observer

hoa/exception

description: The Hoa\Exception library.

keywords: exception library

intervention/image

ext-gd: to use GD library based image processing.

ext-imagick: to use Imagick based image processing.

intervention/imagecache: Caching extension for the Intervention Image library

Image: Intervention\Image\Facades\Image

Intervention\Image\: src/Intervention/Image

description: Image handling and manipulation library with support for Laravel integration

keywords: gd image imagick laravel thumbnail watermark

Masterminds\: src

description: An HTML5 parser and serializer.

keywords: HTML5 dom html parser querypath serializer xml

barnabywalters/mf-cleaner: To more easily handle the canonical data php-mf2 gives you

masterminds/html5: Alternative HTML parser for PHP, for better HTML5 support.

description: A pure, generic microformats2 parser — makes HTML as easy to consume as a JSON API

keywords:
html
microformats
microformats 2
parser
semantic

michelf/php-markdown

Michelf\: Michelf/

description: PHP Markdown

neutron/temporary-filesystem

Neutron: src

description: Symfony filesystem extension to handle temporary files

openid/php-openid

description: OpenID library for PHP5

keywords: Authentication OpenId auth yadis

ParagonIE\ConstantTime\: src/

Steve 'Sc00bz' Thomas

role: Original Developer

description: Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)

ext-libsodium: Provides a modern crypto API that can be used to generate random bytes.

description: PHP 5.x polyfill for random_bytes() and random_int() from PHP 7

keywords: csprng polyfill pseudorandom random

Andrei Zmievski

role: Lead

description: More info available on: http://pear.php.net/package/Console_Getopt

php-ffmpeg/php-ffmpeg

php-ffmpeg/extras: A compilation of common audio & video drivers for PHP-FFMpeg

description: FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg

keywords:
audio
audio processing
avconv
avprobe
ffmpeg
ffprobe
video
video processing

phpseclib/phpseclib

ext-gmp: Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.

ext-libsodium: SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.

ext-mcrypt: Install the Mcrypt extension in order to speed up a few other cryptographic operations.

ext-openssl: Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.

description: PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.

keywords: BigInteger aes asn.1 asn1 blowfish crypto cryptography encryption rsa security sftp signature signing ssh twofish x.509 x509

predis/predis

ext-curl: Allows access to Webdis when paired with phpiredis

ext-phpiredis: Allows faster serialization and deserialization of the Redis protocol

description: Flexible and feature-complete Redis client for PHP and HHVM

psr/http-message

description: Common interface for HTTP messages

keywords: http http-message psr psr-7 request response

psr/log

description: Common interface for logging libraries

ralouphie/getallheaders

description: A polyfill for getallheaders.

stomp-php/stomp-php

description: stomp support for PHP

keywords: activeMQ apollomq jms messaging rabbitmq stomp

symfony/filesystem

description: Symfony Filesystem Component

symfony/process

description: Symfony Process Component

cilex/cilex

description: The PHP micro-framework for Command line tools based on the Symfony2 Components

cilex/console-service-provider

description: Console Service Provider

keywords: cilex console pimple service-provider silex

container-interop/container-interop

description: Promoting the interoperability of container objects (DIC, SL, etc.)

dnoegel/php-xdg-base-dir

description: implementation of xdg base directory specification for php

doctrine/annotations

description: Docblock Annotations Parser

doctrine/instantiator

description: A small, lightweight utility to instantiate objects in PHP without invoking their constructors

doctrine/lexer

description: PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.

erusev/parsedown

description: Parser for Markdown.

jakub-onderka/php-console-highlighter

description: Highlight PHP code in terminal

jms/metadata

description: Class/method/property metadata management in PHP

jms/parser-lib

description: A library for easily creating recursive-descent parsers.

jms/serializer

doctrine/cache: Required if you like to use cache functionality.

doctrine/collections: Required if you like to use doctrine collection types as ArrayCollection.

symfony/yaml: Required if you'd like to serialize data to YAML format.

description: Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.

keywords: deserialization jaxb json serialization xml

monolog/monolog

aws/aws-sdk-php: Allow sending log messages to AWS services like DynamoDB

doctrine/couchdb: Allow sending log messages to a CouchDB server

ext-amqp: Allow sending log messages to an AMQP server (1.0+ required)

ext-mongo: Allow sending log messages to a MongoDB server

graylog2/gelf-php: Allow sending log messages to a GrayLog2 server

mongodb/mongodb: Allow sending log messages to a MongoDB server via PHP Driver

php-amqplib/php-amqplib: Allow sending log messages to an AMQP server using php-amqplib

php-console/php-console: Allow sending log messages to Google Chrome

ruflin/elastica: Allow sending log messages to an Elastic Search server

sentry/sentry: Allow sending log messages to a Sentry server

description: Sends your logs to files, sockets, inboxes, databases and various web services

myclabs/deep-copy

description: Create deep copies (clones) of your objects

keywords:
clone
copy
duplicate
object
object graph

nikic/php-parser

description: A PHP parser written in PHP

description: Secure wrapper for accessing HTTPS resources with file_get_contents for PHP 5.3+

keywords: download file_get_contents http https ssl tls

padraic/phar-updater

description: A thing to make PHAR self-updating easy and secure.

phar-io/manifest

description: Component for reading phar.io manifest information from a PHP Archive (PHAR)

phar-io/version

description: Library for handling version information and constraints

phpcollection/phpcollection

description: General-Purpose Collection Library for PHP

phpdocumentor/fileset

description: Fileset component for collecting a set of files given directories and file paths

phpdocumentor/phpdocumentor

ext-twig: Enabling the twig extension improves the generation of twig based templates.

ext-xslcache: Enabling the XSLCache extension improves the generation of xml based templates.

description: Documentation Generator for PHP

keywords: api application dga documentation phpdoc

phpdocumentor/reflection

description: Reflection library to do Static Analysis for PHP Projects

keywords:
phpDocumentor
phpdoc
reflection
static analysis

phpoption/phpoption

description: Option Type for PHP

phpspec/prophecy

description: Highly opinionated mocking framework for PHP 5.3+

phpunit/php-code-coverage

description: Library that provides collection, processing, and rendering functionality for PHP code coverage information.

phpunit/php-file-iterator

description: FilterIterator implementation that filters files based on a list of suffixes.

phpunit/php-text-template

description: Simple template engine.

phpunit/php-timer

description: Utility class for timing

phpunit/php-token-stream

description: Wrapper around PHP's tokenizer extension.

phpunit/phpunit

description: The PHP Unit Testing framework.

pimple/pimple

description: Pimple is a simple Dependency Injection Container for PHP 5.3

psr/cache

description: Common interface for caching libraries

psr/container

description: Common Container Interface (PHP FIG PSR-11)

keywords: PSR-11 container container-interface container-interop psr

psr/simple-cache

description: Common interfaces for simple caching

psy/psysh

ext-pcntl: Enabling the PCNTL extension makes PsySH a lot happier :)

ext-pdo-sqlite: The doc command requires SQLite to work.

ext-posix: If you have PCNTL, you'll want the POSIX extension as well.

ext-readline: Enables support for arrow-key history navigation, and showing and manipulating command history.

hoa/console: A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit.

description: An interactive shell for modern PHP.

sebastian/code-unit-reverse-lookup

description: Looks up which function or method a line of code belongs to

sebastian/comparator

description: Provides the functionality to compare PHP values for equality

sebastian/diff

description: Diff implementation

sebastian/environment

description: Provides functionality to handle HHVM/PHP environments

sebastian/exporter

description: Provides the functionality to export PHP variables for visualization

sebastian/global-state

description: Snapshotting of global state

sebastian/object-enumerator

description: Traverses array structures and object graphs to enumerate all referenced objects

sebastian/object-reflector

description: Allows reflection of object attributes, including inherited and non-public ones

sebastian/recursion-context

description: Provides functionality to recursively process PHP variables

sebastian/resource-operations

description: Provides a list of PHP built-in functions that operate on resources

sebastian/type

description: Collection of value objects that represent the types of the PHP type system

sebastian/version

description: Library that helps with managing the version number of Git-hosted PHP projects

symfony/config

description: Symfony Config Component

symfony/console

description: Symfony Console Component

symfony/debug

description: Symfony Debug Component

symfony/event-dispatcher

description: Symfony EventDispatcher Component

symfony/finder

description: Symfony Finder Component

symfony/polyfill-ctype

description: Symfony polyfill for ctype functions

symfony/polyfill-mbstring

description: Symfony polyfill for the Mbstring extension

keywords: compatibility mbstring polyfill portable shim

description: Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions

symfony/stopwatch

description: Symfony Stopwatch Component

symfony/translation

description: Symfony Translation Component

symfony/validator

doctrine/annotations: For using the annotation mapping. You will also need doctrine/cache.

doctrine/cache: For using the default cached annotation reader and metadata cache.

description: Symfony Validator Component

symfony/var-dumper

ext-iconv: To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).

description: Symfony mechanism for exploring and dumping PHP variables

theseer/tokenizer

description: A small library for converting tokenized PHP source code into XML and potentially other formats

twig/twig

description: Twig, the flexible, fast, and secure template language for PHP

webmozart/assert

description: Assertions to validate method input/output with nice error messages.

zendframework/zend-cache

ext-apc: APC or compatible extension, to use the APC storage adapter

ext-apcu: APCU >= 5.1.0, to use the APCu storage adapter

ext-memcache: Memcache >= 2.0.0 to use the Memcache storage adapter

ext-memcached: Memcached >= 1.0.0 to use the Memcached storage adapter

ext-mongodb: MongoDB, to use the ExtMongoDb storage adapter

ext-wincache: WinCache, to use the WinCache storage adapter

ext-xcache: XCache, to use the XCache storage adapter

mongodb/mongodb: Required for use with the ext-mongodb adapter

mongofill/mongofill: Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement

description: Caching implementation with a variety of storage options, as well as codified caching strategies for callbacks, classes, and output

zendframework/zend-config

zendframework/zend-json: Zend\Json to use the Json reader or writer classes

zendframework/zend-servicemanager: Zend\ServiceManager for use with the Config Factory to retrieve reader and writer instances

description: provides a nested object property based user interface for accessing this configuration data within application code

zendframework/zend-eventmanager

container-interop/container-interop: ^1.1.0, to use the lazy listeners feature

zendframework/zend-stdlib: ^2.7.3 || ^3.0, to use the FilterChain feature

description: Trigger and listen to events within a PHP application

zendframework/zend-filter

psr/http-factory-implementation: psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters

zendframework/zend-crypt: Zend\Crypt component, for encryption filters

zendframework/zend-i18n: Zend\I18n component for filters depending on i18n functionality

zendframework/zend-servicemanager: Zend\ServiceManager component, for using the filter chain functionality

zendframework/zend-uri: Zend\Uri component, for the UriNormalize filter

description: Programmatically filter and normalize data and files

zendframework/zend-hydrator

zendframework/zend-eventmanager: ^2.6.2 || ^3.0, to support aggregate hydrator usage

zendframework/zend-filter: ^2.6, to support naming strategy hydrator usage

zendframework/zend-servicemanager: ^2.7.5 || ^3.0.3, to support hydrator plugin manager usage

zendframework/zend-eventmanager: You should install this package to use the events in the translator

zendframework/zend-filter: You should install this package to use the provided filters

zendframework/zend-validator: You should install this package to use the provided validators

zendframework/zend-view: You should install this package to use the provided view helpers

description: Provide translations for your application, and filter and validate internationalized values

zendframework/zend-json

description: provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP

zendframework/zend-serializer

zendframework/zend-math: (^2.6 || ^3.0) To support Python Pickle serialization

zendframework/zend-servicemanager: (^2.7.5 || ^3.0.3) To support plugin manager support

description: Serialize and deserialize PHP structures to a variety of representations

zendframework/zend-servicemanager

ocramius/proxy-manager: ProxyManager 0.5.* to handle lazy initialization of services

zendframework/zend-stdlib

zendframework/zend-filter: To support naming strategy hydrator usage

zendframework/zend-servicemanager: To support hydrator plugin manager usage

zetacomponents/base

description: The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.

zetacomponents/document

description: The Document components provides a general conversion framework for different semantic document markup languages like XHTML, Docbook, RST and similar.


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

/**
 *
 * Some notes...
 *
 * decimal <-> numeric
 *
 * MySQL 'timestamp' columns were formerly used for 'modified' files for their
 * auto-updating properties. This didn't play well with changes to cache usage
 * in 0.9.x, as we don't know the timestamp value at INSERT time and never
 * have a chance to load it up again before caching. For now I'm leaving them
 * in, but we may want to clean them up later.
 *
 * Current code should be setting 'created' and 'modified' fields explicitly;
 * this also avoids mismatches between server and client timezone settings.
 *
 *
 * fulltext indexes?
 * got one or two things wanting a custom charset setting on a field?
 *
 * foreign keys are kinda funky...
 *     those specified in inline syntax (as all in the original .sql) are NEVER ENFORCED on mysql
 *     those made with an explicit 'foreign key' WITHIN INNODB and IF there's a proper index, do get enforced
 *     double-check what we've been doing on postgres?
 */

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

$classes = [
    'Schema_version',
    'Profile',
    'Avatar',
    'Sms_carrier',
    'User',
    'User_group',
    'Subscription',
    'Group_join_queue',
    'Subscription_queue',
    'Consumer',
    'Oauth_application',
    'Oauth_token_association',
    'Conversation',
    'Notice',
    'Notice_location',
    'Notice_source',
    'Notice_prefs',
    'Reply',
    'Token',
    'Nonce',
    'Oauth_application_user',
    'Confirm_address',
    'Remember_me',
    'Queue_item',
    'Notice_tag',
    'Foreign_service',
    'Foreign_user',
    'Foreign_link',
    'Foreign_subscription',
    'Invitation',
    'Profile_prefs',
    'Profile_list',
    'Profile_tag',
    'Profile_tag_subscription',
    'Profile_block',
    'Related_group',
    'Group_inbox',
    'Group_member',
    'File',
    'File_redirection',
    'File_thumbnail',
    'File_to_post',
    'Group_block',
    'Group_alias',
    'Session',
    'Config',
    'Profile_role',
    'Location_namespace',
    'Login_token',
    'User_location_prefs',
    'User_im_prefs',
    'Local_group',
    'User_urlshortener_prefs',
    'Old_school_prefs',
    'User_username',
    'Attention'
];

foreach ($classes as $cls) {
    $schema[strtolower($cls)] = call_user_func([$cls, 'schemaDef']);
}


code:
INSERT INTO foreign_service
  (id, name, description, created)
VALUES
  (1, 'Twitter', 'Twitter Micro-blogging service', CURRENT_TIMESTAMP),
  (2, 'Facebook', 'Facebook', CURRENT_TIMESTAMP),
  (3, 'FacebookConnect', 'Facebook Connect', CURRENT_TIMESTAMP);


code:
INSERT INTO location_namespace
  (id, description, created)
VALUES
  (1, 'Geonames', CURRENT_TIMESTAMP),
  (2, 'Where on Earth', CURRENT_TIMESTAMP);


code:
INSERT INTO notice_source
  (code, name, url, created)
VALUES
  ('adium', 'Adium', 'http://www.adiumx.com/', CURRENT_TIMESTAMP),
  ('Afficheur', 'Afficheur', 'http://afficheur.sourceforge.jp/', CURRENT_TIMESTAMP),
  ('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', CURRENT_TIMESTAMP),
  ('anyio', 'Any.IO', 'http://any.io/', CURRENT_TIMESTAMP),
  ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', CURRENT_TIMESTAMP),
  ('bti','bti','http://gregkh.github.com/bti/', CURRENT_TIMESTAMP),
  ('choqok', 'Choqok', 'http://choqok.gnufolks.org/', CURRENT_TIMESTAMP),
  ('cliqset', 'Cliqset', 'http://www.cliqset.com/', CURRENT_TIMESTAMP),
  ('DarterosStatus', 'Darteros Status', 'http://www.darteros.com/doc/Darteros_Status', CURRENT_TIMESTAMP),
  ('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', CURRENT_TIMESTAMP),
  ('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', CURRENT_TIMESTAMP),
  ('drupal','Drupal','http://drupal.org/', CURRENT_TIMESTAMP),
  ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/', CURRENT_TIMESTAMP),
  ('identica-mode','Emacs Identica-mode','http://nongnu.org/identica-mode/', CURRENT_TIMESTAMP),
  ('Facebook','Facebook','http://apps.facebook.com/identica/', CURRENT_TIMESTAMP),
  ('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', CURRENT_TIMESTAMP),
  ('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', CURRENT_TIMESTAMP),
  ('gNewBook', 'gNewBook', 'http://www.gnewbook.org/', CURRENT_TIMESTAMP),
  ('gravity', 'Gravity', 'http://mobileways.de/gravity', CURRENT_TIMESTAMP),
  ('Gwibber','Gwibber','http://launchpad.net/gwibber', CURRENT_TIMESTAMP),
  ('HelloTxt','HelloTxt','http://hellotxt.com/', CURRENT_TIMESTAMP),
  ('identicatools','Laconica Tools','http://bitbucketlabs.net/laconica-tools/', CURRENT_TIMESTAMP),
  ('identichat','identichat','http://identichat.prosody.im/', CURRENT_TIMESTAMP),
  ('IdentiCurse','IdentiCurse','http://identicurse.net/', CURRENT_TIMESTAMP),
  ('IdentiFox','IdentiFox','http://www.bitbucket.org/uncryptic/identifox/', CURRENT_TIMESTAMP),
  ('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', CURRENT_TIMESTAMP),
  ('Jiminy','Jiminy','', CURRENT_TIMESTAMP),
  ('LaTwit','LaTwit','http://latwit.mac65.com/', CURRENT_TIMESTAMP),
  ('LiveTweeter', 'LiveTweeter', 'http://addons.songbirdnest.com/addon/1204', CURRENT_TIMESTAMP),
  ('livetweeter', 'livetweeter', 'http://addons.songbirdnest.com/addon/1204', CURRENT_TIMESTAMP),
  ('maisha', 'Maisha', 'http://maisha.grango.org/', CURRENT_TIMESTAMP),
  ('mbpidgin','mbpidgin','', CURRENT_TIMESTAMP),
  ('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', CURRENT_TIMESTAMP),
  ('moconica','Moconica','http://moconica.com/', CURRENT_TIMESTAMP),
  ('mustard', 'mustard', 'http://mustard.macno.org', CURRENT_TIMESTAMP),
  ('nambu','Nambu','http://www.nambu.com/', CURRENT_TIMESTAMP),
  ('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.com/', CURRENT_TIMESTAMP),
  ('Pikchur','Pikchur','http://www.pikchur.com/', CURRENT_TIMESTAMP),
  ('Ping.fm','Ping.fm','http://ping.fm/', CURRENT_TIMESTAMP),
  ('pingvine','PingVine','http://pingvine.com/', CURRENT_TIMESTAMP),
  ('pocketwit','PockeTwit','', CURRENT_TIMESTAMP),
  ('posty','Posty','http://spreadingfunkyness.com/posty/', CURRENT_TIMESTAMP),
  ('qtwitter','qTwitter','http://qtwitter.ayoy.net/', CURRENT_TIMESTAMP),
  ('qwit', 'Qwit', '', CURRENT_TIMESTAMP),
  ('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', CURRENT_TIMESTAMP),
  ('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', CURRENT_TIMESTAMP),
  ('rygh.no','rygh.no','http://rygh.no/', CURRENT_TIMESTAMP),
  ('ryghsms','ryghsms','http://sms.rygh.no/', CURRENT_TIMESTAMP),
  ('smob','SMOB','http://smob.sioc-project.org/', CURRENT_TIMESTAMP),
  ('socialoomphBfD4pMqz31', 'SocialOomph', 'http://www.socialoomph.com/', CURRENT_TIMESTAMP),
  ('spaz','Spaz','http://funkatron.com/spaz', CURRENT_TIMESTAMP),
  ('StatusNet Desktop', 'StatusNet Desktop', 'http://status.net/desktop', CURRENT_TIMESTAMP),
  ('StatusNet Mobile', 'StatusNet Mobile', 'http://status.net/mobile', CURRENT_TIMESTAMP),
  ('StatusNet iPhone', 'iPhone', 'http://status.net/iphone', CURRENT_TIMESTAMP),
  ('StatusNet Android', 'Android', 'http://status.net/android', CURRENT_TIMESTAMP),
  ('StatusNet Blackberry', 'Blackberry', 'http://status.net/blackberry', CURRENT_TIMESTAMP),
  ('tarpipe','tarpipe','http://tarpipe.com/', CURRENT_TIMESTAMP),
  ('tjunar','Tjunar','http://nederflash.nl/boek/titels/tjunar-air', CURRENT_TIMESTAMP),
  ('tr.im','tr.im','http://tr.im/', CURRENT_TIMESTAMP),
  ('triklepost', 'Tricklepost', 'http://github.com/zcopley/tricklepost/tree/master', CURRENT_TIMESTAMP),
  ('tweenky','Tweenky','http://beta.tweenky.com/', CURRENT_TIMESTAMP),
  ('TweetDeck', 'TweetDeck', 'http://www.tweetdeck.com/', CURRENT_TIMESTAMP),
  ('twhirl','Twhirl','http://www.twhirl.org/', CURRENT_TIMESTAMP),
  ('twibble','twibble','http://www.twibble.de/', CURRENT_TIMESTAMP),
  ('Twidge','Twidge','http://software.complete.org/twidge', CURRENT_TIMESTAMP),
  ('twidge','Twidge','http://software.complete.org/twidge', CURRENT_TIMESTAMP),
  ('twidroid','twidroid','http://www.twidroid.com/', CURRENT_TIMESTAMP),
  ('twittelator','Twittelator','http://www.stone.com/iPhone/Twittelator/', CURRENT_TIMESTAMP),
  ('twitter','Twitter','http://twitter.com/', CURRENT_TIMESTAMP),
  ('twitterfeed','twitterfeed','http://twitterfeed.com/', CURRENT_TIMESTAMP),
  ('twitterphoto','TwitterPhoto','http://richfish.org/twitterphoto/', CURRENT_TIMESTAMP),
  ('twitterpm','Net::Twitter','http://search.cpan.org/dist/Net-Twitter/', CURRENT_TIMESTAMP),
  ('twittertools','Twitter Tools','http://wordpress.org/extend/plugins/twitter-tools/', CURRENT_TIMESTAMP),
  ('twitux','Twitux','http://live.gnome.org/DanielMorales/Twitux', CURRENT_TIMESTAMP),
  ('twitvim','TwitVim','http://vim.sourceforge.net/scripts/script.php?script_id=2204', CURRENT_TIMESTAMP),
  ('Updating.Me','Updating.Me','http://updating.me/', CURRENT_TIMESTAMP),
  ('urfastr','urfastr','http://urfastr.net/', CURRENT_TIMESTAMP),
  ('yatca','Yatca','http://www.yatca.com/', CURRENT_TIMESTAMP),
  ('rss.me', 'rss.me', 'http://rss.me/', CURRENT_TIMESTAMP);


code:
/* For managing multiple sites */

create table status_network (

    site_id  integer auto_increment primary key comment 'unique id',
    nickname varchar(64)  unique key comment 'nickname',
    hostname varchar(255) unique key comment 'alternate hostname if any',
    pathname varchar(255) unique key comment 'alternate pathname if any',

    dbhost varchar(255) comment 'database host',
    dbuser varchar(255) comment 'database username',
    dbpass varchar(255) comment 'database password',
    dbname varchar(255) comment 'database name',

    sitename varchar(255) comment 'display name',
    theme varchar(255) comment 'theme name',
    logo varchar(255) comment 'site logo',

    tags text comment 'site meta-info tags (pipe-separated)',

    created datetime not null comment 'date this record was created' default now(),
    modified timestamp comment 'date this record was modified' default now()

) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

create table status_network_tag (
    site_id integer  comment 'unique id',
    tag varchar(64) comment 'tag name',
    created datetime not null comment 'date the record was created',

    constraint primary key (site_id, tag),
    index status_network_tag_tag_idx (tag)
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;


code:
INSERT INTO sms_carrier
  (id, name, email_pattern, created)
VALUES
  (100056, '3 River Wireless', '%s@sms.3rivers.net', CURRENT_TIMESTAMP),
  (100057, '7-11 Speakout', '%s@cingularme.com', CURRENT_TIMESTAMP),
  (100058, 'Airtel (Karnataka, India)', '%s@airtelkk.com', CURRENT_TIMESTAMP),
  (100059, 'Alaska Communications Systems', '%s@msg.acsalaska.com', CURRENT_TIMESTAMP),
  (100060, 'Alltel Wireless', '%s@message.alltel.com', CURRENT_TIMESTAMP),
  (100061, 'AT&T Wireless', '%s@txt.att.net', CURRENT_TIMESTAMP),
  (100062, 'Bell Mobility (Canada)', '%s@txt.bell.ca', CURRENT_TIMESTAMP),
  (100063, 'Boost Mobile', '%s@myboostmobile.com', CURRENT_TIMESTAMP),
  (100064, 'Cellular One (Dobson)', '%s@mobile.celloneusa.com', CURRENT_TIMESTAMP),
  (100065, 'Cingular (Postpaid)', '%s@cingularme.com', CURRENT_TIMESTAMP),
  (100066, 'Centennial Wireless', '%s@cwemail.com', CURRENT_TIMESTAMP),
  (100067, 'Cingular (GoPhone prepaid)', '%s@cingularme.com', CURRENT_TIMESTAMP),
  (100068, 'Claro (Nicaragua)', '%s@ideasclaro-ca.com', CURRENT_TIMESTAMP),
  (100069, 'Comcel', '%s@comcel.com.co', CURRENT_TIMESTAMP),
  (100070, 'Cricket', '%s@sms.mycricket.com', CURRENT_TIMESTAMP),
  (100071, 'CTI', '%s@sms.ctimovil.com.ar', CURRENT_TIMESTAMP),
  (100072, 'Emtel (Mauritius)', '%s@emtelworld.net', CURRENT_TIMESTAMP),
  (100073, 'Fido (Canada)', '%s@fido.ca', CURRENT_TIMESTAMP),
  (100074, 'General Communications Inc.', '%s@msg.gci.net', CURRENT_TIMESTAMP),
  (100075, 'Globalstar', '%s@msg.globalstarusa.com', CURRENT_TIMESTAMP),
  (100076, 'Helio', '%s@myhelio.com', CURRENT_TIMESTAMP),
  (100077, 'Illinois Valley Cellular', '%s@ivctext.com', CURRENT_TIMESTAMP),
  (100078, 'i wireless', '%s.iws@iwspcs.net', CURRENT_TIMESTAMP),
  (100079, 'Meteor (Ireland)', '%s@sms.mymeteor.ie', CURRENT_TIMESTAMP),
  (100080, 'Mero Mobile (Nepal)', '%s@sms.spicenepal.com', CURRENT_TIMESTAMP),
  (100081, 'MetroPCS', '%s@mymetropcs.com', CURRENT_TIMESTAMP),
  (100082, 'Movicom', '%s@movimensaje.com.ar', CURRENT_TIMESTAMP),
  (100083, 'Mobitel (Sri Lanka)', '%s@sms.mobitel.lk', CURRENT_TIMESTAMP),
  (100084, 'Movistar (Colombia)', '%s@movistar.com.co', CURRENT_TIMESTAMP),
  (100085, 'MTN (South Africa)', '%s@sms.co.za', CURRENT_TIMESTAMP),
  (100086, 'MTS (Canada)', '%s@text.mtsmobility.com', CURRENT_TIMESTAMP),
  (100087, 'Nextel (Argentina)', '%s@nextel.net.ar', CURRENT_TIMESTAMP),
  (100088, 'Orange (Poland)', '%s@orange.pl', CURRENT_TIMESTAMP),
  (100089, 'Personal (Argentina)', '%s@personal-net.com.ar', CURRENT_TIMESTAMP),
  (100090, 'Plus GSM (Poland)', '%s@text.plusgsm.pl', CURRENT_TIMESTAMP),
  (100091, 'President''s Choice (Canada)', '%s@txt.bell.ca', CURRENT_TIMESTAMP),
  (100092, 'Qwest', '%s@qwestmp.com', CURRENT_TIMESTAMP),
  (100093, 'Rogers (Canada)', '%s@pcs.rogers.com', CURRENT_TIMESTAMP),
  (100094, 'Sasktel (Canada)', '%s@sms.sasktel.com', CURRENT_TIMESTAMP),
  (100095, 'Setar Mobile email (Aruba)', '%s@mas.aw', CURRENT_TIMESTAMP),
  (100096, 'Solo Mobile', '%s@txt.bell.ca', CURRENT_TIMESTAMP),
  (100097, 'Sprint (PCS)', '%s@messaging.sprintpcs.com', CURRENT_TIMESTAMP),
  (100098, 'Sprint (Nextel)', '%s@page.nextel.com', CURRENT_TIMESTAMP),
  (100099, 'Suncom', '%s@tms.suncom.com', CURRENT_TIMESTAMP),
  (100100, 'T-Mobile', '%s@tmomail.net', CURRENT_TIMESTAMP),
  (100101, 'T-Mobile (Austria)', '%s@sms.t-mobile.at', CURRENT_TIMESTAMP),
  (100102, 'Telus Mobility (Canada)', '%s@msg.telus.com', CURRENT_TIMESTAMP),
  (100103, 'Thumb Cellular', '%s@sms.thumbcellular.com', CURRENT_TIMESTAMP),
  (100104, 'Tigo (Formerly Ola)', '%s@sms.tigo.com.co', CURRENT_TIMESTAMP),
  (100105, 'Unicel', '%s@utext.com', CURRENT_TIMESTAMP),
  (100106, 'US Cellular', '%s@email.uscc.net', CURRENT_TIMESTAMP),
  (100107, 'Verizon', '%s@vtext.com', CURRENT_TIMESTAMP),
  (100108, 'Virgin Mobile (Canada)', '%s@vmobile.ca', CURRENT_TIMESTAMP),
  (100109, 'Virgin Mobile (USA)', '%s@vmobl.com', CURRENT_TIMESTAMP),
  (100110, 'YCC', '%s@sms.ycc.ru', CURRENT_TIMESTAMP),
  (100111, 'Orange (UK)', '%s@orange.net', CURRENT_TIMESTAMP),
  (100112, 'Cincinnati Bell Wireless', '%s@gocbw.com', CURRENT_TIMESTAMP),
  (100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', CURRENT_TIMESTAMP),
  (100114, 'Vodafone Germany', '%s@vodafone-sms.de', CURRENT_TIMESTAMP),
  (100115, 'E-Plus', '%s@smsmail.eplus.de', CURRENT_TIMESTAMP),
  (100116, 'Cellular South', '%s@csouth1.com', CURRENT_TIMESTAMP),
  (100117, 'ChinaMobile (139)', '%s@139.com', CURRENT_TIMESTAMP),
  (100118, 'Dialog Axiata', '%s@dialog.lk', CURRENT_TIMESTAMP);


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

%%site.name%% is a
social network based on the Free Software [GNU social](https://git.gnu.io/gnu/gnu-social) tool.

If you [register](%%action.register%%) for an account,
you can post small (%%site.textlimit%% chars or less) text notices
about yourself, where you are, what you're doing, or practically
anything you want. You can also subscribe to the notices of your
friends, or other people you're interested in, and follow them on the
Web or in an [RSS](https://en.wikipedia.org/wiki/RSS) feed.

path: doc-src/about


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

%%site.name%% provides APIs that applications can use to interact with it:

* [AtomPub](atompub)
* [Twitter-compatible API](twitterapi)

## API discovery

The base URLs for the APIs can be obtained using [Really Simple Discovery](https://en.wikipedia.org/wiki/Really_Simple_Discovery).

path: doc-src/api


code:
> The Atom Publishing Protocol (AtomPub) is an application-level
> protocol for publishing and editing Web resources.  The protocol is
> based on HTTP transfer of Atom-formatted representations.  The Atom
> format is documented in the Atom Syndication Format.

You can find more information about AtomPub in [RFC5023](https://tools.ietf.org/html/rfc5023).

> Activity Streams is an open format specification for activity stream protocols,
> which are used to syndicate activities taken in social web applications and
> services.

You can find more information about Activity Streams at [activitystrea.ms](http://activitystrea.ms/).

## Authentication

The API supports both
[HTTP Basic](https://en.wikipedia.org/wiki/Basic_access_authentication)
and [OAuth](https://en.wikipedia.org/wiki/OAuth).

## Service document

The service document for an account is found at
`/api/statusnet/app/service/<nickname>.xml`

Each service document has one workspace ('Main') and four collections:

* **notices**: notices generated by the user
* **subscriptions**: subscriptions by the user
* **favorites**: the user's favorites
* **memberships**: the user's group memberships

Collections are identified by the `<activity:verb>` element(s) in their
`<collection>` element.

## Notices

Notice feeds, in reverse-chronological order, are at
`/api/statuses/user_timeline/<id>.atom`.

This is a partial feed; navigation links are included in the feed to scroll forward
and back.

Notices are represented as Activity Streams events with the "Post" verb and "Note" object-type:

    <entry>
     <activity:object-type>
       http://activitystrea.ms/schema/1.0/note
     </activity:object-type>
     [...]
     <activity:verb>
       http://activitystrea.ms/schema/1.0/post
     </activity:verb>
     [...]
    </entry>

Repeats are be represented as Activity Streams events with the "Share" verb, and with the activity object being an entry representing a Notice:

    <entry>
     <activity:verb>
       http://activitystrea.ms/schema/1.0/share
     </activity:verb>
     [...]
     <activity:object>
      <activity:object-type>
        http://activitystrea.ms/schema/1.0/activity
      </activity:object-type>
      [...]
      <activity:verb>
        http://activitystrea.ms/schema/1.0/post
      </activity:verb>
      [...]
      <activity:object>
       <activity:object-type>
         http://activitystrea.ms/schema/1.0/note
       </activity:object-type>
       [...]
      </activity:object>
      [...]
     </activity:object>
     [...]
    </entry>

Posted files will be represented by the "Post" verb and "Image, File, Video" object-type.

### Single-notice URL

Single notices are be available as an Activity Streams event at `/api/statuses/show/<notice-id>.atom`.

    <entry>
     <activity:object-type>
      http://activitystrea.ms/schema/1.0/note
     </activity:object-type>
     [...]
     <activity:verb>
      http://activitystrea.ms/schema/1.0/post
     </activity:verb>
     <author>
      <activity:object-type>
       http://activitystrea.ms/schema/1.0/person
      </activity:object-type>
      [...]
     </author>
    </entry>

### Posting a notice

A notice can be posted by sending a POST request containing a single `<entry>`
element to the URL of the notice feed. These should have a "Post" verb, and a "Note"
object-type, but since these are the default values, Atom entries that aren't
marked up as Activity Streams objects should be fine to post.

The resulting entry will be returned, per the APP, in Activity Streams format. The
location of the notice can be read from the Content-Location HTTP header of the
result or from the rel=self URL in the resulting entry.

### Editing a notice

Notices cannot be edited. PUT requests to a notice URL will fail.

### Deleting a notice

A single notice can be deleted by posting a DELETE HTTP request to the notice's
Atom representation.

Example with cURL:

    curl -u username:password -X DELETE \
      http://example.org/api/statuses/show/<notice-id>.atom

## Subscriptions

The subscriptions feed, in reverse-chronological order, is at
`/api/statusnet/app/subscriptions/<id>.atom`.

This is a partial feed; it includes the navigation links necessary to scroll forward
and back.

Subscriptions are represented as Activity Streams entries with the "Follow" verb and
"Person" object-type.

### Subscription URL

A subscription has an URL at
`/api/statusnet/app/subscriptions/<subscriber id>/<subscribed id>.atom`.

### Adding a new subscription

To add a new subscription, POST an Activity Streams `<entry>` with a "Follow" verb
and "Person" object-type.

The resulting entry will be returned, per the APP, in Activity Streams format. The
location of the subscription can be read from the Content-Location HTTP header of
the result or from the rel=self URL in the resulting entry.

### Editing a subscription

Subscriptions cannot be edited. PUT requests to the subscription URL will result in
an error.

### Deleting a subscription

To delete a subscription, send a DELETE HTTP request to the Subscription URL.

## Favorites

The feed of the user's favorites, in reverse-chronological order, is at
`/api/statusnet/app/favorites/<user id>.atom`.

This is a partial feed; it includes the navigation links necessary to scroll forward
and back.

Favorites are represented as Activity Streams entries with the "Favorite" verb and
"Note" object-type.

### Favorite URL

Favorite entries have a self URL at
`/api/statusnet/app/favorites/<user id>/<notice id>.atom`.

### Favoriting a notice

To favorite a notice, POST an Activity Streams `<entry>` with the "Favorite" verb and
"Note" object-type.

The resulting favorite will be returned, per the APP, in Activity Streams format.
The location of the favorite can be read from the Content-Location HTTP header of
the result or from the rel=self URL in the resulting entry.

### Editing a favorite

Favorites cannot be edited. PUT requests to a favorite URL will fail.

### Deleting a favorite

To "unfavorite" a notice, POST a DELETE request to the URL for the favorite.

## Groups

A feed of group memberships, in reverse-chron order, is available at
`/api/statusnet/app/memberships/<user id>.atom`.

This is a partial feed; it includes the navigation links necessary to scroll forward
and back.

Memberships are represented as Activity Streams entries with the "Join" ber and
"Group" object-type.

### Membership URL

Each membership has a representation at
`/api/statusnet/app/memberships/<user id>/<group id>.atom`.

### Joining a group

To join a group, POST an activity entry with a "Join" verb and "Group" object-type to
the memberships feed.

The resulting membership will be returned, per the APP, in Activity Streams format.
The location of the membership can be read from the Content-Location HTTP header of
the result or from the rel=self URL in the resulting entry.

### Editing group membership

Group memberships cannot be edited. PUT requests to a membership feed will fail.

### Leaving a group

To leave a group, send a DELETE request to the membership URL.

path: doc-src/atompub


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

There are a number of options for getting in contact with responsible
people for %%site.name%%.

Bugs
----

If you think you've found a bug in the underlying [GNU social](https://gnu.io/social/) software,
or if there's a new feature you'd like to see, add it into the [GNU social task list](https://git.gnu.io/gnu/gnu-social/issues).
Don't forget to check the list of existing bugs to make sure it hasn't already been reported!

Email
-----

You can reach the responsible party for this server at [%%site.email%%](mailto:%%site.email%%).

Post a notice
-------------

If you have a question about how to do something, just post a notice with
your question, preferrably tagged with #NewHere. Watch your inbox for replies.

path: doc-src/contact


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

These are some *Frequently Asked Questions* about this service, with
some answers.

What is this site?
----------------------

This is a social network, running the GNU social software.

You can use it to make connections between friends, family and
colleagues -- writing short notices about yourself, where you are, and
what you're doing, and those notices will be sent to all your contacts.

In the future, we'll be adding support for photo, video and file
sharing, as well as events, better contact management and mobile
devices.

path: doc-src/faq


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

Users on %%site.name%% can create *groups* that other users can join.
Groups can be a great way to share information and entertainment with
a group of people who have a common interest or background; who work
together on a team; or who have a particular knowledge or skill.

You can find out about groups on the server on the
[Groups](%%action.groups%%) page. You can join a group by clicking on
the "Join" button either in the group list or on the group's home page.

Starting a new group
--------------------

You can start a new group for friends and colleagues. Note that all
groups are free for anyone to join.

To start a new group, use the [new group](%%action.newgroup%%) tool
and fill out the form. Describe your group as best you can if you want
people to be able to find it.

When choosing the nickname for your group, try to keep it short. The
nickname is sometimes included in messages to and from the group, so
the less chars the better. Try using acronyms for organizations, or
airport codes for places (like 'pdx' instead of 'portland').

Sending messages to a group
---------------------------

You can send a message to a group using the syntax "!groupname"
anywhere in the message. If you have more than one group named, the
notice will go to each group. Only members can send notices to a
group, and groups do not respond to direct messages (DMs).

You can also select the group from the "To:" drop down when posting.

You can make a group message private by clicking the "private" button
before posting.

Receiving messages
------------------

New group messages will appear in your inbox, and will also come to
your phone or IM client if you've set them up to receive notices.

Private groups
--------------

The administrator can make a group private. For a private group, all
notices will marked as private for group members only. Also,
administrators will have to approve all new members to the group.

Private groups are visible in the group directory.

path: doc-src/groups


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

%%site.name%% is a **social service**. Users can post short
(%%site.textlimit%% character) status messages which are broadcast to
their friends and colleagues on the service and (optionally) onto the Web.

You can also broadcast other types of data, like bookmarks, event
invitations, polls, and questions.

If you'd like to try it out, first [register](%%action.register%%) a new account.
Then, on the [public timeline](%%action.top%%), enter your message into
the textbox at the top of the page, and click "Send". It will go out on the
public timeline and to anyone who is subscribed to your notices.

To subscribe to other people's notifications, go to their profile page
and click the "subscribe" button. They'll get a notice that you're now
subscribed to their notifications.

path: doc-src/help


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

You can post messages to %%site.name%% using a [Jabber](http://jabber.org/) client
on your computer, mobile phone, or other platform. This can be a convenient way to
keep up with your friends on %%site.name%%.

If you don't already have a Jabber account, one of the
[public Jabber services](http://www.jabber.org/im-services). You'll probably also
need an IM client like [Pidgin](https://www.pidgin.im/).

Managing your IM settings
-------------------------

Use the [IM settings](%%action.imsettings%%) page to set your IM preferences. You can add or change your Jabber address and set the flags for Jabber update.

When you add or change your address, you'll receive a message from **%%xmpp.user%%@%%xmpp.server%%** asking you to confirm the change. (You may need to
add %%xmpp.user%%@%%xmpp.server%% to your buddy list *before* changing your IM settings.)

Sending updates
---------------

You send updates by sending messages to %%xmpp.user%%@%%xmpp.server%%. Messages
should be less than %%site.textlimit%% characters; longer messages will be truncated.

Commands
--------

You can do some minor management of your account through Jabber. These are the
currently-implemented commands:

* **on** - turn on notifications
* **off** - turn off notifications
* **help** - show this help
* **follow <nickname>** - subscribe to user
* **groups** - lists the groups you have joined
* **subscriptions** - list the people you follow
* **subscribers** - list the people that follow you
* **leave <nickname>** - unsubscribe from user
* **d <nickname> <text>** - direct message to user
* **get <nickname>** - get last notice from user
* **whois <nickname>** - get profile info on user
* **lose <nickname>** - force user to stop following you
* **fav <nickname>** - add user's last notice as a 'fave'
* **fav #<notice_id>** - add notice with the given id as a 'fave'
* **repeat #<notice_id>** - repeat a notice with a given id
* **repeat <nickname>** - repeat the last notice from user
* **reply #<notice_id>** - reply to notice with a given id
* **reply <nickname>** - reply to the last notice from user
* **join <group>** - join group
* **login** - Get a link to login to the web interface
* **drop <group>** - leave group
* **stats** - get your stats
* **stop** - same as 'off'
* **quit** - same as 'off'
* **sub <nickname>** - same as 'follow'
* **unsub <nickname>** - same as 'leave'
* **last <nickname>** - same as 'get'
* **on <nickname>** - not yet implemented.
* **off <nickname>** - not yet implemented.
* **nudge <nickname>** - remind a user to update.
* **invite <phone number>** - not yet implemented.
* **track <word>** - not yet implemented.
* **untrack <word>** - not yet implemented.
* **track off** - not yet implemented.
* **untrack all** - not yet implemented.
* **tracks** - not yet implemented.
* **tracking** - not yet implemented.

path: doc-src/im


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

This document outlines this service's respect for your personal
privacy as a user of the service.

- Almost all the text and files that users upload to this site is
  available under the site license (see the license block at the bottom
  of this page). Users agree to the license when they register to use
  the site for the first time. Typically that means that the data can
  be copied far and wide, for commercial and non-commercial purposes,
  and in modified or unmodified form. If you're not OK with that,
  don't use the service.
- The following data items are considered *private data* that won't be
  shared with other users, business partners, or the public at large:
  * your password
  * your email address
  * your IM address (AIM, Jabber, or other instant messaging address)
  * your phone number
  * your "private messages"
  * your login credentials (username and password) for other services (Twitter, Facebook, etc.)
- Some private data may be published in aggregate, e.g. "30% of our
  users are registered with Hotmail addresses."
- Your notices (including files) can be downloaded and re-used by
  other services, either one-by-one or in bulk as
  [RSS](https://en.wikipedia.org/wiki/RSS) files.
- Your profile information (including subscriptions and avatars) can be
  downloaded and re-used by other services, either scraped from the HTML
  interface or in bulk as [FOAF](https://en.wikipedia.org/wiki/FOAF) files.
- Your notices will be forwarded to users who subscribe to them,
  including users on another microblogging service.
- Your profile information will be sent to microblogging services for
  users who subscribe to you or to whom you subscribe.
- Based on your email preferences, you may receive automated email
  messages for important system events, such as when others subscribe
  to your notices.
- Based on your email preferences, you may receive an email
  newsletter. You can opt out of the newsletter if you don't want to
  receive it.
- In urgent situations, administrators may send you email directly to
  your registered email address, even if you've requested no notices
  or newsletter. *Administrators will use digitally-signed email.*
- This service will comply with court orders to turn over your private
  information.

path: doc-src/privacy


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

You can post messages to %%site.name%% using many kinds of cell
phones that support SMS messaging. This site does not support SMS
directly; rather, it uses your carrier's email gateway to send and
receive messages.

Managing your SMS settings
--------------------------

Use the [SMS settings](%%action.smssettings%%) page to set your SMS
preferences. You can add or change your SMS number and set the
flags for SMS updates.

When you add or change your phone number, you'll receive a message on your
phone with a verification code. Enter it into the SMS settings page to
confirm that the owner of the phone authorizes sending it messages.

Note that only the carriers listed in the drop down list on the form
are supported by %%site.name%%. They're the only ones we know how to
make email addresses for.

Receiving messages
------------------

Once you've verified your phone number, you can enable sending
messages to your phone. If you have a lot of friends and a typical
phone, it can be hard to keep up.

Sending messages
----------------

To send a message, you must send an email to the incoming email
address visible on your SMS settings page. The method for sending
email from your phone varies from carrier to carrier and from handset
to handet; if in doubt, ask your carrier.

Keep your incoming email address a secret -- it's the only way we know
you're really you!

Commands
--------

You can use the following commands with %%site.name%%.

* on - turn on notifications
* off - turn off notifications
* help - show this help
* follow <nickname> - subscribe to user
* leave <nickname> - unsubscribe from user
* d <nickname> <text> - direct message to user
* get <nickname> - get last notice from user
* whois <nickname> - get profile info on user
* fav <nickname> - add user's last notice as a 'fave'
* stats - get your stats
* stop - same as 'off'
* quit - same as 'off'
* sub <nickname> - same as 'follow'
* unsub <nickname> - same as 'leave'
* last <nickname> - same as 'get'
* on <nickname> - not yet implemented.
* off <nickname> - not yet implemented.
* nudge <nickname> - not yet implemented.
* invite <phone number> - not yet implemented.
* track <word> - not yet implemented.
* untrack <word> - not yet implemented.
* track off - not yet implemented.
* untrack all - not yet implemented.
* tracks - not yet implemented.
* tracking - not yet implemented.

path: doc-src/sms


code:
<!-- Copyright 2008-2010 StatusNet Inc. and contributors. -->
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- https://creativecommons.org/licenses/by/3.0/ for details. -->

This service uses a Free microblogging tool called **GNU social**.
GNU social is available under the [GNU Affero General Public License
Version 3.0](https://www.fsf.org/licensing/licenses/agpl-3.0.html), a
Free Software license for network services.

You can get a copy of the software by following links from the
[GNU social main site](https://gnu.io/)¹. The version of the software
that runs on *this* site is unmodified from that version. The site
also depends on certain libraries and other software; these are all
bundled in the [GNU social repository](https://git.gnu.io/gnu/gnu-social)²
and release archive files.

References
----------
1. GNU social main site, <https://gnu.io>
2. GNU social repository, <https://git.gnu.io/gnu/gnu-social>

path: doc-src/source