code:
# Translation of StatusNet - Comet to Macedonian (македонски)
# Exported from translatewiki.net
#
# Author: Bjankuloski06
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:05+0000\n"
"Language-Team: Macedonian <https://translatewiki.net/wiki/Portal:mk>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: mk\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n == 1 || n%10 == 1) ? 0 : 1;\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "Приклучок за вршење на поднови „во живо“ со Comet и Bayeux."


code:
# Translation of StatusNet - Comet to Norwegian Bokmål (norsk (bokmål))
# Exported from translatewiki.net
#
# Author: Nghtwlkr
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:05+0000\n"
"Language-Team: Norwegian Bokmål <https://translatewiki.net/wiki/Portal:nb>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: nb\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "Utvidelse for å gjøre oppdateringer med Comet og Bayeux."

license: mit


code:
# Translation of StatusNet - Comet to Dutch (Nederlands)
# Exported from translatewiki.net
#
# Author: Siebrand
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:05+0000\n"
"Language-Team: Dutch <https://translatewiki.net/wiki/Portal:nl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: nl\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "Plug-in om updates te maken via Comet en Bayeux."


code:
# Translation of StatusNet - Comet to Portuguese (Português)
# Exported from translatewiki.net
#
# Author: GTNS
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-31 21:06+0000\n"
"PO-Revision-Date: 2011-03-31 21:09:05+0000\n"
"Language-Team: Portuguese <http://translatewiki.net/wiki/Portal:pt>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-03-24 15:24:57+0000\n"
"X-Generator: MediaWiki 1.18alpha (r85082); Translate extension (2011-03-11)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pt\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
#, fuzzy
msgid "Plugin to make updates using Comet and Bayeux."
msgstr ""
"Plugin para fazer actualizações em \"tempo real\" utilizando Comet/Bayeux."


code:
# Translation of StatusNet - Comet to Brazilian Portuguese (Português do Brasil)
# Exported from translatewiki.net
#
# Author: Giro720
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-31 21:06+0000\n"
"PO-Revision-Date: 2011-03-31 21:09:05+0000\n"
"Language-Team: Brazilian Portuguese <http://translatewiki.net/wiki/Portal:pt-"
"br>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-03-24 15:24:57+0000\n"
"X-Generator: MediaWiki 1.18alpha (r85082); Translate extension (2011-03-11)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pt-br\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
#, fuzzy
msgid "Plugin to make updates using Comet and Bayeux."
msgstr ""
"Plugin para realizar atualizações em \"tempo real\" usando Comet/Bayeux."


code:
# Translation of StatusNet - Comet to Russian (русский)
# Exported from translatewiki.net
#
# Author: Александр Сигачёв
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:05+0000\n"
"Language-Team: Russian <https://translatewiki.net/wiki/Portal:ru>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: ru\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=3; plural=(n%10 == 1 && n%100 != 11) ? 0 : ( (n%10 >= "
"2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)) ? 1 : 2 );\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "Модуль для обновлений с помощью Comet и Bayeux."


code:
# Translation of StatusNet - Comet to Tagalog (Tagalog)
# Exported from translatewiki.net
#
# Author: AnakngAraw
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:06+0000\n"
"Language-Team: Tagalog <https://translatewiki.net/wiki/Portal:tl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: tl\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr ""
"Pamasak upang makagawa ng mga pagsasapanahon na ginagamit ang Comet at "
"Bayeux."


code:
# Translation of StatusNet - Comet to Ukrainian (українська)
# Exported from translatewiki.net
#
# Author: Boogie
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:06+0000\n"
"Language-Team: Ukrainian <https://translatewiki.net/wiki/Portal:uk>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:49:53+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: uk\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=3; plural=(n%10 == 1 && n%100 != 11) ? 0 : ( (n%10 >= "
"2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20)) ? 1 : 2 );\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "Додаток для надсилання дописів за допомогою Comet та Bayeux."


code:
# Translation of StatusNet - Comet to Simplified Chinese (中文(简体))
# Exported from translatewiki.net
#
# Author: ZhengYiFeng
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Comet\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-31 21:06+0000\n"
"PO-Revision-Date: 2011-03-31 21:09:05+0000\n"
"Language-Team: Simplified Chinese <http://translatewiki.net/wiki/Portal:zh-"
"hans>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-03-24 15:24:57+0000\n"
"X-Generator: MediaWiki 1.18alpha (r85082); Translate extension (2011-03-11)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: zh-hans\n"
"X-Message-Group: #out-statusnet-plugin-comet\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
#. TRANS: and Comet is a web application model.
#, fuzzy
msgid "Plugin to make updates using Comet and Bayeux."
msgstr "通过 Comet/Bayeux 实现“实时更新”的插件。"


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

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

/**
 * @package     UI
 * @maintainer  Mikael Nordfeldth <mmn@hethane.se>
 */
class ConversationTreePlugin extends Plugin
{
    public function onStartShowConversation(Action $action, Conversation $conv, Profile $scoped=null) {
        $nl = new ConversationTree($conv->getNotices(), $action);
        $cnt = $nl->show();
        return false;
    }

    public function onPluginVersion(array &$versions)
    {
        $versions[] = array('name' => 'ConversationTree',
                            'version' => GNUSOCIAL_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => 'http://gnu.io/',
                            'rawdescription' =>
                            // TRANS: Plugin description.
                            _m('Enables conversation tree view.'));

        return true;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Conversation tree widget for oooooold school playas
 *
 * 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('GNUSOCIAL')) { exit(1); }

/**
 * Conversation tree
 *
 * The widget class for displaying a hierarchical list of notices.
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class ConversationTree extends NoticeList
{
    var $tree  = null;
    var $table = null;

    /**
     * Show the tree of notices
     *
     * @return void
     */
    function show()
    {
        $cnt = $this->_buildTree();

        $this->out->elementStart('div', array('id' =>'notices_primary'));
        // TRANS: Header on conversation page. Hidden by default (h2).
        $this->out->element('h2', null, _('Notices'));
        $this->out->elementStart('ol', array('class' => 'notices xoxo old-school'));

        if (array_key_exists('root', $this->tree)) {
            $rootid = $this->tree['root'][0];
            $this->showNoticePlus($rootid);
        }

        $this->out->elementEnd('ol');
        $this->out->elementEnd('div');

        return $cnt;
    }

    function _buildTree()
    {
        $cnt = 0;

        $this->tree  = array();
        $this->table = array();

        while ($this->notice->fetch()) {

            $cnt++;

            $id     = $this->notice->id;
            $notice = clone($this->notice);

            $this->table[$id] = $notice;

            if (is_null($notice->reply_to)) {
                $this->tree['root'] = array($notice->id);
            } else if (array_key_exists($notice->reply_to, $this->tree)) {
                $this->tree[$notice->reply_to][] = $notice->id;
            } else {
                $this->tree[$notice->reply_to] = array($notice->id);
            }
        }

        return $cnt;
    }

