EndShowNoticeItem: just after showing the notice item
- $item: the NoticeListItem object being shown
+StartShowNoticeItemNotice: just before outputting the "top" notice part of a NoticeListItem to HTML
+- $item: The NoticeListItem object being shown
+
+EndShowNoticeItemNotice: just after outputting the "top" notice part of a NoticeListItem to HTML
+- $item: The NoticeListItem object being shown
+
+StartShowNoticeContent: just before outputting the content part of a Notice
+- $stored: The Notice object
+- $out: HTMLOutputter for writing to
+- $scoped: optional Profile object for permission scoping
+
+EndShowNoticeContent: just after outputting the content part of a Notice, plugins must call this manually
+- $stored: The Notice object
+- $out: HTMLOutputter for writing to
+- $scoped: optional Profile object for permission scoping
+
StartShowNoticeInfo: just before showing notice info
- $item: The NoticeListItem object being shown
- PHP 5.4+ For newer versions, some functions that are used may be
disabled by default, such as the pcntl_* family. See the
section on 'Queues and daemons' for more information.
-- MariaDB 5.x GNU Social uses, by default, a MariaDB server for data
+- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
storage. Versions 5.x and 10.x have both reportedly
worked well. It is also possible to run MySQL 5.x.
- Web server Apache, lighttpd and nginx will all work. CGI mode is
Your PHP installation must include the following PHP extensions for a
functional setup of GNU Social:
-- Curl Fetching files by HTTP.
-- XMLWriter For formatting XML and HTML output.
-- mysqlnd The native driver for PHP5 MariaDB connections. If you
- use MySQL, 'mysql' or 'mysqli' may work.
-- GD Image manipulation (scaling).
-- mbstring For handling Unicode (UTF-8) encoded strings.
-- bcmath or gmp For Salmon signatures (part of OStatus).
+- openssl (compiled in for Debian, enabled manually in Arch Linux)
+- php5-curl Fetching files by HTTP.
+- php5-gd Image manipulation (scaling).
+- php5-gmp For Salmon signatures (part of OStatus).
+- php5-json For WebFinger lookups and more.
+- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
+ use MySQL, 'mysql' or 'mysqli' may work.
+
+The above package names are for Debian based systems. In the case of
+Arch Linux, PHP is compiled with support for most extensions but they
+require manual enabling in the relevant php.ini file (mostly php5-gmp).
Better performance
------------------
- More robust handling of errors in distribution.
- Fix error in OStatus subscription for remote groups.
- Fix error in XMPP distribution.
-
+- Tracking of conversation URI metadata (more coherent convos)
### Troubleshooting
follow the upgrade procedures for each respective StatusNet version.
You are now running StatusNet 1.1.1 and want to migrate to GNU social.
+Beware there may be changes in minimum required version of PHP and the
+modules used, so double-check the INSTALL file's requirements list.
Before you begin: Make backups. Always make backups. Of your entire
directory structure and the database too. All tables. All data. Alles.
5. Start your queue daemons: 'php scripts/startdaemons.php'
- 6. Report any issues at https://gitorious.org/statusnet/gnu-social/
+ 6. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
Legacy StatusNet instructions
username 'admin', but blocks the considerably less popular username
'panel'. If you have an existing user named 'panel', you should rename
them before upgrading.
-
-Privacy
-=======
-
-With StatusNet 1.0, our default install profile is for private sites.
-
-If you did not specify the privacy level of your site previously, it
-was public. Now, it's private.
-
-If you upgrade a public site, you will need to reset the privacy
-level. You can do this in your config.php:
-
- $config['site']['private'] = false;
-
-...or with setconfig.php in the db:
-
- php setconfig.php site private false
-
-...or with the site admin panel.
+++ /dev/null
-<?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;
- }
-
- /**
- * 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;
- }
-}
+++ /dev/null
-<?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);
- }
- }
-}
$source = null;
$ns = $notice->getSource();
- if ($ns) {
+ if ($ns instanceof Notice_source) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
*/
function supported($cmd)
{
- static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
+ static $cmdlist = array('SubCommand', 'UnsubCommand',
'OnCommand', 'OffCommand', 'JoinCommand', 'LeaveCommand');
$supported = null;
'id' => $this->target->id),
array('max_id' => $this->next_id))
: null;
- $lastNotice = $this->notices[0];
- $lastId = $lastNotice->id;
+
+ $prevExtra = array();
+ if (!empty($this->notices)) {
+ assert($this->notices[0] instanceof Notice);
+ $prevExtra['since_id'] = $this->notices[0]->id;
+ }
+
$prevUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format,
'id' => $this->target->id),
- array('since_id' => $lastId));
+ $prevExtra);
$firstUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format,
'id' => $this->target->id));
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
- $this->clientError(_('API method not found.'), $code = 404);
+ $this->clientError(_('API method not found.'), 404);
}
}
function showContent() {
$this->areYouSureForm();
+ $block = new GroupProfileBlock($this, $this->group);
+ $block->show();
}
/**
+++ /dev/null
-<?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('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/mailbox.php';
-
-/**
- * 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();
- }
-}
+++ /dev/null
-<?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;
-
- /**
- * 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();
- }
-}
+++ /dev/null
-<?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('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/mailbox.php';
-
-/**
- * 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();
- }
-}
+++ /dev/null
-<?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;
- }
- }
-}
return array(
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'should be set from root notice id (since 2014-03-01 commit)'),
- 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI of the conversation'),
+ 'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 255, 'description' => 'URI of the conversation'),
'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'),
),
*
* @return Conversation the new conversation DO
*/
- static function create(Notice $notice)
+ static function create(Notice $notice, $uri=null)
{
if (empty($notice->id)) {
throw new ServerException(_('Tried to create conversation for not yet inserted notice'));
$conv = new Conversation();
$conv->created = common_sql_now();
$conv->id = $notice->id;
- $conv->uri = sprintf('%s%s=%d:%s=%s',
+ $conv->uri = $uri ?: sprintf('%s%s=%d:%s=%s:%s=%x',
TagURI::mint(),
'noticeId', $notice->id,
- 'objectType', 'thread');
+ 'objectType', 'thread',
+ 'crc32', crc32($notice->content));
$result = $conv->insert();
if ($result === false) {
+++ /dev/null
-<?php
-/**
- * Table Definition for message
- */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-
-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()
- {
- $ns = new Notice_source();
- if (!empty($this->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) {
- $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) {
- $act->generator = ActivityObject::fromNoticeSource($source);
- }
-
- Event::handle('EndMessageAsActivity', array($this, &$act));
- }
-
- return $act;
- }
-}
static function saveNew($profile_id, $content, $source, array $options=null) {
$defaults = array('uri' => null,
'url' => null,
- 'reply_to' => null,
- 'repeat_of' => null,
+ 'conversation' => null, // URI of conversation
+ 'reply_to' => null, // This will override convo URI if the parent is known
+ 'repeat_of' => null, // This will override convo URI if the repeated notice is known
'scope' => null,
'distribute' => true,
'object_type' => null,
// Scope set below
}
+
+ // If we don't know the reply, we might know the conversation!
+ // This will happen if a known remote user replies to an
+ // unknown remote user - within a known conversation.
+ if (empty($notice->conversation) and !empty($options['conversation'])) {
+ $conv = Conversation::getKV('uri', $options['conversation']);
+ if ($conv instanceof Conversation) {
+ common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
+ $notice->conversation = $conv->id;
+ } else {
+ // Conversation URI was not found, so we must create it. But we can't create it
+ // until we have a Notice ID because of the database layout...
+ $notice->tmp_conv_uri = $options['conversation'];
+ }
+ } else {
+ // If we're not using the attached conversation URI let's remove it
+ // so we don't mistake ourselves later, when creating our own Conversation.
+ // This implies that the notice knows which conversation it belongs to.
+ $options['conversation'] = null;
+ }
}
if (!empty($lat) && !empty($lon)) {
try {
$notice->insert(); // throws exception on failure
+ // If it's not part of a conversation, it's
+ // the beginning of a new conversation.
+ if (empty($notice->conversation)) {
+ $orig = clone($notice);
+ // $act->context->conversation will be null if it was not provided
+ $conv = Conversation::create($notice, $options['conversation']);
+ $notice->conversation = $conv->id;
+ $notice->update($orig);
+ }
} catch (Exception $e) {
// Let's test if we managed initial insert, which would imply
// failing on some update-part (check 'insert()'). Delete if
if (!empty($notice->id)) {
$notice->delete();
}
+ throw $e;
}
}
try {
$stored->insert(); // throws exception on error
+ $orig = clone($stored); // for updating later in this try clause
+
+ // If it's not part of a conversation, it's
+ // the beginning of a new conversation.
+ if (empty($stored->conversation)) {
+ // $act->context->conversation will be null if it was not provided
+ $conv = Conversation::create($stored, $act->context->conversation);
+ $stored->conversation = $conv->id;
+ }
$object = null;
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
if (empty($object)) {
throw new ServerException('No object from StoreActivityObject '.$stored->uri . ': '.$act->asString());
}
- $orig = clone($stored);
$stored->object_type = ActivityUtils::resolveUri($object->getObjectType(), true);
$stored->update($orig);
} catch (Exception $e) {
$changed = true;
}
- // If it's not part of a conversation, it's
- // the beginning of a new conversation.
- if (empty($this->conversation)) {
- $conv = Conversation::create($this);
- $this->conversation = $conv->id;
- $changed = true;
- }
-
if ($changed && $this->update($orig) === false) {
common_log_db_error($notice, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when a notice cannot be updated.
*/
function getSource()
{
+ if (empty($this->source)) {
+ return false;
+ }
+
$ns = new Notice_source();
- if (!empty($this->source)) {
- switch ($this->source) {
- case 'web':
- case 'xmpp':
- case 'mail':
- case 'omb':
- case 'system':
- case 'api':
+ 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) {
+ $ns = new Notice_source();
$ns->code = $this->source;
- break;
- default:
- $ns = Notice_source::getKV($this->source);
- if (!$ns) {
- $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;
- }
+ $app = Oauth_application::getKV('name', $this->source);
+ if ($app) {
+ $ns->name = $app->name;
+ $ns->url = $app->source_url;
}
- break;
}
+ break;
}
+
return $ns;
}
{
$this->_deleteNotices();
$this->_deleteSubscriptions();
- $this->_deleteMessages();
$this->_deleteTags();
$this->_deleteBlocks();
$this->_deleteAttentions();
$self->delete();
}
- function _deleteMessages()
- {
- $msg = new Message();
- $msg->from_profile = $this->id;
- $msg->delete();
-
- $msg = new Message();
- $msg->to_profile = $this->id;
- $msg->delete();
- }
-
function _deleteTags()
{
$tag = new Profile_tag();
'Foreign_link',
'Foreign_subscription',
'Invitation',
- 'Message',
'Profile_prefs',
'Profile_tag',
'Profile_list',
$this->out->elementEnd('li');
if ($this->profile->isLocal() && $cur->mutuallySubscribed($this->profile)) {
-
- // message
-
- $this->out->elementStart('li', 'entity_send-a-message');
- $this->out->element('a', array('href' => common_local_url('newmessage', array('to' => $this->user->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'));
- $this->out->elementEnd('li');
-
// nudge
if ($this->user->email && $this->user->emailnotifynudge) {
if (!empty($this->context->conversation)) {
$xs->element('link', array('rel' => ActivityContext::CONVERSATION,
'href' => $this->context->conversation));
+ $xs->element(ActivityContext::CONVERSATION, null, $this->context->conversation);
+ /* Since we use XMLWriter we just use the previously hardcoded prefix for ostatus,
+ otherwise we should use something like this:
+ $xs->elementNS(array(ActivityContext::OSTATUS => 'ostatus'), // namespace
+ 'conversation', // tag (or the element name from ActivityContext::CONVERSATION)
+ null, // attributes
+ $this->context->conversation); // content
+ */
}
foreach ($this->context->attention as $attnURI=>$type) {
const INREPLYTO = 'in-reply-to';
const REF = 'ref';
const HREF = 'href';
+
+ // OStatus element names with prefixes
const OBJECTTYPE = 'ostatus:object-type'; // FIXME: Undocumented!
+ const CONVERSATION = 'ostatus:conversation';
const POINT = 'point';
const MENTIONED = 'mentioned';
- const CONVERSATION = 'ostatus:conversation';
const ATTN_PUBLIC = 'http://activityschema.org/collection/public';
$this->location = $this->getLocation($element);
- $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION);
+ $convs = $element->getElementsByTagNameNS(self::OSTATUS, self::CONVERSATION);
+ foreach ($convs as $conv) {
+ $this->conversation = $conv->textContent;
+ }
+ if (empty($this->conversation)) {
+ // fallback to the atom:link rel="ostatus:conversation" element
+ $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION);
+ }
// Multiple attention links allowed
$nli->showNoticeLink();
$nli->showNoticeSource();
$nli->showNoticeLocation();
- $nli->showContext();
+ $nli->showPermalink();
$nli->showRepeat();
$nli->showNoticeOptions();
}
+ public function onStartShowNoticeItemNotice(NoticeListItem $nli)
+ {
+ if (!$this->isMyNotice($nli->notice)) {
+ return true;
+ }
+
+ $this->showNoticeItemNotice($nli);
+
+ Event::handle('EndShowNoticeItemNotice', array($nli));
+ return false;
+ }
+
+ protected function showNoticeItemNotice(NoticeListItem $nli)
+ {
+ $nli->showNoticeTitle();
+ $nli->showAuthor();
+ $nli->showAddressees();
+ $nli->showContent();
+ }
+
public function onStartShowNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
{
if (!$this->isMyNotice($stored)) {
return true;
}
- $out->text($stored->getContent());
+ $this->showNoticeContent($stored, $out, $scoped);
return false;
}
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ $out->text($stored->getContent());
+ }
}
@subsection usermethods_sec User Methods
- @subsection directmessagemethods_sec Direct Message Methods
+ @subsection directmessagemethods_sec Direct Message Methods (now a plugin)
@subsection friendshipmethods_sec Friendship Methods
$source = null;
$ns = $notice->getSource();
- if ($ns) {
+ if ($ns instanceof Notice_source) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
$this->elementEnd('entry');
}
- 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;
- }
-
- 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 showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
{
$this->initDocument('atom');
{
parent::prepare($args);
- // NOTE: $this->auth_user has to get set in prepare(), not handle(),
- // because subclasses do stuff with it in their prepares.
-
- $oauthReq = $this->getOAuthRequest();
+ // NOTE: $this->scoped and $this->auth_user has to get set in
+ // prepare(), not handle(), as subclasses use them in prepares.
+
+ // Allow regular login session
+ if (common_logged_in()) {
+ $this->scoped = Profile::current();
+ $this->auth_user = $this->scoped->getUser();
+ if (!$this->auth_user->hasRight(Right::API)) {
+ // TRANS: Authorization exception thrown when a user without API access tries to access the API.
+ throw new AuthorizationException(_('Not allowed to use API.'));
+ }
+ $this->access = self::READ_WRITE;
+ } else {
+ $oauthReq = $this->getOAuthRequest();
- if (!$oauthReq) {
- if ($this->requiresAuth()) {
- $this->checkBasicAuthUser(true);
+ if ($oauthReq instanceof OAuthRequest) {
+ $this->checkOAuthRequest($oauthReq);
} else {
- // Check to see if a basic auth user is there even
- // if one's not required
- $this->checkBasicAuthUser(false);
+ // If not using OAuth, check if there is a basic auth
+ // and require it if the current action requires it.
+ $this->checkBasicAuthUser($this->requiresAuth());
}
- } else {
- $this->checkOAuthRequest($oauthReq);
- }
- // NOTE: Make sure we're scoped properly based on the auths!
- if (isset($this->auth_user) && !empty($this->auth_user)) {
- $this->scoped = $this->auth_user->getProfile();
- } else {
- $this->scoped = null;
+ // NOTE: Make sure we're scoped properly based on the auths!
+ if (isset($this->auth_user) && $this->auth_user instanceof User) {
+ $this->scoped = $this->auth_user->getProfile();
+ } else {
+ $this->scoped = null;
+ }
}
// legacy user transferral
// does lots of session stuff.
global $_cur;
$_cur = $this->auth_user;
- Event::handle('EndSetApiUser', array($user));
+ Event::handle('EndSetApiUser', array($user));
}
$msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
header('WWW-Authenticate: Basic realm="' . $realm . '"');
// show error if the user clicks 'cancel'
- // TRANS: Client error thrown when authentication fails becaus a user clicked "Cancel".
+ // TRANS: Client error thrown when authentication fails because a user clicked "Cancel".
$this->clientError(_('Could not authenticate you.'), 401);
- } else {
+ } elseif ($required) {
+ // $this->auth_user_nickname - i.e. PHP_AUTH_USER - will have a value since it was not empty
$user = common_check_user($this->auth_user_nickname,
$this->auth_user_password);
if (Event::handle('StartSetApiUser', array(&$user))) {
- if (!empty($user)) {
+ if ($user instanceof User) {
if (!$user->hasRight(Right::API)) {
// TRANS: Authorization exception thrown when a user without API access tries to access the API.
throw new AuthorizationException(_('Not allowed to use API.'));
// By default, basic auth users have rw access
$this->access = self::READ_WRITE;
- if (empty($this->auth_user) && ($required || isset($_SERVER['PHP_AUTH_USER']))) {
+ if (!$this->auth_user instanceof User) {
$msg = sprintf(
"basic auth nickname = %s",
$this->auth_user_nickname
);
$this->logAuthFailure($msg);
+
+ // We must present WWW-Authenticate in accordance to HTTP status code 401
+ header('WWW-Authenticate: Basic realm="' . $realm . '"');
// TRANS: Client error thrown when authentication fails.
$this->clientError(_('Could not authenticate you.'), 401);
}
+ } else {
+ // all get rw access for actions that don't require auth
+ $this->access = self::READ_WRITE;
}
}
}
}
-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());
- }
- }
-}
-
class RepeatCommand extends Command
{
var $other = null;
// XXX: localise
$text = preg_replace('/\s+/', ' ', trim($text));
- list($cmd, $arg) = $this->split_arg($text);
+ list($cmd, $arg) = self::split_arg($text);
// We try to support all the same commands as Twitter, see
// http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
break;
case 'lose':
if ($arg) {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
break;
case 'on':
if ($arg) {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
break;
case 'off':
if ($arg) {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
}
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = new GetCommand($user, $other);
}
break;
- case 'd':
- case 'dm':
- if (!$arg) {
- $result = null;
- }
- list($other, $extra) = $this->split_arg($arg);
- if (!$extra) {
- $result = null;
- } else {
- $result = new MessageCommand($user, $other, $extra);
- }
- break;
case 'r':
case 'reply':
if (!$arg) {
$result = null;
}
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if (!$extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else {
$result = null;
break;
}
- list($other, $tags) = $this->split_arg($arg);
+ list($other, $tags) = self::split_arg($arg);
if (!$tags) {
$result = null;
} else {
$result = null;
break;
}
- list($other, $tags) = $this->split_arg($arg);
+ list($other, $tags) = self::split_arg($arg);
if (!$tags) {
$result = null;
} else {
if (!$arg) {
$result = null;
} else {
- list($word, $extra) = $this->split_arg($arg);
+ list($word, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else if ($word == 'off') {
if (!$arg) {
$result = null;
} else {
- list($word, $extra) = $this->split_arg($arg);
+ list($word, $extra) = self::split_arg($arg);
if ($extra) {
$result = null;
} else if ($word == 'all') {
/**
* Split arguments without triggering a PHP notice warning
*/
- function split_arg($text)
+ static function split_arg($text)
{
$pieces = explode(' ', $text, 2);
if (count($pieces) == 1) {
'Bookmark' => array(),
'ClientSideShorten' => array(),
'Directory' => array(),
+ 'DirectMessage' => array(),
'EmailAuthentication' => array(),
'Event' => array(),
'Oembed' => array(),
define('GNUSOCIAL_ENGINE', 'GNU social');
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
-define('GNUSOCIAL_BASE_VERSION', '1.1.2');
+define('GNUSOCIAL_BASE_VERSION', '1.1.3');
define('GNUSOCIAL_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
break;
default:
$ns = Notice_source::getKV($source);
- if ($ns) {
+ if ($ns instanceof Notice_source) {
$source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
}
break;
$recipients = $address;
$profile = $user->getProfile();
+ $headers['Date'] = date("r", time());
$headers['From'] = mail_notify_from();
$headers['To'] = $profile->getBestName() . ' <' . $address . '>';
$headers['Subject'] = $subject;
+++ /dev/null
-<?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();
- }
-}
+++ /dev/null
-<?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');
- }
-}
+++ /dev/null
-<?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('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/form.php';
-
-/**
- * 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');
-
- }
-}
+++ /dev/null
-<?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);
-}
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * A single list item for showing in a message list
- *
- * 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);
-}
-
-/**
- * A single item in a message list
- *
- * @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 MessageListItem extends Widget
-{
- var $message;
-
- /**
- * Constructor
- *
- * @param HTMLOutputter $out Output context
- * @param Message $message Message to show
- */
- function __construct($out, $message)
- {
- parent::__construct($out);
- $this->message = $message;
- }
-
- /**
- * Show the widget
- *
- * @return void
- */
- function show()
- {
- $this->out->elementStart('li', array('class' => 'h-entry notice',
- 'id' => 'message-' . $this->message->id));
-
- $profile = $this->getMessageProfile();
-
- $this->out->elementStart('a', array('href' => $profile->profileurl,
- 'class' => 'p-author'));
- $avatarUrl = $profile->avatarUrl(AVATAR_STREAM_SIZE);
- $this->out->element('img', array('src' => $avatarUrl,
- 'class' => 'avatar u-photo',
- 'width' => AVATAR_STREAM_SIZE,
- 'height' => AVATAR_STREAM_SIZE,
- 'alt' => $profile->getBestName()));
- $this->out->element('span', array('class' => 'nickname fn'), $profile->getNickname());
- $this->out->elementEnd('a');
-
- // FIXME: URL, image, video, audio
- $this->out->elementStart('div', array('class' => 'e-content'));
- $this->out->raw($this->message->rendered);
- $this->out->elementEnd('div');
-
- $messageurl = common_local_url('showmessage',
- array('message' => $this->message->id));
-
- // XXX: we need to figure this out better. Is this right?
- if (strcmp($this->message->uri, $messageurl) != 0 &&
- preg_match('/^http/', $this->message->uri)) {
- $messageurl = $this->message->uri;
- }
-
- $this->out->elementStart('div', 'entry-metadata');
- $this->out->elementStart('a', array('rel' => 'bookmark',
- 'class' => 'timestamp',
- 'href' => $messageurl));
- $dt = common_date_iso8601($this->message->created);
- $this->out->element('time', array('class' => 'dt-published',
- 'datetime' => common_date_iso8601($this->message->created),
- // TRANS: Timestamp title (tooltip text) for NoticeListItem
- 'title' => common_exact_date($this->message->created)),
- common_date_string($this->message->created));
- $this->out->elementEnd('a');
-
- if ($this->message->source) {
- $this->out->elementStart('span', 'source');
- // FIXME: bad i18n. Device should be a parameter (from %s).
- // TRANS: Followed by notice source (usually the client used to send the notice).
- $this->out->text(_('from'));
- $this->showSource($this->message->source);
- $this->out->elementEnd('span');
- }
- $this->out->elementEnd('div');
-
- $this->out->elementEnd('li');
- }
-
- /**
- * Dummy method. Serves no other purpose than to make strings available used
- * in self::showSource() through xgettext.
- *
- * @return void
- */
- function messageListItemDummyMessages()
- {
- // A dummy array with messages. These will get extracted by xgettext and
- // are used in self::showSource().
- $dummy_messages = array(
- // TRANS: A possible notice source (web interface).
- _m('SOURCE','web'),
- // TRANS: A possible notice source (XMPP).
- _m('SOURCE','xmpp'),
- // TRANS: A possible notice source (e-mail).
- _m('SOURCE','mail'),
- // TRANS: A possible notice source (OpenMicroBlogging).
- _m('SOURCE','omb'),
- // TRANS: A possible notice source (Application Programming Interface).
- _m('SOURCE','api'),
- );
- }
-
- /**
- * Show the source of the message
- *
- * Returns either the name (and link) of the API client that posted the notice,
- * or one of other other channels.
- *
- * @param string $source the source of the message
- *
- * @return void
- */
- function showSource($source)
- {
- $source_name = _m('SOURCE',$source);
- switch ($source) {
- case 'web':
- case 'xmpp':
- case 'mail':
- case 'omb':
- case 'api':
- $this->out->element('span', 'device', $source_name);
- break;
- default:
- $ns = Notice_source::getKV($source);
- if ($ns) {
- $this->out->elementStart('span', 'device');
- $this->out->element('a', array('href' => $ns->url,
- 'rel' => 'external'),
- $ns->name);
- $this->out->elementEnd('span');
- } else {
- $this->out->element('span', 'device', $source_name);
- }
- break;
- }
- return;
- }
-
- /**
- * Return the profile to show in the message item
- *
- * Overridden in sub-classes to show sender, receiver, or whatever
- *
- * @return Profile profile to show avatar and name of
- */
- abstract function getMessageProfile();
-}
return true;
}
- $adapter = $this->adaptNoticeListItem($nli);
-
- if (empty($adapter)) {
- throw new ServerException('Could not adapt NoticeListItem');
- }
+ // Legacy use was creating a "NoticeListItemAdapter", but
+ // nowadays we solve that using event handling for microapps.
+ // This section will remain until all plugins are fixed.
+ $adapter = $this->adaptNoticeListItem($nli) ?: $nli;
$adapter->showNotice();
$adapter->showNoticeAttachments();
function showNotice()
{
- $this->showNoticeTitle();
- $this->showAuthor();
- $this->showAddressees();
- $this->showContent();
+ if (Event::handle('StartShowNoticeItemNotice', array($this))) {
+ $this->showNoticeTitle();
+ $this->showAuthor();
+ $this->showAddressees();
+ $this->showContent();
+ Event::handle('EndShowNoticeItemNotice', array($this));
+ }
}
function showNoticeTitle()
$this->showNoticeLink();
$this->showNoticeSource();
$this->showNoticeLocation();
- if ($this->notice->hasConversation()) {
- $this->showContext();
- }
+ $this->showPermalink();
$this->showRepeat();
Event::handle('EndShowNoticeInfo', array($this));
}
function showNoticeLink()
{
$this->out->elementStart('a', array('rel' => 'bookmark',
- 'class' => 'u-url timestamp',
- 'href' => $this->notice->getLocalUrl()));
+ 'class' => 'timestamp',
+ 'href' => Conversation::getUrlFromNotice($this->notice)));
$this->out->element('time', array('class' => 'dt-published',
'datetime' => common_date_iso8601($this->notice->created),
// TRANS: Timestamp title (tooltip text) for NoticeListItem
{
$ns = $this->notice->getSource();
- if ($ns) {
- // TRANS: A possible notice source (web interface).
- $source_name = (empty($ns->name)) ? ($ns->code ? _($ns->code) : _m('SOURCE','web')) : _($ns->name);
- $this->out->text(' ');
- $this->out->elementStart('span', 'source');
- // @todo FIXME: probably i18n issue. If "from" is followed by text, that should be a parameter to "from" (from %s).
- // TRANS: Followed by notice source.
- $this->out->text(_('from'));
- $this->out->text(' ');
+ if (!$ns instanceof Notice_source) {
+ return false;
+ }
- $name = $source_name;
- $url = $ns->url;
- $title = null;
+ // TRANS: A possible notice source (web interface).
+ $source_name = (empty($ns->name)) ? ($ns->code ? _($ns->code) : _m('SOURCE','web')) : _($ns->name);
+ $this->out->text(' ');
+ $this->out->elementStart('span', 'source');
+ // @todo FIXME: probably i18n issue. If "from" is followed by text, that should be a parameter to "from" (from %s).
+ // TRANS: Followed by notice source.
+ $this->out->text(_('from'));
+ $this->out->text(' ');
- if (Event::handle('StartNoticeSourceLink', array($this->notice, &$name, &$url, &$title))) {
- $name = $source_name;
- $url = $ns->url;
- }
- Event::handle('EndNoticeSourceLink', array($this->notice, &$name, &$url, &$title));
+ $name = $source_name;
+ $url = $ns->url;
+ $title = null;
- // if $ns->name and $ns->url are populated we have
- // configured a source attr somewhere
- if (!empty($name) && !empty($url)) {
- $this->out->elementStart('span', 'device');
+ if (Event::handle('StartNoticeSourceLink', array($this->notice, &$name, &$url, &$title))) {
+ $name = $source_name;
+ $url = $ns->url;
+ }
+ Event::handle('EndNoticeSourceLink', array($this->notice, &$name, &$url, &$title));
- $attrs = array(
- 'href' => $url,
- 'rel' => 'external'
- );
+ // if $ns->name and $ns->url are populated we have
+ // configured a source attr somewhere
+ if (!empty($name) && !empty($url)) {
+ $this->out->elementStart('span', 'device');
- if (!empty($title)) {
- $attrs['title'] = $title;
- }
+ $attrs = array(
+ 'href' => $url,
+ 'rel' => 'external'
+ );
- $this->out->element('a', $attrs, $name);
- $this->out->elementEnd('span');
- } else {
- $this->out->element('span', 'device', $name);
+ if (!empty($title)) {
+ $attrs['title'] = $title;
}
+ $this->out->element('a', $attrs, $name);
$this->out->elementEnd('span');
+ } else {
+ $this->out->element('span', 'device', $name);
}
+
+ $this->out->elementEnd('span');
}
/**
- * show link to notice this notice is a reply to
+ * show link to single-notice view for this notice item
*
- * If this notice is a reply, show a link to the notice it is replying to. The
- * heavy lifting for figuring out replies happens at save time.
+ * A permalink that goes to this specific object and nothing else
*
* @return void
*/
- function showContext()
+ function showPermalink()
{
- $this->out->element('a',
- array('href' => $this->notice->getConversationUrl(),
- 'class' => 'conversation'),
- // TRANS: Addition in notice list item if notice is part of a conversation.
- _('in context'));
+ $class = 'permalink u-url';
+ if (!$this->notice->isLocal()) {
+ $class .= ' external';
+ }
+ try {
+ $this->out->element('a',
+ array('href' => $this->notice->getUrl(),
+ 'class' => $class),
+ // TRANS: Addition in notice list item for single-notice view.
+ _('permalink'));
+ } catch (InvalidUrlException $e) {
+ // no permalink available
+ }
}
/**
sprintf(_('Replies to %s'), $name),
$mine && $action =='replies', 'nav_timeline_replies');
-
- if ($scoped instanceof Profile && $scoped->id == $target->id &&
- !common_config('singleuser', 'enabled')) {
-
- $this->out->menuItem(common_local_url('inbox', array('nickname' =>
- $nickname)),
- // TRANS: Menu item in personal group navigation menu.
- _m('MENU','Messages'),
- // TRANS: Menu item title in personal group navigation menu.
- _('Your incoming messages'),
- $mine && $action =='inbox');
- }
-
Event::handle('EndPersonalGroupNav', array($this, $target, $scoped));
}
$this->out->elementEnd('ul');
array('action' => 'conversation'),
array('id' => '[0-9]+'));
- $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]+'));
-
$m->connect('user/:id',
array('action' => 'userbyid'),
array('id' => '[0-9]+'));
'screen_name' => Nickname::DISPLAY_FMT,
'format' => '(xml|json)'));
- // 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)'));
-
// friendships
$m->connect('api/friendships/show.:format',
// TRANS: Standard search suggestions shown when a search does not give any results.
$message .= sprintf(_("You can also try your search on other engines:
-* [Twingly](http://www.twingly.com/search?q=%s&content=microblog&site=%%%%site.server%%%%)
-* [Tweet scan](http://www.tweetscan.com/indexi.php?s=%s)
-* [Google](http://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
-* [Yahoo](http://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
-* [Collecta](http://collecta.com/#q=%s)"), $qe, $qe, $qe, $qe, $qe);
+* [DuckDuckGo](https://duckduckgo.com/?q=site%%3A%%%%site.server%%%%+%s)
+* [Ixquick](https://ixquick.com/do/search?query=site%%3A%%%%site.server%%%%+%s)
+* [Google](https://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
+* [Yahoo!](https://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
+"), $qe, $qe, $qe, $qe);
$message .= "\n";
}
$this->elementStart('div', 'help instructions');
return 3;
}
- function showContext()
- {
- // Silence!
- }
-
/**
* finish the notice
*
//
}
- function showContext()
- {
- //
- }
-
function getReplyProfiles()
{
$all = parent::getReplyProfiles();
$subscriptions = $this->getSubscriptions();
$subscribers = $this->getSubscribers();
$groups = $this->getGroups();
- $messagesFrom = $this->getMessagesFrom();
- $messagesTo = $this->getMessagesTo();
- $objs = array_merge($subscriptions, $subscribers, $groups, $notices, $messagesFrom, $messagesTo);
+ $objs = array_merge($subscriptions, $subscribers, $groups, $notices);
Event::handle('AppendUserActivityStreamObjects', array($this, &$objs));
return $groups;
}
- function getMessagesTo()
- {
- $msgMap = Message::listGet('to_profile', array($this->user->id));
-
- $messages = $msgMap[$this->user->id];
-
- if (!empty($this->after)) {
- $messages = array_filter($messages, array($this, 'createdAfter'));
- }
-
- return $messages;
- }
-
- function getMessagesFrom()
- {
- $msgMap = Message::listGet('from_profile', array($this->user->id));
-
- $messages = $msgMap[$this->user->id];
-
- if (!empty($this->after)) {
- $messages = array_filter($messages, array($this, 'createdAfter'));
- }
-
- return $messages;
- }
-
function createdAfter($item) {
$created = strtotime((empty($item->created)) ? $item->modified : $item->created);
return ($created >= $this->after);
$pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
if (($ssl && (common_config('site', 'ssl') === 'sometimes'))
+ || StatusNet::isHTTPS()
|| common_config('site', 'ssl') === 'always') {
$proto = 'https';
if (is_string(common_config('site', 'sslserver')) &&
$this->elementEnd($tag);
}
+ function elementNS(array $ns, $tag, $attrs=null, $content=null)
+ {
+ $this->elementStartNS($ns, $tag, $attrs);
+ if (!is_null($content)) {
+ $this->xw->text($content);
+ }
+ $this->elementEnd($tag);
+ }
+
/**
* output a start tag for an element
*
}
}
+ function elementStartNS(array $ns, $tag, $attrs=null)
+ {
+ reset($ns); // array pointer to 0
+ $uri = key($ns);
+ $this->xw->startElementNS($ns[$uri], $tag, $uri);
+ if (is_array($attrs)) {
+ foreach ($attrs as $name => $value) {
+ $this->xw->writeAttribute($name, $value);
+ }
+ } else if (is_string($attrs)) {
+ $this->xw->writeAttribute('class', $attrs);
+ }
+ }
+
/**
* output an end tag for an element
*
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
// Discovery actions
$m->connect('main/amcd.json',
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/train/spam',
array('action' => 'train', 'category' => 'spam'));
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('panel/bitly',
array('action' => 'bitlyadminpanel'));
/**
* Add our actions to the URL router
*
- * @param Net_URL_Mapper $m URL mapper for this hit
+ * @param URLMapper $m URL mapper for this hit
*
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('panel/blacklist', array('action' => 'blacklistadminpanel'));
return true;
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('blog/new',
array('action' => 'newblogentry'));
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
if (common_config('singleuser', 'enabled')) {
$nickname = User::singleUserNickname();
return $object;
}
- /**
- * Given a notice list item, returns an adapter specific
- * to this plugin.
- *
- * @param NoticeListItem $nli item to adapt
- *
- * @return NoticeListItemAdapter adapter or null
- */
- function adaptNoticeListItem($nli)
- {
- return new BookmarkListItem($nli);
- }
-
function entryForm($out)
{
return new InitialBookmarkForm($out);
return true;
}
+
+ protected function showNoticeItemNotice(NoticeListItem $nli)
+ {
+ $nli->out->elementStart('div', 'entry-title');
+ $nli->showAuthor();
+ $nli->showContent();
+ $nli->out->elementEnd('div');
+ }
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ $nb = Bookmark::getByNotice($stored);
+
+ if (empty($nb)) {
+ common_log(LOG_ERR, "No bookmark for notice {$stored->id}");
+ parent::showContent();
+ return;
+ } else if (empty($nb->url)) {
+ common_log(LOG_ERR, "No url for bookmark {$nb->id} for notice {$stored->id}");
+ parent::showContent();
+ return;
+ }
+
+ $profile = $stored->getProfile();
+
+ // Whether to nofollow
+ $attrs = array('href' => $nb->url, 'class' => 'bookmark-title');
+
+ $nf = common_config('nofollow', 'external');
+
+ if ($nf == 'never' || ($nf == 'sometimes' and $out instanceof ShowstreamAction)) {
+ $attrs['rel'] = 'external';
+ } else {
+ $attrs['rel'] = 'nofollow external';
+ }
+
+ $out->elementStart('h3');
+ $out->element('a', $attrs, $nb->title);
+ $out->elementEnd('h3');
+
+ // Replies look like "for:" tags
+ $replies = $stored->getReplies();
+ $tags = $stored->getTags();
+
+ if (!empty($replies) || !empty($tags)) {
+
+ $out->elementStart('ul', array('class' => 'bookmark-tags'));
+
+ foreach ($replies as $reply) {
+ $other = Profile::getKV('id', $reply);
+ if (!empty($other)) {
+ $out->elementStart('li');
+ $out->element('a', array('rel' => 'tag',
+ 'href' => $other->profileurl,
+ 'title' => $other->getBestName()),
+ sprintf('for:%s', $other->nickname));
+ $out->elementEnd('li');
+ $out->text(' ');
+ }
+ }
+
+ foreach ($tags as $tag) {
+ $tag = trim($tag);
+ if (!empty($tag)) {
+ $out->elementStart('li');
+ $out->element('a',
+ array('rel' => 'tag',
+ 'href' => Notice_tag::url($tag)),
+ $tag);
+ $out->elementEnd('li');
+ $out->text(' ');
+ }
+ }
+
+ $out->elementEnd('ul');
+ }
+
+ if (!empty($nb->description)) {
+ $out->element('p',
+ array('class' => 'bookmark-description'),
+ $nb->description);
+ }
+ }
}
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Adapter to show bookmarks in a nicer way
- *
- * 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 Bookmark
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
-
-/**
- * An adapter to show bookmarks in a nicer way
- *
- * @category Bookmark
- * @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 BookmarkListItem extends NoticeListItemAdapter
-{
- function showNotice()
- {
- $this->nli->out->elementStart('div', 'entry-title');
- $this->nli->showAuthor();
- $this->showContent();
- $this->nli->out->elementEnd('div');
- }
-
- function showContent()
- {
- $notice = $this->nli->notice;
- $out = $this->nli->out;
-
- $nb = Bookmark::getByNotice($notice);
-
- if (empty($nb)) {
- common_log(LOG_ERR, "No bookmark for notice {$notice->id}");
- parent::showContent();
- return;
- } else if (empty($nb->url)) {
- common_log(LOG_ERR, "No url for bookmark {$nb->id} for notice {$notice->id}");
- parent::showContent();
- return;
- }
-
- $profile = $notice->getProfile();
-
- $out->elementStart('div', array('class' => 'e-content'));
-
- // Whether to nofollow
-
- $attrs = array('href' => $nb->url,
- 'class' => 'bookmark-title');
-
- $nf = common_config('nofollow', 'external');
-
- if ($nf == 'never' || ($nf == 'sometimes' and $out instanceof ShowstreamAction)) {
- $attrs['rel'] = 'external';
- } else {
- $attrs['rel'] = 'nofollow external';
- }
-
- $out->elementStart('h3');
- $out->element('a',
- $attrs,
- $nb->title);
- $out->elementEnd('h3');
-
- // Replies look like "for:" tags
-
- $replies = $notice->getReplies();
- $tags = $notice->getTags();
-
- if (!empty($replies) || !empty($tags)) {
-
- $out->elementStart('ul', array('class' => 'bookmark-tags'));
-
- foreach ($replies as $reply) {
- $other = Profile::getKV('id', $reply);
- if (!empty($other)) {
- $out->elementStart('li');
- $out->element('a', array('rel' => 'tag',
- 'href' => $other->profileurl,
- 'title' => $other->getBestName()),
- sprintf('for:%s', $other->nickname));
- $out->elementEnd('li');
- $out->text(' ');
- }
- }
-
- foreach ($tags as $tag) {
- $tag = trim($tag);
- if (!empty($tag)) {
- $out->elementStart('li');
- $out->element('a',
- array('rel' => 'tag',
- 'href' => Notice_tag::url($tag)),
- $tag);
- $out->elementEnd('li');
- $out->text(' ');
- }
- }
-
- $out->elementEnd('ul');
- }
-
- if (!empty($nb->description)) {
- $out->element('p',
- array('class' => 'bookmark-description'),
- $nb->description);
- }
-
- $out->elementEnd('div');
- }
-}
return;
}
- /**
- * show link to notice conversation page
- *
- * Since we're only used on the conversation page, we skip this
- *
- * @return void
- */
- function showContext()
- {
- return;
- }
-
/**
* show people this notice is in reply to
*
--- /dev/null
+<?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($this->user->id));
+ $messages = $msgMap[$uas->user->id];
+ if (!empty($uas->after)) {
+ $messages = array_filter($messages, array($uas, 'createdAfter'));
+ }
+ $objs[] = $messages;
+
+ // Messages _to_ the user
+ $msgMap = Message::listGet('to_profile', array($this->user->id));
+ $messages = $msgMap[$uas->user->id];
+ if (!empty($uas->after)) {
+ $messages = array_filter($messages, array($uas, 'createdAfter'));
+ }
+ $objs[] = $messages;
+
+ 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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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);
+ }
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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;
+
+ /**
+ * 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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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;
+ }
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<?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());
+ }
+ }
+}
--- /dev/null
+<?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');
+
+ }
+}
--- /dev/null
+<?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);
+}
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * A single list item for showing in a message list
+ *
+ * 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);
+}
+
+/**
+ * A single item in a message list
+ *
+ * @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 MessageListItem extends Widget
+{
+ var $message;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out Output context
+ * @param Message $message Message to show
+ */
+ function __construct($out, $message)
+ {
+ parent::__construct($out);
+ $this->message = $message;
+ }
+
+ /**
+ * Show the widget
+ *
+ * @return void
+ */
+ function show()
+ {
+ $this->out->elementStart('li', array('class' => 'h-entry notice',
+ 'id' => 'message-' . $this->message->id));
+
+ $profile = $this->getMessageProfile();
+
+ $this->out->elementStart('a', array('href' => $profile->profileurl,
+ 'class' => 'p-author'));
+ $avatarUrl = $profile->avatarUrl(AVATAR_STREAM_SIZE);
+ $this->out->element('img', array('src' => $avatarUrl,
+ 'class' => 'avatar u-photo',
+ 'width' => AVATAR_STREAM_SIZE,
+ 'height' => AVATAR_STREAM_SIZE,
+ 'alt' => $profile->getBestName()));
+ $this->out->element('span', array('class' => 'nickname fn'), $profile->getNickname());
+ $this->out->elementEnd('a');
+
+ // FIXME: URL, image, video, audio
+ $this->out->elementStart('div', array('class' => 'e-content'));
+ $this->out->raw($this->message->rendered);
+ $this->out->elementEnd('div');
+
+ $messageurl = common_local_url('showmessage',
+ array('message' => $this->message->id));
+
+ // XXX: we need to figure this out better. Is this right?
+ if (strcmp($this->message->uri, $messageurl) != 0 &&
+ preg_match('/^http/', $this->message->uri)) {
+ $messageurl = $this->message->uri;
+ }
+
+ $this->out->elementStart('div', 'entry-metadata');
+ $this->out->elementStart('a', array('rel' => 'bookmark',
+ 'class' => 'timestamp',
+ 'href' => $messageurl));
+ $dt = common_date_iso8601($this->message->created);
+ $this->out->element('time', array('class' => 'dt-published',
+ 'datetime' => common_date_iso8601($this->message->created),
+ // TRANS: Timestamp title (tooltip text) for NoticeListItem
+ 'title' => common_exact_date($this->message->created)),
+ common_date_string($this->message->created));
+ $this->out->elementEnd('a');
+
+ if ($this->message->source) {
+ $this->out->elementStart('span', 'source');
+ // FIXME: bad i18n. Device should be a parameter (from %s).
+ // TRANS: Followed by notice source (usually the client used to send the notice).
+ $this->out->text(_('from'));
+ $this->showSource($this->message->source);
+ $this->out->elementEnd('span');
+ }
+ $this->out->elementEnd('div');
+
+ $this->out->elementEnd('li');
+ }
+
+ /**
+ * Dummy method. Serves no other purpose than to make strings available used
+ * in self::showSource() through xgettext.
+ *
+ * @return void
+ */
+ function messageListItemDummyMessages()
+ {
+ // A dummy array with messages. These will get extracted by xgettext and
+ // are used in self::showSource().
+ $dummy_messages = array(
+ // TRANS: A possible notice source (web interface).
+ _m('SOURCE','web'),
+ // TRANS: A possible notice source (XMPP).
+ _m('SOURCE','xmpp'),
+ // TRANS: A possible notice source (e-mail).
+ _m('SOURCE','mail'),
+ // TRANS: A possible notice source (OpenMicroBlogging).
+ _m('SOURCE','omb'),
+ // TRANS: A possible notice source (Application Programming Interface).
+ _m('SOURCE','api'),
+ );
+ }
+
+ /**
+ * Show the source of the message
+ *
+ * Returns either the name (and link) of the API client that posted the notice,
+ * or one of other other channels.
+ *
+ * @param string $source the source of the message
+ *
+ * @return void
+ */
+ function showSource($source)
+ {
+ $source_name = _m('SOURCE',$source);
+ switch ($source) {
+ case 'web':
+ case 'xmpp':
+ case 'mail':
+ case 'omb':
+ case 'api':
+ $this->out->element('span', 'device', $source_name);
+ break;
+ default:
+ $ns = Notice_source::getKV($source);
+ if ($ns) {
+ $this->out->elementStart('span', 'device');
+ $this->out->element('a', array('href' => $ns->url,
+ 'rel' => 'external'),
+ $ns->name);
+ $this->out->elementEnd('span');
+ } else {
+ $this->out->element('span', 'device', $source_name);
+ }
+ break;
+ }
+ return;
+ }
+
+ /**
+ * Return the profile to show in the message item
+ *
+ * Overridden in sub-classes to show sender, receiver, or whatever
+ *
+ * @return Profile profile to show avatar and name of
+ */
+ abstract function getMessageProfile();
+}
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing,
* false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect(
$this->endProfile();
}
+ function endActions()
+ {
+ // delete button
+ $cur = common_current_user();
+ list($action, $r2args) = $this->out->returnToArgs();
+ $r2args['action'] = $action;
+ if ($cur instanceof User && $cur->hasRight(Right::DELETEGROUP)) {
+ $this->out->elementStart('li', 'entity_delete');
+ $df = new DeleteGroupForm($this->out, $this->profile, $r2args);
+ $df->show();
+ $this->out->elementEnd('li');
+ }
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('td');
+ }
+
function showActions()
{
$this->startActions();
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/event/new',
array('action' => 'newevent'));
return true;
}
- function adaptNoticeListItem($nli)
- {
- $notice = $nli->notice;
-
- switch ($notice->object_type) {
- case Happening::OBJECT_TYPE:
- return new EventListItem($nli);
- break;
- case RSVP::POSITIVE:
- case RSVP::NEGATIVE:
- case RSVP::POSSIBLE:
- return new RSVPListItem($nli);
- break;
- }
- return null;
- }
-
-
/**
* Form for our app
*
}
return true;
}
+
+ protected function showNoticeItemNotice(NoticeListItem $nli)
+ {
+ $nli->showAuthor();
+ $nli->showContent();
+ }
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ switch ($stored->object_type) {
+ case Happening::OBJECT_TYPE:
+ $this->showEvent($stored, $out, $scoped);
+ break;
+ case RSVP::POSITIVE:
+ case RSVP::NEGATIVE:
+ case RSVP::POSSIBLE:
+ $this->showRSVP($stored, $out, $scoped);
+ break;
+ }
+ }
+
+ protected function showEvent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ $profile = $stored->getProfile();
+ $event = Happening::fromNotice($stored);
+
+ if (!$event instanceof Happening) {
+ // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
+ $out->element('p', null, _m('Deleted.'));
+ return;
+ }
+
+ $out->elementStart('div', 'h-event');
+
+ $out->elementStart('h3', 'p-summary p-name');
+
+ try {
+ $out->element('a', array('href' => $event->getUrl()), $event->title);
+ } catch (InvalidUrlException $e) {
+ $out->text($event->title);
+ }
+
+ $out->elementEnd('h3');
+
+ $now = new DateTime();
+ $startDate = new DateTime($event->start_time);
+ $endDate = new DateTime($event->end_time);
+ $userTz = new DateTimeZone(common_timezone());
+
+ // Localize the time for the observer
+ $now->setTimeZone($userTz);
+ $startDate->setTimezone($userTz);
+ $endDate->setTimezone($userTz);
+
+ $thisYear = $now->format('Y');
+ $startYear = $startDate->format('Y');
+ $endYear = $endDate->format('Y');
+
+ $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31
+
+ if ($startYear != $thisYear || $endYear != $thisYear) {
+ $dateFmt .= 'Y,'; // append year if we need to think about years
+ }
+
+ $startDateStr = $startDate->format($dateFmt);
+ $endDateStr = $endDate->format($dateFmt);
+
+ $timeFmt = 'g:ia';
+
+ $startTimeStr = $startDate->format($timeFmt);
+ $endTimeStr = $endDate->format("{$timeFmt} (T)");
+
+ $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN
+
+ // TRANS: Field label for event description.
+ $out->element('strong', null, _m('Time:'));
+
+ $out->element('time', array('class' => 'dt-start',
+ 'datetime' => common_date_iso8601($event->start_time)),
+ $startDateStr . ' ' . $startTimeStr);
+ $out->text(' – ');
+ $out->element('time', array('class' => 'dt-end',
+ 'datetime' => common_date_iso8601($event->end_time)),
+ $startDateStr != $endDateStr
+ ? "$endDateStr $endTimeStr"
+ : $endTimeStr);
+
+ $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT
+
+ if (!empty($event->location)) {
+ $out->elementStart('div', 'event-location');
+ // TRANS: Field label for event description.
+ $out->element('strong', null, _m('Location:'));
+ $out->element('span', 'p-location', $event->location);
+ $out->elementEnd('div');
+ }
+
+ if (!empty($event->description)) {
+ $out->elementStart('div', 'event-description');
+ // TRANS: Field label for event description.
+ $out->element('strong', null, _m('Description:'));
+ $out->element('div', 'p-description', $event->description);
+ $out->elementEnd('div');
+ }
+
+ $rsvps = $event->getRSVPs();
+
+ $out->elementStart('div', 'event-rsvps');
+
+ // TRANS: Field label for event description.
+ $out->element('strong', null, _m('Attending:'));
+ $out->elementStart('ul', 'attending-list');
+
+ foreach ($rsvps as $verb => $responses) {
+ $out->elementStart('li', 'rsvp-list');
+ switch ($verb) {
+ case RSVP::POSITIVE:
+ $out->text(_('Yes:'));
+ break;
+ case RSVP::NEGATIVE:
+ $out->text(_('No:'));
+ break;
+ case RSVP::POSSIBLE:
+ $out->text(_('Maybe:'));
+ break;
+ }
+ $ids = array();
+ foreach ($responses as $response) {
+ $ids[] = $response->profile_id;
+ }
+ $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1);
+ $minilist = new ProfileMiniList(Profile::multiGet('id', $ids), $out);
+ $minilist->show();
+
+ $out->elementEnd('li');
+ }
+
+ $out->elementEnd('ul');
+ $out->elementEnd('div');
+
+ if ($scoped instanceof Profile) {
+ $rsvp = $event->getRSVP($scoped);
+
+ if (empty($rsvp)) {
+ $form = new RSVPForm($event, $out);
+ } else {
+ $form = new CancelRSVPForm($rsvp, $out);
+ }
+
+ $form->show();
+ }
+ $out->elementEnd('div');
+ }
+
+ protected function showRSVP(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ $rsvp = RSVP::fromNotice($stored);
+
+ if (empty($rsvp)) {
+ // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
+ $out->element('p', null, _m('Deleted.'));
+ return;
+ }
+
+ $out->elementStart('div', 'rsvp');
+ $out->raw($rsvp->asHTML());
+ $out->elementEnd('div');
+ }
}
// TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
// TRANS: %s is the non-existing response code.
throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
- break;
}
if (empty($event)) {
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Notice-list representation of an event
- *
- * 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 Event
- * @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);
-}
-
-/**
- * Notice-list representation of an event
- *
- * @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 EventListItem extends NoticeListItemAdapter
-{
- function showNotice()
- {
- $this->nli->showAuthor();
- $this->showContent();
- }
-
- function showContent()
- {
- $notice = $this->nli->notice;
- $out = $this->nli->out;
-
- $profile = $notice->getProfile();
- $event = Happening::fromNotice($notice);
-
- if (empty($event)) {
- // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
- $out->element('p', null, _m('Deleted.'));
- return;
- }
-
- // e-content since we're part of a h-entry
- $out->elementStart('div', 'h-event e-content'); // VEVENT IN
-
- $out->elementStart('h3', 'p-summary p-name'); // VEVENT/H3 IN
-
- try {
- $out->element('a', array('href' => $event->getUrl()), $event->title);
- } catch (InvalidUrlException $e) {
- $out->text($event->title);
- }
-
- $out->elementEnd('h3'); // VEVENT/H3 OUT
-
- $now = new DateTime();
- $startDate = new DateTime($event->start_time);
- $endDate = new DateTime($event->end_time);
- $userTz = new DateTimeZone(common_timezone());
-
- // Localize the time for the observer
- $now->setTimeZone($userTz);
- $startDate->setTimezone($userTz);
- $endDate->setTimezone($userTz);
-
- $thisYear = $now->format('Y');
- $startYear = $startDate->format('Y');
- $endYear = $endDate->format('Y');
-
- $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31
-
- if ($startYear != $thisYear || $endYear != $thisYear) {
- $dateFmt .= 'Y,'; // append year if we need to think about years
- }
-
- $startDateStr = $startDate->format($dateFmt);
- $endDateStr = $endDate->format($dateFmt);
-
- $timeFmt = 'g:ia';
-
- $startTimeStr = $startDate->format($timeFmt);
- $endTimeStr = $endDate->format("{$timeFmt} (T)");
-
- $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN
-
- // TRANS: Field label for event description.
- $out->element('strong', null, _m('Time:'));
-
- $out->element('time', array('class' => 'dt-start',
- 'datetime' => common_date_iso8601($event->start_time)),
- $startDateStr . ' ' . $startTimeStr);
- $out->text(' – ');
- $out->element('time', array('class' => 'dt-end',
- 'datetime' => common_date_iso8601($event->end_time)),
- $startDateStr != $endDateStr
- ? "$endDateStr $endTimeStr"
- : $endTimeStr);
-
- $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT
-
- if (!empty($event->location)) {
- $out->elementStart('div', 'event-location');
- // TRANS: Field label for event description.
- $out->element('strong', null, _m('Location:'));
- $out->element('span', 'p-location', $event->location);
- $out->elementEnd('div');
- }
-
- if (!empty($event->description)) {
- $out->elementStart('div', 'event-description');
- // TRANS: Field label for event description.
- $out->element('strong', null, _m('Description:'));
- $out->element('div', 'p-description', $event->description);
- $out->elementEnd('div');
- }
-
- $rsvps = $event->getRSVPs();
-
- $out->elementStart('div', 'event-rsvps');
-
- // TRANS: Field label for event description.
- $out->element('strong', null, _m('Attending:'));
- $out->elementStart('ul', 'attending-list');
-
- foreach ($rsvps as $verb => $responses) {
- $out->elementStart('li', 'rsvp-list');
- switch ($verb) {
- case RSVP::POSITIVE:
- $out->text(_('Yes:'));
- break;
- case RSVP::NEGATIVE:
- $out->text(_('No:'));
- break;
- case RSVP::POSSIBLE:
- $out->text(_('Maybe:'));
- break;
- }
- $ids = array();
- foreach ($responses as $response) {
- $ids[] = $response->profile_id;
- }
- $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1);
- $minilist = new ProfileMiniList(Profile::multiGet('id', $ids), $out);
- $minilist->show();
-
- $out->elementEnd('li');
- }
-
- $out->elementEnd('ul');
- $out->elementEnd('div');
-
- $user = common_current_user();
-
- if (!empty($user)) {
- $rsvp = $event->getRSVP($user->getProfile());
-
- if (empty($rsvp)) {
- $form = new RSVPForm($event, $out);
- } else {
- $form = new CancelRSVPForm($rsvp, $out);
- }
-
- $form->show();
- }
-
- $out->elementEnd('div'); // vevent out
- }
-}
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Title of module
- *
- * 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);
-}
-
-/**
- * Class comment
- *
- * @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 RSVPListItem extends NoticeListItemAdapter
-{
- function showNotice()
- {
- $this->nli->out->elementStart('div', 'entry-title');
- $this->nli->showAuthor();
- $this->showContent();
- $this->nli->out->elementEnd('div');
- }
-
- function showContent()
- {
- $notice = $this->nli->notice;
- $out = $this->nli->out;
-
- $rsvp = RSVP::fromNotice($notice);
-
- if (empty($rsvp)) {
- // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
- $out->element('p', null, _m('Deleted.'));
- return;
- }
-
- $out->elementStart('div', 'rsvp');
- $out->raw($rsvp->asHTML());
- $out->elementEnd('div');
- return;
- }
-}
*
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m URL mapper
+ * @param URLMapper $m URL mapper
*
* @return boolean hook return
*/
- function onStartInitializeRouter($m)
+ public function onStartInitializeRouter(URLMapper $m)
{
$m->connect(
':nickname/detail',
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
// Always add the admin panel route
$m->connect('panel/facebook', array('action' => 'facebookadminpanel'));
$user->whereAdd('emailnotifyfav IS NOT NULL');
if ($user->find()) {
printfnq("Detected old User table (emailnotifyfav IS NOT NULL). Moving 'emailnotifyfav' property to Profile_prefs...");
+ // First we'll make sure Profile_prefs exists
+ $schema = Schema::get();
+ $schema->ensureTable('profile_prefs', Profile_prefs::schemaDef());
+
// Make sure we have our own tables setup properly
while ($user->fetch()) {
$user->setPref('email', 'notify_fave', $user->emailnotifyfav);
if (empty($arg)) {
$result = null;
} else {
- list($other, $extra) = $this->split_arg($arg);
+ list($other, $extra) = CommandInterpreter::split_arg($arg);
if (!empty($extra)) {
$result = null;
} else {
/**
* Are we allowed to perform a certain command over the API?
*/
- public function onCommandSupportedAPI(Command $cmd, array &$supported)
+ public function onCommandSupportedAPI(Command $cmd, &$supported)
{
$supported = $supported || $cmd instanceof FavCommand;
}
return;
}
- function showContext()
- {
- return;
- }
-
//Just changing the link...
function showReplyLink()
{
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized(Net_URL_Mapper $m)
+ function onRouterInitialized(URLMapper $m)
{
$m->connect('group/:nickname/favorited',
array('action' => 'groupfavorited'),
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('group/:nickname/inbox',
array('action' => 'groupinbox'),
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m URL mapper
+ * @param URLMapper $m URL mapper
*
* @return boolean hook return
*/
- function onStartInitializeRouter($m)
+ public function onStartInitializeRouter(URLMapper $m)
{
$m->connect('main/oembed/proxy',
array('action' => 'oembedproxy'));
*
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m URL mapper
+ * @param URLMapper $m URL mapper
*
* @return boolean hook return
*/
- function onStartInitializeRouter($m)
+ public function onStartInitializeRouter(URLMapper $m)
{
$m->connect('user/remote/:id',
array('action' => 'remoteprofile'),
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
// Discovery actions
$m->connect('main/ostatustag',
if ($magicsig instanceof Magicsig) {
$xrd->links[] = new XML_XRD_Element_Link(Magicsig::PUBLICKEYREL,
'data:application/magic-public-key,'. $magicsig->toString());
+ $xrd->links[] = new XML_XRD_Element_Link(Magicsig::DIASPORA_PUBLICKEYREL,
+ base64_encode($magicsig->exportPublicKey()));
}
// TODO - finalize where the redirect should go on the publisher
static public function onCheckActivityAuthorship(Activity $activity, Profile &$profile)
{
try {
- $oprofile = Ostatus_profile::getFromProfile($profile);
- $oprofile = $oprofile->checkAuthorship($activity);
- $profile = $oprofile->localProfile();
+ $oprofile = Ostatus_profile::ensureProfileURI($profile->getUri());
+ $profile = $oprofile->checkAuthorship($activity);
} catch (Exception $e) {
common_log(LOG_ERR, 'Could not get a profile or check authorship ('.get_class($e).': "'.$e->getMessage().'") for activity ID: '.$activity->id);
$profile = null;
class Magicsig extends Managed_DataObject
{
const PUBLICKEYREL = 'magic-public-key';
+ const DIASPORA_PUBLICKEYREL = 'diaspora-public-key';
public $__table = 'magicsig';
return 'RSA.' . $mod . '.' . $exp . $private_exp;
}
+ public function exportPublicKey($format=CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
+ {
+ $this->publicKey->setPublicKey();
+ return $this->publicKey->getPublicKey($format);
+ }
+
/**
* importKeys will load the object's keypair string, which initiates
* loadKey() and configures Crypt_RSA objects.
*/
public function localProfile()
{
+ if ($this->isGroup()) {
+ return $this->localGroup()->getProfile();
+ }
+
$profile = Profile::getKV('id', $this->profile_id);
if ($profile instanceof Profile) {
return $profile;
{
$notice = null;
- $profile = $this->checkAuthorship($activity, $this->localProfile());
+ $profile = ActivityUtils::checkAuthorship($activity, $this->localProfile());
// It's not always an ActivityObject::NOTE, but... let's just say it is.
$options['reply_to'] = $orig->id;
}
}
+ if (!empty($activity->context->conversation)) {
+ // we store the URI here, Notice class can look it up later
+ $options['conversation'] = $activity->context->conversation;
+ }
$location = $activity->context->location;
if ($location) {
}
// Try looking it up
- $oprofile = Ostatus_profile::getKV('uri', 'acct:'.$addr);
+ $oprofile = Ostatus_profile::getKV('uri', Discovery::normalize($addr));
if ($oprofile instanceof Ostatus_profile) {
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->getUri());
common_log(LOG_WARNING,
"OStatus: skipping post with group listed ".
"as author: " . $oprofile->getUri() . " in feed from " . $this->getUri());
- return false;
+ throw new ServerException('Activity author is a non-actor');
}
} else {
$actor = $activity->actor;
*
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m URL mapper
+ * @param URLMapper $m URL mapper
*
* @return boolean hook return
*/
- function onStartInitializeRouter($m)
+ public function onStartInitializeRouter(URLMapper $m)
{
$m->connect('main/openid', array('action' => 'openidlogin'));
$m->connect('main/openidtrust', array('action' => 'openidtrust'));
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/poll/new',
array('action' => 'newpoll'));
return array(self::POLL_OBJECT, self::POLL_RESPONSE_OBJECT);
}
-
- function adaptNoticeListItem($nli) {
- return new PollListItem($nli);
- }
-
/**
* When a notice is deleted, delete the related Poll
*
return true;
}
+
+ protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+ {
+ if ($stored->object_type == self::POLL_RESPONSE_OBJECT) {
+ parent::showNoticeContent($stored, $out, $scoped);
+ return;
+ }
+
+ // If the stored notice is a POLL_OBJECT
+ $poll = Poll::getByNotice($stored);
+ if ($poll instanceof Poll) {
+ if (!$scoped instanceof Profile || $poll->getResponse($scoped) instanceof Poll_response) {
+ // Either the user is not logged in or it has already responded; show the results.
+ $form = new PollResultForm($poll, $out);
+ } else {
+ $form = new PollResponseForm($poll, $out);
+ }
+ $form->show();
+ } else {
+ // TRANS: Error text displayed if no poll data could be found.
+ $out->text(_m('Poll data is missing'));
+ }
+ }
}
+++ /dev/null
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Adapter to show polls in a nicer way
- *
- * 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 Poll
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
-
-/**
- * An adapter to show polls in a nicer way
- *
- * @category Poll
- * @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 PollListItem extends NoticeListItemAdapter
-{
- // @fixme which domain should we use for these namespaces?
- const POLL_OBJECT = 'http://activityschema.org/object/poll';
- const POLL_RESPONSE_OBJECT = 'http://activityschema.org/object/poll-response';
-
- function showNotice(Notice $notice, $out)
- {
- switch ($notice->object_type) {
- case self::POLL_OBJECT:
- return $this->showNoticePoll($notice, $out);
- case self::POLL_RESPONSE_OBJECT:
- return $this->showNoticePollResponse($notice, $out);
- default:
- // TRANS: Exception thrown when performing an unexpected action on a poll.
- // TRANS: %s is the unexpected object type.
- throw new Exception(sprintf(_m('Unexpected type for poll plugin: %s.'), $notice->object_type));
- }
- }
-
- function showNoticePoll(Notice $notice, $out)
- {
- $user = common_current_user();
-
- // @hack we want regular rendering, then just add stuff after that
- $nli = new NoticeListItem($notice, $out);
- $nli->showNotice();
-
- $out->elementStart('div', array('class' => 'e-content poll-content'));
- $poll = Poll::getByNotice($notice);
- if ($poll) {
- if ($user) {
- $profile = $user->getProfile();
- $response = $poll->getResponse($profile);
- if ($response) {
- // User has already responded; show the results.
- $form = new PollResultForm($poll, $out);
- } else {
- $form = new PollResponseForm($poll, $out);
- }
- $form->show();
- }
- } else {
- // TRANS: Error text displayed if no poll data could be found.
- $out->text(_m('Poll data is missing'));
- }
- $out->elementEnd('div');
-
- // @fixme
- $out->elementStart('div', array('class' => 'e-content'));
- }
-
- function showNoticePollResponse(Notice $notice, $out)
- {
- $user = common_current_user();
-
- // @hack we want regular rendering, then just add stuff after that
- $nli = new NoticeListItem($notice, $out);
- $nli->showNotice();
-
- // @fixme
- $out->elementStart('div', array('class' => 'e-content'));
- }
-}
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$UUIDregex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';
$nli->showNoticeLink();
$nli->showNoticeSource();
$nli->showNoticeLocation();
- $nli->showContext();
+ $nli->showPermalink();
$nli->showRepeat();
$nli->showNoticeOptions();
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/channel/:channelkey/keepalive',
array('action' => 'keepalivechannel'),
* action will be named 'FoobarAction', where action = 'foobar'. The class
* must be loaded in the onAutoload() method.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/hello',
array('action' => 'hello'));
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('search/:search/subscribe',
array('action' => 'searchsub'),
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('sitemapindex.xml',
array('action' => 'sitemapindex'));
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('favorited/:slice',
array('action' => 'favoritedslice'),
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('settings/mirror',
array('action' => 'mirrorsettings'));
/**
* Map URLs to actions
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook value; true means continue processing, false means stop.
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('tag/:tag/subscribe',
array('action' => 'tagsub'),
------------
OAuth 1.0a (http://oauth.net) is used to to access protected resources
-on Twitter (as opposed to HTTP Basic Auth)*. To use Twitter bridging
-you will need to register your instance of StatusNet as an application
-on Twitter (http://twitter.com/apps). During the application
-registration process your application will be assigned a "consumer" key
-and secret, which the plugin will use to make OAuth requests to Twitter.
-You can either pass the consumer key and secret in when you enable the
-plugin, or set it using the Twitter administration panel**.
+on Twitter. To use Twitter bridging you will need to register your
+instance of StatusNet as an application on Twitter (http://twitter.com/apps).
+During the application registration process your application will be assigned
+a "consumer" key and secret, which the plugin will use to make OAuth requests
+to Twitter. You can either pass the consumer key and secret in when you
+enable the plugin, or set it using the Twitter administration panel.
When registering your application with Twitter set the type to "Browser"
and your Callback URL to:
)
);
-or just:
+or if you want to set the variables from the website's administration panel:
addPlugin('TwitterBridge');
-if you want to set the consumer key and secret from the Twitter bridge
-administration panel. (The Twitter bridge wont work at all
-unless you configure it with a consumer key and secret.)
-
-* Note: The plugin will still push notices to Twitter for users who
- have previously set up the Twitter bridge using their Twitter name and
- password under an older version of StatusNet, but all new Twitter
- bridge connections will use OAuth.
-
-** For multi-site setups you can also set a global consumer key and
- secret. The Twitter bridge will fall back on the global key pair if
- it can't find a local pair, e.g.:
-
- $config['twitter']['global_consumer_key'] = 'YOUR_CONSUMER_KEY';
- $config['twitter']['global_consumer_secret'] = 'YOUR_CONSUMER_SECRET';
-
-Upgrade
--------
-
-If you've used the Twitter bridge plugin prior to version 0.9.5,
-you'll need to run the new scripts/initialize_notice_to_status.php
-script to initialize the new notice-to-status mapping file, which
-greatly improves the integration between StatusNet and Twitter.
+After saving your configuration file, please run 'php scripts/upgrade.php'
+and also restart the background daemons if they are active on your instance.
Administration panel
--------------------
-As of StatusNet 0.9.0 there is a new administration panel that allows
-you to configure Twitter bridge settings within StatusNet itself,
-instead of having to specify them manually in your config.php.
-
-To access it, you'll need to use a user with the "administrator"
-role (see: scripts/userrole.php).
+To access the administration panel, you'll need to use a user with the
+"administrator" role (see: scripts/userrole.php).
Sign in with Twitter
--------------------
-With 0.9.0, StatusNet optionally allows users to register and
-authenticate using their Twitter credentials via the "Sign in with
-Twitter" pattern described here:
+GNU social optionally allows users to register and authenticate using their
+Twitter credentials via the "Sign in with Twitter" pattern described here:
- http://apiwiki.twitter.com/Sign-in-with-Twitter
+ https://dev.twitter.com/web/sign-in
The option is _on_ by default when you install the plugin, but it can
disabled via the Twitter bridge administration panel, or by adding the
*
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
*
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('panel/twitter', array('action' => 'twitteradminpanel'));
/**
* Add our actions to the URL router
*
- * @param Net_URL_Mapper $m URL mapper for this hit
+ * @param URLMapper $m URL mapper for this hit
*
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/flag/profile', array('action' => 'flagprofile'));
$m->connect('main/flag/clear', array('action' => 'clearflag'));
/**
* Hook for RouterInitialized event.
*
- * @param Net_URL_Mapper $m path-to-action mapper
+ * @param URLMapper $m path-to-action mapper
* @return boolean hook return
*/
- function onRouterInitialized($m)
+ public function onRouterInitialized(URLMapper $m)
{
$m->connect('panel/yammer',
array('action' => 'yammeradminpanel'));
$notice = Notice::saveNew($user->id, $content, 'createsim', $options);
}
+/* Plugins should be part of the simulation too!
function newMessage($i)
{
global $userprefix;
$other = $friends[$j];
$message = Message::saveNew($user->id, $other->id, $content, 'createsim');
-}
+}*/
function newSub($i)
{
}
//function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $favesavg, $messageavg, $tagmax)
-function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $messageavg, $tagmax)
+function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $tagmax)
{
global $config;
$config['site']['dupelimit'] = -1;
// # registrations + # notices + # subs
//$events = $usercount + $groupcount + ($usercount * ($noticeavg + $subsavg + $joinsavg + $favesavg + $messageavg));
- $events = $usercount + $groupcount + ($usercount * ($noticeavg + $subsavg + $joinsavg + $messageavg));
+ $events = $usercount + $groupcount + ($usercount * ($noticeavg + $subsavg + $joinsavg));
$events -= $preuser;
$events -= $pregroup;
$st = $nt + ($usercount * $subsavg);
$jt = $st + ($usercount * $joinsavg);
// $ft = $jt + ($usercount * $favesavg);
- $mt = $ft + ($usercount * $messageavg);
+// $mt = $ft + ($usercount * $messageavg);
- printfv("$events events ($ut, $gt, $nt, $st, $jt, $ft, $mt)\n");
+// printfv("$events events ($ut, $gt, $nt, $st, $jt, $ft, $mt)\n");
+ printfv("$events events ($ut, $gt, $nt, $st, $jt)\n");
for ($i = 0; $i < $events; $i++)
{
/* } else if ($e > $jt && $e <= $ft) {
printfv("$i Making a new fave\n");
newFave($n);*/
- } else if ($e > $ft && $e <= $mt) {
+/* } else if ($e > $ft && $e <= $mt) {
printfv("$i Making a new message\n");
- newMessage($n);
+ newMessage($n);*/
} else {
printfv("No event for $i!");
}
$subsavg = (have_option('b', 'subscriptions')) ? get_option_value('b', 'subscriptions') : max($usercount/20, 10);
$joinsavg = (have_option('j', 'joins')) ? get_option_value('j', 'joins') : 5;
//$favesavg = (have_option('f', 'faves')) ? get_option_value('f', 'faves') : max($noticeavg/10, 5);
-$messageavg = (have_option('m', 'messages')) ? get_option_value('m', 'messages') : max($noticeavg/10, 5);
+//$messageavg = (have_option('m', 'messages')) ? get_option_value('m', 'messages') : max($noticeavg/10, 5);
$tagmax = (have_option('t', 'tags')) ? get_option_value('t', 'tags') : 10000;
$userprefix = (have_option('x', 'prefix')) ? get_option_value('x', 'prefix') : 'testuser';
$groupprefix = (have_option('z', 'groupprefix')) ? get_option_value('z', 'groupprefix') : 'testgroup';
try {
//main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $favesavg, $messageavg, $tagmax);
- main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $messageavg, $tagmax);
+ main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $tagmax);
} catch (Exception $e) {
printfv("Got an exception: ".$e->getMessage());
}
function main()
{
if (Event::handle('StartUpgrade')) {
+ fixupConversationURIs();
+
updateSchemaCore();
updateSchemaPlugins();
printfnq("DONE.\n");
}
+function fixupConversationURIs()
+{
+ printfnq("Ensuring all conversations have a URI...");
+
+ $conv = new Conversation();
+ $conv->whereAdd('uri IS NULL');
+
+ if ($conv->find()) {
+ $rounds = 0;
+ while ($conv->fetch()) {
+ $uri = common_local_url('conversation', array('id' => $conv->id));
+ $sql = sprintf('UPDATE conversation SET uri="%1$s" WHERE id="%2$d";',
+ $conv->escape($uri), $conv->id);
+ $conv->query($sql);
+ if (($conv->N-++$rounds) % 500 == 0) {
+ printfnq(sprintf(' %d items left...', $conv->N-$rounds));
+ }
+ }
+ }
+
+ printfnq("DONE.\n");
+}
+
function initGroupProfileId()
{
printfnq("Ensuring all User_group entries have a Profile and profile_id...");
margin-bottom: 15px;
}
-h1 {font-size: 2.2em;}
-h2 {font-size: 1.8em;}
-h3 {font-size: 1.6em;}
-h4 {font-size: 1.4em;}
-h5 {font-size: 1.2em;}
+h1 {font-size: 2.0em;}
+h2 {font-size: 1.6em;}
+h3 {font-size: 1.4em;}
+h4 {font-size: 1.2em;}
+h5 {font-size: 1.1em;}
h6 {font-size: 1em;}
p {
z-index: 1;
}
+.form_settings {
+ clear: both;
+}
+
/* site nav local views */
display: none;
}
+.notice .permalink:after {
+ content: ']';
+}
+
+.notice .permalink:before {
+ content: '[';
+}
+
+.notice .permalink.external {
+ display: none;
+}
+
/* old school conversation style */
#conversation .notices .notices {
display:none;
}
-.form_settings {
- clear:both;
-}
-
.form_settings fieldset {
margin-top: 10px;
- margin-bottom: 30px;
+ margin-bottom: 10px;
border: none;
}
.form_settings fieldset fieldset {
- margin-bottom: 40px;
+ margin-bottom: 20px;
padding: 10px;
- padding-top: 30px;
+ padding-top: 10px;
border-width:1px;
border-style:solid;
background:rgba(240, 240, 240, 0.2);
}
-.form_settings .form_data input {
-width:39%;
-}
.form_settings .form_data input.submit,
.form_settings .form_data input.checkbox,
.form_settings .form_data input.radio {
abbr {border-bottom: none;}
-h1 {font-size: 1.6em;}
-h2 {font-size: 1.6em;}
-h3 {font-size: 1.4em;}
-h4 {font-size: 1.4em;}
-h5 {font-size: 1.2em;}
-h6 {font-size: 1em;}
-
#wrap {
background: #fff url('../images/brdr_black_dot.png') repeat-x 0px 10px;
}