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

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

/**
 * Client class for AtomPub
 *
 * @category  General
 * @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 AtomPubClient
{
    public $url;
    private $user, $pass;

    /**
     *
     * @param string $url collection feed URL
     * @param string $user auth username
     * @param string $pass auth password
     */
    function __construct($url, $user, $pass)
    {
        $this->url = $url;
        $this->user = $user;
        $this->pass = $pass;
    }

    /**
     * Set up an HTTPClient with auth for our resource.
     *
     * @param string $method
     * @return HTTPClient
     */
    private function httpClient($method='GET')
    {
        $client = new HTTPClient($this->url);
        $client->setMethod($method);
        $client->setAuth($this->user, $this->pass);
        return $client;
    }

    function get()
    {
        $client = $this->httpClient('GET');
        $response = $client->send();
        if ($response->isOk()) {
            return $response->getBody();
        } else {
            throw new Exception("Bogus return code: " . $response->getStatus() . ': ' . $response->getBody());
        }
    }

    /**
     * Create a new resource by POSTing it to the collection.
     * If successful, will return the URL representing the
     * canonical location of the new resource. Neat!
     *
     * @param string $data
     * @param string $type defaults to Atom entry
     * @return string URL to the created resource
     *
     * @throws exceptions on failure
     */
    function post($data, $type='application/atom+xml;type=entry')
    {
        $client = $this->httpClient('POST');
        $client->setHeader('Content-Type', $type);
        // optional Slug header not used in this case
        $client->setBody($data);
        $response = $client->send();

        if ($response->getStatus() != '201') {
            throw new Exception("Expected HTTP 201 on POST, got " . $response->getStatus() . ': ' . $response->getBody());
        }
        $loc = $response->getHeader('Location');
        $contentLoc = $response->getHeader('Content-Location');

        if (empty($loc)) {
            throw new Exception("AtomPub POST response missing Location header.");
        }
        if (!empty($contentLoc)) {
            if ($loc != $contentLoc) {
                throw new Exception("AtomPub POST response Location and Content-Location headers do not match.");
            }

            // If Content-Location and Location match, that means the response
            // body is safe to interpret as the resource itself.
            if ($type == 'application/atom+xml;type=entry') {
                self::validateAtomEntry($response->getBody());
            }
        }

        return $loc;
    }

    /**
     * Note that StatusNet currently doesn't allow PUT editing on notices.
     *
     * @param string $data
     * @param string $type defaults to Atom entry
     * @return true on success
     *
     * @throws exceptions on failure
     */
    function put($data, $type='application/atom+xml;type=entry')
    {
        $client = $this->httpClient('PUT');
        $client->setHeader('Content-Type', $type);
        $client->setBody($data);
        $response = $client->send();

        if ($response->getStatus() != '200' && $response->getStatus() != '204') {
            throw new Exception("Expected HTTP 200 or 204 on PUT, got " . $response->getStatus() . ': ' . $response->getBody());
        }

        return true;
    }

    /**
     * Delete the resource.
     *
     * @return true on success
     *
     * @throws exceptions on failure
     */
    function delete()
    {
        $client = $this->httpClient('DELETE');
        $client->setBody($data);
        $response = $client->send();

        if ($response->getStatus() != '200' && $response->getStatus() != '204') {
            throw new Exception("Expected HTTP 200 or 204 on DELETE, got " . $response->getStatus() . ': ' . $response->getBody());
        }

        return true;
    }

    /**
     * Ensure that the given string is a parseable Atom entry.
     *
     * @param string $str
     * @return boolean
     * @throws Exception on invalid input
     */
    static function validateAtomEntry($str)
    {
        if (empty($str)) {
            throw new Exception('Bad Atom entry: empty');
        }
        $dom = new DOMDocument;
        if (!$dom->loadXML($str)) {
            throw new Exception('Bad Atom entry: XML is not well formed.');
        }

        $activity = new Activity($dom->documentRoot);
        return true;
    }

    static function entryEditURL($str) {
        $dom = new DOMDocument;
        $dom->loadXML($str);
        $path = new DOMXPath($dom);
        $path->registerNamespace('atom', 'http://www.w3.org/2005/Atom');

        $links = $path->query('/atom:entry/atom:link[@rel="edit"]', $dom->documentRoot);
        if ($links && $links->length) {
            if ($links->length > 1) {
                throw new Exception('Bad Atom entry; has multiple rel=edit links.');
            }
            $link = $links->item(0);
            $url = $link->getAttribute('href');
            return $url;
        } else {
            throw new Exception('Atom entry lists no rel=edit link.');
        }
    }

    static function entryId($str) {
        $dom = new DOMDocument;
        $dom->loadXML($str);
        $path = new DOMXPath($dom);
        $path->registerNamespace('atom', 'http://www.w3.org/2005/Atom');

        $links = $path->query('/atom:entry/atom:id', $dom->documentRoot);
        if ($links && $links->length) {
            if ($links->length > 1) {
                throw new Exception('Bad Atom entry; has multiple id entries.');
            }
            $link = $links->item(0);
            $url = $link->textContent;
            return $url;
        } else {
            throw new Exception('Atom entry lists no id.');
        }
    }

    static function getEntryInFeed($str, $id)
    {
        $dom = new DOMDocument;
        $dom->loadXML($str);
        $path = new DOMXPath($dom);
        $path->registerNamespace('atom', 'http://www.w3.org/2005/Atom');

        $query = '/atom:feed/atom:entry[atom:id="'.$id.'"]';
        $items = $path->query($query, $dom->documentRoot);
        if ($items && $items->length) {
            return $items->item(0);
        } else {
            return null;
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Class for building an in-memory Atom feed for a particular user's
 * timeline.
 *
 * 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  Feed
 * @package   StatusNet
 * @author    Zach Copley <zach@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);
}