    /**
     * Shows a notice plus its list of children.
     *
     * @param integer $id ID of the notice to show
     *
     * @return void
     */
    function showNoticePlus($id)
    {
        $notice = $this->table[$id];

        $this->out->elementStart('li', array('class' => 'h-entry notice',
                                             'id' => 'notice-' . $id));

        $item = $this->newListItem($notice);
        $item->show();

        if (array_key_exists($id, $this->tree)) {
            $children = $this->tree[$id];

            $this->out->elementStart('ol', array('class' => 'notices threaded-replies xoxo'));

            sort($children);

            foreach ($children as $child) {
                $this->showNoticePlus($child);
            }

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

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

    /**
     * Override parent class to return our preferred item.
     *
     * @param Notice $notice Notice to display
     *
     * @return NoticeListItem a list item to show
     */
    function newListItem($notice)
    {
        return new ConversationTreeItem($notice, $this->out);
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Conversation tree widget for oooooold school playas
 *
 * 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('GNUSOCIAL')) { exit(1); }

/**
 * Conversation tree list item
 *
 * Special class of NoticeListItem for use inside conversation trees.
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class ConversationTreeItem extends NoticeListItem
{
    /**
     * start a single notice.
     *
     * The default creates the <li>; we skip, since the ConversationTree
     * takes care of that.
     *
     * @return void
     */
    function showStart()
    {
        return;
    }

    /**
     * finish the notice
     *
     * The default closes the <li>; we skip, since the ConversationTree
     * takes care of that.
     *
     * @return void
     */
    function showEnd()
    {
        return;
    }

    /**
     * show people this notice is in reply to
     *
     * Tree context shows this, so we skip it.
     *
     * @return void
     */
    function showAddressees()
    {
        return;
    }
}


code:
<?php
/**
 * GNU social cronish plugin, to imitate cron actions
 *
 * @category  Cron
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2013 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://gnu.io/social/
 */

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

class CronishPlugin extends Plugin {
    public function onCronMinutely()
    {
        common_debug('CRON: Running near-minutely cron job!');
    }

    public function onCronHourly()
    {
        common_debug('CRON: Running near-hourly cron job!');
    }

    public function onCronDaily()
    {
        common_debug('CRON: Running near-daily cron job!');
    }

    public function onCronWeekly()
    {
        common_debug('CRON: Running near-weekly cron job!');
    }

    /**
     * When the page has finished rendering, let's do some cron jobs
     * if we have the time.
     */
    public function onEndActionExecute($status, Action $action)
    {
        $cron = new Cronish();
        $cron->callTimedEvents();

        return true;
    }

    public function onPluginVersion(&$versions)
    {
        $versions[] = array('name' => 'Cronish',
                            'version' => GNUSOCIAL_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => 'http://www.gnu.org/software/social/',
                            'description' =>
                            // TRANS: Plugin description.
                            _m('Cronish plugin that executes events on a near-minutely/hour/day/week basis.'));
        return true;
    }
}


code:
<?php
/**
 * GNU social cron-on-visit class
 *
 * Keeps track, through Config dataobject class, of relative time since the
 * last run in order to to run event handlers with certain intervals.
 *
 * @category  Cron
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2013 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class Cronish
{
    /**
     * Will call events as close as it gets to one hour. Event handlers
     * which use this MUST be as quick as possible, maybe only adding a
     * queue item to be handled later or something. Otherwise execution
     * will timeout for PHP - or at least cause unnecessary delays for
     * the unlucky user who visits the site exactly at one of these events.
     */
    public function callTimedEvents()
    {
        $timers = array('minutely' => 60,   // this is NOT guaranteed to run every minute (only on busy sites)
                        'hourly' => 3600,
                        'daily'  => 86400,
                        'weekly' => 604800);

        foreach($timers as $name=>$interval) {
            $run = false;

            $lastrun = new Config();
            $lastrun->section = 'cron';
            $lastrun->setting = 'last_' . $name;
            $found = $lastrun->find(true);

            if (!$found) {
                $lastrun->value = time();
                if ($lastrun->insert() === false) {
                    common_log(LOG_WARNING, "Could not save 'cron' setting '{$name}'");
                    continue;
                }
                $run = true;
            } elseif ($lastrun->value < time() - $interval) {
                $orig    = clone($lastrun);
                $lastrun->value = time();
                $lastrun->update($orig);
                $run = true;
            }

            if ($run === true) {
                // such as CronHourly, CronDaily, CronWeekly
                Event::handle('Cron' . ucfirst($name));
            }
        }
    }
}


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

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

/**
 * @maintainer  Mikael Nordfeldth <mmn@hethane.se>
 */
class DirectMessagePlugin extends Plugin
{
    public function onCheckSchema()
    {
        $schema = Schema::get();
        $schema->ensureTable('message', Message::schemaDef());
        return true;
    }

    public function onRouterInitialized(URLMapper $m)
    {
        // web front-end actions
        $m->connect('message/new', array('action' => 'newmessage'));
        $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => Nickname::DISPLAY_FMT));
        $m->connect('message/:message',
                    array('action' => 'showmessage'),
                    array('message' => '[0-9]+'));

        // direct messages
        $m->connect('api/direct_messages.:format',
                    array('action' => 'ApiDirectMessage',
                          'format' => '(xml|json|rss|atom)'));
        $m->connect('api/direct_messages/sent.:format',
                    array('action' => 'ApiDirectMessage',
                          'format' => '(xml|json|rss|atom)',
                          'sent' => true));
        $m->connect('api/direct_messages/new.:format',
                    array('action' => 'ApiDirectMessageNew',
                          'format' => '(xml|json)'));

