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

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * A class for presenting a filtered notice stream based on an upstream stream
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

abstract class FilteringNoticeStream extends NoticeStream
{
    protected $upstream;

    function __construct(NoticeStream $upstream)
    {
        $this->upstream = $upstream;
    }

    /**
     * @return boolean  true if we allow it, false if we deny it
     */
    abstract protected function filter(Notice $notice);

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        // "offset" is virtual; we have to get a lot
        $total = $offset + $limit;

        $filtered = array();

        $startAt = 0;
        $askFor  = $total;

        // Keep going till we have $total notices in $notices array,
        // or we get nothing from upstream.

        $results = null;
        $round = 0;

        do {
            $raw = $this->upstream->getNotices($startAt, $askFor, $since_id, $max_id);

            $results = $raw->N;
            if ($results == 0) {
                break;
            }

            $notices = $raw->fetchAll();
            $this->prefill($notices);
            foreach ($notices as $notice) {
                if ($this->filter($notice)) {
                    $filtered[] = $notice->id;
                    if (count($filtered) >= $total) {
                        break;
                    }
                }
            }

            // XXX: make these smarter; factor hit rate into $askFor
            $startAt += $askFor;
            $hits = count($filtered);
            $lastAsk = $askFor;

            if ($hits === 0) {
                $askFor = max(min(2 * $askFor, NOTICES_PER_PAGE * 50), NOTICES_PER_PAGE);
            } else {
                $askFor = max(min(intval(ceil(($total - $hits)*$startAt/$hits)), NOTICES_PER_PAGE * 50), NOTICES_PER_PAGE);
            }

            $round++;
        } while (count($filtered) < $total && $results >= $lastAsk);

        return array_slice(array_values($filtered), $offset, $limit);
    }

    function prefill($notices)
    {
        return;
    }
}


code:
<?php

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

/**
 * Class for notice streams that does not filter anything out.
 */
abstract class FullNoticeStream extends NoticeStream
{
    protected $selectVerbs = [];
}

repo_name: suraj/sloth

license: mit


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

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * A threaded notice list that shows all notices
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Brion Vibber <brion@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class FullThreadedNoticeList extends ThreadedNoticeList
{
    function newListItem(Notice $notice)
    {
        return new FullThreadedNoticeListItem($notice, $this->out, $this->userProfile);
    }
}

/**
 * A threaded notice list item that shows everything
 *
 * @category  General
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Brion Vibber <brion@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class FullThreadedNoticeListItem extends ThreadedNoticeListItem
{
    function initialItems()
    {
        return 1000; // @fixme
    }
}


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

/**
 * Stream of notices for a profile's "all" feed
 *
 * @category  NoticeStream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @author    Alexei Sorokin <sor.alexei@meowr.ru>
 * @author    Stephane Berube <chimo@chromic.org>
 * @copyright 2011 StatusNet, Inc.
 * @copyright 2014 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * @category  General
 * @copyright 2011 StatusNet, Inc.
 * @copyright 2014 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class InboxNoticeStream extends ScopingNoticeStream
{
    /**
     * Constructor
     *
     * @param Profile $target Profile to get a stream for
     * @param Profile $scoped Currently scoped profile (if null, it is fetched)
     */
    public function __construct(Profile $target, Profile $scoped = null)
    {
        parent::__construct(new CachingNoticeStream(new RawInboxNoticeStream($target), 'profileall:'.$target->getID()), $scoped);
    }
}