/**
 * Class for user notice feeds.  May contain a reference to the user.
 *
 * @category Feed
 * @package  StatusNet
 * @author   Zach Copley <zach@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 AtomUserNoticeFeed extends AtomNoticeFeed
{
    protected $user;

    /**
     * Constructor
     *
     * @param User    $user    the user for the feed
     * @param User    $cur     the current authenticated user, if any
     * @param boolean $indent  flag to turn indenting on or off
     *
     * @return void
     */
    function __construct($user, $cur = null, $indent = true) {
        parent::__construct($cur, $indent);
        $this->user = $user;
        if (!empty($user)) {

            $profile = $user->getProfile();

            $ao = $profile->asActivityObject();

            array_push($ao->extra, $profile->profileInfo($this->scoped));

            $this->addAuthorRaw($ao->asString('author'));
        }

        // TRANS: Title in atom user notice feed. %s is a user name.
        $title      = sprintf(_("%s timeline"), $user->nickname);
        $this->setTitle($title);

        $sitename   = common_config('site', 'name');
        $subtitle   = sprintf(
            // TRANS: Message is used as a subtitle in atom user notice feed.
            // TRANS: %1$s is a user name, %2$s is a site name.
            _('Updates from %1$s on %2$s!'),
            $user->nickname, $sitename
        );
        $this->setSubtitle($subtitle);

        $this->setLogo($profile->avatarUrl(AVATAR_PROFILE_SIZE));

        $this->setUpdated('now');

        $this->addLink(
            common_local_url(
                'showstream',
                array('nickname' => $user->nickname)
            )
        );

        $self = common_local_url('ApiTimelineUser',
                                 array('id' => $user->id,
                                       'format' => 'atom'));
        $this->setId($self);
        $this->setSelfLink($self);

        $this->addLink(
            common_local_url('sup', null, null, $user->id),
            array(
                'rel' => 'http://api.friendfeed.com/2008/03#sup',
                'type' => 'application/json'
            )
        );
    }

    function getUser()
    {
        return $this->user;
    }

    function showSource()
    {
        return false;
    }

    function showAuthor()
    {
        return false;
    }
}

repo_name: piratas/tortuga

license: mit


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

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

/**
 * used for one-off attachment action
 */
class Attachment extends AttachmentListItem
{
    function showLink() {
        if (Event::handle('StartShowAttachmentLink', array($this->out, $this->attachment))) {
            $this->out->elementStart('div', array('id' => 'attachment_view',
                                                  'class' => 'h-entry'));
            $this->out->elementStart('div', 'entry-title');
            $this->out->element('a', $this->linkAttr(), _('Download link'));
            $this->out->elementEnd('div');

            $this->out->elementStart('article', 'e-content');
            $this->showRepresentation();
            $this->out->elementEnd('article');
            Event::handle('EndShowAttachmentLink', array($this->out, $this->attachment));
            $this->out->elementEnd('div');
        }
    }

    function show() {
        $this->showNoticeAttachment();
    }

    function linkAttr() {
        return array('rel' => 'external', 'href' => $this->attachment->getUrl());
    }
}


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

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

/**
 * widget for displaying a list of notice attachments
 *
 * 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 AttachmentList extends Widget
{
    /** the current stream of notices being displayed. */

    var $notice = null;

    /**
     * constructor
     *
     * @param Notice $notice stream of notices from DB_DataObject
     */
    function __construct($notice, $out=null)
    {
        parent::__construct($out);
        $this->notice = $notice;
    }

    /**
     * show the list of attachments
     *
     * "Uses up" the stream by looping through it. So, probably can't
     * be called twice on the same list.
     *
     * @return int count of items listed.
     */
    function show()
    {
    	$attachments = $this->notice->attachments();
        $representable = false;
        foreach ($attachments as $key=>$att) {
            // Only show attachments representable with a title
            if ($att->getTitle() === null) {
                unset($attachments[$key]);
            }
        }
        if (!count($attachments)) {
            return 0;
        }

        $this->showListStart();

        foreach ($attachments as $att) {
            $item = $this->newListItem($att);
            $item->show();
        }

        $this->showListEnd();

        return count($attachments);
    }

    function showListStart()
    {
        $this->out->elementStart('ol', array('class' => 'attachments'));
    }

    function showListEnd()
    {
        $this->out->elementEnd('ol');
    }

    /**
     * returns a new list item for the current attachment
     *
     * @param File $attachment the current attachment
     *
     * @return AttachmentListItem a list item for displaying the attachment
     */
    function newListItem(File $attachment)
    {
        return new AttachmentListItem($attachment, $this->out);
    }
}


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

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