        return true;
    }

    public function onAppendUserActivityStreamObjects(UserActivityStream $uas, array &$objs)
    {
        // Messages _from_ the user
        $msgMap = Message::listGet('from_profile', array($uas->getUser()->id));
        $messages = $msgMap[$uas->getUser()->id];
        if (!empty($uas->after)) {
            $messages = array_filter($messages, array($uas, 'createdAfter'));
        }
        foreach ($messages as $message) {
            $objs[] = clone($message);
        }

        // Messages _to_ the user
        $msgMap = Message::listGet('to_profile', array($uas->getUser()->id));
        $messages = $msgMap[$uas->getUser()->id];
        if (!empty($uas->after)) {
            $messages = array_filter($messages, array($uas, 'createdAfter'));
        }
        foreach ($messages as $message) {
            $objs[] = clone($message);
        }

        return true;
    }

    /**
     * Are we allowed to perform a certain command over the API?
     */
    public function onCommandSupportedAPI(Command $cmd, &$supported)
    {
        $supported = $supported || $cmd instanceof MessageCommand;
        return true;
    }

    /**
     * EndInterpretCommand will handle the 'd' and 'dm' commands.
     *
     * @param string  $cmd     Command being run
     * @param string  $arg     Rest of the message (including address)
     * @param User    $user    User sending the message
     * @param Command &$result The resulting command object to be run.
     *
     * @return boolean hook value
     */
    public function onStartInterpretCommand($cmd, $arg, $user, &$result)
    {
        $dm_cmds = array('d', 'dm');

        if ($result === false && in_array($cmd, $dm_cmds)) {
            if (!empty($arg)) {
                list($other, $extra) = CommandInterpreter::split_arg($arg);
                if (!empty($extra)) {
                    $result = new MessageCommand($user, $other, $extra);
                }
            }
            return false;
        }
        return true;
    }

    public function onEndPersonalGroupNav(Menu $menu, Profile $target, Profile $scoped=null)
    {
        if ($scoped instanceof Profile && $scoped->id == $target->id
                && !common_config('singleuser', 'enabled')) {

            $menu->out->menuItem(common_local_url('inbox', array('nickname' =>
                                                                 $target->getNickname())),
                                 // TRANS: Menu item in personal group navigation menu.
                                 _m('MENU','Messages'),
                                 // TRANS: Menu item title in personal group navigation menu.
                                 _('Your incoming messages'),
                                 $scoped->id === $target->id && $menu->actionName =='inbox');
        }
    }

    public function onEndProfilePageActionsElements(HTMLOutputter $out, Profile $profile)
    {
        $scoped = Profile::current();
        if (!$scoped instanceof Profile) {
            return true;
        }

        if ($profile->isLocal() && $scoped->mutuallySubscribed($profile)) {
            $out->elementStart('li', 'entity_send-a-message');
            $out->element('a', array('href' => common_local_url('newmessage', array('to' => $profile->id)),
                                     // TRANS: Link title for link on user profile.
                                     'title' => _('Send a direct message to this user.')),
                                // TRANS: Link text for link on user profile.
                                _m('BUTTON','Message'));
            $out->elementEnd('li');
        }
        return true;
    }

    public function onProfileDeleteRelated(Profile $profile, &$related)
    {
        $msg = new Message();
        $msg->from_profile = $profile->id;
        $msg->delete();

        $msg = new Message();
        $msg->to_profile = $profile->id;
        $msg->delete();
        return true;
    }

    public function onPluginVersion(array &$versions)
    {
        $versions[] = array('name' => 'Direct Message',
                            'version' => GNUSOCIAL_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => 'http://gnu.io/',
                            'rawdescription' =>
                            // TRANS: Plugin description.
                            _m('Direct Message to other local users (broken out of core).'));

        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show a the direct messages from or to 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  API
 * @package   StatusNet
 * @author    Adrian Lang <mail@adrianlang.de>
 * @author    Evan Prodromou <evan@status.net>
 * @author    Robin Millette <robin@millette.info>
 * @author    Zach Copley <zach@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);
}