/**
 * Raw stream of notices for the target's inbox
 *
 * @category  General
 * @copyright 2011 StatusNet, Inc.
 * @copyright 2014 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class RawInboxNoticeStream extends FullNoticeStream
{
    protected $target = null;
    protected $inbox = null;

    /**
     * Constructor
     *
     * @param Profile $target Profile to get a stream for
     */
    public function __construct(Profile $target)
    {
        parent::__construct();
        $this->target = $target;
    }

    /**
     * Get IDs in a range
     *
     * @param int $offset Offset from start
     * @param int $limit Limit of number to get
     * @param int $since_id Since this notice
     * @param int $max_id Before this notice
     *
     * @return array IDs found
     */
    public function getNoticeIds($offset, $limit, $since_id = null, $max_id = null)
    {
        $notice = new Notice();
        $notice->selectAdd();
        $notice->selectAdd('id');
        // Reply:: is a table of mentions
        // Subscription:: is a table of subscriptions (every user is subscribed to themselves)
        $notice->_join .= sprintf(
            "\n" . 'NATURAL INNER JOIN (' .
            '(SELECT id FROM notice WHERE profile_id IN (SELECT subscribed FROM subscription WHERE subscriber = %1$d)) ' .
            'UNION (SELECT notice_id AS id FROM reply WHERE profile_id = %1$d) ' .
            'UNION (SELECT notice_id AS id FROM attention WHERE profile_id = %1$d) ' .
            'UNION (SELECT notice_id AS id FROM group_inbox WHERE group_id IN (SELECT group_id FROM group_member WHERE profile_id = %1$d))' .
            ') AS t1',
            $this->target->getID()
        );

        $notice->whereAdd(sprintf(
            "notice.created > TIMESTAMP '%s'",
            $notice->escape($this->target->created)
        ));

        if (!empty($since_id)) {
            $notice->whereAdd('id > ' . $since_id);
        }
        if (!empty($max_id)) {
            $notice->whereAdd('id <= ' . $max_id);
        }

        $notice->whereAdd('scope <> ' . Notice::MESSAGE_SCOPE);

        self::filterVerbs($notice, $this->selectVerbs);

        // notice.id will give us even really old posts, which were recently
        // imported. For example if a remote instance had problems and just
        // managed to post here.
        $notice->orderBy('id DESC');

        $notice->limit($offset, $limit);

        if (!$notice->find()) {
            return [];
        }

        return $notice->fetchAll('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/>.

/**
 * Widget for displaying a list of notices.
 *
 * @category  Search
 * @package   GNUsocial
 * @author    Zach Copley <zach@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * Widget-like class for showing JSON search results.
 *
 * @category  Search
 * @package   GNUsocial
 * @author    Zach Copley <zach@status.net>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 *
 */

class JSONSearchResultsList
{
    protected $notice;  // protected attrs invisible to json_encode()
    protected $rpp;

    // The below attributes are carefully named so the JSON output from
    // this obj matches the output from search.twitter.com

    public $results;
    public $since_id;
    public $max_id;
    public $refresh_url;
    public $results_per_page;
    public $completed_in;
    public $page;
    public $query;

    /**
     * constructor
     *
     * @param Notice $notice   stream of notices from DB_DataObject
     * @param string $query    the original search query
     * @param int    $rpp      the number of results to display per page
     * @param int    $page     a page offset
     * @param int    $since_id only display notices newer than this
     */

    public function __construct($notice, $query, $rpp, $page, $since_id = 0)
    {
        $this->notice           = $notice;
        $this->query            = urlencode($query);
        $this->results_per_page = $rpp;
        $this->rpp              = $rpp;
        $this->page             = $page;
        $this->since_id         = $since_id;
        $this->results          = array();
    }

    /**
     * show the list of search results
     *
     * @return int $count of the search results listed.
     */

    public function show()
    {
        $cnt = 0;
        $this->max_id = 0;

        $time_start = hrtime(true);

        while ($this->notice->fetch() && $cnt <= $this->rpp) {
            $cnt++;

            // XXX: Hmmm. this depends on desc sort order
            if (!$this->max_id) {
                $this->max_id = (int)$this->notice->id;
            }

            if ($this->since_id && $this->notice->id <= $this->since_id) {
                break;
            }

            if ($cnt > $this->rpp) {
                break;
            }

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

            // Don't show notices from deleted users

            if (!empty($profile)) {
                $item = new ResultItem($this->notice);
                array_push($this->results, $item);
            }
        }

        $time_end           = hrtime(true);
        $this->completed_in = ($time_end - $time_start) / 1000000000;

        // Set other attrs

        $this->refresh_url = '?since_id=' . $this->max_id .
            '&q=' . $this->query;

        // pagination stuff

        if ($cnt > $this->rpp) {
            $this->next_page = '?page=' . ($this->page + 1) .
                '&max_id=' . $this->max_id;
            if ($this->rpp != 15) {
                $this->next_page .= '&rpp=' . $this->rpp;
            }
            $this->next_page .= '&q=' . $this->query;
        }

        if ($this->page > 1) {
            $this->previous_page = '?page=' . ($this->page - 1) .
                '&max_id=' . $this->max_id;
            if ($this->rpp != 15) {
                $this->previous_page .= '&rpp=' . $this->rpp;
            }
            $this->previous_page .= '&q=' . $this->query;
        }

        print json_encode($this);

        return $cnt;
    }
}

/**
 * Widget for displaying a single JSON search result.
 *
 * @category  UI
 * @package   GNUsocial
 * @author    Zach Copley <zach@status.net>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 * @see       JSONSearchResultsList
 */

class ResultItem
{
    /** The notice this item is based on. */

    protected $notice;  // protected attrs invisible to json_encode()

    /** The profile associated with the notice. */

    protected $profile;

    // The below attributes are carefully named so the JSON output from
    // this obj matches the output from search.twitter.com

    public $text;
    public $to_user_id;
    public $to_user;
    public $from_user;
    public $id;
    public $from_user_id;
    public $iso_language_code;
    public $source = null;
    public $source_link = null;
    public $profile_image_url;
    public $created_at;

    /**
     * constructor
     *
     * Also initializes the profile attribute.
     *
     * @param Notice $notice The notice we'll display
     */

    public function __construct($notice)
    {
        $this->notice  = $notice;
        $this->profile = $notice->getProfile();
        $this->buildResult();
    }

    /**
     * Build a search result object
     *
     * This populates the the result in preparation for JSON encoding.
     *
     * @return void
     */

    public function buildResult()
    {
        $this->text      = $this->notice->content;
        $replier_profile = null;

        if ($this->notice->reply_to) {
            $reply = Notice::getKV(intval($this->notice->reply_to));
            if ($reply) {
                $replier_profile = $reply->getProfile();
            }
        }

        $this->to_user_id = ($replier_profile) ?
            intval($replier_profile->id) : null;
        $this->to_user    = ($replier_profile) ?
            $replier_profile->nickname : null;

        $this->from_user    = $this->profile->nickname;
        $this->id           = $this->notice->id;
        $this->from_user_id = $this->profile->id;

        $this->iso_language_code = Profile_prefs::getConfigData($this->profile, 'site', 'language');

        // set source and source_link
        $this->setSourceData();

        $this->profile_image_url = $this->profile->avatarUrl(AVATAR_STREAM_SIZE);

        $this->created_at = common_date_rfc2822($this->notice->created);
    }

    /**
     * Set the notice's source data (api/app name and URL)
     *
     * Either the name (and link) of the API client that posted the notice,
     * or one of other other channels. Uses the local notice object.
     *
     * @return void
     */
    public function setSourceData()
    {
        $source = null;
        $source_link = null;

        switch ($source) {
        case 'web':
        case 'xmpp':
        case 'mail':
        case 'omb':
        case 'api':
            // Gettext translations for the below source types are available.
            $source = _($this->notice->source);
            break;

        default:
            $ns = Notice_source::getKV($this->notice->source);
            if ($ns instanceof Notice_source) {
                $source = $ns->code;
                if (!empty($ns->url)) {
                    $source_link = $ns->url;
                    if (!empty($ns->name)) {
                        $source = $ns->name;
                    }
                }
            }
            break;
        }

        $this->source = $source;
        $this->source_link = $source_link;
    }
}


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

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

/**
 * Peopletags a user has subscribed to
 *
 * @category Widget
 * @package  StatusNet
 * @author   Shashi Gowda <connect2shashi@gmail.com>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ListsNav extends MoreMenu
{
    var $profile=null;
    var $lists=null;

    function __construct($out, Profile $profile)
    {
        parent::__construct($out);
        $this->profile = $profile;

        $this->lists = $profile->getLists(Profile::current());
    }

    function tag()
    {
        return 'lists';
    }

    function getItems()
    {
        $items = array();

        while ($this->lists->fetch()) {
                $mode = $this->lists->private ? 'private' : 'public';
                $items[] = array('showprofiletag',
                                 array('nickname' => $this->profile->getNickname(),
                                       'tag'    => $this->lists->tag),
                                 $this->lists->tag,
                                 '');
         }

         return $items;
    }

    function hasLists()
    {
        return (!empty($this->lists) && $this->lists->N > 0);
    }

    function seeAllItem()
    {
        return array('peopletagsbyuser',
                     array('nickname' => $this->profile->nickname),
                     // TRANS: Link description for seeing all lists.
                     _('See all'),
                     // TRANS: Link title for seeing all lists.
                     _('See all lists you have created.'));
    }
}


code:
<?php

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

/**
 * Moderated notice stream, will take into account the scoping of
 * notices as well as whether the profile is moderated somehow,
 * such as by sandboxing or silencing.
 *
 * Inherits $this->scoped from ScopingNoticeStream as the Profile
 * this stream is meant for. Can be null in case we're not logged in.
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2016 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      https://gnu.io/social
 */

class ModeratedNoticeStream extends ScopingNoticeStream
{
    protected function filter(Notice $notice)
    {
        if (!parent::filter($notice)) {
            return false;
        }

        if(self::include_or_not($notice) === false) {
            return false;
        }

        // If this is a repeat the repeated notice is moderated
        if($notice->isRepeat()) {
            try {
                $repeated_notice = Notice::getById($notice->repeat_of);
            } catch (Exception $e) {
                // if we can't get the repeated notice by id, something is seriously wrong with it, so don't include it
                return false;
            }

            if(self::include_or_not($repeated_notice) === false) {
                return false;
            }
        }

        return true;
    }

    protected function include_or_not(Notice $notice)
    {
        $profile = $notice->getProfile();

        if ($profile->isSandboxed()) {
            if (!$this->scoped instanceof Profile) {
                // Non-logged in users don't get to see posts by sandboxed users
                return false;
            } elseif (!$profile->sameAs($this->scoped) && !$this->scoped->hasRight(Right::REVIEWSPAM)) {
                // And if we are logged in, deny if scoped user is neither the author nor has the right to review spam
                return false;
            }
        }
    }
}


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

/**
 * Raw public stream
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

class NetworkPublicNoticeStream extends ModeratedNoticeStream
{
    public function __construct(Profile $scoped = null)
    {
        parent::__construct(
            new CachingNoticeStream(
                new RawNetworkPublicNoticeStream(),
                'networkpublic'
            ),
            $scoped
        );
    }
}

/**
 * Raw public stream
 *
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class RawNetworkPublicNoticeStream extends FullNoticeStream
{
    public function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $notice = new Notice();

        $notice->selectAdd(); // clears it
        $notice->selectAdd('id');

        $notice->orderBy('created DESC, id DESC');

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

        $notice->whereAdd('is_local = '. Notice::REMOTE);
        // -1 == blacklisted, -2 == gateway (i.e. Twitter)
        $notice->whereAdd('is_local <> '. Notice::LOCAL_NONPUBLIC);
        $notice->whereAdd('is_local <> '. Notice::GATEWAY);
        $notice->whereAdd('scope <> ' . Notice::MESSAGE_SCOPE);

        Notice::addWhereSinceId($notice, $since_id);
        Notice::addWhereMaxId($notice, $max_id);

        self::filterVerbs($notice, $this->selectVerbs);

        $ids = array();

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

        $notice->free();
        $notice = null;

        return $ids;
    }
}


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

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

/**
 * Form for posting a notice
 *
 * Frequently-used form for posting a notice
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Sarven Capadisli <csarven@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      HTMLOutputter
 */
class NoticeForm extends Form
{
    /**
     * Current action, used for returning to this page.
     */
    var $actionName = null;

    /**
     * Pre-filled content of the form
     */
    var $content = null;

    /**
     * The current user
     */
    var $user = null;

    /**
     * The notice being replied to
     */
    var $inreplyto = null;

    /**
     * Pre-filled location content of the form
     */

    var $lat;
    var $lon;
    var $location_id;
    var $location_ns;

    /** select this group from the drop-down by default. */
    var $to_group;

    /** select this user from the drop-down by default. */
    var $to_profile;

    /** Pre-click the private checkbox. */
    var $private;

    /**
     * Constructor
     *
     * @param Action $action Action we're being embedded into
     * @param array $options Array of optional parameters
     *                        'user' a user instead of current
     *                        'content' notice content
     *                        'inreplyto' ID of notice to reply to
     *                        'lat' Latitude
     *                        'lon' Longitude
     *                        'location_id' ID of location
     *                        'location_ns' Namespace of location
     * @throws UserNoProfileException
     */
    function __construct(Action $action, array $options = [])
    {
        parent::__construct($action);

        // When creating a notice form we don't want to collide with
        // possibly existing HTML elements, as naming conventions are similar.
        $this->id_suffix = rand();

        $this->actionName  = $action->trimmed('action');

        $prefill = array('content', 'inreplyto', 'lat',
                         'lon', 'location_id', 'location_ns',
                         'to_group', 'to_profile', 'private');

        foreach ($prefill as $fieldName) {
            if (array_key_exists($fieldName, $options)) {
                $this->$fieldName = $options[$fieldName];
            }
        }

        // Prefill the profile if we're replying

        if (empty($this->to_profile) &&
            !empty($this->inreplyto)) {
            $notice = Notice::getKV('id', $this->inreplyto);
            if (!empty($notice)) {
                $this->to_profile = $notice->getProfile();
            }
        }

        if (array_key_exists('user', $options)) {
            $this->user = $options['user'];
        } else {
            $this->user = common_current_user();
        }

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

        if (common_config('attachments', 'uploads')) {
            $this->enctype = 'multipart/form-data';
        }
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */

    function id()
    {
        return 'form_notice_' . $this->id_suffix;
    }

   /**
     * Class of the form
     *
     * @return string class of the form
     */

    function formClass()
    {
        return 'form_notice ajax-notice';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */

    function action()
    {
        return common_local_url('newnotice');
    }

    /**
     * Legend of the Form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Form legend for notice form.
        $this->out->element('legend', null, _('Send a notice'));
    }

    protected function placeholderText()
    {
        if ($this->inreplyto) {
            return _('Write a reply...');
        }

        return _('Share your status...');
    }

    /**
     * Data elements
     *
     * @return void
     */
    function formData()
    {
        if (Event::handle('StartShowNoticeFormData', array($this))) {
            $this->out->element('label', array('for' => 'notice_data-text',
                                               'id' => 'notice_data-text-label'),
                                // TRANS: Title for notice label. %s is the user's nickname.
                                sprintf(_('What\'s up, %s?'), $this->user->nickname));
            // XXX: vary by defined max size
            $this->out->element('textarea', array('class' => 'notice_data-text',
                                                  'required' => 'required',
                                                  'placeholder' => $this->placeholderText(),
                                                  'cols' => 35,
                                                  'rows' => 4,
                                                  'name' => 'status_textarea'),
                                ($this->content) ? $this->content : '');

            $contentLimit = Notice::maxContent();

            if ($contentLimit > 0) {
                $this->out->element('span',
                                    array('class' => 'count'),
                                    $contentLimit);
            }

            if (common_config('attachments', 'uploads')) {
                $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
                $this->out->element('label', array('class' => 'notice_data-attach',
                                                   'for'   => $this->id().'-notice_data-attach'),
                                    // TRANS: Input label in notice form for adding an attachment.
                                    _('Attach'));
                // The actual input element tends to be hidden with CSS.
                $this->out->element('input', array('class' => 'notice_data-attach',
                                                   'type' => 'file',
                                                   'name' => 'attach',
                                                   'id' => $this->id().'-notice_data-attach',
                                                   // TRANS: Title for input field to attach a file to a notice.
                                                   'title' => _('Attach a file.')));
            }
            if (!empty($this->actionName)) {
                $this->out->hidden('notice_return-to', $this->actionName, 'returnto');
            }
            $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');

            $this->out->elementStart('div', 'to-selector');
            $toWidget = new ToSelector($this->out,
                                       $this->user,
                                       (!empty($this->to_group) ? $this->to_group : $this->to_profile));

            $toWidget->show();
            $this->out->elementEnd('div');

            if ($this->profile->shareLocation()) {
                $this->out->hidden('notice_data-lat', empty($this->lat) ? (empty($this->profile->lat) ? null : $this->profile->lat) : $this->lat, 'lat');
                $this->out->hidden('notice_data-lon', empty($this->lon) ? (empty($this->profile->lon) ? null : $this->profile->lon) : $this->lon, 'lon');
                $this->out->hidden('notice_data-location_id', empty($this->location_id) ? (empty($this->profile->location_id) ? null : $this->profile->location_id) : $this->location_id, 'location_id');
                $this->out->hidden('notice_data-location_ns', empty($this->location_ns) ? (empty($this->profile->location_ns) ? null : $this->profile->location_ns) : $this->location_ns, 'location_ns');

                $this->out->elementStart('div', array('class' => 'notice_data-geo_wrap',
                                                      'data-api' => common_local_url('geocode')));

                // @fixme checkbox method allows no way to change the id without changing the name
                //$this->out->checkbox('notice_data-geo', _('Share my location'), true);
                $this->out->element('input', array(
                    'name' => 'notice_data-geo',
                    'type' => 'checkbox',
                    'class' => 'checkbox',
                    'id' => $this->id() . '-notice_data-geo',
                    'checked' => true, // ?
                ));
                $this->out->element('label', array('class' => 'notice_data-geo',
                                                   'for'   => $this->id().'-notice_data-geo'),
                                    // TRANS: Checkbox label to allow sharing geo location in notices.
                                    _('Share my location'));

                $this->out->elementEnd('div');
                // TRANS: Text to not share location for a notice in notice form.
                $share_disable_text = _('Do not share my location');
                // TRANS: Timeout error text for location retrieval in notice form.
                $error_timeout_text = _('Sorry, retrieving your geo location is taking longer than expected, please try again later');
                $this->out->inlineScript(' var NoticeDataGeo_text = {'.
                    'ShareDisable: ' .json_encode($share_disable_text).','.
                    'ErrorTimeout: ' .json_encode($error_timeout_text).
                    '}');
            }

            Event::handle('EndShowNoticeFormData', array($this));
        }
    }

    /**
     * Action elements
     *
     * @return void
     */

    function formActions()
    {
        $this->out->element('input', array('id' => 'notice_action-submit',
                                           'class' => 'submit',
                                           'name' => 'status_submit',
                                           'type' => 'submit',
                                           // TRANS: Button text for sending notice.
                                           'value' => _m('BUTTON', 'Send')));
    }
}


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

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

/**
 * widget for displaying a list of notices
 *
 * There are a number of actions that display a list of notices, in
 * reverse chronological order. This widget abstracts out most of the
 * code for UI for notice lists. It's overridden to hide some
 * data for e.g. the profile page.
 *
 * @category UI
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 * @see      Notice
 * @see      NoticeListItem
 * @see      ProfileNoticeList
 */
class NoticeList extends Widget
{
    /** the current stream of notices being displayed. */

    var $notice = null;

    protected $addressees = true;
    protected $attachments = true;
    protected $id_prefix = null;
    protected $maxchars = 0;
    protected $options = true;
    protected $show_n = NOTICES_PER_PAGE;

    /**
     * constructor
     *
     * @param mixed $notice stream of notices from DB_DataObject (may be a Notice but can also be something like an ArrayWrapper)
     */
    function __construct($notice, $out = null, array $prefs = [])
    {
        parent::__construct($out);
        $this->notice = $notice;

        // integer preferences
        foreach(array('show_n', 'maxchars') as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = (int)$prefs[$key];
            }
        }
        // boolean preferences
        foreach(array('addressees', 'attachments', 'options') as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = (bool)$prefs[$key];
            }
        }
        // string preferences
        foreach(array('id_prefix') as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = $prefs[$key];
            }
        }
    }

    /**
     * show the list of notices
     *
     * "Uses up" the stream by looping through it. So, probably can't
     * be called twice on the same list.
     *
     * @param integer $n    The amount of notices to show.
     *
     * @return int  Total amount of notices actually available.
     */
    public function show()
    {
        $this->out->elementStart('ol', array('class' => 'notices xoxo'));

		$notices = $this->notice->fetchAll();
		$total   = count($notices);
		$notices = array_slice($notices, 0, $this->show_n);

    	self::prefill($notices);

    	foreach ($notices as $notice) {

            try {
                $item = $this->newListItem($notice);
                $item->show();
            } catch (Exception $e) {
                // we log exceptions and continue
                common_log(LOG_ERR, $e->getMessage());
                continue;
            }
        }

        $this->out->elementEnd('ol');
        return $total;
    }

    /**
     * returns a new list item for the current notice
     *
     * Recipe (factory?) method; overridden by sub-classes to give
     * a different list item class.
     *
     * @param Notice $notice the current notice
     *
     * @return NoticeListItem a list item for displaying the notice
     */
    function newListItem(Notice $notice)
    {
        $prefs = array('addressees' => $this->addressees,
                       'attachments' => $this->attachments,
                       'id_prefix' => $this->id_prefix,
                       'maxchars' => $this->maxchars,
                       'options' => $this->options);
        return new NoticeListItem($notice, $this->out, $prefs);
    }

    static function prefill(array &$notices)
    {
        $scoped = Profile::current();
        $notice_ids = Notice::_idsOf($notices);

        if (Event::handle('StartNoticeListPrefill', array(&$notices, $notice_ids, $scoped))) {

            // Prefill attachments
            Notice::fillAttachments($notices);
            // Prefill the profiles
            $profiles = Notice::fillProfiles($notices);

            Event::handle('EndNoticeListPrefill', array(&$notices, &$profiles, $notice_ids, $scoped));
        }
    }
}


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

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

/**
 * Placeholder for showing faves...
 */
abstract class NoticeListActorsItem extends NoticeListItem
{
    /**
     * @return array of profile IDs
     */
    abstract function getProfiles();

    abstract function getListMessage($count, $you);

    function show()
    {
        $links = array();
        $you = false;
        $cur = common_current_user();
        foreach ($this->getProfiles() as $id) {
            if ($cur && $cur->id == $id) {
                $you = true;
                // TRANS: Reference to the logged in user in favourite list.
                array_unshift($links, _m('FAVELIST', 'You'));
            } else {
                $profile = Profile::getKV('id', $id);
                if ($profile instanceof Profile) {
                    $links[] = sprintf('%s',
                                       htmlspecialchars($profile->getUrl()),
                                       htmlspecialchars($profile->getBestName()));
                }
            }
        }

        if ($links) {
            $count = count($links);
            $msg = $this->getListMessage($count, $you);
            $out = sprintf($msg, $this->magicList($links));

            $this->showStart();
            $this->out->raw($out);
            $this->showEnd();
            return $count;
        } else {
            return 0;
        }
    }

    function magicList($items)
    {
        if (count($items) == 0) {
            return '';
        } else if (count($items) == 1) {
            return $items[0];
        } else {
            $first = array_slice($items, 0, -1);
            $last = array_slice($items, -1, 1);
            // TRANS: Separator in list of user names like "Jim, Bob, Mary".
            $separator = _(', ');
            // TRANS: For building a list such as "Jim, Bob, Mary and 5 others like this".
            // TRANS: %1$s is a list of users, separated by a separator (default: ", "), %2$s is the last user in the list.
            return sprintf(_m('FAVELIST', '%1$s and %2$s'), implode($separator, $first), implode($separator, $last));
        }
    }
}


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

/**
 * An item in a notice list
 *
 * @category  Widget
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * widget for displaying a single notice
 *
 * This widget has the core smarts for showing a single notice: what to display,
 * where, and under which circumstances. Its key method is show(); this is a recipe
 * that calls all the other show*() methods to build up a single notice. The
 * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip
 * author info (since that's implicit by the data in the page).
 *
 * @category  UI
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 * @see       NoticeList
 * @see       ProfileNoticeListItem
 */
class NoticeListItem extends Widget
{
    /** The notice this item will show. */
    public $notice = null;

    /** The notice that was repeated. */
    public $repeat = null;

    /** The profile of the author of the notice, extracted once for convenience. */
    public $profile = null;

    protected $addressees = true;
    protected $attachments = true;
    protected $id_prefix = null;
    protected $options = true;
    protected $maxchars = 0;   // if <= 0 it means use full posts
    protected $item_tag = 'li';
    protected $pa = null;

    /**
     * constructor
     *
     * Also initializes the profile attribute.
     *
     * @param Notice $notice The notice we'll display
     * @param Action|null $out
     * @param array $prefs
     */
    public function __construct(Notice $notice, Action $out = null, array $prefs = [])
    {
        parent::__construct($out);
        if (!empty($notice->repeat_of)) {
            $original = Notice::getKV('id', $notice->repeat_of);
            if (!$original instanceof Notice) { // could have been deleted
                $this->notice = $notice;
            } else {
                $this->notice = $original;
                $this->repeat = $notice;
            }
        } else {
            $this->notice = $notice;
        }

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

        // integer preferences
        foreach (['maxchars'] as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = (int)$prefs[$key];
            }
        }
        // boolean preferences
        foreach (['addressees', 'attachments', 'options'] as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = (bool)$prefs[$key];
            }
        }
        // string preferences
        foreach (['id_prefix', 'item_tag'] as $key) {
            if (array_key_exists($key, $prefs)) {
                $this->$key = $prefs[$key];
            }
        }
    }

    /**
     * recipe function for displaying a single notice.
     *
     * This uses all the other methods to correctly display a notice. Override
     * it or one of the others to fine-tune the output.
     *
     * @return void
     */
    public function show()
    {
        if (empty($this->notice)) {
            common_log(LOG_WARNING, "Trying to show missing notice; skipping.");
            return;
        } elseif (empty($this->profile)) {
            common_log(LOG_WARNING, "Trying to show missing profile (" . $this->notice->profile_id . "); skipping.");
            return;
        }

        $this->showStart();
        if (Event::handle('StartShowNoticeItem', array($this))) {
            $this->showNotice();
            Event::handle('EndShowNoticeItem', array($this));
        }
        $this->showEnd();
    }

    public function showNotice()
    {
        if (Event::handle('StartShowNoticeItemNotice', array($this))) {
            $this->showNoticeHeaders();
            $this->showContent();
            $this->showNoticeFooter();
            Event::handle('EndShowNoticeItemNotice', array($this));
        }
    }

    public function showNoticeHeaders()
    {
        $this->elementStart('section', ['class' => 'notice-headers']);
        $this->showNoticeTitle();
        $this->showAuthor();

        if (!empty($this->notice->reply_to) || count($this->getProfileAddressees()) > 0) {
            $this->elementStart('div', array('class' => 'parents'));
            try {
                $this->showParent();
            } catch (NoParentNoticeException $e) {
                // no parent notice
            } catch (InvalidUrlException $e) {
                // parent had an invalid URL so we can't show it
            }
            if ($this->addressees) {
                $this->showAddressees();
            }
            $this->elementEnd('div');
        }
        $this->elementEnd('section');
    }

    public function showNoticeFooter()
    {
        $this->elementStart('footer');
        $this->showNoticeInfo();
        if ($this->options) {
            $this->showNoticeOptions();
        }
        if ($this->attachments) {
            $this->showNoticeAttachments();
        }
        $this->elementEnd('footer');
    }

    public function showNoticeTitle()
    {
        if (Event::handle('StartShowNoticeTitle', array($this))) {
            $nameClass = $this->notice->getTitle(false) ? 'p-name ' : '';
            $this->element(
                'a',
                [
                    'href'  => $this->notice->getUri(),
                    'class' => $nameClass . 'u-uid',
                ],
                $this->notice->getTitle()
            );
            Event::handle('EndShowNoticeTitle', array($this));
        }
    }

    public function showNoticeInfo()
    {
        if (Event::handle('StartShowNoticeInfo', array($this))) {
            $this->showContextLink();
            $this->showNoticeLink();
            $this->showNoticeSource();
            $this->showNoticeLocation();
            $this->showPermalink();
            Event::handle('EndShowNoticeInfo', array($this));
        }
    }

    public function showNoticeOptions()
    {
        if (Event::handle('StartShowNoticeOptions', array($this))) {
            $user = common_current_user();
            if ($user) {
                $this->out->elementStart('div', 'notice-options');
                if (Event::handle('StartShowNoticeOptionItems', array($this))) {
                    $this->showReplyLink();
                    $this->showDeleteLink();
                    Event::handle('EndShowNoticeOptionItems', array($this));
                }
                $this->out->elementEnd('div');
            }
            Event::handle('EndShowNoticeOptions', array($this));
        }
    }

    /**
     * start a single notice.
     *
     * @return void
     */
    public function showStart()
    {
        if (Event::handle('StartOpenNoticeListItemElement', [$this])) {
            // Build up the attributes
            $attrs = [];

            // -> The id
            $id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
            $id_prefix = (strlen($this->id_prefix) ? $this->id_prefix . '-' : '');
            $id_decl = "${id_prefix}notice-${id}";
            $attrs['id'] = $id_decl;

            // -> The class
            $class = 'h-entry notice';
            if ($this->notice->scope != 0 && $this->notice->scope != 1) {
                $class .= ' limited-scope';
            }
            try {
                $class .= ' notice-source-' . common_to_alphanumeric($this->notice->source);
            } catch (Exception $e) {
                // either source or what we filtered out was a zero-length string
            }
            $attrs['class'] = $class;

            // -> Robots
            if (!$this->notice->isLocal()) {
                $attrs['data-nosnippet'] = 'true';
            }

            $this->out->elementStart($this->item_tag, $attrs);
            Event::handle('EndOpenNoticeListItemElement', [$this]);
        }
    }

    /**
     * show the author of a notice
     *
     * By default, this shows the avatar and (linked) nickname of the author.
     *
     * @return void
     */

    public function showAuthor()
    {
        $attrs = [
            'href'  => $this->profile->getUrl(),
            'class' => 'h-card',
            'title' => $this->profile->getHtmlTitle(),
        ];
        if (empty($this->repeat)) {
            $attrs['class'] .= ' p-author';
        }

        if (Event::handle('StartShowNoticeItemAuthor', array($this->profile, $this->out, &$attrs))) {
            $this->out->elementStart('a', $attrs);
            $this->showAvatar($this->profile);
            $this->out->text($this->profile->getStreamName());
            $this->out->elementEnd('a');
            Event::handle('EndShowNoticeItemAuthor', array($this->profile, $this->out));
        }
    }

    public function showParent()
    {
        $this->out->element(
            'a',
            array(
                'href' => $this->notice->getParent()->getUrl(),
                'class' => 'u-in-reply-to',
                'rel' => 'in-reply-to'
            ),
            'in reply to'
        );
    }

    public function showAddressees()
    {
        $pa = $this->getProfileAddressees();

        if (!empty($pa)) {
            $this->out->elementStart('ul', 'addressees');
            $first = true;
            foreach ($pa as $addr) {
                $this->out->elementStart('li');
                $text = $addr['text'];
                unset($addr['text']);
                $this->out->element('a', $addr, $text);
                $this->out->elementEnd('li');
            }
            $this->out->elementEnd('ul');
        }
    }

    public function getProfileAddressees()
    {
        if ($this->pa) {
            return $this->pa;
        }
        $this->pa = array();

        $attentions = $this->getAttentionProfiles();

        foreach ($attentions as $attn) {
            if ($attn->isGroup()) {
                $class = 'group';
                $profileurl = common_local_url('groupbyid', array('id' => $attn->getGroup()->getID()));
            } else {
                $class = 'account';
                $profileurl = common_local_url('userbyid', array('id' => $attn->getID()));
            }
            $this->pa[] = [
                'href'  => $profileurl,
                'title' => $attn->getHtmlTitle(),
                'class' => "addressee {$class} p-name u-url",
                'text'  => $attn->getStreamName(),
            ];
        }

        return $this->pa;
    }

    public function getAttentionProfiles()
    {
        return $this->notice->getAttentionProfiles();
    }

    /**
     * show the nickname of the author
     *
     * Links to the author's profile page
     *
     * @return void
     */
    public function showNickname()
    {
        $this->out->raw(
            '' .
            htmlspecialchars($this->profile->getNickname()) .
            ''
        );
    }

    /**
     * show the content of the notice
     *
     * Shows the content of the notice. This is pre-rendered for efficiency
     * at save time. Some very old notices might not be pre-rendered, so
     * they're rendered on the spot.
     *
     * @return void
     */
    public function showContent()
    {
        // FIXME: URL, image, video, audio
        $nameClass = $this->notice->getTitle(false) ? '' : 'p-name ';
        $this->out->elementStart('article', array('class' => $nameClass . 'e-content'));
        if (Event::handle('StartShowNoticeContent', array($this->notice, $this->out, $this->out->getScoped()))) {
            if ($this->maxchars > 0 && mb_strlen($this->notice->content) > $this->maxchars) {
                $this->out->text(mb_substr($this->notice->content, 0, $this->maxchars) . '[…]');
            } else {
                $this->out->raw($this->notice->getRendered());
            }
            Event::handle('EndShowNoticeContent', array($this->notice, $this->out, $this->out->getScoped()));
        }
        $this->out->elementEnd('article');
    }

    public function showNoticeAttachments()
    {
        if (common_config('attachments', 'show_thumbs')) {
            $al = new InlineAttachmentList($this->notice, $this->out);
            $al->show();
        }
    }

    /**
     * show the link to the main page for the notice
     *
     * Displays a local link to the rendered notice, with "relative" time.
     *
     * @return void
     */
    public function showNoticeLink()
    {
        $this->out->element(
            'time',
            [
                'class' => 'dt-published',
                'datetime' => common_date_iso8601($this->notice->created),
                'title' => common_exact_date($this->notice->created),
            ],
            common_date_string($this->notice->created)
        );
    }

    /**
     * show the notice location
     *
     * shows the notice location in the correct language.
     *
     * If an URL is available, makes a link. Otherwise, just a span.
     *
     * @return void
     */
    public function showNoticeLocation()
    {
        try {
            $location = Notice_location::locFromStored($this->notice);
        } catch (NoResultException $e) {
            return;
        } catch (ServerException $e) {
            return;
        }

        $name = $location->getName();

        $lat = $location->lat;
        $lon = $location->lon;
        $latlon = (!empty($lat) && !empty($lon)) ? $lat . ';' . $lon : '';

        if (empty($name)) {
            $latdms = $this->decimalDegreesToDMS(abs($lat));
            $londms = $this->decimalDegreesToDMS(abs($lon));
            // TRANS: Used in coordinates as abbreviation of north.
            $north = _('N');
            // TRANS: Used in coordinates as abbreviation of south.
            $south = _('S');
            // TRANS: Used in coordinates as abbreviation of east.
            $east = _('E');
            // TRANS: Used in coordinates as abbreviation of west.
            $west = _('W');
            $name = sprintf(
                // TRANS: Coordinates message.
                // TRANS: %1$s is lattitude degrees, %2$s is lattitude minutes,
                // TRANS: %3$s is lattitude seconds, %4$s is N (north) or S (south) depending on lattitude,
                // TRANS: %5$s is longitude degrees, %6$s is longitude minutes,
                // TRANS: %7$s is longitude seconds, %8$s is E (east) or W (west) depending on longitude,
                _('%1$u°%2$u\'%3$u"%4$s %5$u°%6$u\'%7$u"%8$s'),
                $latdms['deg'],
                $latdms['min'],
                $latdms['sec'],
                ($lat > 0 ? $north : $south),
                $londms['deg'],
                $londms['min'],
                $londms['sec'],
                ($lon > 0 ? $east : $west)
            );
        }

        $url = $location->getUrl();

        $this->out->text(' ');
        $this->out->elementStart('span', array('class' => 'location'));
        // TRANS: Followed by geo location.
        $this->out->text(_('at'));
        $this->out->text(' ');
        if (empty($url)) {
            $this->out->element(
                'abbr',
                [
                    'class' => 'geo',
                    'title' => $latlon,
                ],
                $name
            );
        } else {
            $xstr = new XMLStringer(false);
            $xstr->elementStart(
                'a',
                ['href' => $url, 'rel' => 'external']
            );
            $xstr->element(
                'abbr',
                ['class' => 'geo', 'title' => $latlon],
                $name
            );
            $xstr->elementEnd('a');
            $this->out->raw($xstr->getString());
        }
        $this->out->elementEnd('span');
    }

    /**
     * @param number $dec decimal degrees
     * @return array split into 'deg', 'min', and 'sec'
     */
    public function decimalDegreesToDMS($dec)
    {
        $deg = intval($dec);
        $tempma = abs($dec) - abs($deg);

        $tempma = $tempma * 3600;
        $min = floor($tempma / 60);
        $sec = $tempma - ($min * 60);

        return ['deg' => $deg, 'min' => $min, 'sec' => $sec];
    }

    /**
     * Show the source of the notice
     *
     * Either the name (and link) of the API client that posted the notice,
     * or one of other other channels.
     *
     * @return void
     * @throws Exception
     */
    public function showNoticeSource()
    {
        $ns = $this->notice->getSource();

        if (!$ns instanceof Notice_source) {
            return;
        }

        // TRANS: A possible notice source (web interface).
        $source_name = (empty($ns->name)) ? ($ns->code ? _($ns->code) : _m('SOURCE', 'web')) : _($ns->name);
        $this->out->text(' ');
        $this->out->elementStart('span', 'source');
        // @todo FIXME: probably i18n issue. If "from" is followed by text, that should be a parameter to "from" (from %s).
        // TRANS: Followed by notice source.
        $this->out->text(_('from'));
        $this->out->text(' ');

        $name = $source_name;
        $url = $ns->url;
        $title = null;

        if (Event::handle('StartNoticeSourceLink', array($this->notice, &$name, &$url, &$title))) {
            $name = $source_name;
            $url = $ns->url;
        }
        Event::handle('EndNoticeSourceLink', array($this->notice, &$name, &$url, &$title));

        // if $ns->name and $ns->url are populated we have
        // configured a source attr somewhere
        if (!empty($name) && !empty($url)) {
            $this->out->elementStart('span', 'device');

            $attrs = array(
                'href' => $url,
                'rel' => 'external'
            );

            if (!empty($title)) {
                $attrs['title'] = $title;
            }

            $this->out->element('a', $attrs, $name);
            $this->out->elementEnd('span');
        } else {
            $this->out->element('span', 'device', $name);
        }

        $this->out->elementEnd('span');
    }

    /**
     * show link to single-notice view for this notice item
     *
     * A permalink that goes to this specific object and nothing else
     *
     * @return void
     */
    public function showPermalink()
    {
        $class = 'permalink u-url';
        if (!$this->notice->isLocal()) {
            $class .= ' external';
        }

        try {
            if ($this->repeat) {
                $this->out->element(
                    'a',
                    [
                        'href'  => $this->repeat->getUrl(),
                        'class' => 'u-url',
                    ],
                    ''
                );
                $class = str_replace('u-url', 'u-repost-of', $class);
            }
        } catch (InvalidUrlException $e) {
            // no permalink available
        }

        try {
            $this->out->element(
                'a',
                [
                    'href'  => $this->notice->getUrl(true),
                    'class' => $class,
                ],
                // TRANS: Addition in notice list item for single-notice view.
                _('permalink')
            );
        } catch (InvalidUrlException $e) {
            // no permalink available
        }
    }

    /**
     * Show link to conversation view.
     */
    public function showContextLink()
    {
        $this->out->element(
            'a',
            [
                'rel'   => 'bookmark',
                'class' => 'timestamp',
                'href'  => Conversation::getUrlFromNotice($this->notice),
            ],
            // TRANS: A link to the conversation view of a notice, on the local server.
            _('In conversation')
        );
    }

    /**
     * show a link to reply to the current notice
     *
     * Should either do the reply in the current notice form (if available), or
     * link out to the notice-posting form. A little flakey, doesn't always work.
     *
     * @return void
     */
    public function showReplyLink()
    {
        if (common_logged_in()) {
            $this->out->text(' ');
            $reply_url = common_local_url(
                'newnotice',
                [
                    'replyto'   => $this->profile->getNickname(),
                    'inreplyto' => $this->notice->id,
                ]
            );
            $this->out->elementStart(
                'a',
                [
                    'href' => $reply_url,
                    'class' => 'notice_reply',
                    // TRANS: Link title in notice list item to reply to a notice.
                    'title' => _('Reply to this notice.'),
                ]
            );
            // TRANS: Link text in notice list item to reply to a notice.
            $this->out->text(_('Reply'));
            $this->out->text(' ');
            $this->out->element('span', 'notice_id', $this->notice->id);
            $this->out->elementEnd('a');
        }
    }

    /**
     * if the user is the author, let them delete the notice
     *
     * @return void
     * @throws Exception
     */
    public function showDeleteLink()
    {
        $user = common_current_user();

        $todel = (empty($this->repeat)) ? $this->notice : $this->repeat;

        if (!empty($user) &&
            !$this->notice->isVerb([ActivityVerb::DELETE]) &&
            ($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
            $this->out->text(' ');
            $deleteurl = common_local_url(
                'deletenotice',
                ['notice' => $todel->id]
            );
            $this->out->element(
                'a',
                [
                    'href'  => $deleteurl,
                    'class' => 'notice_delete popup',
                    // TRANS: Link title in notice list item to delete a notice.
                    'title' => _m('Delete this notice from the timeline.'),
                ],
                // TRANS: Link text in notice list item to delete a notice.
                _m('Delete')
            );
        }
    }

    /**
     * finish the notice
     *
     * Close the last elements in the notice list item
     *
     * @return void
     */
    public function showEnd()
    {
        if (Event::handle('StartCloseNoticeListItemElement', [$this])) {
            $this->out->elementEnd('li');
            Event::handle('EndCloseNoticeListItemElement', [$this]);
        }
    }

    /**
     * Get the notice in question
     *
     * For hooks, etc., this may be useful
     *
     * @return Notice The notice we're showing
     */

    public function getNotice()
    {
        return $this->notice;
    }
}


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