/**
 * 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 AttachmentListItem extends Widget
{
    /** The attachment this item will show. */

    var $attachment = null;

    /**
     * @param File $attachment the attachment we will display
     */
    function __construct(File $attachment, $out=null)
    {
        parent::__construct($out);
        $this->attachment  = $attachment;
    }

    function title() {
        return $this->attachment->getTitle();
    }

    function linkTitle() {
        return $this->title();
    }

    /**
     * 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->showNoticeAttachment();
        $this->showEnd();
    }

    function linkAttr() {
        return array('class' => 'attachment',
                     'href' => $this->attachment->getUrl(),
                     'id' => 'attachment-' . $this->attachment->id,
                     'title' => $this->linkTitle());
    }

    function showLink() {
        $this->out->elementStart('a', $this->linkAttr());
        $this->out->element('span', null, $this->linkTitle());
        $this->showRepresentation();
        $this->out->elementEnd('a');
    }

    function showNoticeAttachment()
    {
        $this->showLink();
    }

    function showRepresentation() {
        if (Event::handle('StartShowAttachmentRepresentation', array($this->out, $this->attachment))) {
            if (!empty($this->attachment->mimetype)) {
                $mediatype = common_get_mime_media($this->attachment->mimetype);

                // FIXME: Get proper mime recognition of Ogg files! If system has 'mediainfo', this should do it:
                // $ mediainfo --inform='General;%InternetMediaType%'
                if ($this->attachment->mimetype === 'application/ogg') {
                    $mediatype = 'video';   // because this element can handle Ogg/Vorbis etc. on its own
                }
                switch ($mediatype) {
                // Anything we understand as an image, if we need special treatment, do it in StartShowAttachmentRepresentation
                case 'image':
                    try {
                        // Tell getThumbnail that we can show an animated image if it has one (4th arg, "force_still")
                        $thumb = $this->attachment->getThumbnail(null, null, false, false);
                        $this->out->element('img', array('class'=>'u-photo', 'src' => $thumb->getUrl(), 'alt' => ''));
                    } catch (UseFileAsThumbnailException $e) {
                        $this->out->element('img', array('class'=>'u-photo', 'src' => $e->file->getUrl(), 'alt' => $e->file->title));
                    } catch (UnsupportedMediaException $e) {
                        // FIXME: Show a good representation of unsupported/unshowable images
                    }
                    break;

                // HTML5 media elements
                case 'audio':
                case 'video':
                    try {
                        $thumb = $this->attachment->getThumbnail();
                        $poster = $thumb->getUrl();
                        unset ($thumb);
                    } catch (Exception $e) {
                        $poster = null;
                    }
                    $this->out->elementStart($mediatype,
                                        array('class'=>"attachment_player u-{$mediatype}",
                                            'poster'=>$poster,
                                            'controls'=>'controls'));
                    $this->out->element('source',
                                        array('src'=>$this->attachment->getUrl(),
                                            'type'=>$this->attachment->mimetype));
                    $this->out->elementEnd($mediatype);
                    break;

                default:
                    switch ($this->attachment->mimetype) {
                    case 'text/html':
                        if (!empty($this->attachment->filename)
                                && (GNUsocial::isAjax() || common_config('attachments', 'show_html'))) {
                            // Locally-uploaded HTML. Scrub and display inline.
                            $this->showHtmlFile($this->attachment);
                            break;
                        }
                        // Fall through to default if it wasn't a _local_ text/html File object
                    default:
                        Event::handle('ShowUnsupportedAttachmentRepresentation', array($this->out, $this->attachment));
                    }
                }
            } else {
                Event::handle('ShowUnsupportedAttachmentRepresentation', array($this->out, $this->attachment));
            }
        }
        Event::handle('EndShowAttachmentRepresentation', array($this->out, $this->attachment));
    }

    protected function showHtmlFile(File $attachment)
    {
        $body = $this->scrubHtmlFile($attachment);
        if ($body) {
            $this->out->raw($body);
        }
    }

    /**
     * @return mixed false on failure, HTML fragment string on success
     */
    protected function scrubHtmlFile(File $attachment)
    {
        $path = File::path($attachment->filename);
        if (!file_exists($path) || !is_readable($path)) {
            common_log(LOG_ERR, "Missing local HTML attachment $path");
            return false;
        }
        $raw = file_get_contents($path);

        // Normalize...
        $dom = new DOMDocument();
        if(!$dom->loadHTML($raw)) {
            common_log(LOG_ERR, "Bad HTML in local HTML attachment $path");
            return false;
        }

        // Remove <script>s or htmlawed will dump their contents into output!
        // Note: removing child nodes while iterating seems to mess things up,
        // hence the double loop.
        $scripts = array();
        foreach ($dom->getElementsByTagName('script') as $script) {
            $scripts[] = $script;
        }
        foreach ($scripts as $script) {
            common_log(LOG_DEBUG, $script->textContent);
            $script->parentNode->removeChild($script);
        }

        // Trim out everything outside the body...
        $body = $dom->saveHTML();
        $body = preg_replace('/^.*<body[^>]*>/is', '', $body);
        $body = preg_replace('/<\/body[^>]*>.*$/is', '', $body);

        require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
        $config = array('safe' => 1,
                        'deny_attribute' => 'id,style,on*',
                        'comment' => 1); // remove comments
        $scrubbed = htmLawed($body, $config);

        return $scrubbed;
    }

    /**
     * start a single notice.
     *
     * @return void
     */
    function showStart()
    {
        // XXX: RDFa
        // TODO: add notice_type class e.g., notice_video, notice_image
        $this->out->elementStart('li');
    }

    /**
     * finish the notice
     *
     * Close the last elements in the notice list item
     *
     * @return void
     */
    function showEnd()
    {
        $this->out->elementEnd('li');
    }
}


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