/**
 * Show a list of direct messages from or to the authenticating user
 *
 * @category API
 * @package  StatusNet
 * @author   Adrian Lang <mail@adrianlang.de>
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <robin@millette.info>
 * @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 ApiDirectMessageAction extends ApiAuthAction
{
    var $messages     = null;
    var $title        = null;
    var $subtitle     = null;
    var $link         = null;
    var $selfuri_base = null;
    var $id           = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    function prepare($args)
    {
        parent::prepare($args);

        $this->user = $this->auth_user;

        if (empty($this->user)) {
            // TRANS: Client error given when a user was not found (404).
            $this->clientError(_('No such user.'), 404);
        }

        $server   = common_root_url();
        $taguribase = TagURI::base();

        if ($this->arg('sent')) {

            // Action was called by /api/direct_messages/sent.format

            $this->title = sprintf(
                // TRANS: Title. %s is a user nickname.
                _("Direct messages from %s"),
                $this->user->nickname
            );
            $this->subtitle = sprintf(
                // TRANS: Subtitle. %s is a user nickname.
                _("All the direct messages sent from %s"),
                $this->user->nickname
            );
            $this->link = $server . $this->user->nickname . '/outbox';
            $this->selfuri_base = common_root_url() . 'api/direct_messages/sent';
            $this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
        } else {
            $this->title = sprintf(
                // TRANS: Title. %s is a user nickname.
                _("Direct messages to %s"),
                $this->user->nickname
            );
            $this->subtitle = sprintf(
                // TRANS: Subtitle. %s is a user nickname.
                _("All the direct messages sent to %s"),
                $this->user->nickname
            );
            $this->link = $server . $this->user->nickname . '/inbox';
            $this->selfuri_base = common_root_url() . 'api/direct_messages';
            $this->id = "tag:$taguribase:DirectMessages:" . $this->user->id;
        }

        $this->messages = $this->getMessages();

        return true;
    }

    /**
     * Handle the request
     *
     * Show the messages
     *
     * @param array $args $_REQUEST data (unused)
     *
     * @return void
     */
    function handle($args)
    {
        parent::handle($args);
        $this->showMessages();
    }

    /**
     * Show the messages
     *
     * @return void
     */
    function showMessages()
    {
        switch($this->format) {
        case 'xml':
            $this->showXmlDirectMessages();
            break;
        case 'rss':
            $this->showRssDirectMessages();
            break;
        case 'atom':
            $this->showAtomDirectMessages();
            break;
        case 'json':
            $this->showJsonDirectMessages();
            break;
        default:
            // TRANS: Client error displayed when coming across a non-supported API method.
            $this->clientError(_('API method not found.'), $code = 404);
            break;
        }
    }

    /**
     * Get notices
     *
     * @return array notices
     */
    function getMessages()
    {
        $message  = new Message();

        if ($this->arg('sent')) {
            $message->from_profile = $this->user->id;
        } else {
            $message->to_profile = $this->user->id;
        }

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

        if (!empty($this->since_id)) {
            $message->whereAdd('id > ' . $this->since_id);
        }

        $message->orderBy('created DESC, id DESC');
        $message->limit((($this->page - 1) * $this->count), $this->count);
        $message->find();

        $messages = array();

        while ($message->fetch()) {
            $messages[] = clone($message);
        }

        return $messages;
    }

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

    /**
     * When was this notice last modified?
     *
     * @return string datestamp of the latest notice in the stream
     */
    function lastModified()
    {
        if (!empty($this->messages)) {
            return strtotime($this->messages[0]->created);
        }

        return null;
    }

    // BEGIN import from lib/apiaction.php

    function showSingleXmlDirectMessage($message)
    {
        $this->initDocument('xml');
        $dmsg = $this->directMessageArray($message);
        $this->showXmlDirectMessage($dmsg, true);
        $this->endDocument('xml');
    }

    function showSingleJsonDirectMessage($message)
    {
        $this->initDocument('json');
        $dmsg = $this->directMessageArray($message);
        $this->showJsonObjects($dmsg);
        $this->endDocument('json');
    }

    function showXmlDirectMessage($dm, $namespaces=false)
    {
        $attrs = array();
        if ($namespaces) {
            $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
        }
        $this->elementStart('direct_message', $attrs);
        foreach($dm as $element => $value) {
            switch ($element) {
            case 'sender':
            case 'recipient':
                $this->showTwitterXmlUser($value, $element);
                break;
            case 'text':
                $this->element($element, null, common_xml_safe_str($value));
                break;
            default:
                $this->element($element, null, $value);
                break;
            }
        }
        $this->elementEnd('direct_message');
    }

    function directMessageArray($message)
    {
        $dmsg = array();

        $from_profile = $message->getFrom();
        $to_profile = $message->getTo();

        $dmsg['id'] = intval($message->id);
        $dmsg['sender_id'] = intval($from_profile->id);
        $dmsg['text'] = trim($message->content);
        $dmsg['recipient_id'] = intval($to_profile->id);
        $dmsg['created_at'] = $this->dateTwitter($message->created);
        $dmsg['sender_screen_name'] = $from_profile->nickname;
        $dmsg['recipient_screen_name'] = $to_profile->nickname;
        $dmsg['sender'] = $this->twitterUserArray($from_profile, false);
        $dmsg['recipient'] = $this->twitterUserArray($to_profile, false);

        return $dmsg;
    }

    function rssDirectMessageArray($message)
    {
        $entry = array();

        $from = $message->getFrom();

        $entry['title'] = sprintf('Message from %1$s to %2$s',
            $from->nickname, $message->getTo()->nickname);

        $entry['content'] = common_xml_safe_str($message->rendered);
        $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
        $entry['published'] = common_date_iso8601($message->created);

        $taguribase = TagURI::base();

        $entry['id'] = "tag:$taguribase:$entry[link]";
        $entry['updated'] = $entry['published'];

        $entry['author-name'] = $from->getBestName();
        $entry['author-uri'] = $from->homepage;

        $entry['avatar'] = $from->avatarUrl(AVATAR_STREAM_SIZE);
        try {
            $avatar = $from->getAvatar(AVATAR_STREAM_SIZE);
            $entry['avatar-type'] = $avatar->mediatype;
        } catch (Exception $e) {
            $entry['avatar-type'] = 'image/png';
        }

        // RSS item specific

        $entry['description'] = $entry['content'];
        $entry['pubDate'] = common_date_rfc2822($message->created);
        $entry['guid'] = $entry['link'];

        return $entry;
    }

    // END import from lib/apiaction.php

    /**
     * Shows a list of direct messages as Twitter-style XML array
     *
     * @return void
     */
    function showXmlDirectMessages()
    {
        $this->initDocument('xml');
        $this->elementStart('direct-messages', array('type' => 'array',
                                                     'xmlns:statusnet' => 'http://status.net/schema/api/1/'));

        foreach ($this->messages as $m) {
            $dm_array = $this->directMessageArray($m);
            $this->showXmlDirectMessage($dm_array);
        }

        $this->elementEnd('direct-messages');
        $this->endDocument('xml');
    }

    /**
     * Shows a list of direct messages as a JSON encoded array
     *
     * @return void
     */
    function showJsonDirectMessages()
    {
        $this->initDocument('json');

        $dmsgs = array();

        foreach ($this->messages as $m) {
            $dm_array = $this->directMessageArray($m);
            array_push($dmsgs, $dm_array);
        }

        $this->showJsonObjects($dmsgs);
        $this->endDocument('json');
    }

    /**
     * Shows a list of direct messages as RSS items
     *
     * @return void
     */
    function showRssDirectMessages()
    {
        $this->initDocument('rss');

        $this->element('title', null, $this->title);

        $this->element('link', null, $this->link);
        $this->element('description', null, $this->subtitle);
        $this->element('language', null, 'en-us');

        $this->element(
            'atom:link',
            array(
                'type' => 'application/rss+xml',
                'href' => $this->selfuri_base . '.rss',
                'rel' => self
                ),
            null
        );
        $this->element('ttl', null, '40');

        foreach ($this->messages as $m) {
            $entry = $this->rssDirectMessageArray($m);
            $this->showTwitterRssItem($entry);
        }

        $this->endTwitterRss();
    }

    /**
     * Shows a list of direct messages as Atom entries
     *
     * @return void
     */
    function showAtomDirectMessages()
    {
        $this->initDocument('atom');

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

        $selfuri = common_root_url() . 'api/direct_messages.atom';

        $this->element(
            'link', array(
            'href' => $this->link,
            'rel' => 'alternate',
            'type' => 'text/html'),
            null
        );
        $this->element(
            'link', array(
            'href' => $this->selfuri_base . '.atom', 'rel' => 'self',
            'type' => 'application/atom+xml'),
            null
        );
        $this->element('updated', null, common_date_iso8601('now'));
        $this->element('subtitle', null, $this->subtitle);

        foreach ($this->messages as $m) {
            $entry = $this->rssDirectMessageArray($m);
            $this->showTwitterAtomEntry($entry);
        }

        $this->endDocument('atom');
    }

    /**
     * An entity tag for this notice
     *
     * Returns an Etag based on the action name, language, and
     * timestamps of the notice
     *
     * @return string etag
     */
    function etag()
    {
        if (!empty($this->messages)) {

            $last = count($this->messages) - 1;

            return '"' . implode(
                ':',
                array($this->arg('action'),
                      common_user_cache_hash($this->auth_user),
                      common_language(),
                      strtotime($this->messages[0]->created),
                      strtotime($this->messages[$last]->created)
                )
            )
            . '"';
        }

        return null;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Send a direct message via the API
 *
 * 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  API
 * @package   StatusNet
 * @author    Adrian Lang <mail@adrianlang.de>
 * @author    Evan Prodromou <evan@status.net>
 * @author    Robin Millette <robin@millette.info>
 * @author    Zach Copley <zach@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);
}