if (!defined('STATUSNET')) {
    // This check helps protect against security problems;
    // your code file can't be executed directly from the web.
    exit(1);
}

/**
 * For use by microapps to customize NoticeListItem output
 *
 * @category  Microapp
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class NoticeListItemAdapter
{
    protected $nli;

    /**
     * Wrap a notice list item.
     *
     * @param NoticeListItem $nli item to wrap
     */
    function __construct($nli)
    {
        $this->nli = $nli;
    }

    /**
     * Delegate unimplemented methods to the notice list item attribute.
     *
     * @param string $name      Name of the method
     * @param array  $arguments Arguments called
     *
     * @return mixed Return value of the method.
     */
    function __call($name, $arguments)
    {
        return call_user_func_array(array($this->nli, $name), $arguments);
    }

    function __get($name)
    {
        return $this->nli->$name;
    }
}


code:
<?php

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

/**
 * Base exception class for when a notice cannot be saved
 *
 * @category Exception
 * @package  GNUsocial
 * @author   Mikael Nordfeldth <mmn@hethane.se>
 * @license  https://www.gnu.org/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     https://gnu.io/social
 */

class NoticeSaveException extends ServerException
{
}


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

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

define('NOTICES_PER_SECTION', 6);

/**
 * Base class for sections showing lists of notices
 *
 * These are the widgets that show interesting data about a person
 * group, or site.
 *
 * @todo migrate this to use a variant of NoticeList
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */

abstract class NoticeSection extends Section
{
    protected $addressees = false;
    protected $attachments = false;
    protected $maxchars = 140;
    protected $options = false;
    protected $show_n = NOTICES_PER_SECTION;

    function showContent()
    {
        $prefs = array();
        foreach (array('addressees', 'attachments', 'maxchars', 'options', 'show_n') as $key) {
            $prefs[$key] = $this->$key;
        }
        $prefs['id_prefix'] = $this->divId();

        // args: notice object, html outputter, preference array for notice lists and their items
        $list = new NoticeList($this->getNotices(), $this->out, $prefs);
        $total = $list->show(); // returns total amount of notices available
        return ($total > $this->show_n);  // do we have more to show?
    }

    function getNotices()
    {
        return null;
    }
}


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

/**
 * A stream of notices
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * Class for notice streams
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
abstract class NoticeStream
{
    protected $selectVerbs = [
        ActivityVerb::POST  => true,
        ActivityVerb::SHARE => true,
    ];

    public function __construct()
    {
        foreach ($this->selectVerbs as $key => $val) {
            $this->selectVerbs[ActivityUtils::resolveUri($key)] = $val;
            // to avoid database inconsistency issues we can select both relative and absolute verbs
            //$this->selectVerbs[ActivityUtils::resolveUri($key, true)] = $val;
        }
    }

    abstract public function getNoticeIds($offset, $limit, $since_id, $max_id);

    public function getNotices($offset, $limit, $sinceId = null, $maxId = null)
    {
        $ids = $this->getNoticeIds($offset, $limit, $sinceId, $maxId);

        return self::getStreamByIds($ids);
    }

    public static function getStreamByIds($ids)
    {
        return Notice::multiGet('id', $ids);
    }

    public static function filterVerbs(Notice $notice, array $selectVerbs)
    {
        $filter = array_keys(array_filter($selectVerbs));
        if (!empty($filter)) {
            // include verbs in selectVerbs with values that equate to true
            $notice->whereAddIn('verb', $filter, $notice->columnType('verb'));
        }

        $filter = array_keys(array_filter($selectVerbs, function ($v) {
            return !$v;
        }));
        if (!empty($filter)) {
            // exclude verbs in selectVerbs with values that equate to false
            $notice->whereAddIn('!verb', $filter, $notice->columnType('verb'));
        }

        unset($filter);
    }
}


code:
<?php

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

abstract class NoticestreamAction extends ProfileAction
{
    protected $notice = null;   // holds the stream result

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

        // In case we need more info than ProfileAction->doPreparation() gives us
        $this->doStreamPreparation();

        // fetch the actual stream stuff
        try {
            $stream = $this->getStream();
            $this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
        } catch (PrivateStreamException $e) {
            $this->notice = new Notice();
            $this->notice->whereAdd('FALSE');
        }

        if ($this->page > 1 && $this->notice->N == 0) {
            // TRANS: Client error when page not found (404).
            $this->clientError(_('No such page.'), 404);
        }

        return true;
    }

    protected function doStreamPreparation()
    {
        // pass by default
    }

    public function extraHeaders()
    {
        parent::extraHeaders();
        foreach ($this->getFeeds() as $feed) {
            header('Link: <'.htmlspecialchars($feed->getUrl()).'>;' .
                        ' rel="'.htmlspecialchars($feed->rel()).'";' .
                        ' type="'.htmlspecialchars($feed->mimeType()).'"',
                    false    // don't overwrite previous headers of this sort
                    );
        }
    }

    // this fetches the NoticeStream
    abstract public function getStream();
}


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

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

/**
 * widget for displaying a list of notices in the primary content area
 *
 * @category UI
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 * @see      Notice
 * @see      NoticeListItem
 * @see      ProfileNoticeList
 */