/**
 * FIXME
 *
 * 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 AttachmentNoticeSection extends NoticeSection
{
    function showContent() {
        parent::showContent();
        return false;
    }

    function getNotices()
    {
        $notice = new Notice;

        $notice->joinAdd(array('id', 'file_to_post:post_id'));
        $notice->whereAdd(sprintf('file_to_post.file_id = %d', $this->out->attachment->id));

        $notice->orderBy('created desc');
        $notice->selectAdd('post_id as id');
        $notice->find();
        return $notice;
    }

    function title()
    {
        // TRANS: Title.
        return _('Notices where this attachment appears');
    }

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


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Attachment tag cloud section
 *
 * 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);
}

/**
 * Attachment tag cloud section
 *
 * @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 AttachmentTagCloudSection extends TagCloudSection
{
    function title()
    {
        // TRANS: Title.
        return _('Tags for this attachment');
    }

    function showTag($tag, $weight, $relative)
    {
        if ($relative > 0.5) {
            $rel =  'tag-cloud-7';
        } else if ($relative > 0.4) {
            $rel = 'tag-cloud-6';
        } else if ($relative > 0.3) {
            $rel = 'tag-cloud-5';
        } else if ($relative > 0.2) {
            $rel = 'tag-cloud-4';
        } else if ($relative > 0.1) {
            $rel = 'tag-cloud-3';
        } else if ($relative > 0.05) {
            $rel = 'tag-cloud-2';
        } else {
            $rel = 'tag-cloud-1';
        }

        $this->out->elementStart('li', $rel);
        $this->out->element('a', array('href' => $this->tagUrl($tag)),
                       $tag);
        $this->out->elementEnd('li');
    }

    function getTags()
    {
        $notice_tag = new Notice_tag;
        $query = 'select tag,count(tag) as weight from notice_tag join file_to_post on (notice_tag.notice_id=post_id) join notice on notice_id = notice.id where file_id=' . $notice_tag->escape($this->out->attachment->id) . ' group by tag order by weight desc';
        $notice_tag->query($query);
        return $notice_tag;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Superclass for plugins that do authentication and/or authorization
 *
 * 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>
 * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
 * @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); }

/**
 * Superclass for plugins that do authentication
 *
 * @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 AuthenticationPlugin extends Plugin
{
    //is this plugin authoritative for authentication?
    public $authoritative = false;

    //should accounts be automatically created after a successful login attempt?
    public $autoregistration = false;

    //can the user change their email address
    public $password_changeable=true;

    //unique name for this authentication provider
    public $provider_name;

    //------------Auth plugin should implement some (or all) of these methods------------\\
    /**
    * Check if a nickname/password combination is valid
    * @param username
    * @param password
    * @return boolean true if the credentials are valid, false if they are invalid.
    */
    function checkPassword($username, $password)
    {
        return false;
    }

    /**
    * Automatically register a user when they attempt to login with valid credentials.
    * User::register($data) is a very useful method for this implementation
    * @param username username (that is used to login and find the user in the authentication provider) of the user to be registered
    * @param nickname nickname of the user in the SN system. If nickname is null, then set nickname = username
    * @return mixed instance of User, or false (if user couldn't be created)
    */
    function autoRegister($username, $nickname = null)
    {
        if(is_null($nickname)){
            $nickname = $username;
        }
        $registration_data = array();
        $registration_data['nickname'] = $nickname;
        return User::register($registration_data);
    }

    /**
    * Change a user's password
    * The old password has been verified to be valid by this plugin before this call is made
    * @param username
    * @param oldpassword
    * @param newpassword
    * @return boolean true if the password was changed, false if password changing failed for some reason
    */
    function changePassword($username,$oldpassword,$newpassword)
    {
        return false;
    }

    /**
    * Given a username, suggest what the nickname should be
    * Used during autoregistration
    * Useful if your usernames are ugly, and you want to suggest
    * nice looking nicknames when users initially sign on
    * All nicknames returned by this function should be valid
    *  implementations may want to use common_nicknamize() to ensure validity
    * @param username
    * @return string nickname
    */
    function suggestNicknameForUsername($username)
    {
        return common_nicknamize($username);
    }

    //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\
    function onInitializePlugin(){
        if(!isset($this->provider_name)){
            throw new Exception("must specify a provider_name for this authentication provider");
        }
    }

    /**
    * Internal AutoRegister event handler
    * @param nickname
    * @param provider_name
    * @param user - the newly registered user
    */
    function onAutoRegister($nickname, $provider_name, &$user)
    {
        if($provider_name == $this->provider_name && $this->autoregistration){
            $suggested_nickname = $this->suggestNicknameForUsername($nickname);
            $test_user = User::getKV('nickname', $suggested_nickname);
            if($test_user) {
                //someone already exists with the suggested nickname, so used the passed nickname
                $suggested_nickname = common_nicknamize($nickname);
            }
            $test_user = User::getKV('nickname', $suggested_nickname);
            if($test_user) {
                //someone already exists with the suggested nickname
                //not much else we can do
            }else{
                $user = $this->autoRegister($nickname, $suggested_nickname);
                if ($user instanceof User) {
                    User_username::register($user,$nickname,$this->provider_name);
                    return false;
                }
            }
        }
    }

    function onStartCheckPassword($nickname, $password, &$authenticatedUser){
        //map the nickname to a username
        $user_username = new User_username();
        $user_username->username=$nickname;
        $user_username->provider_name=$this->provider_name;
        if($user_username->find() && $user_username->fetch()){
            $authenticated = $this->checkPassword($user_username->username, $password);
            if($authenticated){
                $authenticatedUser = User::getKV('id', $user_username->user_id);
                return false;
            }
        }else{
            //$nickname is the username used to login
            //$suggested_nickname is the nickname the auth provider suggests for that username
            $suggested_nickname = $this->suggestNicknameForUsername($nickname);
            $user = User::getKV('nickname', $suggested_nickname);
            if($user){
                //make sure this user isn't claimed
                $user_username = new User_username();
                $user_username->user_id=$user->id;
                $we_can_handle = false;
                if($user_username->find()){
                    //either this provider, or another one, has already claimed this user
                    //so we cannot. Let another plugin try.
                    return;
                }else{
                    //no other provider claims this user, so it's safe for us to handle it
                    $authenticated = $this->checkPassword($nickname, $password);
                    if($authenticated){
                        $authenticatedUser = $user;
                        User_username::register($authenticatedUser,$nickname,$this->provider_name);
                        return false;
                    }
                }
            }else{
                $authenticated = $this->checkPassword($nickname, $password);
                if($authenticated){
                    if(! Event::handle('AutoRegister', array($nickname, $this->provider_name, &$authenticatedUser))){
                        //unlike most Event::handle lines of code, this one has a ! (not)
                        //we want to do this if the event *was* handled - this isn't a "default" implementation
                        //like most code of this form.
                        if($authenticatedUser){
                            return false;
                        }
                    }
                }
            }
        }
        if($this->authoritative){
            return false;
        }else{
            //we're not authoritative, so let other handlers try
            return;
        }
    }

    function onStartChangePassword($user,$oldpassword,$newpassword)
    {
        if($this->password_changeable){
            $user_username = new User_username();
            $user_username->user_id=$user->id;
            $user_username->provider_name=$this->provider_name;
            if($user_username->find() && $user_username->fetch()){
                $authenticated = $this->checkPassword($user_username->username, $oldpassword);
                if($authenticated){
                    $result = $this->changePassword($user_username->username,$oldpassword,$newpassword);
                    if($result){
                        //stop handling of other handlers, because what was requested was done
                        return false;
                    }else{
                        // TRANS: Exception thrown when a password change fails.
                        throw new Exception(_('Password changing failed.'));
                    }
                }else{
                    if($this->authoritative){
                        //since we're authoritative, no other plugin could do this
                        // TRANS: Exception thrown when a password change fails.
                        throw new Exception(_('Password changing failed.'));
                    }else{
                        //let another handler try
                        return null;
                    }
                }
            }
        }else{
            if($this->authoritative){
                //since we're authoritative, no other plugin could do this
                // TRANS: Exception thrown when a password change attempt fails because it is not allowed.
                throw new Exception(_('Password changing is not allowed.'));
            }
        }
    }

    function onStartAccountSettingsPasswordMenuItem($widget)
    {
        if($this->authoritative && !$this->password_changeable){
            //since we're authoritative, no other plugin could change passwords, so do not render the menu item
            return false;
        }
    }

    function onCheckSchema() {
        $schema = Schema::get();
        $schema->ensureTable('user_username', User_username::schemaDef());
        return true;
    }

    function onUserDeleteRelated($user, &$tables)
    {
        $tables[] = 'User_username';
        return true;
    }
}


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