/**
 * Creates a new direct message from the authenticating user to
 * the user specified by id.
 *
 * @category API
 * @package  StatusNet
 * @author   Adrian Lang <mail@adrianlang.de>
 * @author   Evan Prodromou <evan@status.net>
 * @author   Robin Millette <robin@millette.info>
 * @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 ApiDirectMessageNewAction extends ApiAuthAction
{
    protected $needPost = true;

    var $other   = null;    // Profile we're sending to
    var $content = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        if (empty($this->user)) {
            // TRANS: Client error when user not found for an API direct message action.
            $this->clientError(_('No such user.'), 404);
        }

        $this->content = $this->trimmed('text');

        $user_param  = $this->trimmed('user');
        $user_id     = $this->arg('user_id');
        $screen_name = $this->trimmed('screen_name');

        if (isset($user_param) || isset($user_id) || isset($screen_name)) {
            $this->other = $this->getTargetProfile($user_param);
        }

        return true;
    }

    /**
     * Handle the request
     *
     * Save the new message
     *
     * @return void
     */
    protected function handle()
    {
        parent::handle();

        if (empty($this->content)) {
            // TRANS: Client error displayed when no message text was submitted (406).
            $this->clientError(_('No message text!'), 406);
        } else {
            $content_shortened = $this->auth_user->shortenLinks($this->content);
            if (Message::contentTooLong($content_shortened)) {
                // TRANS: Client error displayed when message content is too long.
                // TRANS: %d is the maximum number of characters for a message.
                $this->clientError(
                    sprintf(_m('That\'s too long. Maximum message size is %d character.', 'That\'s too long. Maximum message size is %d characters.', Message::maxContent()), Message::maxContent()),
                    406);
            }
        }

        if (!$this->other instanceof Profile) {
            // TRANS: Client error displayed if a recipient user could not be found (403).
            $this->clientError(_('Recipient user not found.'), 403);
        } else if (!$this->user->mutuallySubscribed($this->other)) {
            // TRANS: Client error displayed trying to direct message another user who's not a friend (403).
            $this->clientError(_('Cannot send direct messages to users who aren\'t your friend.'), 403);
        } else if ($this->user->id == $this->other->id) {

            // Note: sending msgs to yourself is allowed by Twitter

            // TRANS: Client error displayed trying to direct message self (403).
            $this->clientError(_('Do not send a message to yourself; just say it to yourself quietly instead.'), 403);
        }

        $message = Message::saveNew(
            $this->user->id,
            $this->other->id,
            html_entity_decode($this->content, ENT_NOQUOTES, 'UTF-8'),
            $this->source
        );

        $message->notify();

        if ($this->format == 'xml') {
            $this->showSingleXmlDirectMessage($message);
        } elseif ($this->format == 'json') {
            $this->showSingleJsondirectMessage($message);
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * action handler for message inbox
 *
 * 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  Message
 * @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('GNUSOCIAL')) { exit(1); }

/**
 * action handler for message inbox
 *
 * @category Message
 * @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      MailboxAction
 */
class InboxAction extends MailboxAction
{

    /**
     * Title of the page
     *
     * @return string page title
     */
    function title()
    {
        if ($this->page > 1) {
            // TRANS: Title for all but the first page of the inbox page.
            // TRANS: %1$s is the user's nickname, %2$s is the page number.
            return sprintf(_('Inbox for %1$s - page %2$d'), $this->user->nickname,
                $this->page);
        } else {
            // TRANS: Title for the first page of the inbox page.
            // TRANS: %s is the user's nickname.
            return sprintf(_('Inbox for %s'), $this->user->nickname);
        }
    }

    /**
     * Retrieve the messages for this user and this page
     *
     * Does a query for the right messages
     *
     * @return Message data object with stream for messages
     *
     * @see MailboxAction::getMessages()
     */
    function getMessages()
    {
        $message = new Message();

        $message->to_profile = $this->user->id;
        $message->orderBy('created DESC, id DESC');
        $message->limit((($this->page - 1) * MESSAGES_PER_PAGE),
            MESSAGES_PER_PAGE + 1);

        if ($message->find()) {
            return $message;
        } else {
            return null;
        }
    }

    function getMessageList($message)
    {
        return new InboxMessageList($this, $message);
    }

    /**
     * Instructions for using this page
     *
     * @return string localised instructions for using the page
     */
    function getInstructions()
    {
        // TRANS: Instructions for user inbox page.
        return _('This is your inbox, which lists your incoming private messages.');
    }
}

class InboxMessageList extends MessageList
{
    function newItem($message)
    {
        return new InboxMessageListItem($this->out, $message);
    }
}

class InboxMessageListItem extends MessageListItem
{
    /**
     * Returns the profile we want to show with the message
     *
     * @return Profile The profile that matches the message
     */
    function getMessageProfile()
    {
        return $this->message->getFrom();
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Handler for posting new messages
 *
 * 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  Personal
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @author    Zach Copley <zach@status.net>
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @copyright 2013 Free Software Foundation, 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);
}

/**
 * Action for posting new direct messages
 *
 * @category Personal
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Zach Copley <zach@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/
 */

class NewmessageAction extends FormAction
{
    var $content = null;
    var $to = null;
    var $other = null;

    protected $form = 'message';    // will become MessageForm later

    /**
     * Title of the page
     *
     * Note that this usually doesn't get called unless something went wrong
     *
     * @return string page title
     */

    function title()
    {
        // TRANS: Page title for new direct message page.
        return _('New message');
    }

    /**
     * Handle input, produce output
     *
     * @param array $args $_REQUEST contents
     *
     * @return void
     */

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

        $this->content = $this->trimmed('content');
        $this->to = $this->trimmed('to');

        if ($this->to) {

            $this->other = Profile::getKV('id', $this->to);

            if (!$this->other instanceof Profile) {
                // TRANS: Client error displayed trying to send a direct message to a non-existing user.
                $this->clientError(_('No such user.'), 404);
            }

            if (!$this->other->isLocal()) {
                // TRANS: Explains that current federation does not support direct, private messages yet.
                $this->clientError(_('You cannot send direct messages to federated users yet.'));
            }

            if (!$this->scoped->mutuallySubscribed($this->other)) {
                // TRANS: Client error displayed trying to send a direct message to a user while sender and
                // TRANS: receiver are not subscribed to each other.
                $this->clientError(_('You cannot send a message to this user.'), 404);
            }
        }

        return true;
    }

    protected function handlePost()
    {
        parent::handlePost();

        assert($this->scoped instanceof Profile); // XXX: maybe an error instead...

        if (!$this->content) {
            // TRANS: Form validator error displayed trying to send a direct message without content.
            $this->clientError(_('No content!'));
        } else {
            $content_shortened = $this->scoped->shortenLinks($this->content);

            if (Message::contentTooLong($content_shortened)) {
                // TRANS: Form validation error displayed when message content is too long.
                // TRANS: %d is the maximum number of characters for a message.
                $this->clientError(sprintf(_m('That\'s too long. Maximum message size is %d character.',
                                           'That\'s too long. Maximum message size is %d characters.',
                                           Message::maxContent()),
                                        Message::maxContent()));
            }
        }

        if (!$this->other) {
            // TRANS: Form validation error displayed trying to send a direct message without specifying a recipient.
            $this->clientError(_('No recipient specified.'));
        } else if (!$this->scoped->mutuallySubscribed($this->other)) {
            // TRANS: Client error displayed trying to send a direct message to a user while sender and
            // TRANS: receiver are not subscribed to each other.
            $this->clientError(_('You cannot send a message to this user.'), 404);
        } else if ($this->scoped->id == $this->other->id) {
            // TRANS: Client error displayed trying to send a direct message to self.
            $this->clientError(_('Do not send a message to yourself; ' .
                'just say it to yourself quietly instead.'), 403);
        }

        $message = Message::saveNew($this->scoped->id, $this->other->id, $this->content, 'web');
        $message->notify();

        if ($this->boolean('ajax')) {
            $this->startHTML('text/xml;charset=utf-8');
            $this->elementStart('head');
            // TRANS: Page title after sending a direct message.
            $this->element('title', null, _('Message sent'));
            $this->elementEnd('head');
            $this->elementStart('body');
            $this->element('p', array('id' => 'command_result'),
                // TRANS: Confirmation text after sending a direct message.
                // TRANS: %s is the direct message recipient.
                sprintf(_('Direct message to %s sent.'),
                    $this->other->nickname));
            $this->elementEnd('body');
            $this->endHTML();
        } else {
            $url = common_local_url('outbox',
                array('nickname' => $this->scoped->nickname));
            common_redirect($url, 303);
        }
    }

    /**
     * Show an Ajax-y error message
     *
     * Goes back to the browser, where it's shown in a popup.
     *
     * @param string $msg Message to show
     *
     * @return void
     */

    function ajaxErrorMsg($msg)
    {
        $this->startHTML('text/xml;charset=utf-8', true);
        $this->elementStart('head');
        // TRANS: Page title after an AJAX error occurred on the "send direct message" page.
        $this->element('title', null, _('Ajax Error'));
        $this->elementEnd('head');
        $this->elementStart('body');
        $this->element('p', array('id' => 'error'), $msg);
        $this->elementEnd('body');
        $this->endHTML();
    }

    function showForm($msg = null)
    {
        if ($msg && $this->boolean('ajax')) {
            $this->ajaxErrorMsg($msg);
            return;
        }

        $this->msg = $msg;
        if ($this->trimmed('ajax')) {
            $this->startHTML('text/xml;charset=utf-8');
            $this->elementStart('head');
            // TRANS: Page title on page for sending a direct message.
            $this->element('title', null, _('New message'));
            $this->elementEnd('head');
            $this->elementStart('body');
            $this->showNoticeForm();
            $this->elementEnd('body');
            $this->endHTML();
        }
        else {
            $this->showPage();
        }
    }

    function showPageNotice()
    {
        if ($this->msg) {
            $this->element('p', 'error', $this->msg);
        }
    }

    // Do nothing (override)

    function showNoticeForm()
    {
        $message_form = new MessageForm($this, $this->other, $this->content);
        $message_form->show();
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * action handler for message inbox
 *
 * 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  Message
 * @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('GNUSOCIAL')) { exit(1); }

/**
 * action handler for message outbox
 *
 * @category Message
 * @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      MailboxAction
 */
class OutboxAction extends MailboxAction
{
    /**
     * Title of the page
     *
     * @return string page title
     */
    function title()
    {
        if ($this->page > 1) {
            // TRANS: Title for outbox for any but the fist page.
            // TRANS: %1$s is the user nickname, %2$d is the page number.
            return sprintf(_('Outbox for %1$s - page %2$d'),
                $this->user->nickname, $page);
        } else {
            // TRANS: Title for first page of outbox.
            return sprintf(_('Outbox for %s'), $this->user->nickname);
        }
    }

    /**
     * retrieve the messages for this user and this page
     *
     * Does a query for the right messages
     *
     * @return Message data object with stream for messages
     *
     * @see MailboxAction::getMessages()
     */
    function getMessages()
    {
        $message = new Message();

        $message->from_profile = $this->user->id;
        $message->orderBy('created DESC, id DESC');
        $message->limit((($this->page - 1) * MESSAGES_PER_PAGE),
            MESSAGES_PER_PAGE + 1);

        if ($message->find()) {
            return $message;
        } else {
            return null;
        }
    }

    function getMessageList($message)
    {
        return new OutboxMessageList($this, $message);
    }

    /**
     * instructions for using this page
     *
     * @return string localised instructions for using the page
     */
    function getInstructions()
    {
        // TRANS: Instructions for outbox.
        return _('This is your outbox, which lists private messages you have sent.');
    }
}

class OutboxMessageList extends MessageList
{
    function newItem($message)
    {
        return new OutboxMessageListItem($this->out, $message);
    }
}

class OutboxMessageListItem extends MessageListItem
{
    /**
     * Returns the profile we want to show with the message
     *
     * @return Profile The profile that matches the message
     */
    function getMessageProfile()
    {
        return $this->message->getTo();
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show a single message
 *
 * 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  Personal
 * @package   StatusNet
 * @author    Evan Prodromou <evan@status.net>
 * @copyright 2008-2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */
if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

/**
 * Show a single message
 *
 * @category Personal
 * @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 ShowmessageAction extends Action
{
    /**
     * Message object to show
     */
    var $message = null;

    /**
     * The current user
     */

    var $user = null;

    /**
     * Load attributes based on database arguments
     *
     * Loads all the DB stuff
     *
     * @param array $args $_REQUEST array
     *
     * @return success flag
     */
    function prepare($args)
    {
        parent::prepare($args);

        $this->page = 1;

        $id            = $this->trimmed('message');
        $this->message = Message::getKV('id', $id);

        if (!$this->message) {
            // TRANS: Client error displayed requesting a single message that does not exist.
            $this->clientError(_('No such message.'), 404);
        }

        $this->user = common_current_user();

        if (empty($this->user) ||
            ($this->user->id != $this->message->from_profile &&
             $this->user->id != $this->message->to_profile)) {
            // TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
            throw new ClientException(_('Only the sender and recipient ' .
                                        'may read this message.'), 403);
        }

        return true;
    }

    function handle($args)
    {
        $this->showPage();
    }

    function title()
    {
        if ($this->user->id == $this->message->from_profile) {
            $to = $this->message->getTo();
            // @todo FIXME: Might be nice if the timestamp could be localised.
            // TRANS: Page title for single direct message display when viewing user is the sender.
            // TRANS: %1$s is the addressed user's nickname, $2$s is a timestamp.
            return sprintf(_('Message to %1$s on %2$s'),
                             $to->nickname,
                             common_exact_date($this->message->created));
        } else if ($this->user->id == $this->message->to_profile) {
            $from = $this->message->getFrom();
            // @todo FIXME: Might be nice if the timestamp could be localised.
            // TRANS: Page title for single message display.
            // TRANS: %1$s is the sending user's nickname, $2$s is a timestamp.
            return sprintf(_('Message from %1$s on %2$s'),
                             $from->nickname,
                             common_exact_date($this->message->created));
        }
    }

    function showContent()
    {
        $this->elementStart('ul', 'notices messages');
        $ml = new ShowMessageListItem($this, $this->message, $this->user);
        $ml->show();
        $this->elementEnd('ul');
    }

    function isReadOnly($args)
    {
        return true;
    }

    /**
     * Don't show aside
     *
     * @return void
     */

    function showAside() {
    }
}

class ShowMessageListItem extends MessageListItem
{
    var $user;

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

    function getMessageProfile()
    {
        if ($this->user->id == $this->message->from_profile) {
            return $this->message->getTo();
        } else if ($this->user->id == $this->message->to_profile) {
            return $this->message->getFrom();
        } else {
            // This shouldn't happen
            return null;
        }
    }
}


code:
<?php

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

/**
 * Table Definition for message
 */

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

    public $__table = 'message';                         // table name
    public $id;                              // int(4)  primary_key not_null
    public $uri;                             // varchar(255)  unique_key
    public $from_profile;                    // int(4)   not_null
    public $to_profile;                      // int(4)   not_null
    public $content;                         // text()
    public $rendered;                        // text()
    public $url;                             // varchar(255)
    public $created;                         // datetime()   not_null
    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
    public $source;                          // varchar(32)

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

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
                'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier'),
                'from_profile' => array('type' => 'int', 'not null' => true, 'description' => 'who the message is from'),
                'to_profile' => array('type' => 'int', 'not null' => true, 'description' => 'who the message is to'),
                'content' => array('type' => 'text', 'description' => 'message content'),
                'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
                'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
                'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
            ),
            'primary key' => array('id'),
            'unique keys' => array(
                'message_uri_key' => array('uri'),
            ),
            'foreign keys' => array(
                'message_from_profile_fkey' => array('profile', array('from_profile' => 'id')),
                'message_to_profile_fkey' => array('profile', array('to_profile' => 'id')),
            ),
            'indexes' => array(
                // @fixme these are really terrible indexes, since you can only sort on one of them at a time.
                // looks like we really need a (to_profile, created) for inbox and a (from_profile, created) for outbox
                'message_from_idx' => array('from_profile'),
                'message_to_idx' => array('to_profile'),
                'message_created_idx' => array('created'),
            ),
        );
    }

    function getFrom()
    {
        return Profile::getKV('id', $this->from_profile);
    }

    function getTo()
    {
        return Profile::getKV('id', $this->to_profile);
    }

    static function saveNew($from, $to, $content, $source) {
        $sender = Profile::getKV('id', $from);

        if (!$sender->hasRight(Right::NEWMESSAGE)) {
            // TRANS: Client exception thrown when a user tries to send a direct message while being banned from sending them.
            throw new ClientException(_('You are banned from sending direct messages.'));
        }

        $user = User::getKV('id', $sender->id);

        $msg = new Message();

        $msg->from_profile = $from;
        $msg->to_profile = $to;
        if ($user) {
            // Use the sender's URL shortening options.
            $msg->content = $user->shortenLinks($content);
        } else {
            $msg->content = common_shorten_links($content);
        }
        $msg->rendered = common_render_text($msg->content);
        $msg->created = common_sql_now();
        $msg->source = $source;

        $result = $msg->insert();

        if (!$result) {
            common_log_db_error($msg, 'INSERT', __FILE__);
            // TRANS: Message given when a message could not be stored on the server.
            throw new ServerException(_('Could not insert message.'));
        }

        $orig = clone($msg);
        $msg->uri = common_local_url('showmessage', array('message' => $msg->id));

        $result = $msg->update($orig);

        if (!$result) {
            common_log_db_error($msg, 'UPDATE', __FILE__);
            // TRANS: Message given when a message could not be updated on the server.
            throw new ServerException(_('Could not update message with new URI.'));
        }

        return $msg;
    }

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

    static function contentTooLong($content)
    {
        $contentlimit = self::maxContent();
        return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
    }

    function notify()
    {
        $from = User::getKV('id', $this->from_profile);
        $to   = User::getKV('id', $this->to_profile);

        mail_notify_message($this, $from, $to);
    }

    function getSource()
    {
        if (empty($this->source)) {
            return false;
        }

        $ns = new Notice_source();
        switch ($this->source) {
        case 'web':
        case 'xmpp':
        case 'mail':
        case 'omb':
        case 'system':
        case 'api':
            $ns->code = $this->source;
            break;
        default:
            $ns = Notice_source::getKV($this->source);
            if (!$ns instanceof Notice_source) {
                $ns = new Notice_source();
                $ns->code = $this->source;
                $app = Oauth_application::getKV('name', $this->source);
                if ($app) {
                    $ns->name = $app->name;
                    $ns->url  = $app->source_url;
                }
            }
            break;
        }
        return $ns;
    }

    function asActivity()
    {
        $act = new Activity();

        if (Event::handle('StartMessageAsActivity', array($this, &$act))) {

            $act->id      = TagURI::mint(sprintf('activity:message:%d', $this->id));
            $act->time    = strtotime($this->created);
            $act->link    = $this->url;

            $profile = Profile::getKV('id', $this->from_profile);

            if (empty($profile)) {
                throw new Exception(sprintf("Sender profile not found: %d", $this->from_profile));
            }

            $act->actor            = $profile->asActivityObject();
            $act->actor->extra[]   = $profile->profileInfo();

            $act->verb = ActivityVerb::POST;

            $act->objects[] = ActivityObject::fromMessage($this);

            $ctx = new ActivityContext();

            $rprofile = Profile::getKV('id', $this->to_profile);

            if (empty($rprofile)) {
                throw new Exception(sprintf("Receiver profile not found: %d", $this->to_profile));
            }

            $ctx->attention[$rprofile->getUri()] = ActivityObject::PERSON;

            $act->context = $ctx;

            $source = $this->getSource();

            if ($source instanceof Notice_source) {
                $act->generator = ActivityObject::fromNoticeSource($source);
            }

            Event::handle('EndMessageAsActivity', array($this, &$act));
        }

        return $act;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * common superclass for direct messages inbox and outbox
 *
 * 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  Message
 * @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);
}