class PrimaryNoticeList extends NoticeList
{
    public function show()
    {
        $this->out->elementStart('div', array('id' =>'notices_primary'));
        $total = parent::show();
        $this->out->elementEnd('div');
        return $total;
    }
}


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

/**
 * Public stream
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * Public stream
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

class PublicNoticeStream extends ModeratedNoticeStream
{
    public function __construct(Profile $scoped = null)
    {
        parent::__construct(
            new CachingNoticeStream(
                new RawPublicNoticeStream(),
                'public'
            ),
            $scoped
        );
    }
}

/**
 * Raw public stream
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

class RawPublicNoticeStream extends FullNoticeStream
{
    public function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $notice = new Notice();

        $notice->selectAdd(); // clears it
        $notice->selectAdd('id');

        $notice->orderBy('created DESC, id DESC');

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

        // This feed always gives only local activities.
        $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
        $notice->whereAdd('scope <> ' . Notice::MESSAGE_SCOPE);

        Notice::addWhereSinceId($notice, $since_id);
        Notice::addWhereMaxId($notice, $max_id);

        self::filterVerbs($notice, $this->selectVerbs);

        $ids = array();

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

        $notice->free();
        $notice = null;

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

/**
 * Stream of mentions of me
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

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

/**
 * Stream of mentions of me
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

class ReplyNoticeStream extends ScopingNoticeStream
{
    public function __construct($userId, Profile $scoped = null)
    {
        parent::__construct(
            new CachingNoticeStream(
                new RawReplyNoticeStream($userId),
                'reply:stream:' . $userId
            ),
            $scoped
        );
    }
}

/**
 * Raw stream of mentions of me
 *
 * @category  Stream
 * @package   GNUsocial
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

class RawReplyNoticeStream extends NoticeStream
{
    protected $userId;

    public function __construct($userId)
    {
        parent::__construct();
        $this->userId = $userId;
    }

    public function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $reply = new Reply();

        $reply->selectAdd();
        $reply->selectAdd('notice_id');

        $reply->whereAdd(sprintf('reply.profile_id = %u', $this->userId));

        Notice::addWhereSinceId($reply, $since_id, 'notice_id', 'reply.modified');
        Notice::addWhereMaxId($reply, $max_id, 'notice_id', 'reply.modified');

        if (!empty($this->selectVerbs)) {
            // this is a little special since we have to join in Notice
            $reply->joinAdd(array('notice_id', 'notice:id'));

            $filter = array_keys(array_filter($this->selectVerbs));
            if (!empty($filter)) {
                // include verbs in selectVerbs with values that equate to true
                $reply->whereAddIn('notice.verb', $filter, 'string');
            }

            $filter = array_keys(array_filter($this->selectVerbs, function ($v) {
                return !$v;
            }));
            if (!empty($filter)) {
                // exclude verbs in selectVerbs with values that equate to false
                $reply->whereAddIn('!notice.verb', $filter, 'string');
            }
        }

        $reply->whereAdd('notice.scope <> ' . NOTICE::MESSAGE_SCOPE);

        $reply->orderBy('reply.modified DESC, reply.notice_id DESC');

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

        $ids = array();

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

        return $ids;
    }
}


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

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

/**
 * Class comment
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class ScopingNoticeStream extends FilteringNoticeStream
{
    protected $profile;

    function __construct(NoticeStream $upstream, Profile $scoped=null)
    {
        parent::__construct($upstream);

        $this->profile = $scoped;   // legacy
        $this->scoped = $scoped;
    }

    /**
     * Only return notices where the profile is in scope
     *
     * @param Notice $notice The notice to check
     *
     * @return boolean whether to include the notice
     */

    protected function filter(Notice $notice)
    {
        return $notice->inScope($this->scoped);
    }

    function prefill($notices)
    {
        // XXX: this should probably only be in the scoping one.

        Notice::fillGroups($notices);
        Notice::fillReplies($notices);

        if (common_config('notice', 'hidespam')) {

            $profiles = Notice::getProfiles($notices);

            foreach ($profiles as $profile) {
                $pids[] = $profile->id;
            }

            Profile_role::pivotGet('profile_id',
                                   $pids,
                                   array('role' => Profile_role::SILENCED));
        }
    }
}