/**
 * An exception for authorization issues
 *
 * @category  Exception
 * @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 AuthorizationException extends ClientException
{
    /**
     * Constructor
     *
     * @param string $message Message for the exception
     */
    public function __construct($message=null)
    {
        parent::__construct($message, 403);
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Superclass for plugins that do authorization
 *
 * 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>
 * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
 * @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 authorization
 *
 * @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 AuthorizationPlugin extends Plugin
{
    //is this plugin authoritative for authorization?
    public $authoritative = false;

    //------------Auth plugin should implement some (or all) of these methods------------\\

    /**
    * Is a user allowed to log in?
    * @param user
    * @return boolean true if the user is allowed to login, false if explicitly not allowed to login, null if we don't explicitly allow or deny login
    */
    function loginAllowed($user) {
        return null;
    }

    /**
    * Does a profile grant the user a named role?
    * @param profile
    * @return boolean true if the profile has the role, false if not
    */
    function hasRole($profile, $name) {
        return false;
    }

    //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\

    function onStartSetUser($user) {
        $loginAllowed = $this->loginAllowed($user);
        if($loginAllowed === true){
            return;
        }else if($loginAllowed === false){
            $user = null;
            return false;
        }else{
            if($this->authoritative) {
                $user = null;
                return false;
            }else{
                return;
            }
        }
    }

    function onStartSetApiUser($user) {
        return $this->onStartSetUser($user);
    }

    function onStartHasRole($profile, $name, &$has_role) {
        if($this->hasRole($profile, $name)){
            $has_role = true;
            return false;
        }else{
            if($this->authoritative) {
                $has_role = false;
                return false;
            }else{
                return;
            }
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * An activity
 *
 * 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  Feed
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
 * @link      http://status.net/
 */

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

// XXX: Arg! This wouldn't be necessary if we used Avatars conistently
class AvatarLink
{
    public $url;
    public $type;
    public $size;
    public $width;
    public $height;

    function __construct($element=null)
    {
        if ($element) {
            // @fixme use correct namespaces
            $this->url = $element->getAttribute('href');
            $this->type = $element->getAttribute('type');
            $width = $element->getAttribute('media:width');
            if ($width != null) {
                $this->width = intval($width);
            }
            $height = $element->getAttribute('media:height');
            if ($height != null) {
                $this->height = intval($height);
            }
        }
    }

    static function fromAvatar(Avatar $avatar)
    {
        $alink = new AvatarLink();
        $alink->type   = $avatar->mediatype;
        $alink->height = $avatar->height;
        $alink->width  = $avatar->width;
        $alink->url    = $avatar->displayUrl();
        return $alink;
    }

    static function fromFilename($filename, $size)
    {
        $alink = new AvatarLink();
        $alink->url    = $filename;
        $alink->height = $size;
        $alink->width  = $size;
        if (!empty($filename)) {
            $alink->type   = self::mediatype($filename);
        } else {
            $alink->url    = User_group::defaultLogo($size);
            $alink->type   = 'image/png';
        }
        return $alink;
    }

    // yuck!
    static function mediatype($filename) {
        $ext = strtolower(end(explode('.', $filename)));
        if ($ext == 'jpeg') {
            $ext = 'jpg';
        }
        // hope we don't support any others
        $types = array('png', 'gif', 'jpg', 'jpeg');
        if (in_array($ext, $types)) {
            return 'image/' . $ext;
        }
        return null;
    }
}


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

/**
 * Form for blocking 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      UnblockForm
 */
class BlockForm extends ProfileActionForm
{
    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */

    function target()
    {
        return 'block';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Title for the form to block a user.
        return _('Block');
    }

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


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Cache interface plus default in-memory cache implementation
 *
 * 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  Cache
 * @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/
 */

/**
 * Interface for caching
 *
 * An abstract interface for caching. Because we originally used the
 * Memcache plugin directly, the interface uses a small subset of the
 * Memcache interface.
 *
 * @category  Cache
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class Cache
{
    /**
     * @var array additional in-process cache for web requests;
     *      disabled on CLI, unsafe for long-running daemons
     */
    var $_items = array();
    var $_inlineCache = true;
    static $_inst = null;

    const COMPRESSED = 1;

    private function __construct() {
        // Potentially long-running daemons or maintenance scripts
        // should not use an in-process cache as it becomes out of
        // date.
        $this->_inlineCache = (php_sapi_name() != 'cli');
    }

    /**
     * Singleton constructor
     *
     * Use this to get the singleton instance of Cache.
     *
     * @return Cache cache object
     */
    static function instance()
    {
        if (is_null(self::$_inst)) {
            self::$_inst = new Cache();
        }

        return self::$_inst;
    }

    /**
     * Create a cache key from input text
     *
     * Builds a cache key from input text. Helps to namespace
     * the cache area (if shared with other applications or sites)
     * and prevent conflicts.
     *
     * @param string $extra the real part of the key
     *
     * @return string full key
     */
    static function key($extra)
    {
        $base_key = common_config('cache', 'base');

        if (empty($base_key)) {
            $base_key = self::keyize(common_config('site', 'name'));
        }

        return 'gnusocial:' . $base_key . ':' . $extra;
    }

    /**
     * Create a cache key for data dependent on code
     *
     * For cache elements that are dependent on changes in code, this creates
     * a more-or-less fingerprint of the current running code and adds it to
     * the cache key. In the case of an upgrade of core, or addition or
     * removal of plugins, a new unique fingerprint is generated and used.
     *
     * There can still be problems with a) differences in versions of the
     * plugins and b) people running code between official versions. This is
     * usually a problem only for experienced users like developers, who know
     * how to clear their cache.
     *
     * For sites that run code between versions (like the status.net cloud),
     * there's an additional build number configuration setting.
     *
     * @param string $extra the real part of the key
     *
     * @return string full key
     */

    static function codeKey($extra)
    {
        static $prefix = null;

        if (empty($prefix)) {

            $names   = array();

            foreach (GNUsocial::getActivePlugins() as $plugin=>$attrs) {
                $names[] = $plugin;
            }

            asort($names);

            // Unique enough.

            $uniq = crc32(implode(',', $names));

            $build = common_config('site', 'build');

            $prefix = GNUSOCIAL_VERSION.':'.$build.':'.$uniq;
        }

        return Cache::key($prefix.':'.$extra);
    }

    /**
     * Make a string suitable for use as a key
     *
     * Useful for turning primary keys of tables into cache keys.
     *
     * @param string $str string to turn into a key
     *
     * @return string keyized string
     */
    static function keyize($str)
    {
        $str = strtolower($str);
        $str = preg_replace('/\s/', '_', $str);
        return $str;
    }

    /**
     * Get a value associated with a key
     *
     * The value should have been set previously.
     *
     * @param string $key Lookup key
     *
     * @return string retrieved value or null if unfound
     */
    function get($key)
    {
        $value = false;

        common_perf_counter('Cache::get', $key);
        if (Event::handle('StartCacheGet', array(&$key, &$value))) {
            if ($this->_inlineCache && array_key_exists($key, $this->_items)) {
                $value = unserialize($this->_items[$key]);
            }
            Event::handle('EndCacheGet', array($key, &$value));
        }

        return $value;
    }

    /**
     * Set the value associated with a key
     *
     * @param string  $key    The key to use for lookups
     * @param string  $value  The value to store
     * @param integer $flag   Flags to use, may include Cache::COMPRESSED
     * @param integer $expiry Expiry value, mostly ignored
     *
     * @return boolean success flag
     */
    function set($key, $value, $flag=null, $expiry=null)
    {
        $success = false;

        common_perf_counter('Cache::set', $key);
        if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
                                                 &$expiry, &$success))) {

            if ($this->_inlineCache) {
                $this->_items[$key] = serialize($value);
            }

            $success = true;

            Event::handle('EndCacheSet', array($key, $value, $flag,
                                               $expiry));
        }

        return $success;
    }

    /**
     * Atomically increment an existing numeric value.
     * Existing expiration time should remain unchanged, if any.
     *
     * @param string  $key    The key to use for lookups
     * @param int     $step   Amount to increment (default 1)
     *
     * @return mixed incremented value, or false if not set.
     */
    function increment($key, $step=1)
    {
        $value = false;
        common_perf_counter('Cache::increment', $key);
        if (Event::handle('StartCacheIncrement', array(&$key, &$step, &$value))) {
            // Fallback is not guaranteed to be atomic,
            // and may original expiry value.
            $value = $this->get($key);
            if ($value !== false) {
                $value += $step;
                $ok = $this->set($key, $value);
                $got = $this->get($key);
            }
            Event::handle('EndCacheIncrement', array($key, $step, $value));
        }
        return $value;
    }

    /**
     * Delete the value associated with a key
     *
     * @param string $key Key to delete
     *
     * @return boolean success flag
     */
    function delete($key)
    {
        $success = false;

        common_perf_counter('Cache::delete', $key);
        if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
            if ($this->_inlineCache && array_key_exists($key, $this->_items)) {
                unset($this->_items[$key]);
            }
            $success = true;
            Event::handle('EndCacheDelete', array($key));
        }

        return $success;
    }

    /**
     * Close or reconnect any remote connections, such as to give
     * daemon processes a chance to reconnect on a fresh socket.
     *
     * @return boolean success flag
     */
    function reconnect()
    {
        $success = false;

        if (Event::handle('StartCacheReconnect', array(&$success))) {
            $success = true;
            Event::handle('EndCacheReconnect', array());
        }

        return $success;
    }
}


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

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