/**
 * common superclass for direct messages inbox and outbox
 *
 * @category Message
 * @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      InboxAction
 * @see      OutboxAction
 */
class MailboxAction extends Action
{
    var $page = null;

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

        $nickname   = common_canonical_nickname($this->arg('nickname'));
        $this->user = User::getKV('nickname', $nickname);
        $this->page = $this->trimmed('page');

        if (!$this->page) {
            $this->page = 1;
        }

        common_set_returnto($this->selfUrl());

        return true;
    }

    /**
     * output page based on arguments
     *
     * @param array $args HTTP arguments (from $_REQUEST)
     *
     * @return void
     */
    function handle($args)
    {
        parent::handle($args);

        if (!$this->user) {
            // TRANS: Client error displayed when trying to access a mailbox without providing a user.
            $this->clientError(_('No such user.'), 404);
        }

        $cur = common_current_user();

        if (!$cur || $cur->id != $this->user->id) {
            // TRANS: Client error displayed when trying to access a mailbox that is not of the logged in user.
            $this->clientError(_('Only the user can read their own mailboxes.'), 403);
        }

        $this->showPage();
    }

    function showNoticeForm()
    {
        $message_form = new MessageForm($this);
        $message_form->show();
    }

    function showContent()
    {
        $message = $this->getMessages();

        if ($message) {

            $ml = $this->getMessageList($message);

            $cnt = $ml->show();

            $this->pagination($this->page > 1,
                              $cnt > MESSAGES_PER_PAGE,
                              $this->page,
                              $this->trimmed('action'),
                              array('nickname' => $this->user->nickname));
        } else {
            $this->element('p',
                           'guide',
                           // TRANS: Message displayed when there are no private messages in the inbox of a user.
                           _('You have no private messages. '.
                             'You can send private message to engage other users in conversation. '.
                             'People can send you messages for your eyes only.'));
        }
    }

    function getMessages()
    {
        return null;
    }

    function getMessageList($message)
    {
        return null;
    }

    /**
     * Show the page notice
     *
     * Shows instructions for the page
     *
     * @return void
     */
    function showPageNotice()
    {
        $instr  = $this->getInstructions();
        $output = common_markup_to_html($instr);

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

    /**
     * Mailbox actions are read only
     *
     * @param array $args other arguments
     *
     * @return boolean
     */
    function isReadOnly($args)
    {
         return true;
    }

    function showObjectNav()
    {
        $mm = new MailboxMenu($this);
        $mm->show();
    }
}


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