code:
<?php

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

// Farther than any human will go

define('MAX_PUBLIC_PAGE', 100);

class SitestreamAction extends ManagedAction
{
    /**
     * page of the stream we're on; default = 1
     */

    var $page = null;
    var $notice;

    protected $stream = null;

    function isReadOnly($args)
    {
        return true;
    }

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

        if ($this->page > MAX_PUBLIC_PAGE) {
            // TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
            // TRANS: %s is the page limit.
            $this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
        }

        common_set_returnto($this->selfUrl());

        $this->streamPrepare();

        $this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
                                            NOTICES_PER_PAGE + 1);

        if (!$this->notice) {
            // TRANS: Server error displayed when a public timeline cannot be retrieved.
            $this->serverError(_('Could not retrieve public timeline.'));
        }

        if ($this->page > 1 && $this->notice->N == 0){
            // TRANS: Client error when page not found (404).
            $this->clientError(_('No such page.'), 404);
        }

        return true;
    }

    /**
     * Title of the page
     *
     * @return page title, including page number if over 1
     */
    function title()
    {
        if ($this->page > 1) {
            // TRANS: Title for all public timeline pages but the first.
            // TRANS: %d is the page number.
            return sprintf(_('Public timeline, page %d'), $this->page);
        } else {
            // TRANS: Title for the first public timeline page.
            return _('Public timeline');
        }
    }

    function extraHead()
    {
        parent::extraHead();
        $rsd = common_local_url('rsd');

        // RSD, http://tales.phrasewise.com/rfc/rsd

        $this->element('link', array('rel' => 'EditURI',
                                     'type' => 'application/rsd+xml',
                                     'href' => $rsd));

        if ($this->page != 1) {
            $this->element('link', array('rel' => 'canonical',
                                         'href' => common_local_url('public')));
        }
    }

    /**
     * Output <head> elements for RSS and Atom feeds
     *
     * @return void
     */
    function getFeeds()
    {
        return array(new Feed(Feed::JSON,
                              common_local_url('ApiTimelinePublic',
                                               array('format' => 'as')),
                              // TRANS: Link description for public timeline feed.
                              _('Public Timeline Feed (Activity Streams JSON)')),
                    new Feed(Feed::RSS1, common_local_url('publicrss'),
                              // TRANS: Link description for public timeline feed.
                              _('Public Timeline Feed (RSS 1.0)')),
                     new Feed(Feed::RSS2,
                              common_local_url('ApiTimelinePublic',
                                               array('format' => 'rss')),
                              // TRANS: Link description for public timeline feed.
                              _('Public Timeline Feed (RSS 2.0)')),
                     new Feed(Feed::ATOM,
                              common_local_url('ApiTimelinePublic',
                                               array('format' => 'atom')),
                              // TRANS: Link description for public timeline feed.
                              _('Public Timeline Feed (Atom)')));
    }

    function showEmptyList()
    {
        // TRANS: Text displayed for public feed when there are no public notices.
        $message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';

        if (common_logged_in()) {
            // TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
            $message .= _('Be the first to post!');
        }
        else {
            if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
                // TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
                $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
            }
        }

        $this->elementStart('div', 'guide');
        $this->raw(common_markup_to_html($message));
        $this->elementEnd('div');
    }

    /**
     * Fill the content area
     *
     * Shows a list of the notices in the public stream, with some pagination
     * controls.
     *
     * @return void
     */
    function showContent()
    {
        if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
            $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
        } else {
            $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
        }

        $cnt = $nl->show();

        if ($cnt == 0) {
            $this->showEmptyList();
        }

        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                          $this->page, $this->action);
    }

    function showAnonymousMessage()
    {
        if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
            // TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
            // TRANS: This message contains Markdown links. Please mind the formatting.
            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                   'based on the Free Software [StatusNet](http://status.net/) tool. ' .
                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
                   '([Read more](%%doc.help%%))');
        } else {
            // TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
            // TRANS: This message contains Markdown links. Please mind the formatting.
            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                   'based on the Free Software [StatusNet](http://status.net/) tool.');
        }
        $this->elementStart('div', array('id' => 'anon_notice'));
        $this->raw(common_markup_to_html($m));
        $this->elementEnd('div');
    }
}


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

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