class CachingNoticeStream extends NoticeStream
{
    const CACHE_WINDOW = 200;

    public $stream   = null;
    public $cachekey = null;
    public $useLast  = true;

    function __construct($stream, $cachekey, $useLast = true)
    {
        $this->stream   = $stream;
        $this->cachekey = $cachekey;
        $this->useLast  = $useLast;
    }

    function getNoticeIds($offset, $limit, $sinceId, $maxId)
    {
        $cache = Cache::instance();

        // We cache self::CACHE_WINDOW elements at the tip of the stream.
        // If the cache won't be hit, just generate directly.

        if (empty($cache) ||
            $sinceId != 0 || $maxId != 0 ||
            is_null($limit) ||
            ($offset + $limit) > self::CACHE_WINDOW) {
            return $this->stream->getNoticeIds($offset, $limit, $sinceId, $maxId);
        }

        // Check the cache to see if we have the stream.

        $idkey = Cache::key($this->cachekey);

        $idstr = $cache->get($idkey);

        if ($idstr !== false) {
            // Cache hit! Woohoo!
            $window = explode(',', $idstr);
            $ids = array_slice($window, $offset, $limit);
            return $ids;
        }

        if ($this->useLast) {
            // Check the cache to see if we have a "last-known-good" version.
            // The actual cache gets blown away when new notices are added, but
            // the "last" value holds a lot of info. We might need to only generate
            // a few at the "tip", which can bound our queries and save lots
            // of time.

            $laststr = $cache->get($idkey.';last');

            if ($laststr !== false) {
                $window = explode(',', $laststr);
                $last_id = $window[0];
                $new_ids = $this->stream->getNoticeIds(0, self::CACHE_WINDOW, $last_id, 0);

                $new_window = array_merge($new_ids, $window);

                $new_windowstr = implode(',', $new_window);

                $result = $cache->set($idkey, $new_windowstr);
                $result = $cache->set($idkey . ';last', $new_windowstr);

                $ids = array_slice($new_window, $offset, $limit);

                return $ids;
            }
        }

        // No cache hits :( Generate directly and stick the results
        // into the cache. Note we generate the full cache window.

        $window = $this->stream->getNoticeIds(0, self::CACHE_WINDOW, 0, 0);

        $windowstr = implode(',', $window);

        $result = $cache->set($idkey, $windowstr);

        if ($this->useLast) {
            $result = $cache->set($idkey . ';last', $windowstr);
        }

        // Return just the slice that was requested

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

        return $ids;
    }
}


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

    var $group = null;
    var $profile = null;

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

        $this->group = $group;
        $this->profile = $profile;
    }

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

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

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        $params = array();
        if ($this->profile) {
            $params['profile_id'] = $this->profile->id;
        }
        return common_local_url('cancelgroup',
                                array('id' => $this->group->id), $params);
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Submit button text on form to cancel group join request.
        $this->out->submit('submit', _m('BUTTON','Cancel join request'));
    }
}


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

    var $profile = null;

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

        $this->profile = $profile;
    }

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

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

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

    /**
     * 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 for form action to cancel a subscription request.
        $this->out->submit('submit', _m('BUTTON','Cancel subscription request'));
    }
}


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

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

/**
 * @todo Needs documentation.
 */