/**
 * Menu of existing mailboxes
 *
 * @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 MailboxMenu extends Menu
{
    function show()
    {
        $cur = common_current_user();
        $nickname = $cur->nickname;

        $this->out->elementStart('ul', array('class' => 'nav'));

        $this->item('inbox',
                    array('nickname' => $nickname),
                    // TRANS: Menu item in mailbox menu. Leads to incoming private messages.
                    _m('MENU','Inbox'),
                    // TRANS: Menu item title in mailbox menu. Leads to incoming private messages.
                    _('Your incoming messages.'));

        $this->item('outbox',
                    array('nickname' => $nickname),
                    // TRANS: Menu item in mailbox menu. Leads to outgoing private messages.
                    _m('MENU','Outbox'),
                    // TRANS: Menu item title in mailbox menu. Leads to outgoing private messages.
                    _('Your sent messages.'));

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


code:
<?php

class MessageCommand extends Command
{
    var $other = null;
    var $text = null;
    function __construct($user, $other, $text)
    {
        parent::__construct($user);
        $this->other = $other;
        $this->text = $text;
    }

    function handle($channel)
    {
        try {
            $other = $this->getUser($this->other)->getProfile();
        } catch (CommandException $e) {
            try {
                $profile = $this->getProfile($this->other);
            } catch (CommandException $f) {
                throw $e;
            }
            // TRANS: Command exception text shown when trying to send a direct message to a remote user (a user not registered at the current server).
            // TRANS: %s is a remote profile.
            throw new CommandException(sprintf(_('%s is a remote profile; you can only send direct messages to users on the same server.'), $this->other));
        }

        $len = mb_strlen($this->text);

        if ($len == 0) {
            // TRANS: Command exception text shown when trying to send a direct message to another user without content.
            $channel->error($this->user, _('No content!'));
            return;
        }

        $this->text = $this->user->shortenLinks($this->text);

        if (Message::contentTooLong($this->text)) {
            // XXX: i18n. Needs plural support.
            // TRANS: Message given if content is too long. %1$sd is used for plural.
            // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
            $channel->error($this->user, sprintf(_m('Message too long - maximum is %1$d character, you sent %2$d.',
                                                    'Message too long - maximum is %1$d characters, you sent %2$d.',
                                                    Message::maxContent()),
                                                 Message::maxContent(), mb_strlen($this->text)));
            return;
        }

        if (!$other instanceof Profile) {
            // TRANS: Error text shown when trying to send a direct message to a user that does not exist.
            $channel->error($this->user, _('No such user.'));
            return;
        } else if (!$this->user->mutuallySubscribed($other)) {
            // TRANS: Error text shown when trying to send a direct message to a user without a mutual subscription (each user must be subscribed to the other).
            $channel->error($this->user, _('You can\'t send a message to this user.'));
            return;
        } else if ($this->user->id == $other->id) {
            // TRANS: Error text shown when trying to send a direct message to self.
            $channel->error($this->user, _('Do not send a message to yourself; just say it to yourself quietly instead.'));
            return;
        }
        try {
            $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
            $message->notify();
            // TRANS: Message given have sent a direct message to another user.
            // TRANS: %s is the name of the other user.
            $channel->output($this->user, sprintf(_('Direct message to %s sent.'), $this->other));
        } catch (Exception $e) {
            // TRANS: Error text shown sending a direct message fails with an unknown reason.
            $channel->error($this->user, $e->getMessage());
        }
    }
}


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

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

/**
 * Form for posting a direct message
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @author   Sarven Capadisli <csarven@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      HTMLOutputter
 */