/**
 * Stream of notices with a given profile and tag
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class TaggedProfileNoticeStream extends ScopingNoticeStream
{
    function __construct($profile, $tag, Profile $scoped=null)
    {
        parent::__construct(new CachingNoticeStream(new RawTaggedProfileNoticeStream($profile, $tag),
                                                    'profile:notice_ids_tagged:'.$profile->id.':'.Cache::keyize($tag)),
                            $scoped);
    }
}

/**
 * Raw stream of notices with a given profile and tag
 *
 * @category  Stream
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

class RawTaggedProfileNoticeStream extends NoticeStream
{
    protected $profile;
    protected $tag;

    function __construct($profile, $tag)
    {
        $this->profile = $profile;
        $this->tag     = $tag;
    }

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        // XXX It would be nice to do this without a join
        // (necessary to do it efficiently on accounts with long history)

        $notice = new Notice();

        $query =
          "select id from notice join notice_tag on id=notice_id where tag='".
          $notice->escape($this->tag) .
          "' and profile_id=" . intval($this->profile->id);

        $since = Notice::whereSinceId($since_id, 'id', 'notice.created');
        if ($since) {
            $query .= " and ($since)";
        }

        $max = Notice::whereMaxId($max_id, 'id', 'notice.created');
        if ($max) {
            $query .= " and ($max)";
        }

        $query .= ' order by notice.created DESC, id DESC';

        if (!is_null($offset)) {
            $query .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset);
        }

        $notice->query($query);

        $ids = array();

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

        return $ids;
    }
}