class Channel
{
    function on($user)
    {
        return false;
    }

    function off($user)
    {
        return false;
    }

    function output($user, $text)
    {
        return false;
    }

    function error($user, $text)
    {
        return false;
    }

    function source()
    {
        return null;
    }
}

class CLIChannel extends Channel
{
    function source()
    {
        return 'cli';
    }

    function output($user, $text)
    {
        $site = common_config('site', 'name');
        print "[{$user->nickname}@{$site}] $text\n";
    }

    function error($user, $text)
    {
        $this->output($user, $text);
    }
}

class WebChannel extends Channel
{
    var $out = null;

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

    function source()
    {
        return 'web';
    }

    function on($user)
    {
        return false;
    }

    function off($user)
    {
        return false;
    }

    function output($user, $text)
    {
        // XXX: buffer all output and send it at the end
        // XXX: even better, redirect to appropriate page
        //      depending on what command was run
        $this->out->startHTML();
        $this->out->elementStart('head');
        // TRANS: Title for command results.
        $this->out->element('title', null, _('Command results'));
        $this->out->elementEnd('head');
        $this->out->elementStart('body');
        $this->out->element('p', array('id' => 'command_result'), $text);
        $this->out->elementEnd('body');
        $this->out->endHTML();
    }

    function error($user, $text)
    {
        common_user_error($text);
    }
}

class AjaxWebChannel extends WebChannel
{
    function output($user, $text)
    {
        $this->out->startHTML('text/xml;charset=utf-8');
        $this->out->elementStart('head');
        // TRANS: Title for command results.
        $this->out->element('title', null, _('Command results'));
        $this->out->elementEnd('head');
        $this->out->elementStart('body');
        $this->out->element('p', array('id' => 'command_result'), $text);
        $this->out->elementEnd('body');
        $this->out->endHTML();
    }

    function error($user, $text)
    {
        $this->out->startHTML('text/xml;charset=utf-8');
        $this->out->elementStart('head');
        // TRANS: Title for command results.
        $this->out->element('title', null, _('AJAX error'));
        $this->out->elementEnd('head');
        $this->out->elementStart('body');
        $this->out->element('p', array('id' => 'error'), $text);
        $this->out->elementEnd('body');
        $this->out->endHTML();
    }
}

class MailChannel extends Channel
{
    var $addr = null;

    function source()
    {
        return 'mail';
    }

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

    function on($user)
    {
        return $this->setNotify($user, 1);
    }

    function off($user)
    {
        return $this->setNotify($user, 0);
    }

    function output($user, $text)
    {
        $headers['From'] = $user->incomingemail;
        $headers['To'] = $this->addr;

        // TRANS: E-mail subject when a command has completed.
        $headers['Subject'] = _('Command complete');

        return mail_send(array($this->addr), $headers, $text);
    }