class MessageForm extends Form
{
    /**
     * User to send a direct message to
     */
    var $to = null;

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

    /**
     * Constructor
     *
     * @param HTMLOutputter $out     output channel
     * @param User          $to      user to send a message to
     * @param string        $content content to pre-fill
     */
    function __construct($out=null, $to=null, $content=null)
    {
        parent::__construct($out);

        $this->to      = $to;
        $this->content = $content;
    }

    /**
     * ID of the form
     *
     * @return string ID of the form
     */
    function id()
    {
        return 'form_notice-direct';
    }

   /**
     * Class of the form
     *
     * @return string class of the form
     */
    function formClass()
    {
        return 'form_notice ajax-notice';
    }

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

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

    /**
     * Data elements
     *
     * @return void
     */
    function formData()
    {
        $user = common_current_user();

        $mutual_users = $user->mutuallySubscribedUsers();

        $mutual = array();
        // TRANS: Label entry in drop-down selection box in direct-message inbox/outbox.
        // TRANS: This is the default entry in the drop-down box, doubling as instructions
        // TRANS: and a brake against accidental submissions with the first user in the list.
        $mutual[0] = _('Select recipient:');

        while ($mutual_users->fetch()) {
            if ($mutual_users->id != $user->id) {
                $mutual[$mutual_users->id] = $mutual_users->nickname;
            }
        }

        $mutual_users->free();
        unset($mutual_users);

        if (count($mutual) == 1) {
            // TRANS: Entry in drop-down selection box in direct-message inbox/outbox when no one is available to message.
            $mutual[0] = _('No mutual subscribers.');
        }

        // TRANS: Dropdown label in direct notice form.
        $this->out->dropdown('to', _('To'), $mutual, null, false,
                             ($this->to) ? $this->to->id : null);

        $this->out->element('textarea', array('class' => 'notice_data-text',
                                              'cols' => 35,
                                              'rows' => 4,
                                              'name' => 'content'),
                            ($this->content) ? $this->content : '');

        $contentLimit = Message::maxContent();

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

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        $this->out->element('input', array('id' => 'notice_action-submit',
                                           'class' => 'submit',
                                           'name' => 'message_send',
                                           'type' => 'submit',
                                           // TRANS: Button text for sending a direct notice.
                                           'value' => _m('Send button for sending notice', 'Send')));
    }

    /**
     * Show the form
     *
     * Uses a recipe to output the form.
     *
     * @return void
     * @see Widget::show()
     */

    function show()
    {
        $this->elementStart('div', 'input_forms');
        $this->elementStart(
            'div',
            array(
                'id'    => 'input_form_direct',
                'class' => 'input_form current nonav'
            )
        );

        parent::show();

        $this->elementEnd('div');
        $this->elementEnd('div');

    }
}


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

/**
 * Message list widget
 *
 * @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/
 */
abstract class MessageList extends Widget
{
    var $message;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out     Output context
     * @param Message       $message Stream of messages to show
     */
    function __construct($out, $message)
    {
        parent::__construct($out);
        $this->message = $message;
    }

    /**
     * Show the widget
     *
     * Uses newItem() to create each new item.
     *
     * @return integer count of messages seen.
     */
    function show()
    {
            $cnt = 0;

            $this->out->elementStart('div', array('id' =>'notices_primary'));

            // TRANS: Header in message list.
            $this->out->element('h2', null, _('Messages'));

            $this->out->elementStart('ul', 'notices messages');

            while ($this->message->fetch() && $cnt <= MESSAGES_PER_PAGE) {

                $cnt++;

                if ($cnt > MESSAGES_PER_PAGE) {
                    break;
                }

                $mli = $this->newItem($this->message);

                $mli->show();
            }

            $this->out->elementEnd('ul');

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

    /**
     * Create a new message item for a message
     *
     * @param Message $message The message to show
     *
     * @return MessageListItem an item to show
     */
    abstract function newItem($message);
}