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

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

/**
 * Class for querying and manipulating a theme
 *
 * Themes are directories with some expected sub-directories and files
 * in them. They're found in either local/theme (for locally-installed themes)
 * or theme/ subdir of installation dir.
 *
 * Note that the 'local' directory can be overridden as $config['local']['path']
 * and $config['local']['dir'] etc.
 *
 * This used to be a couple of functions, but for various reasons it's nice
 * to have a class instead.
 *
 * @category Output
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class Theme
{
    const FALLBACK = 'neo';

    var $name = null;
    var $dir  = null;
    var $path = null;
    protected $metadata = null; // access via getMetadata() lazy-loader
    protected $externals = null;
    protected $deps = null;

    /**
     * Constructor
     *
     * Determines the proper directory and path for this theme.
     *
     * @param string $name Name of the theme; defaults to config value
     */
    function __construct($name=null)
    {
        if (empty($name)) {
            $name = common_config('site', 'theme');
        }
        if (!self::validName($name)) {
            // TRANS: Server exception displayed if a theme name was invalid.
            throw new ServerException(_('Invalid theme name.'));
        }
        $this->name = $name;

        // Check to see if it's in the local dir

        $localroot = self::localRoot();

        $fulldir = $localroot.'/'.$name;

        if (file_exists($fulldir) && is_dir($fulldir)) {
            $this->dir  = $fulldir;
            $this->path = $this->relativeThemePath('local', 'local', 'theme/' . $name);
            return;
        }

        // Check to see if it's in the distribution dir

        $instroot = self::installRoot();

        $fulldir = $instroot.'/'.$name;

        if (file_exists($fulldir) && is_dir($fulldir)) {
            $this->dir = $fulldir;
            $this->path = $this->relativeThemePath('theme', 'theme', $name);
            return;
        }

        // Ruh roh. Fall back to default, then.

        common_log(LOG_WARNING, sprintf("Unable to find theme '%s', falling back to default theme '%s'",
                                        $name,
                                        Theme::FALLBACK));

        $this->name = Theme::FALLBACK;
        $this->dir  = $instroot.'/'.Theme::FALLBACK;
        $this->path = $this->relativeThemePath('theme', 'theme', Theme::FALLBACK);

    }

    /**
     * Build a full URL to the given theme's base directory, possibly
     * using an offsite theme server path.
     *
     * @param string $group configuration section name to pull paths from
     * @param string $fallbackSubdir default subdirectory under INSTALLDIR
     * @param string $name theme name
     *
     * @return string URL
     *
     * @todo consolidate code with that for other customizable paths
     */
    protected function relativeThemePath($group, $fallbackSubdir, $name)
    {
        if (GNUsocial::isHTTPS()) {
            $sslserver = common_config($group, 'sslserver');

            if (empty($sslserver)) {
                if (is_string(common_config('site', 'sslserver')) &&
                    mb_strlen(common_config('site', 'sslserver')) > 0) {
                    $server = common_config('site', 'sslserver');
                } else if (common_config('site', 'server')) {
                    $server = common_config('site', 'server');
                }
                $path   = common_config('site', 'path') . '/';
                if ($fallbackSubdir) {
                    $path .= $fallbackSubdir . '/';
                }
            } else {
                $server = $sslserver;
                $path   = common_config($group, 'sslpath');
                if (empty($path)) {
                    $path = common_config($group, 'path');
                }
            }

            $protocol = 'https';
        } else {
            $path = common_config($group, 'path');

            if (empty($path)) {
                $path = common_config('site', 'path') . '/';
                if ($fallbackSubdir) {
                    $path .= $fallbackSubdir . '/';
                }
            }

            $server = common_config($group, 'server');

            if (empty($server)) {
                $server = common_config('site', 'server');
            }

            $protocol = 'http';
        }

        if ($path[strlen($path)-1] != '/') {
            $path .= '/';
        }

        if ($path[0] != '/') {
            $path = '/'.$path;
        }

        return $protocol.'://'.$server.$path.$name;
    }

    /**
     * Gets the full local filename of a file in this theme.
     *
     * @param string $relative relative name, like 'logo.png'
     *
     * @return string full pathname, like /var/www/mublog/theme/default/logo.png
     */
    function getFile($relative)
    {
        return $this->dir.'/'.$relative;
    }

    /**
     * Gets the full HTTP url of a file in this theme
     *
     * @param string $relative relative name, like 'logo.png'
     *
     * @return string full URL, like 'http://example.com/theme/default/logo.png'
     */
    function getPath($relative)
    {
        return $this->path.'/'.$relative;
    }

    /**
     * Fetch a list of other themes whose CSS needs to be pulled in before
     * this theme's, based on following the theme.ini 'include' settings.
     * (May be empty if this theme has no include dependencies.)
     *
     * @return array of strings with theme names
     */
    function getDeps()
    {
        if ($this->deps === null) {
            $chain = $this->doGetDeps(array($this->name));
            array_pop($chain); // Drop us back off
            $this->deps = $chain;
        }
        return $this->deps;
    }

    protected function doGetDeps($chain)
    {
        $data = $this->getMetadata();
        if (!empty($data['include'])) {
            $include = $data['include'];

            // Protect against cycles!
            if (!in_array($include, $chain)) {
                try {
                    $theme = new Theme($include);
                    array_unshift($chain, $include);
                    return $theme->doGetDeps($chain);
                } catch (Exception $e) {
                    common_log(LOG_ERR,
                            "Exception while fetching theme dependencies " .
                            "for $this->name: " . $e->getMessage());
                }
            }
        }
        return $chain;
    }

    /**
     * Pull data from the theme's theme.ini file.
     * @fixme calling getFile will fall back to default theme, this may be unsafe.
     *
     * @return associative array of strings
     */
    function getMetadata()
    {
        if ($this->metadata == null) {
            $this->metadata = $this->doGetMetadata();
        }
        return $this->metadata;
    }

    /**
     * Pull data from the theme's theme.ini file.
     * @fixme calling getFile will fall back to default theme, this may be unsafe.
     *
     * @return associative array of strings
     */
    private function doGetMetadata()
    {
        $iniFile = $this->getFile('theme.ini');
        if (file_exists($iniFile)) {
            return parse_ini_file($iniFile);
        } else {
            return array();
        }
    }

    /**
     * Get list of any external URLs required by this theme and any
     * dependencies. These are lazy-loaded from theme.ini.
     *
     * @return array of URL strings
     */
    function getExternals()
    {
        if ($this->externals == null) {
            $data = $this->getMetadata();
            if (!empty($data['external'])) {
                $ext = (array)$data['external'];
            } else {
                $ext = array();
            }

            if (!empty($data['include'])) {
                $theme = new Theme($data['include']);
                $ext = array_merge($ext, $theme->getExternals());
            }

            $this->externals = array_unique($ext);
        }
        return $this->externals;
    }

    /**
     * Gets the full path of a file in a theme dir based on its relative name
     *
     * @param string $relative relative path within the theme directory
     * @param string $name     name of the theme; defaults to current theme
     *
     * @return string File path to the theme file
     */
    static function file($relative, $name=null)
    {
        $theme = new Theme($name);
        return $theme->getFile($relative);
    }

    /**
     * Gets the full URL of a file in a theme dir based on its relative name
     *
     * @param string $relative relative path within the theme directory
     * @param string $name     name of the theme; defaults to current theme
     *
     * @return string URL of the file
     */
    static function path($relative, $name=null)
    {
        $theme = new Theme($name);
        return $theme->getPath($relative);
    }

    /**
     * list available theme names
     *
     * @return array list of available theme names
     */
    static function listAvailable()
    {
        $local   = self::subdirsOf(self::localRoot());
        $install = self::subdirsOf(self::installRoot());

        $i = array_search('base', $install);

        unset($install[$i]);

        return array_merge($local, $install);
    }

    /**
     * Utility for getting subdirs of a directory
     *
     * @param string $dir full path to directory to check
     *
     * @return array relative filenames of subdirs, or empty array
     */
    protected static function subdirsOf($dir)
    {
        $subdirs = array();

        if (is_dir($dir)) {
            if ($dh = opendir($dir)) {
                while (($filename = readdir($dh)) !== false) {
                    if ($filename != '..' && $filename !== '.' &&
                        is_dir($dir.'/'.$filename)) {
                        $subdirs[] = $filename;
                    }
                }
                closedir($dh);
            }
        }

        return $subdirs;
    }

    /**
     * Local root dir for themes
     *
     * @return string local root dir for themes
     */
    protected static function localRoot()
    {
        $basedir = common_config('local', 'dir');

        if (empty($basedir)) {
            $basedir = INSTALLDIR . '/local';
        }

        return $basedir . '/theme';
    }

    /**
     * Root dir for themes that are shipped with StatusNet
     *
     * @return string root dir for StatusNet themes
     */
    protected static function installRoot()
    {
        $instroot = common_config('theme', 'dir');

        if (empty($instroot)) {
            $instroot = INSTALLDIR.'/theme';
        }

        return $instroot;
    }

    static function validName($name)
    {
        return preg_match('/^[a-z0-9][a-z0-9_-]*$/i', $name);
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Utilities for theme files and paths
 *
 * 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  Paths
 * @package   StatusNet
 * @author    Brion Vibber <brion@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Encapsulation of the validation-and-save process when dealing with
 * a user-uploaded StatusNet theme archive...
 *
 * @todo extract theme metadata from css/display.css
 * @todo allow saving multiple themes
 */
class ThemeUploader
{
    protected $sourceFile;
    protected $isUpload;
    private $prevErrorReporting;

    public function __construct($filename)
    {
        if (!class_exists('ZipArchive')) {
            // TRANS: Exception thrown when a compressed theme is uploaded while no support present in PHP configuration.
            throw new Exception(_('This server cannot handle theme uploads without ZIP support.'));
        }
        $this->sourceFile = $filename;
    }

    public static function fromUpload($name)
    {
        if (!isset($_FILES[$name]['error'])) {
            // TRANS: Server exception thrown when uploading a theme fails.
            throw new ServerException(_('The theme file is missing or the upload failed.'));
        }
        if ($_FILES[$name]['error'] != UPLOAD_ERR_OK) {
            // TRANS: Server exception thrown when uploading a theme fails.
            throw new ServerException(_('The theme file is missing or the upload failed.'));
        }
        return new ThemeUploader($_FILES[$name]['tmp_name']);
    }

    /**
     * @param string $destDir
     * @throws Exception on bogus files
     */
    public function extract($destDir)
    {
        $zip = $this->openArchive();

        // First pass: validate but don't save anything to disk.
        // Any errors will trip an exception.
        $this->traverseArchive($zip);

        // Second pass: now that we know we're good, actually extract!
        $tmpDir = $destDir . '.tmp' . getmypid();
        $this->traverseArchive($zip, $tmpDir);

        $zip->close();

        if (file_exists($destDir)) {
            $killDir = $tmpDir . '.old';
            $this->quiet();
            $ok = rename($destDir, $killDir);
            $this->loud();
            if (!$ok) {
                common_log(LOG_ERR, "Could not move old custom theme from $destDir to $killDir");
                // TRANS: Server exception thrown when saving an uploaded theme after decompressing it fails.
                throw new ServerException(_('Failed saving theme.'));
            }
        } else {
            $killDir = false;
        }

        $this->quiet();
        $ok = rename($tmpDir, $destDir);
        $this->loud();
        if (!$ok) {
            common_log(LOG_ERR, "Could not move saved theme from $tmpDir to $destDir");
            // TRANS: Server exception thrown when saving an uploaded theme after decompressing it fails.
            throw new ServerException(_('Failed saving theme.'));
        }

        if ($killDir) {
            $this->recursiveRmdir($killDir);
        }
    }

    /**
     *
     */
    protected function traverseArchive($zip, $outdir=false)
    {
        $sizeLimit = 2 * 1024 * 1024; // 2 megabyte space limit?
        $blockSize = 4096; // estimated; any entry probably takes this much space

        $totalSize = 0;
        $hasMain = false;
        $commonBaseDir = false;

        for ($i = 0; $i < $zip->numFiles; $i++) {
            $data = $zip->statIndex($i);
            $name = str_replace('\\', '/', $data['name']);

            if (substr($name, -1) == '/') {
                // A raw directory... skip!
                continue;
            }

            // Is this a safe or skippable file?
            $path = pathinfo($name);
            if ($this->skippable($path['filename'], $path['extension'])) {
                // Documentation and such... booooring
                continue;
            } else {
                $this->validateFile($path['filename'], $path['extension']);
            }

            // Check the directory structure...
            $dirs = explode('/', $path['dirname']);
            $baseDir = array_shift($dirs);
            if ($commonBaseDir === false) {
                $commonBaseDir = $baseDir;
            } else {
                if ($commonBaseDir != $baseDir) {
                    // TRANS: Server exception thrown when an uploaded theme has an incorrect structure.
                    throw new ClientException(_('Invalid theme: Bad directory structure.'));
                }
            }

            foreach ($dirs as $dir) {
                $this->validateFileOrFolder($dir);
            }

            $fullPath = $dirs;
            $fullPath[] = $path['basename'];
            $localFile = implode('/', $fullPath);
            if ($localFile == 'css/display.css') {
                $hasMain = true;
            }

            $size = $data['size'];
            $estSize = $blockSize * max(1, intval(ceil($size / $blockSize)));
            $totalSize += $estSize;
            if ($totalSize > $sizeLimit) {
                // TRANS: Client exception thrown when an uploaded theme is larger than the limit.
                // TRANS: %d is the number of bytes of the uncompressed theme.
                $msg = sprintf(_m('Uploaded theme is too large; must be less than %d byte uncompressed.',
                                  'Uploaded theme is too large; must be less than %d bytes uncompressed.',
                                  $sizeLimit),
                               $sizeLimit);
                throw new ClientException($msg);
            }

            if ($outdir) {
                $this->extractFile($zip, $data['name'], "$outdir/$localFile");
            }
        }

        if (!$hasMain) {
            // TRANS: Server exception thrown when an uploaded theme is incomplete.
            throw new ClientException(_('Invalid theme archive: ' .
                                        "Missing file css/display.css"));
        }
    }

    /**
     * @fixme Probably most unrecognized files should just be skipped...
     */
    protected function skippable($filename, $ext)
    {
        $skip = array('txt', 'html', 'rtf', 'doc', 'docx', 'odt', 'xcf');
        if (strtolower($filename) == 'readme') {
            return true;
        }
        if (in_array(strtolower($ext), $skip)) {
            return true;
        }
        if ($filename == '' || substr($filename, 0, 1) == '.') {
            // Skip Unix-style hidden files
            return true;
        }
        if ($filename == '__MACOSX') {
            // Skip awful metadata files Mac OS X slips in for you.
            // Thanks Apple!
            return true;
        }
        return false;
    }

    protected function validateFile($filename, $ext)
    {
        $this->validateFileOrFolder($filename);
        $this->validateExtension($filename, $ext);
        // @fixme validate content
    }

    protected function validateFileOrFolder($name)
    {
        if (!preg_match('/^[a-z0-9_\.-]+$/i', $name)) {
            common_log(LOG_ERR, "Bad theme filename: $name");
            // TRANS: Server exception thrown when an uploaded theme has an incorrect file or folder name.
            $msg = _("Theme contains invalid file or folder name. " .
                     'Stick with ASCII letters, digits, underscore, and minus sign.');
            throw new ClientException($msg);
        }
        if (preg_match('/\.(php|cgi|asp|aspx|js|vb)\w/i', $name)) {
            common_log(LOG_ERR, "Unsafe theme filename: $name");
            // TRANS: Server exception thrown when an uploaded theme contains files with unsafe file extensions.
            $msg = _('Theme contains unsafe file extension names; may be unsafe.');
            throw new ClientException($msg);
        }
        return true;
    }

    protected function validateExtension($base, $ext)
    {
        $allowed = array('css', // CSS may need validation
                         'png', 'gif', 'jpg', 'jpeg',
                         'svg', // SVG images/fonts may need validation
                         'ttf', 'eot', 'woff');
        if (!in_array(strtolower($ext), $allowed)) {
            if ($ext == 'ini' && $base == 'theme') {
                // theme.ini exception
                return true;
            }
            // TRANS: Server exception thrown when an uploaded theme contains a file type that is not allowed.
            // TRANS: %s is the file type that is not allowed.
            $msg = sprintf(_('Theme contains file of type ".%s", which is not allowed.'),
                           $ext);
            throw new ClientException($msg);
        }
        return true;
    }

    /**
     * @return ZipArchive
     */
    protected function openArchive()
    {
        $zip = new ZipArchive;
        $ok = $zip->open($this->sourceFile);
        if ($ok !== true) {
            common_log(LOG_ERR, "Error opening theme zip archive: " .
                                "{$this->sourceFile} code: {$ok}");
            // TRANS: Server exception thrown when an uploaded compressed theme cannot be opened.
            throw new Exception(_('Error opening theme archive.'));
        }
        return $zip;
    }

    /**
     * @param ZipArchive $zip
     * @param string $from original path inside ZIP archive
     * @param string $to final destination path in filesystem
     */
    protected function extractFile($zip, $from, $to)
    {
        $dir = dirname($to);
        if (!file_exists($dir)) {
            $this->quiet();
            $ok = mkdir($dir, 0755, true);
            $this->loud();
            if (!$ok) {
                common_log(LOG_ERR, "Failed to mkdir $dir while uploading theme");
                // TRANS: Server exception thrown when an uploaded theme cannot be saved during extraction.
                throw new ServerException(_('Failed saving theme.'));
            }
        } else if (!is_dir($dir)) {
            common_log(LOG_ERR, "Output directory $dir not a directory while uploading theme");
            // TRANS: Server exception thrown when an uploaded theme cannot be saved during extraction.
            throw new ServerException(_('Failed saving theme.'));
        }

        // ZipArchive::extractTo would be easier, but won't let us alter
        // the directory structure.
        $in = $zip->getStream($from);
        if (!$in) {
            common_log(LOG_ERR, "Couldn't open archived file $from while uploading theme");
            // TRANS: Server exception thrown when an uploaded theme cannot be saved during extraction.
            throw new ServerException(_('Failed saving theme.'));
        }
        $this->quiet();
        $out = fopen($to, "wb");
        $this->loud();
        if (!$out) {
            common_log(LOG_ERR, "Couldn't open output file $to while uploading theme");
            // TRANS: Server exception thrown when an uploaded theme cannot be saved during extraction.
            throw new ServerException(_('Failed saving theme.'));
        }
        while (!feof($in)) {
            $buffer = fread($in, 65536);
            fwrite($out, $buffer);
        }
        fclose($in);
        fclose($out);
    }

    private function quiet()
    {
        $this->prevErrorReporting = error_reporting();
        error_reporting($this->prevErrorReporting & ~E_WARNING);
    }

    private function loud()
    {
        error_reporting($this->prevErrorReporting);
    }

    private function recursiveRmdir($dir)
    {
        $list = dir($dir);
        while (($file = $list->read()) !== false) {
            if ($file == '.' || $file == '..') {
                continue;
            }
            $full = "$dir/$file";
            if (is_dir($full)) {
                $this->recursiveRmdir($full);
            } else {
                unlink($full);
            }
        }
        $list->close();
        rmdir($dir);
    }
}

repo_name: dansup/gnu

license: mit


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    Brion Vibber <brion@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * 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 ThreadedNoticeList extends NoticeList
{
    protected $userProfile;

    function __construct(Notice $notice, Action $out=null, $profile=-1)
    {
        parent::__construct($notice, $out);
        if (is_int($profile) && $profile == -1) {
            $profile = Profile::current();
        }
        $this->userProfile = $profile;
    }

    /**
     * show the list of notices
     *
     * "Uses up" the stream by looping through it. So, probably can't
     * be called twice on the same list.
     *
     * @return int count of notices listed.
     */
    function show()
    {
        $this->out->elementStart('div', array('id' =>'notices_primary'));
        // TRANS: Header for Notices section.
        $this->out->element('h2', null, _m('HEADER','Notices'));
        $this->out->elementStart('ol', array('class' => 'notices threaded-notices xoxo'));

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

        $allnotices = self::_allNotices($notices);
    	self::prefill($allnotices);

        $conversations = array();

        foreach ($notices as $notice) {

            // Collapse repeats into their originals...

            if ($notice->repeat_of) {
                $orig = Notice::getKV('id', $notice->repeat_of);
                if ($orig instanceof Notice) {
                    $notice = $orig;
                }
            }
            $convo = $notice->conversation;
            if (!empty($conversations[$convo])) {
                // Seen this convo already -- skip!
                continue;
            }
            $conversations[$convo] = true;

            // Get the convo's root notice
            $root = $notice->conversationRoot($this->userProfile);
            if ($root instanceof Notice) {
                $notice = $root;
            }

            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');
        $this->out->elementEnd('div');

        return $total;
    }

    function _allNotices($notices)
    {
        $convId = array();
        foreach ($notices as $notice) {
            $convId[] = $notice->conversation;
        }
        $convId = array_unique($convId);
        $allMap = Notice::listGet('conversation', $convId);
        $allArray = array();
        foreach ($allMap as $convId => $convNotices) {
            $allArray = array_merge($allArray, $convNotices);
        }
        return $allArray;
    }

    /**
     * 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)
    {
        return new ThreadedNoticeListItem($notice, $this->out, $this->userProfile);
    }
}

/**
 * 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  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      NoticeList
 * @see      ProfileNoticeListItem
 */
class ThreadedNoticeListItem extends NoticeListItem
{
    protected $userProfile = null;

    function __construct(Notice $notice, Action $out=null, $profile=null)
    {
        parent::__construct($notice, $out);
        $this->userProfile = $profile;
    }

    function initialItems()
    {
        return 3;
    }

    /**
     * finish the notice
     *
     * Close the last elements in the notice list item
     *
     * @return void
     */
    function showEnd()
    {
        $max = $this->initialItems();
        if (!$this->repeat instanceof Notice) {
            $stream = new ConversationNoticeStream($this->notice->conversation, $this->userProfile);
            $notice = $stream->getNotices(0, $max + 2);
            $notices = array();
            $cnt = 0;
            $moreCutoff = null;
            while ($notice->fetch()) {
                if (Event::handle('StartAddNoticeReply', array($this, $this->notice, $notice))) {
                    // Don't list repeats as separate notices in a conversation
                    if (!empty($notice->repeat_of)) {
                        continue;
                    }

                    if ($notice->id == $this->notice->id) {
                        // Skip!
                        continue;
                    }
                    $cnt++;
                    if ($cnt > $max) {
                        // boo-yah
                        $moreCutoff = clone($notice);
                        break;
                    }
                    $notices[] = clone($notice); // *grumble* inefficient as hell
                    Event::handle('EndAddNoticeReply', array($this, $this->notice, $notice));
                }
            }

            if (Event::handle('StartShowThreadedNoticeTail', array($this, $this->notice, &$notices))) {
                $threadActive = count($notices) > 0; // has this thread had any activity?

                $this->out->elementStart('ul', 'notices threaded-replies xoxo');

                if (Event::handle('StartShowThreadedNoticeTailItems', array($this, $this->notice, &$threadActive))) {
                    // Repeats and Faves/Likes are handled in plugins.
                    Event::handle('EndShowThreadedNoticeTailItems', array($this, $this->notice, &$threadActive));
                }

                if (count($notices)>0) {
                    if ($moreCutoff) {
                        $item = new ThreadedNoticeListMoreItem($moreCutoff, $this->out, count($notices));
                        $item->show();
                    }
                    foreach (array_reverse($notices) as $notice) {
                        if (Event::handle('StartShowThreadedNoticeSub', array($this, $this->notice, $notice))) {
                            $item = new ThreadedNoticeListSubItem($notice, $this->notice, $this->out);
                            $item->show();
                            Event::handle('EndShowThreadedNoticeSub', array($this, $this->notice, $notice));
                        }
                    }
                }

                Event::handle('EndShowThreadedNoticeTail', array($this, $this->notice, $notices));
                $this->out->elementEnd('ul');
            }
        }

        parent::showEnd();
    }
}

// @todo FIXME: needs documentation.
class ThreadedNoticeListSubItem extends NoticeListItem
{
    protected $root = null;

    function __construct(Notice $notice, $root, $out)
    {
        $this->root = $root;
        parent::__construct($notice, $out);
    }

    function avatarSize()
    {
        return AVATAR_STREAM_SIZE; // @fixme would like something in between
    }

    function showNoticeLocation()
    {
        //
    }

    function showNoticeSource()
    {
        //
    }

    function getAttentionProfiles()
    {
        $all = parent::getAttentionProfiles();

        $profiles = array();

        $rootAuthor = $this->root->getProfile();

        foreach ($all as $profile) {
            if ($profile->id != $rootAuthor->id) {
                $profiles[] = $profile;
            }
        }

        return $profiles;
    }

    function showEnd()
    {
        $threadActive = null;   // unused here for now, but maybe in the future?
        if (Event::handle('StartShowThreadedNoticeTailItems', array($this, $this->notice, &$threadActive))) {
            // Repeats and Faves/Likes are handled in plugins.
            Event::handle('EndShowThreadedNoticeTailItems', array($this, $this->notice, &$threadActive));
        }
        parent::showEnd();
    }
}

/**
 * Placeholder for loading more replies...
 */
class ThreadedNoticeListMoreItem extends NoticeListItem
{
    protected $cnt;

    function __construct(Notice $notice, Action $out, $cnt)
    {
        parent::__construct($notice, $out);
        $this->cnt = $cnt;
    }

    /**
     * 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
     */
    function show()
    {
        $this->showStart();
        $this->showMiniForm();
        $this->showEnd();
    }

    /**
     * start a single notice.
     *
     * @return void
     */
    function showStart()
    {
        $this->out->elementStart('li', array('class' => 'notice-reply-comments'));
    }

    function showEnd()
    {
        $this->out->elementEnd('li');
    }

    function showMiniForm()
    {
        $id = $this->notice->conversation;
        $url = common_local_url('conversation', array('id' => $id));

        $n = Conversation::noticeCount($id) - 1;

        // TRANS: Link to show replies for a notice.
        // TRANS: %d is the number of replies to a notice and used for plural.
        $msg = sprintf(_m('Show reply', 'Show all %d replies', $n), $n);

        $this->out->element('a', array('href' => $url), $msg);
    }
}


code:
<?php

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

class ThreadingGroupNoticeStream extends ThreadingNoticeStream
{
    function __construct($group, $profile)
    {
        parent::__construct(new GroupNoticeStream($group, $profile));
    }
}


code:
<?php

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

class ThreadingNetworkPublicNoticeStream extends ThreadingNoticeStream
{
    public function __construct(Profile $scoped=null)
    {
        parent::__construct(new NetworkPublicNoticeStream($scoped));
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Notice stream that's good for threading lists
 *
 * 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  Notice 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);
}

/**
 * This notice stream filters notices by whether their conversation
 * has been seen before. It's a good (well, OK) way to get streams
 * for a ThreadedNoticeList display.
 *
 * @category  Notice 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 ThreadingNoticeStream extends FilteringNoticeStream
{
    protected $seen = array();

    function getNotices($offset, $limit, $sinceId=null, $maxId=null)
    {
        // Clear this each time we're called
        $this->seen = array();
        return parent::getNotices($offset, $limit, $sinceId, $maxId);
    }

    function filter($notice)
    {
        if (!array_key_exists($notice->conversation, $this->seen)) {
            $this->seen[$notice->conversation] = true;
            return true;
        } else {
            return false;
        }
    }
}


code:
<?php

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

class ThreadingPublicNoticeStream extends ThreadingNoticeStream
{
    function __construct($scoped)
    {
        parent::__construct(new PublicNoticeStream($scoped));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for editing a peopletag
 *
 * 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    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);
}

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

/**
 * Form for editing a peopletag
 *
 * @category Form
 * @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/
 *
 * @see      GroupEditForm
 */
class SearchProfileForm extends Form
{
    var $peopletag;

    function __construct($out, Profile_list $peopletag)
    {
        parent::__construct($out);
        $this->peopletag = $peopletag;
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'form_peopletag-add-' . $this->peopletag->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_peopletag_edit_user_search';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('profilecompletion');
    }

    /**
     * Name of the form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Form legend.
        $this->out->element('legend', null, sprintf(_('Search and list people')));
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        // TRANS: Dropdown option for searching in profiles.
        $fields = array('fulltext'    => _('Everything'),
                        // TRANS: Dropdown option for searching in profiles.
                        'nickname'    => _('Nickname'),
                        // TRANS: Dropdown option for searching in profiles.
                        'fullname'    => _('Fullname'),
                        // TRANS: Dropdown option for searching in profiles.
                        'description' => _('Description'),
                        // TRANS: Dropdown option for searching in profiles.
                        'location'    => _('Location'),
                        // TRANS: Dropdown option for searching in profiles.
                        'uri'         => _('URI (Remote users)'));

        $this->out->hidden('peopletag_id', $this->peopletag->id);
        $this->out->input('q', null);
        // TRANS: Dropdown field label.
        $this->out->dropdown('field', _m('LABEL','Search in'), $fields,
                        // TRANS: Dropdown field title.
                        _('Choose a field to search.'), false, 'fulltext');
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text to search profiles.
        $this->out->submit('submit', _m('BUTTON','Search'));
    }
}

class UntagButton extends Form
{
    var $profile;
    var $peopletag;

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

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'form_peopletag-' . $this->peopletag->id . '-remove-' . $this->profile->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_user_remove_peopletag';
    }

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

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

    /**
     * Name of the form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Form legend.
        // TRANS: %1$s is a nickname, $2$s is a list.
        $this->out->element('legend', null, sprintf(_('Remove %1$s from list %2$s'),
            $this->profile->nickname, $this->peopletag->tag));
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        $this->out->hidden('peopletag_id', $this->peopletag->id);
        $this->out->hidden('tagged', $this->profile->id);
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text to untag a profile.
        $this->out->submit('submit', _m('BUTTON','Remove'));
    }
}

class TagButton extends Form
{
    var $profile;
    var $peopletag;

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

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'form_peopletag-' . $this->peopletag->id . '-add-' . $this->profile->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_user_add_peopletag';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('addpeopletag');
    }

    /**
     * Name of the form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Legend on form to add a profile to a list.
        // TRANS: %1$s is a nickname, %2$s is a list.
        $this->out->element('legend', null, sprintf(_('Add %1$s to list %2$s'),
            $this->profile->nickname, $this->peopletag->tag));
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        UntagButton::formData();
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text to tag a profile.
        $this->out->submit('submit', _m('BUTTON','Add'));
    }
}

class TaggedProfileItem extends Widget
{
    var $profile=null;

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

    function show()
    {
        $this->out->elementStart('a', array('class' => 'url',
                                            'href' => $this->profile->profileurl,
                                            'title' => $this->profile->getBestName()));
        $avatarUrl = $this->profile->avatarUrl(AVATAR_MINI_SIZE);
        $this->out->element('img', array('src' => $avatarUrl,
                                         'width' => AVATAR_MINI_SIZE,
                                         'height' => AVATAR_MINI_SIZE,
                                         'class' => 'avatar photo',
                                         'alt' => $this->profile->getBestName()));
        $this->out->element('span', 'fn nickname', $this->profile->nickname);
        $this->out->elementEnd('a');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Base class for sections showing lists of people
 *
 * 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('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Base class for sections
 *
 * These are the widgets that show interesting data about a person
 * group, or site.
 *
 * @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/
 */
class TopPostersSection extends ProfileSection
{
    function getProfiles()
    {
        $qry = 'SELECT profile.*, count(*) as value ' .
          'FROM profile JOIN notice ON profile.id = notice.profile_id ' .
          (common_config('public', 'localonly') ? 'WHERE is_local = 1 ' : '') .
          'GROUP BY profile.id,nickname,fullname,profileurl,homepage,bio,location,profile.created,profile.modified,textsearch ' .
          'ORDER BY value DESC ';

        $limit = PROFILES_PER_SECTION;
        $offset = 0;

        if (common_config('db','type') == 'pgsql') {
            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
        } else {
            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
        }

        $profile = Memcached_DataObject::cachedQuery('Profile',
                                                     $qry,
                                                     6 * 3600);
        return $profile;
    }

    function title()
    {
        // TRANS: Title for top posters section.
        return _('Top posters');
    }

    function divId()
    {
        return 'top_posters';
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Widget showing a drop-down of potential addressees
 *
 * 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  Widget
 * @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);
}

/**
 * Widget showing a drop-down of potential addressees
 *
 * @category  Widget
 * @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 ToSelector extends Widget
{
    protected $user;
    protected $to;
    protected $id;
    protected $name;
    protected $private;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out  output context
     * @param User          $user Current user
     * @param mixed         $to   Default selection for addressee
     */
    function __construct($out, $user, $to, $private=false, $id='notice_to', $name='notice_to')
    {
        parent::__construct($out);

        $this->user    = $user;
        $this->to      = $to;
        $this->private = $private;
        $this->id      = $id;
        $this->name    = $name;
    }

    /**
     * Constructor
     *
     * @param HTMLOutputter $out  output context
     * @param User          $user Current user
     * @param mixed         $to   Default selection for addressee
     */
    function show()
    {
        $choices = array();
        $default = common_config('site', 'private') ? 'public:site' : 'public:everyone';

        $groups = $this->user->getGroups();

        while ($groups instanceof User_group && $groups->fetch()) {
            $value = 'group:'.$groups->getID();
            if (($this->to instanceof User_group) && $this->to->id == $groups->id) {
                $default = $value;
            }
            $choices[$value] = "!{$groups->getNickname()} [{$groups->getBestName()}]";
        }

        // Add subscribed users to dropdown menu
        $users = $this->user->getSubscribed();
        while ($users->fetch()) {
            $value = 'profile:'.$users->getID();
            try {
                $choices[$value] = substr($users->getAcctUri(), 5) . " [{$users->getBestName()}]";
            } catch (ProfileNoAcctUriException $e) {
                $choices[$value] = "[?@?] " . $e->profile->getBestName();
            }
        }

        if ($this->to instanceof Profile) {
            $value = 'profile:'.$this->to->getID();
            $default = $value;
            try {
                $choices[$value] = substr($this->to->getAcctUri(), 5) . " [{$this->to->getBestName()}]";
            } catch (ProfileNoAcctUriException $e) {
                $choices[$value] = "[?@?] " . $e->profile->getBestName();
            }
        }

        // alphabetical order
        asort($choices);

        // Reverse so we can add entries at the end (can't unshift with a key)
        $choices = array_reverse($choices);

        if (common_config('notice', 'allowprivate')) {
            // TRANS: Option in drop-down of potential addressees.
            // TRANS: %s is a StatusNet sitename.
            $choices['public:site'] = sprintf(_('Everyone at %s'), common_config('site', 'name'));
        }

        if (!common_config('site', 'private')) {
            // TRANS: Option in drop-down of potential addressees.
            $choices['public:everyone'] = _m('SENDTO','Everyone');
        }

        // Return the order
        $choices = array_reverse($choices);

        $this->out->dropdown($this->id,
                             // TRANS: Label for drop-down of potential addressees.
                             _m('LABEL','To:'),
                             $choices,
                             null,
                             false,
                             $default);

        $this->out->elementStart('span', 'checkbox-wrapper');
        if (common_config('notice', 'allowprivate')) {
            $this->out->checkbox('notice_private',
                                 // TRANS: Checkbox label in widget for selecting potential addressees to mark the notice private.
                                 _('Private?'),
                                 $this->private);
        }
        $this->out->elementEnd('span');
    }

    static function fillActivity(Action $action, Activity $act, array &$options)
    {
        if (!$act->context instanceof ActivityContext) {
            $act->context = new ActivityContext();
        }
        self::fillOptions($action, $options);
        if (isset($options['groups'])) {
            foreach ($options['groups'] as $group_id) {
                $group = User_group::getByID($group_id);
                $act->context->attention[$group->getUri()] = $group->getObjectType();
            }
        }
        if (isset($options['replies'])) {
            foreach ($options['replies'] as $profile_uri) {
                $profile = Profile::fromUri($profile_uri);
                $act->context->attention[$profile->getUri()] = $profile->getObjectType();
            }
        }
    }

    static function fillOptions($action, &$options)
    {
        // XXX: make arg name selectable
        $toArg = $action->trimmed('notice_to');
        $private = common_config('notice', 'allowprivate') ? $action->boolean('notice_private') : false;

        if (empty($toArg)) {
            return;
        }

        list($prefix, $value) = explode(':', $toArg);
        switch ($prefix) {
        case 'group':
            $options['groups'] = array($value);
            if ($private) {
                $options['scope'] = Notice::GROUP_SCOPE;
            }
            break;
        case 'profile':
            $profile = Profile::getKV('id', $value);
            $options['replies'] = array($profile->getUri());
            if ($private) {
                $options['scope'] = Notice::ADDRESSEE_SCOPE;
            }
            break;
        case 'public':
            if ($value == 'everyone' && !common_config('site', 'private')) {
                $options['scope'] = 0;
            } else if ($value == 'site') {
                $options['scope'] = Notice::SITE_SCOPE;
            }
            break;
        default:
            // TRANS: Client exception thrown in widget for selecting potential addressees when an invalid fill option was received.
            throw new ClientException(sprintf(_('Unknown to value: "%s".'),$toArg));
            break;
        }
    }
}


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

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

/**
 * Abstract superclass for advertising plugins
 *
 * Plugins for showing ads should derive from this plugin.
 *
 * Outputs the following ad types (based on UAP):
 *
 * Medium Rectangle 300x250
 * Rectangle        180x150
 * Leaderboard      728x90
 * Wide Skyscraper  160x600
 *
 * @category Plugin
 * @package  StatusNet
 * @author   Sarven Capadisli <csarven@status.net>
 * @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 UAPPlugin extends Plugin
{
    public $mediumRectangle = null;
    public $rectangle       = null;
    public $leaderboard     = null;
    public $wideSkyscraper  = null;

    /**
     * Output our dedicated stylesheet
     *
     * @param Action $action Action being shown
     *
     * @return boolean hook flag
     */
    public function onEndShowStylesheets(Action $action)
    {
        // XXX: allow override by theme
        $action->cssLink('css/uap.css', 'base', 'screen, projection, tv');
        return true;
    }

    /**
     * Add a medium rectangle ad at the beginning of sidebar
     *
     * @param Action $action Action being shown
     *
     * @return boolean hook flag
     */
    function onStartShowAside(Action $action)
    {
        if (!is_null($this->mediumRectangle)) {

            $action->elementStart('div',
                                  array('id' => 'ad_medium-rectangle',
                                        'class' => 'ad'));

            $this->showMediumRectangle($action);

            $action->elementEnd('div');
        }

        // XXX: Hack to force ads to show on single-notice pages

        if (!is_null($this->rectangle) &&
            $action->trimmed('action') == 'shownotice') {

            $action->elementStart('div', array('id' => 'aside_primary',
                                               'class' => 'aside'));

            if (Event::handle('StartShowSections', array($action))) {
                $action->showSections();
                Event::handle('EndShowSections', array($action));
            }

            $action->elementEnd('div');

            return false;
        }

        return true;
    }

    /**
     * Add a leaderboard in the header
     *
     * @param Action $action Action being shown
     *
     * @return boolean hook flag
     */

    function onEndShowHeader($action)
    {
        if (!is_null($this->leaderboard)) {
            $action->elementStart('div',
                                  array('id' => 'ad_leaderboard',
                                        'class' => 'ad'));
            $this->showLeaderboard($action);
            $action->elementEnd('div');
        }

        return true;
    }

    /**
     * Add a rectangle before aside sections
     *
     * @param Action $action Action being shown
     *
     * @return boolean hook flag
     */
    function onStartShowSections(Action $action)
    {
        if (!is_null($this->rectangle)) {
            $action->elementStart('div',
                                  array('id' => 'ad_rectangle',
                                        'class' => 'ad'));
            $this->showRectangle($action);
            $action->elementEnd('div');
        }

        return true;
    }

    /**
     * Add a wide skyscraper after the aside
     *
     * @param Action $action Action being shown
     *
     * @return boolean hook flag
     */
    function onEndShowAside(Action $action)
    {
        if (!is_null($this->wideSkyscraper)) {
            $action->elementStart('div',
                                  array('id' => 'ad_wide-skyscraper',
                                        'class' => 'ad'));

            $this->showWideSkyscraper($action);

            $action->elementEnd('div');
        }
        return true;
    }

    /**
     * Show a medium rectangle ad
     *
     * @param Action $action Action being shown
     *
     * @return void
     */
    abstract protected function showMediumRectangle($action);

    /**
     * Show a rectangle ad
     *
     * @param Action $action Action being shown
     *
     * @return void
     */
    abstract protected function showRectangle($action);

    /**
     * Show a wide skyscraper ad
     *
     * @param Action $action Action being shown
     *
     * @return void
     */
    abstract protected function showWideSkyscraper($action);

    /**
     * Show a leaderboard ad
     *
     * @param Action $action Action being shown
     *
     * @return void
     */
    abstract protected function showLeaderboard($action);
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for unblocking a user
 *
 * 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('STATUSNET')) {
    exit(1);
}

/**
 * Form for unblocking a user
 *
 * @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      BlockForm
 */
class UnblockForm extends ProfileActionForm
{
    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'unblock';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Title for the form to unblock a user.
        return _m('TITLE','Unblock');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */
    function description()
    {
        // TRANS: Description of the form to unblock a user.
        return _('Unblock this user');
    }
}


code:
<?php
/**
 * GNU social - a federating social network
 *
 * class for exception thrown when a lookup by URI fails
 *
 * 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  Exception
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2014 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link      https://www.gnu.org/software/social/
 */

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

class UnknownUriException extends ServerException
{
    public $object_uri = null;

    public function __construct($object_uri, $msg=null)
    {
        $this->object_uri = $object_uri;

        if ($msg === null) {
            // TRANS: Exception text shown when no object found with certain URI
            // TRANS: %s is the URI.
            $msg = sprintf(_('No object found with URI "%s"'), $this->object_uri);
            common_debug(__CLASS__ . ': ' . $msg);
        }

        parent::__construct($msg, 404);
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * A queue manager interface for just doing things immediately
 *
 * 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  QueueManager
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @author    Brion Vibber <brion@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/
 */

class UnQueueManager extends QueueManager
{

    /**
     * Dummy queue storage manager: instead of saving events for later,
     * we just process them immediately. This is only suitable for events
     * that can be processed quickly and don't need polling or long-running
     * connections to another server such as XMPP.
     *
     * @param Notice $object    this specific manager just handles Notice objects anyway
     * @param string $queue
     */
    function enqueue($object, $transport)
    {
        try {
            $handler = $this->getHandler($transport);
            $handler->handle($object);
        } catch (NoQueueHandlerException $e) {
            if (Event::handle('UnqueueHandleNotice', array(&$object, $transport))) {
                throw new ServerException("UnQueueManager: Unknown queue transport: $transport");
            }
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for unsandboxing a user
 *
 * 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>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Form for unsandboxing a user
 *
 * Removes the "sandboxed" role for a user.
 *
 * @category Form
 * @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      UnSandboxForm
 */
class UnsandboxForm extends ProfileActionForm
{
    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'unsandbox';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Title for unsandbox form.
        return _m('TITLE','Unsandbox');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */
    function description()
    {
        // TRANS: Description for unsandbox form.
        return _('Unsandbox this user');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for unsilencing a user
 *
 * 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>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Form for unsilencing a user
 *
 * @category Form
 * @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      SilenceForm
 */
class UnSilenceForm extends ProfileActionForm
{
    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'unsilence';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Title for unsilence form.
        return _('Unsilence');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */
    function description()
    {
        // TRANS: Form description for unsilence form.
        return _('Unsilence this user');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for unsubscribing from a user
 *
 * 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('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

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

/**
 * Form for unsubscribing from a user
 *
 * @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      SubscribeForm
 */
class UnsubscribeForm extends Form
{
    /**
     * Profile of user to unsubscribe from
     */

    var $profile = null;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out     output channel
     * @param Profile       $profile profile of user to unsub from
     */
    function __construct($out=null, $profile=null)
    {
        parent::__construct($out);

        $this->profile = $profile;
    }

    /**
     * ID of the form
     *
     * @return int ID of the form
     */
    function id()
    {
        return 'unsubscribe-' . $this->profile->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_user_unsubscribe ajax';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('unsubscribe');
    }

    /**
     * Legend of the Form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: Form legend on unsubscribe form.
        $this->out->element('legend', null, _('Unsubscribe from this user'));
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        $this->out->hidden('unsubscribeto-' . $this->profile->id,
                           $this->profile->id,
                           'unsubscribeto');
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text on unsubscribe form.
        $this->out->submit('submit', _m('BUTTON','Unsubscribe'), 'submit', null,
                           // TRANS: Button title on unsubscribe form.
                           _('Unsubscribe from this user.'));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for unsubscribing to a peopletag
 *
 * 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    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);
}

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

/**
 * Form for unsubscribing to a peopletag
 *
 * @category Form
 * @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/
 *
 * @see      UnunsubscribeForm
 */
class UnsubscribePeopletagForm extends Form
{
    /**
     * peopletag for the user to join
     */
    var $peopletag = null;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out   output channel
     * @param peopletag     $peopletag peopletag to unsubscribe to
     */
    function __construct($out=null, $peopletag=null)
    {
        parent::__construct($out);

        $this->peopletag = $peopletag;
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'peopletag-unsubscribe-' . $this->peopletag->id;
    }

    /**
     * class of the form
     *
     * @return string of the form class
     */
    function formClass()
    {
        return 'form_peopletag_unsubscribe';
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('unsubscribepeopletag',
                                array('id' => $this->peopletag->id));
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text for unsubscribing from a list.
        $this->out->submit('submit', _m('BUTTON','Unsubscribe'));
    }
}


code:
<?php
/**
 * GNU social - a federating social network
 *
 * class for an exception when a File is of unsupported media type
 *
 * 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  Exception
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2014 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link      https://www.gnu.org/software/social/
 */

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

class UnsupportedMediaException extends ServerException
{
    public function __construct($msg, $path=null)
    {
        //common_debug(sprintf('UnsupportedMediaException "%1$s". File path (if given): "%2$s"', $msg, $path));
        parent::__construct($msg);
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * URL mapper
 *
 * 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  URL
 * @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);
}

/**
 * URL mapper
 *
 * Converts a path into a set of parameters, and vice versa
 *
 * We used to use Net_URL_Mapper, so there's a wrapper class at Router, q.v.
 *
 * NUM's vagaries are the main reason we have weirdnesses here.
 *
 * @category  URL
 * @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 URLMapper
{
    const ACTION = 'action';

    protected $statics = array();
    protected $variables = array();
    protected $reverse = array();
    protected $allpaths = array();

    function connect($path, $args, $paramPatterns=array())
    {
        if (!array_key_exists(self::ACTION, $args)) {
            throw new Exception(sprintf("Can't connect %s; path has no action.", $path));
        }

        $this->allpaths[] = $path;

        $action = $args[self::ACTION];

        $paramNames = $this->getParamNames($path);

        if (empty($paramNames)) {
            $this->statics[$path] = $args;
            if (array_key_exists($action, $this->reverse)) {
                $this->reverse[$args[self::ACTION]][] = array($args, $path);
            } else {
                $this->reverse[$args[self::ACTION]] = array(array($args, $path));
            }
        } else {

            // Eff if I understand why some go here and some go there.
            // Anyways, fixup my preconceptions

            foreach ($paramNames as $name) {
                if (!array_key_exists($name, $paramPatterns) &&
                    array_key_exists($name, $args)) {
                    $paramPatterns[$name] = $args[$name];
                    unset($args[$name]);
                }
            }

            $regex = self::makeRegex($path, $paramPatterns);

            $this->variables[] = array($args, $regex, $paramNames);

            $format = $this->makeFormat($path, $paramPatterns);

            if (array_key_exists($action, $this->reverse)) {
                $this->reverse[$args[self::ACTION]][] = array($args, $format, $paramNames);
            } else {
                $this->reverse[$args[self::ACTION]] = array(array($args, $format, $paramNames));
            }
        }
    }

    function match($path)
    {
        if (array_key_exists($path, $this->statics)) {
            return $this->statics[$path];
        }

        foreach ($this->variables as $pattern) {
            list($args, $regex, $paramNames) = $pattern;
            if (preg_match($regex, $path, $match)) {
                $results = $args;
                foreach ($paramNames as $name) {
                    $results[$name] = $match[$name];
                }
                return $results;
            }
        }

        throw new NoRouteMapException($path);
    }

    function generate($args, $qstring, $fragment)
    {
        if (!array_key_exists(self::ACTION, $args)) {
            throw new Exception("Every path needs an action.");
        }

        $action = $args[self::ACTION];

        if (!array_key_exists($action, $this->reverse)) {
            throw new Exception(sprintf('No candidate paths for action "%s"', $action));
        }

        $candidates = $this->reverse[$action];

        foreach ($candidates as $candidate) {
            if (count($candidate) == 2) { // static
                list($tryArgs, $tryPath) = $candidate;
                foreach ($tryArgs as $key => $value) {
                    if (!array_key_exists($key, $args) || $args[$key] != $value) {
                        // next candidate
                        continue 2;
                    }
                }
                // success
                $path = $tryPath;
            } else {
                list($tryArgs, $format, $paramNames) = $candidate;

                foreach ($tryArgs as $key => $value) {
                    if (!array_key_exists($key, $args) || $args[$key] != $value) {
                        // next candidate
                        continue 2;
                    }
                }

                // success

                $toFormat = array();

                foreach ($paramNames as $name) {
                    if (!array_key_exists($name, $args)) {
                        // next candidate
                        continue 2;
                    }
                    $toFormat[] = $args[$name];
                }

                $path = vsprintf($format, $toFormat);
            }

            if (!empty($qstring)) {
                $formatted = http_build_query($qstring);
                $path .= '?' . $formatted;
            }

            return $path;
        }

        // failure; some reporting twiddles

        unset($args['action']);

        if (empty($args)) {
            throw new Exception(sprintf('No matches for action "%s"', $action));
        }

        $argstring = '';

        foreach ($args as $key => $value) {
            $argstring .= "$key=$value ";
        }

        throw new Exception(sprintf('No matches for action "%s" with arguments "%s"', $action, $argstring));
    }

    protected function getParamNames($path)
    {
        preg_match_all('/:(?P<name>\w+)/', $path, $match);
        return $match['name'];
    }

    static function makeRegex($path, $paramPatterns)
    {
        $pr = new PatternReplacer($paramPatterns);

        $regex = preg_replace_callback('/:(\w+)/',
                                       array($pr, 'toPattern'),
                                       $path);

        $regex = '#^' . str_replace('#', '\#', $regex) . '$#';

        return $regex;
    }

    protected function makeFormat($path, $paramPatterns)
    {
        $format = preg_replace('/(:\w+)/', '%s', $path);

        return $format;
    }

    public function getPaths()
    {
        return $this->allpaths;
    }
}

class PatternReplacer
{
    private $patterns;

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

    function toPattern($matches)
    {
        // trim out the :
        $name = $matches[1];
        if (array_key_exists($name, $this->patterns)) {
            $pattern = $this->patterns[$name];
        } else {
            // ???
            $pattern = '\w+';
        }
        return '(?P<'.$name.'>'.$pattern.')';
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Superclass for plugins that do URL shortening
 *
 * 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 Plugin
 * @package  StatusNet
 * @author   Craig Andrews <candrews@integralblue.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);
}

/**
 * Superclass for plugins that do URL shortening
 *
 * @category Plugin
 * @package  StatusNet
 * @author   Craig Andrews <candrews@integralblue.com>
 * @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 UrlShortenerPlugin extends Plugin
{
    public $shortenerName;
    public $freeService = false;

    // Url Shortener plugins should implement some (or all)
    // of these methods

    /**
     * Make an URL shorter.
     *
     * @param string $url URL to shorten
     *
     * @return string shortened version of the url, or null on failure
     */

    protected abstract function shorten($url);

    /**
     * Utility to get the data at an URL
     *
     * @param string $url URL to fetch
     *
     * @return string response body
     *
     * @todo rename to code-standard camelCase httpGet()
     */

    protected function http_get($url)
    {
        $request  = HTTPClient::start();
        $response = $request->get($url);
        return $response->getBody();
    }

    /**
     * Utility to post a request and get a response URL
     *
     * @param string $url  URL to fetch
     * @param array  $data post parameters
     *
     * @return string response body
     *
     * @todo rename to code-standard httpPost()
     */

    protected function http_post($url, $data)
    {
        $request  = HTTPClient::start();
        $response = $request->post($url, null, $data);
        return $response->getBody();
    }

    // Hook handlers

    /**
     * Called when all plugins have been initialized
     *
     * @return boolean hook value
     */

    function onInitializePlugin()
    {
        if (!isset($this->shortenerName)) {
            throw new Exception("must specify a shortenerName");
        }
        return true;
    }

    /**
     * Called when a showing the URL shortener drop-down box
     *
     * Properties of the shortening service currently only
     * include whether it's a free service.
     *
     * @param array &$shorteners array mapping shortener name to properties
     *
     * @return boolean hook value
     */

    function onGetUrlShorteners(&$shorteners)
    {
        $shorteners[$this->shortenerName] =
          array('freeService' => $this->freeService);
        return true;
    }

    /**
     * Called to shorten an URL
     *
     * @param string $url           URL to shorten
     * @param string $shortenerName Shortening service. Don't handle if it's
     *                              not you!
     * @param string &$shortenedUrl URL after shortening; out param.
     *
     * @return boolean hook value
     */

    function onStartShortenUrl($url, $shortenerName, &$shortenedUrl)
    {
        if ($shortenerName == $this->shortenerName) {
            $result = $this->shorten($url);
            if (isset($result) && $result != null && $result !== false) {
                $shortenedUrl = $result;
                common_log(LOG_INFO,
                           __CLASS__ . ": $this->shortenerName ".
                           "shortened $url to $shortenedUrl");
                return false;
            }
        }
        return true;
    }
}


code:
<?php
/**
 * GNU social - a federating social network
 *
 * class for an exception when a thumbnail has not been generated from a File
 * object and the intention is to use the original File instead.
 *
 * 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  Exception
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2014 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link      https://www.gnu.org/software/social/
 */

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

class UseFileAsThumbnailException extends UnsupportedMediaException
{
    public $file = null;

    public function __construct($file_id)
    {
        $this->file = File::getKV('id', $file_id);
        if (!$this->file instanceof File) {
            throw new ServerException('No File ID supplied to exception');
        }

        parent::__construct('Thumbnail not generated', $this->file->getPath());
    }
}