    function error($user, $text)
    {
        $headers['From'] = $user->incomingemail;
        $headers['To'] = $this->addr;

        // TRANS: E-mail subject when a command has failed.
        $headers['Subject'] = _('Command failed');

        return mail_send(array($this->addr), $headers, $text);
    }

    function setNotify($user, $value)
    {
        $orig = clone($user);
        $user->smsnotify = $value;
        $result = $user->update($orig);
        if (!$result) {
            common_log_db_error($user, 'UPDATE', __FILE__);
            return false;
        }
        return true;
    }
}


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

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

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

/**
 * Class for displaying HTTP client errors
 *
 * @category Action
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class ClientErrorAction extends ErrorAction
{
    static $status = array(400 => 'Bad Request',
                           401 => 'Unauthorized',
                           402 => 'Payment Required',
                           403 => 'Forbidden',
                           404 => 'Not Found',
                           405 => 'Method Not Allowed',
                           406 => 'Not Acceptable',
                           407 => 'Proxy Authentication Required',
                           408 => 'Request Timeout',
                           409 => 'Conflict',
                           410 => 'Gone',
                           411 => 'Length Required',
                           412 => 'Precondition Failed',
                           413 => 'Request Entity Too Large',
                           414 => 'Request-URI Too Long',
                           415 => 'Unsupported Media Type',
                           416 => 'Requested Range Not Satisfiable',
                           417 => 'Expectation Failed');

    function __construct($message='Error', $code=400)
    {
        parent::__construct($message, $code);
        $this->default = 400;
    }

    // XXX: Should these error actions even be invokable via URI?

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

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

        if (!$this->code || $code < 400 || $code > 499) {
            $this->code = $this->default;
        }

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

        if (!$this->message) {
            $this->message = "Client Error $this->code";
        }

        $this->showPage();
    }

    /**
     *  To specify additional HTTP headers for the action
     *
     *  @return void
     */
    function extraHeaders()
    {
        $status_string = @self::$status[$this->code];
        header('HTTP/1.1 '.$this->code.' '.$status_string);
    }

    /**
     * Page title.
     *
     * @return page title
     */

    function title()
    {
        return @self::$status[$this->code];
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * class for a client exception (user error)
 *
 * 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   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

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

/**
 * Class for client exceptions
 *
 * Subclass of PHP Exception for user errors.
 *
 * @category Exception
 * @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 ClientException extends Exception
{
    public function __construct($message = null, $code = 400) {
        parent::__construct($message, $code);
    }

    // custom string representation of object
    public function __toString() {
        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Database schema utilities
 *
 * 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  Database
 * @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);
}

/**
 * A class encapsulating the structure of a column in a table.
 *
 * @category Database
 * @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 ColumnDef
{
    /** name of the column. */
    public $name;
    /** type of column, e.g. 'int', 'varchar' */
    public $type;
    /** size of the column. */
    public $size;
    /** boolean flag; can it be null? */
    public $nullable;
    /**
     * type of key: null = no key; 'PRI' => primary;
     * 'UNI' => unique key; 'MUL' => multiple values.
     */
    public $key;
    /** default value if any. */
    public $default;
    /** 'extra' stuff. Returned by MySQL, largely
     * unused. */
    public $extra;
    /** auto increment this field if no value is specific for it during an insert **/
    public $auto_increment;

    /**
     * Constructor.
     *
     * @param string  $name     name of the column
     * @param string  $type     type of the column
     * @param int     $size     size of the column
     * @param boolean $nullable can this be null?
     * @param string  $key      type of key
     * @param value   $default  default value
     * @param value   $extra    unused
     * @param boolean $auto_increment
     */
    function __construct($name=null, $type=null, $size=null,
                         $nullable=true, $key=null, $default=null,
                         $extra=null, $auto_increment=false)
    {
        $this->name     = strtolower($name);
        $this->type     = strtolower($type);
        $this->size     = $size+0;
        $this->nullable = $nullable;
        $this->key      = $key;
        $this->default  = $default;
        $this->extra    = $extra;
        $this->auto_increment = $auto_increment;
    }

    /**
     * Compares this columndef with another to see
     * if they're functionally equivalent.
     *
     * @param ColumnDef $other column to compare
     *
     * @return boolean true if equivalent, otherwise false.
     */
    function equals($other)
    {
        return ($this->name == $other->name &&
                $this->_typeMatch($other) &&
                $this->_defaultMatch($other) &&
                $this->_nullMatch($other) &&
                $this->key == $other->key &&
                $this->auto_increment == $other->auto_increment);
    }

    /**
     * Does the type of this column match the
     * type of the other column?
     *
     * Checks the type and size of a column. Tries
     * to ignore differences between synonymous
     * data types, like 'integer' and 'int'.
     *
     * @param ColumnDef $other other column to check
     *
     * @return boolean true if they're about equivalent
     */
    private function _typeMatch($other)
    {
        switch ($this->type) {
        case 'integer':
        case 'int':
            return ($other->type == 'integer' ||
                    $other->type == 'int');
            break;
        default:
            return ($this->type == $other->type &&
                    $this->size == $other->size);
        }
    }

    /**
     * Does the default behaviour of this column match
     * the other?
     *
     * @param ColumnDef $other other column to check
     *
     * @return boolean true if defaults are effectively the same.
     */
    private function _defaultMatch($other)
    {
        return ((is_null($this->default) && is_null($other->default)) ||
                ($this->default == $other->default));
    }

    /**
     * Does the null behaviour of this column match
     * the other?
     *
     * @param ColumnDef $other other column to check
     *
     * @return boolean true if these columns 'null' the same.
     */
    private function _nullMatch($other)
    {
        return ((!is_null($this->default) && !is_null($other->default) &&
                 $this->default == $other->default) ||
                ($this->nullable == $other->nullable));
    }
}