diff --git a/lib/mail.php b/lib/mail.php
index 497637eb44..b51406dbad 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -576,53 +576,66 @@ function mail_notify_nudge($from, $to)
* This function checks to see if the recipient wants notification
* of DMs and has a configured email address.
*
- * @param Message $message message to notify about
- * @param User $from user sending message; default to sender
- * @param User $to user receiving message; default to recipient
+ * @param Notice $message message to notify about
+ * @param User $from user sending message
+ * @param array $to users receiving the message
*
* @return boolean success code
*/
-function mail_notify_message($message, $from=null, $to=null)
+function mail_notify_message(Notice $message, Profile $from = null, ?array $to = null)
{
if (is_null($from)) {
- $from = User::getKV('id', $message->from_profile);
+ $from = $message->getProfile();
}
if (is_null($to)) {
- $to = User::getKV('id', $message->to_profile);
+ $to = [];
+ foreach ($message->getAttentionProfiles() as $attention) {
+ if ($attention->isLocal()) {
+ $to[] = $attention;
+ }
+ }
}
- if (is_null($to->email) || !$to->emailnotifymsg) {
- return true;
+ $success = true;
+
+ foreach ($to as $t) {
+ if (is_null($t->email) || !$t->emailnotifymsg) {
+ continue;
+ }
+
+ common_switch_locale($t->language);
+ // TRANS: Subject for direct-message notification email.
+ // TRANS: %s is the sending user's nickname.
+ $subject = sprintf(_('New private message from %s'), $from->getNickname());
+
+ // TRANS: Body for direct-message notification email.
+ // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
+ // TRANS: %3$s is the message content, %4$s a URL to the message,
+ $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
+ "------------------------------------------------------\n".
+ "%3\$s\n".
+ "------------------------------------------------------\n\n".
+ "You can reply to their message here:\n\n".
+ "%4\$s\n\n".
+ "Don't reply to this email; it won't get to them."),
+ $from->getBestName(),
+ $from->getNickname(),
+ $message->getContent(),
+ common_local_url('newmessage', ['to' => $from->getID()])) .
+ mail_footer_block();
+
+ $headers = _mail_prepare_headers('message', $t->getNickname(), $from->getNickname());
+
+ common_switch_locale();
+
+ if (!mail_to_user($t, $subject, $body, $headers)) {
+ common_log(LOG_ERR, "Failed to notify user:{$t->getID()} about the new message:{$message->getID()} sent by user:{$from->getID()}");
+ $success = false;
+ }
}
- common_switch_locale($to->language);
- // TRANS: Subject for direct-message notification email.
- // TRANS: %s is the sending user's nickname.
- $subject = sprintf(_('New private message from %s'), $from->nickname);
-
- $from_profile = $from->getProfile();
-
- // TRANS: Body for direct-message notification email.
- // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
- // TRANS: %3$s is the message content, %4$s a URL to the message,
- $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
- "------------------------------------------------------\n".
- "%3\$s\n".
- "------------------------------------------------------\n\n".
- "You can reply to their message here:\n\n".
- "%4\$s\n\n".
- "Don't reply to this email; it won't get to them."),
- $from_profile->getBestName(),
- $from->nickname,
- $message->content,
- common_local_url('newmessage', array('to' => $from->id))) .
- mail_footer_block();
-
- $headers = _mail_prepare_headers('message', $to->nickname, $from->nickname);
-
- common_switch_locale();
- return mail_to_user($to, $subject, $body, $headers);
+ return $success;
}
/**
diff --git a/plugins/DirectMessage/DirectMessagePlugin.php b/plugins/DirectMessage/DirectMessagePlugin.php
index 57492189df..63f822fd54 100644
--- a/plugins/DirectMessage/DirectMessagePlugin.php
+++ b/plugins/DirectMessage/DirectMessagePlugin.php
@@ -1,47 +1,66 @@
.
- */
-
-if (!defined('GNUSOCIAL')) { exit(1); }
+// This file is part of GNU social - https://www.gnu.org/software/social
+//
+// GNU social is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// GNU social is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with GNU social. If not, see .
/**
- * @maintainer Mikael Nordfeldth
+ * GNUsocial implementation of Direct Messages
+ *
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+
+defined('GNUSOCIAL') || die();
+
+// require needed abstractions first
+require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'messagelist.php';
+require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'messagelistitem.php';
+
+// Import plugin libs
+foreach (glob(__DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . '*.php') as $filename) {
+ require_once $filename;
+}
+// Import plugin models
+foreach (glob(__DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'models' . DIRECTORY_SEPARATOR . '*.php') as $filename) {
+ require_once $filename;
+}
+
+/**
+ * @category Plugin
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class DirectMessagePlugin extends Plugin
{
- const PLUGIN_VERSION = '2.0.0';
-
- public function onCheckSchema()
- {
- $schema = Schema::get();
- $schema->ensureTable('message', Message::schemaDef());
- return true;
- }
+ const PLUGIN_VERSION = '3.0.0';
public function onRouterInitialized(URLMapper $m)
{
// web front-end actions
- $m->connect('message/new', ['action' => 'newmessage']);
+ $m->connect('message/new',
+ ['action' => 'newmessage']);
$m->connect('message/new?to=:to',
['action' => 'newmessage'],
- ['to' => Nickname::DISPLAY_FMT]);
+ ['to' => '[0-9]+']);
+
$m->connect('message/:message',
- ['action' => 'showmessage'],
+ ['action' => 'showmessage'],
['message' => '[0-9]+']);
// direct messages
@@ -59,35 +78,14 @@ class DirectMessagePlugin extends Plugin
return true;
}
- public function onAppendUserActivityStreamObjects(UserActivityStream $uas, array &$objs)
- {
- // Messages _from_ the user
- $msgMap = Message::listGet('from_profile', array($uas->getUser()->id));
- $messages = $msgMap[$uas->getUser()->id];
- if (!empty($uas->after)) {
- $messages = array_filter($messages, array($uas, 'createdAfter'));
- }
- foreach ($messages as $message) {
- $objs[] = clone($message);
- }
-
- // Messages _to_ the user
- $msgMap = Message::listGet('to_profile', array($uas->getUser()->id));
- $messages = $msgMap[$uas->getUser()->id];
- if (!empty($uas->after)) {
- $messages = array_filter($messages, array($uas, 'createdAfter'));
- }
- foreach ($messages as $message) {
- $objs[] = clone($message);
- }
-
- return true;
- }
-
/**
* Are we allowed to perform a certain command over the API?
+ *
+ * @param Command $cmd
+ * @param bool &$supported
+ * @return bool hook value
*/
- public function onCommandSupportedAPI(Command $cmd, &$supported)
+ public function onCommandSupportedAPI(Command $cmd, ?bool &$supported) : bool
{
$supported = $supported || $cmd instanceof MessageCommand;
return true;
@@ -99,13 +97,12 @@ class DirectMessagePlugin extends Plugin
* @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
+ * @param Command|bool &$result The resulting command object to be run.
+ * @return bool hook value
*/
- public function onStartInterpretCommand($cmd, $arg, $user, &$result)
+ public function onStartInterpretCommand(string $cmd, string $arg, User $user, &$result) : bool
{
- $dm_cmds = array('d', 'dm');
+ $dm_cmds = ['d', 'dm'];
if ($result === false && in_array($cmd, $dm_cmds)) {
if (!empty($arg)) {
@@ -119,13 +116,20 @@ class DirectMessagePlugin extends Plugin
return true;
}
- public function onEndPersonalGroupNav(Menu $menu, Profile $target, Profile $scoped=null)
+ /**
+ * Show Message button in someone's left-side navigation menu
+ *
+ * @param Menu $menu
+ * @param Profile $target
+ * @param Profile $scoped
+ * @return void
+ */
+ 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())),
+ $menu->out->menuItem(common_local_url('inbox', ['nickname' => $target->getNickname()]),
// TRANS: Menu item in personal group navigation menu.
_m('MENU','Messages'),
// TRANS: Menu item title in personal group navigation menu.
@@ -134,46 +138,89 @@ class DirectMessagePlugin extends Plugin
}
}
- public function onEndProfilePageActionsElements(HTMLOutputter $out, Profile $profile)
+ /**
+ * Show Message button in someone's profile page
+ *
+ * @param HTMLOutputter $out
+ * @param Profile $profile
+ * @return bool hook flag
+ */
+ public function onEndProfilePageActionsElements(HTMLOutputter $out, Profile $profile) : bool
{
$scoped = Profile::current();
- if (!$scoped instanceof Profile) {
+ if (!$scoped instanceof Profile || $scoped->getID() === $profile->getID()) {
return true;
}
- if ($profile->isLocal() && $scoped->mutuallySubscribed($profile)) {
+ if (!$profile->isLocal() && Event::handle('DirectMessageProfilePageActions', [$profile])) {
+ // nothing to do if remote profile and no one to validate it
+ return true;
+ }
+
+ if (!$profile->hasBlocked($scoped)) {
$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->element('a',
+ ['href' => common_local_url('newmessage', ['to' => $profile->getID()]),
+ // 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)
+ /**
+ * Notice table is used to store private messages in a newer version of the plugin,
+ * this ensures we migrate entries from the old message table.
+ *
+ * @return bool hook flag
+ */
+ public function onEndUpgrade() : bool
{
- $msg = new Message();
- $msg->from_profile = $profile->id;
- $msg->delete();
+ try {
+ $schema = Schema::get();
+ $schema->getTableDef('message');
+ } catch (SchemaTableMissingException $e) {
+ return true;
+ }
+
+ $message = new Message();
+
+ $message->selectAdd(); // clears it
+ $message->selectAdd('id');
+ $message->orderBy('created ASC');
+
+ if ($message->find()) {
+ while ($message->fetch()) {
+ $msg = Message::getKV('id', $message->id);
+ $act = $msg->asActivity();
+
+ Notice::saveActivity($act,
+ $msg->getFrom(),
+ ['source' => 'web',
+ 'scope' => NOTICE::MESSAGE_SCOPE]);
+ }
+ }
+
+ $message->free();
+ $message = null;
+
+ $schema->dropTable('message');
- $msg = new Message();
- $msg->to_profile = $profile->id;
- $msg->delete();
return true;
}
- public function onPluginVersion(array &$versions)
+ public function onPluginVersion(array &$versions) : bool
{
- $versions[] = array('name' => 'Direct Message',
- 'version' => self::PLUGIN_VERSION,
- 'author' => 'Mikael Nordfeldth',
- 'homepage' => 'http://gnu.io/',
- 'rawdescription' =>
- // TRANS: Plugin description.
- _m('Direct Message to other local users (broken out of core).'));
+ $versions[] = ['name' => 'Direct Message',
+ 'version' => self::PLUGIN_VERSION,
+ 'author' => 'Mikael Nordfeldth, Bruno Casteleiro',
+ 'homepage' => 'http://gnu.io/',
+ 'rawdescription' =>
+ // TRANS: Plugin description.
+ _m('Direct Message to other local users.')];
return true;
}
diff --git a/plugins/DirectMessage/EVENTS.txt b/plugins/DirectMessage/EVENTS.txt
new file mode 100644
index 0000000000..13459f1028
--- /dev/null
+++ b/plugins/DirectMessage/EVENTS.txt
@@ -0,0 +1,9 @@
+FillDirectMessageRecipients: after the plugin populates the recipients select-box; federation plugins must add their own recipients; note that only subscriptions should be added
+- User $current: Currently logged user
+- array &$recipeints: Profiles to be shown in the select-box
+
+DirectMessageProfilePageActions: when about to show the direct message button in someone's profile; federation plugins must validate their users otherwise the button is ommited
+- Profile $target: Profile receiving the message button
+
+SendDirectMessage: after storing a new private message; federation plugins must distribute the message to the remote profiles
+- Notice $message: Message to be distributed
\ No newline at end of file
diff --git a/plugins/DirectMessage/README b/plugins/DirectMessage/README
index b908a2472f..4d5cd06b71 100755
--- a/plugins/DirectMessage/README
+++ b/plugins/DirectMessage/README
@@ -1,4 +1,4 @@
-The DirectMessage plugin allows users to send Direct Message to other local users
+The DirectMessage plugin allows users to send Direct Messages
Installation
============
@@ -8,3 +8,28 @@ Settings
========
none
+Changes from previous release
+=============================
+
+- Migrate from message table to notice table
+
+This change implied the write of upgrading logic, the addition of a new
+Notice scope (NOTICE::MESSAGE_SCOPE) and updating the save logic.
+
+- Support Federation
+
+DM is still in charge of local communications-only but it now uses a few new
+custom events to allow remote handling of the private messages.
+
+TODO
+====
+
+- Review API actions, broken after new update
+- Review Command events
+- Update messagelistitem (UI) to support multi-recipient. Right now we present only
+one of the recipients in the message header.
+- Update messagelistitem (UI) to support no-recipient, which happens when a message
+is sent to profiles that blocked the sender. Right now we don't present this messages
+at all because of the UI requirements, but it is still stored in the database.
+- Add delete, like and reply actions. Replies need further changes like adding
+support for private-conversations.
\ No newline at end of file
diff --git a/plugins/DirectMessage/actions/inbox.php b/plugins/DirectMessage/actions/inbox.php
index b3dc88598b..cd92b7d4d3 100644
--- a/plugins/DirectMessage/actions/inbox.php
+++ b/plugins/DirectMessage/actions/inbox.php
@@ -1,104 +1,87 @@
.
+
/**
- * StatusNet, the distributed open-source microblogging tool
+ * GNUsocial implementation of Direct Messages
*
- * 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 .
- *
- * @category Message
- * @package StatusNet
- * @author Evan Prodromou
- * @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/
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('GNUSOCIAL')) { exit(1); }
+defined('GNUSOCIAL') || die();
/**
- * action handler for message inbox
+ * Action handler for the inbox
*
- * @category Message
- * @package StatusNet
- * @author Evan Prodromou
- * @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
+ * @category Plugin
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class InboxAction extends MailboxAction
{
-
/**
- * Title of the page
+ * Title of the page.
*
* @return string page title
*/
- function title()
+ function title() : string
{
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,
+ return sprintf(_m('Inbox for %1$s - page %2$d'), $this->user->getNickname(),
$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);
+ return sprintf(_m('Inbox for %s'), $this->user->getNickname());
}
}
/**
- * Retrieve the messages for this user and this page
+ * 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()
+ * @return Notice data object with stream for messages
*/
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;
- }
+ return MessageModel::inboxMessages($this->user, $this->page);
}
+ /**
+ * Retrieve inbox MessageList widget
+ */
function getMessageList($message)
{
return new InboxMessageList($this, $message);
}
/**
- * Instructions for using this page
+ * Instructions for using this page.
*
* @return string localised instructions for using the page
*/
- function getInstructions()
+ function getInstructions() : string
{
// TRANS: Instructions for user inbox page.
- return _('This is your inbox, which lists your incoming private messages.');
+ return _m('This is your inbox, which lists your incoming private messages.');
}
}
diff --git a/plugins/DirectMessage/actions/newmessage.php b/plugins/DirectMessage/actions/newmessage.php
index 8d1e5920fa..4e3d7039a5 100644
--- a/plugins/DirectMessage/actions/newmessage.php
+++ b/plugins/DirectMessage/actions/newmessage.php
@@ -1,66 +1,55 @@
.
+
/**
- * StatusNet, the distributed open-source microblogging tool
+ * GNUsocial implementation of Direct Messages
*
- * 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 .
- *
- * @category Personal
- * @package StatusNet
- * @author Evan Prodromou
- * @author Zach Copley
- * @author Sarven Capadisli
- * @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/
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('GNUSOCIAL')) { exit(1); }
+defined('GNUSOCIAL') || die();
/**
* Action for posting new direct messages
*
- * @category Personal
- * @package StatusNet
+ * @category Plugin
+ * @package GNUsocial
* @author Evan Prodromou
* @author Zach Copley
* @author Sarven Capadisli
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
class NewmessageAction extends FormAction
{
- var $content = null;
- var $to = null;
- var $other = null;
-
- protected $form = 'Message'; // will become MessageForm later
+ protected $form = 'Message';
+ protected $to = null;
+ protected $content = null;
/**
- * Title of the page
- *
- * Note that this usually doesn't get called unless something went wrong
+ * Title of the page.
+ * Note that this usually doesn't get called unless something went wrong.
*
* @return string page title
*/
-
- function title()
+ function title() : string
{
// TRANS: Page title for new direct message page.
return _('New message');
@@ -68,31 +57,29 @@ class NewmessageAction extends FormAction
protected function doPreparation()
{
- $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) {
+ if ($this->trimmed('to')) {
+ $this->to = Profile::getKV('id', $this->trimmed('to'));
+ if (!$this->to 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);
- }
+ $this->formOpts['to'] = $this->to;
}
- return true;
+ if ($this->trimmed('content')) {
+ $this->content = $this->trimmed('content');
+ $this->formOpts['content'] = $this->content;
+ }
+
+ if ($this->trimmed('to-box')) {
+ $selected = explode(':', $this->trimmed('to-box'));
+
+ if (sizeof($selected) == 2) {
+ $this->to = Profile::getKV('id', $selected[1]);
+ // validating later
+ }
+ }
}
protected function doPost()
@@ -106,35 +93,43 @@ class NewmessageAction extends FormAction
$content_shortened = $this->scoped->shortenLinks($this->content);
- if (Message::contentTooLong($content_shortened)) {
+ if (MessageModel::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()));
+ MessageModel::maxContent()),
+ MessageModel::maxContent()));
}
- if (!$this->other instanceof Profile) {
- // 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);
+ // validate recipients
+ if (!$this->to instanceof Profile) {
+ $mentions = common_find_mentions($this->content, $this->scoped);
+ if (empty($mentions)) {
+ $this->clientError(_('No recipients specified.'));
+ }
+ } else {
+ // push to-box profile to the content message, will be
+ // detected during Notice save
+ try {
+ if ($this->to->isLocal()) {
+ $this->content = "@{$this->to->getNickname()} {$this->content}";
+ } else {
+ $this->content = '@' . substr($this->to->getAcctUri(), 5) . " {$this->content}";
+ }
+ } catch (ProfileNoAcctUriException $e) {
+ // well, I'm no magician
+ }
}
- $message = Message::saveNew($this->scoped->id, $this->other->id, $this->content, 'web');
- $message->notify();
+ $message = MessageModel::saveNew($this->scoped, $this->content);
+ Event::handle('SendDirectMessage', [$message]);
+ mail_notify_message($message);
if (GNUsocial::isAjax()) {
// TRANS: Confirmation text after sending a direct message.
// TRANS: %s is the direct message recipient.
- return sprintf(_('Direct message to %s sent.'), $this->other->getNickname());
+ return sprintf(_('Direct message to %s sent.'), $this->to->getNickname());
}
$url = common_local_url('outbox', array('nickname' => $this->scoped->getNickname()));
diff --git a/plugins/DirectMessage/actions/outbox.php b/plugins/DirectMessage/actions/outbox.php
index 5b11a1fd22..215778110f 100644
--- a/plugins/DirectMessage/actions/outbox.php
+++ b/plugins/DirectMessage/actions/outbox.php
@@ -1,43 +1,39 @@
.
+
/**
- * StatusNet, the distributed open-source microblogging tool
+ * GNUsocial implementation of Direct Messages
*
- * 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 .
- *
- * @category Message
- * @package StatusNet
- * @author Evan Prodromou
- * @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/
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('GNUSOCIAL')) { exit(1); }
+defined('GNUSOCIAL') || die();
/**
- * action handler for message outbox
+ * Action handler for the outbox
*
- * @category Message
- * @package StatusNet
- * @author Evan Prodromou
- * @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
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class OutboxAction extends MailboxAction
{
@@ -46,61 +42,57 @@ class OutboxAction extends MailboxAction
*
* @return string page title
*/
- function title()
+ function title() : string
{
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);
+ return sprintf(_m('Outbox for %1$s - page %2$d'),
+ $this->user->getNickname(), $page);
} else {
// TRANS: Title for first page of outbox.
- return sprintf(_('Outbox for %s'), $this->user->nickname);
+ return sprintf(_m('Outbox for %s'), $this->user->getNickname());
}
}
/**
- * retrieve the messages for this user and this page
+ * 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()
+ * @return Notice data object with stream for messages
*/
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;
- }
+ return MessageModel::outboxMessages($this->user, $this->page);
}
+ /**
+ * Retrieve outbox MessageList widget.
+ */
function getMessageList($message)
{
return new OutboxMessageList($this, $message);
}
/**
- * instructions for using this page
+ * Instructions for using this page.
*
* @return string localised instructions for using the page
*/
- function getInstructions()
+ function getInstructions() : string
{
// TRANS: Instructions for outbox.
- return _('This is your outbox, which lists private messages you have sent.');
+ return _m('This is your outbox, which lists private messages you have sent.');
}
}
+/**
+ * Outbox MessageList widget
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
class OutboxMessageList extends MessageList
{
function newItem($message)
@@ -109,15 +101,30 @@ class OutboxMessageList extends MessageList
}
}
+/**
+ * Outbox MessageListItem widget
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
class OutboxMessageListItem extends MessageListItem
{
/**
* Returns the profile we want to show with the message
*
- * @return Profile The profile that matches the message
+ * Note that the plugin now handles sending for multiple profiles,
+ * but since the UI isn't changed yet, we still retrieve a single
+ * profile from this function (or null, if for blocking reasons
+ * there are no attentions stored).
+ *
+ * @return Profile|null
*/
- function getMessageProfile()
+ function getMessageProfile() : ?Profile
{
- return $this->message->getTo();
+ $attentions = $this->message->getAttentionProfiles();
+ return empty($attentions) ? null : $attentions[0];
}
}
diff --git a/plugins/DirectMessage/actions/showmessage.php b/plugins/DirectMessage/actions/showmessage.php
index 7c4b2a0f73..38d6239d99 100644
--- a/plugins/DirectMessage/actions/showmessage.php
+++ b/plugins/DirectMessage/actions/showmessage.php
@@ -1,130 +1,151 @@
.
+
/**
- * StatusNet, the distributed open-source microblogging tool
+ * GNUsocial implementation of Direct Messages
*
- * Show a single message
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+
+defined('GNUSOCIAL') || die();
+
+/**
+ * Action for showing 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 .
- *
- * @category Personal
- * @package StatusNet
+ * @category Plugin
+ * @package GNUsocial
* @author Evan Prodromou
- * @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/
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-/**
- * Show a single message
- *
- * @category Personal
- * @package StatusNet
- * @author Evan Prodromou
- * @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;
+ protected $message = null;
+ protected $from = null;
+ protected $attentions = null;
+ protected $user = null;
/**
- * The current user
- */
-
- var $user = null;
-
- /**
- * Load attributes based on database arguments
- *
- * Loads all the DB stuff
+ * Load attributes based on database arguments.
*
* @param array $args $_REQUEST array
- *
- * @return success flag
+ * @return bool success flag
*/
- function prepare(array $args = array())
+ 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);
+ if (!$this->trimmed('message')) {
+ return true;
}
+ $this->message = Notice::getKV('id', $this->trimmed('message'));
+
+ if (!$this->message instanceof Notice) {
+ // TRANS: Client error displayed requesting a single message that does not exist.
+ $this->clientError(_m('No such message.'), 404);
+ }
+
+ $this->from = $this->message->getProfile();
+ $this->attentions = $this->message->getAttentionProfiles();
+
$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);
+ if (empty($this->user) || $this->user->getID() != $this->from->getID()) {
+
+ $receiver = false;
+ foreach ($this->attentions as $attention) {
+ if ($this->user->getID() == $attention->getID()) {
+ $receiver = true;
+ break;
+ }
+ }
+
+ if (!$receiver) {
+ // TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
+ throw new ClientException(_m('Only the sender and recipients may read this message.'), 403);
+ }
}
return true;
}
+ /**
+ * Handler method.
+ *
+ * @return void
+ */
function handle()
{
$this->showPage();
}
- function title()
+ /**
+ * Title of the page.
+ *
+ * @return string page title
+ */
+ function title() : string
{
- 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();
+ if ($this->user->getID() == $this->from->getID()) {
+ if (sizeof($this->attentions) > 1) {
+ return sprintf(_m('Message to many on %1$s'), common_exact_date($this->message->getCreated()));
+ } else {
+ $to = Profile::getKV('id', $this->attentions[0]->getID());
+ // @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(_m('Message to %1$s on %2$s'),
+ $to->getBestName(),
+ common_exact_date($this->message->getCreated()));
+ }
+ } else {
// @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));
+ return sprintf(_m('Message from %1$s on %2$s'),
+ $this->from->getBestName(),
+ common_exact_date($this->message->getCreated()));
}
}
-
+ /**
+ * Show content.
+ *
+ * @return void
+ */
function showContent()
{
$this->elementStart('ul', 'notices messages');
- $ml = new ShowMessageListItem($this, $this->message, $this->user);
+ $ml = new ShowMessageListItem($this, $this->message, $this->user, $this->from, $this->attentions);
$ml->show();
$this->elementEnd('ul');
}
- function isReadOnly($args)
+ /**
+ * Is this action read-only?
+ *
+ * @param array $args other arguments
+ * @return bool true if read-only action, false otherwise
+ */
+ function isReadOnly($args) : bool
{
return true;
}
@@ -134,30 +155,38 @@ class ShowmessageAction extends Action
*
* @return void
*/
-
function showAside() {
+
}
}
+/**
+ * showmessage action's MessageListItem widget.
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
class ShowMessageListItem extends MessageListItem
{
- var $user;
+ protected $user;
+ protected $from;
+ protected $attentions;
- function __construct($out, $message, $user)
+ function __construct($out, $message, $user, $from, $attentions)
{
parent::__construct($out, $message);
- $this->user = $user;
+
+ $this->user = $user;
+ $this->from = $from;
+ $this->attentions = $attentions;
}
- function getMessageProfile()
+ function getMessageProfile() : ?Profile
{
- 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 $this->user->getID() == $this->from->getID() ?
+ $this->attentions[0] : $this->from;
}
}
diff --git a/plugins/DirectMessage/classes/Message.php b/plugins/DirectMessage/classes/Message.php
index 5f8d27b4f1..1fe2ccc1a0 100644
--- a/plugins/DirectMessage/classes/Message.php
+++ b/plugins/DirectMessage/classes/Message.php
@@ -1,17 +1,49 @@
.
/**
- * Table Definition for message
+ * GNUsocial implementation of Direct Messages
+ *
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
+defined('GNUSOCIAL') || die();
+
+/**
+ * Table definition for message.
+ *
+ * Since the new updates this class only has the necessary
+ * logic to upgrade te plugin.
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
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 $__table = 'message'; // table name
public $id; // int(4) primary_key not_null
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $from_profile; // int(4) not_null
@@ -69,76 +101,6 @@ class Message extends Managed_DataObject
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)) {
@@ -176,38 +138,30 @@ class Message extends Managed_DataObject
$act = new Activity();
if (Event::handle('StartMessageAsActivity', array($this, &$act))) {
+ $act->verb = ActivityVerb::POST;
+ $act->time = strtotime($this->created);
- $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)) {
+ $actor_profile = $this->getFrom();
+ if (is_null($actor_profile)) {
throw new Exception(sprintf("Sender profile not found: %d", $this->from_profile));
}
+ $act->actor = $actor_profile->asActivityObject();
- $act->actor = $profile->asActivityObject();
- $act->actor->extra[] = $profile->profileInfo();
+ $act->context = new ActivityContext();
+ $options = ['source' => $this->source,
+ 'uri' => TagURI::mint(sprintf('activity:message:%d', $this->id)),
+ 'url' => $this->uri,
+ 'scope' => Notice::MESSAGE_SCOPE];
- $act->verb = ActivityVerb::POST;
+ $to_profile = $this->getTo();
+ if (is_null($to_profile)) {
+ throw new Exception(sprintf("Receiver profile not found: %d", $this->to_profile));
+ }
+ $act->context->attention[$to_profile->getUri()] = ActivityObject::PERSON;
$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);
}
diff --git a/plugins/DirectMessage/lib/inboxmessagelistitem.php b/plugins/DirectMessage/lib/inboxmessagelistitem.php
index 929c67018f..7631264ce9 100644
--- a/plugins/DirectMessage/lib/inboxmessagelistitem.php
+++ b/plugins/DirectMessage/lib/inboxmessagelistitem.php
@@ -1,7 +1,39 @@
.
-if (!defined('GNUSOCIAL')) { exit(1); }
+/**
+ * GNUsocial implementation of Direct Messages
+ *
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+defined('GNUSOCIAL') || die();
+
+/**
+ * Inbox MessageListItem widget
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
class InboxMessageListItem extends MessageListItem
{
/**
@@ -9,8 +41,8 @@ class InboxMessageListItem extends MessageListItem
*
* @return Profile The profile that matches the message
*/
- function getMessageProfile()
+ function getMessageProfile(): ?Profile
{
- return $this->message->getFrom();
+ return $this->message->getProfile();
}
}
diff --git a/plugins/DirectMessage/lib/messageform.php b/plugins/DirectMessage/lib/messageform.php
index 96059e4fac..04d095a2c9 100644
--- a/plugins/DirectMessage/lib/messageform.php
+++ b/plugins/DirectMessage/lib/messageform.php
@@ -1,117 +1,106 @@
.
+
/**
- * StatusNet, the distributed open-source microblogging tool
+ * GNUsocial implementation of Direct Messages
*
- * 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 .
- *
- * @category Form
- * @package StatusNet
- * @author Evan Prodromou
- * @author Sarven Capadisli
- * @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/
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('GNUSOCIAL')) { exit(1); }
+defined('GNUSOCIAL') || die();
/**
* Form for posting a direct message
*
- * @category Form
- * @package StatusNet
- * @author Evan Prodromou
- * @author Sarven Capadisli
- * @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
+ * @category Plugin
+ * @package GNUsocial
+ * @author Evan Prodromou
+ * @author Sarven Capadisli
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class MessageForm extends Form
{
- /**
- * User to send a direct message to
- */
- var $to = null;
+ protected $to = null;
+ protected $content = null;
/**
- * Pre-filled content of the form
- */
- var $content = null;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param HTMLOutputter $out output channel
- * @param User $to user to send a message to
- * @param string $content content to pre-fill
+ * @param HTMLOutputter $out output channel
+ * @param array|null $formOpts
*/
- function __construct($out=null, $to=null, $content=null)
+ function __construct(HTMLOutputter $out = null, ?array $formOpts = null)
{
parent::__construct($out);
- $this->to = $to;
- $this->content = $content;
+ if (isset($formOpts['to'])) {
+ $this->to = $formOpts['to'];
+ }
+
+ $this->content = $formOpts['content'] ?? '';
}
/**
- * ID of the form
+ * ID of the form.
*
* @return string ID of the form
*/
- function id()
+ function id(): string
{
return 'form_notice-direct';
}
- /**
- * Class of the form
+ /**
+ * Class of the form.
*
* @return string class of the form
*/
- function formClass()
+ function formClass(): string
{
return 'form_notice ajax-notice';
}
/**
- * Action of the form
+ * Action of the form.
*
* @return string URL of the action
*/
- function action()
+ function action(): string
{
return common_local_url('newmessage');
}
/**
- * Legend of the Form
+ * Legend of the Form.
*
* @return void
*/
function formLegend()
{
// TRANS: Form legend for direct notice.
- $this->out->element('legend', null, _('Send a direct notice'));
+ $this->out->element('legend', null, _m('Send a direct notice'));
}
/**
- * Data elements
+ * Data elements.
*
* @return void
*/
@@ -119,87 +108,112 @@ class MessageForm extends Form
{
$user = common_current_user();
- $mutual_users = $user->mutuallySubscribedUsers();
+ $recipients = [];
+ $default = 'default';
- $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:');
+ $subs = $user->getSubscribed();
+ $n_subs = 0;
- while ($mutual_users->fetch()) {
- if ($mutual_users->id != $user->id) {
- $mutual[$mutual_users->id] = $mutual_users->nickname;
+ // Add local-subscriptions
+ while ($subs->fetch()) {
+ $n_subs++;
+ if ($subs->isLocal()) {
+ $value = 'profile:'.$subs->getID();
+ try {
+ $recipients[$value] = substr($subs->getAcctUri(), 5) . " [{$subs->getBestName()}]";
+ } catch (ProfileNoAcctUriException $e) {
+ $recipients[$value] = "[?@?] " . $e->profile->getBestName();
+ }
}
}
- $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.');
+ if (sizeof($recipients) < $n_subs) {
+ // some subscriptions aren't local and therefore weren't added,
+ // worth checking if others want to add them
+ Event::handle('FillDirectMessageRecipients', [$user, &$recipients]);
}
+ // if we came from a profile page, then lets make the message receiver visible
+ if (!is_null($this->to)) {
+ if (isset($recipients['profile:'.$this->to->getID()])) {
+ $default = 'profile' . $this->to->getID();
+ } else {
+ try {
+ if ($this->to->isLocal()) {
+ $this->content = "@{$this->to->getNickname()} {$this->content}";
+ } else {
+ $this->content = substr($this->to->getAcctUri(), 5) . " {$this->content}";
+ }
+ } catch (ProfileNoAcctUriException $e) {
+ // well, I'm no magician
+ }
+ }
+ }
+
+ if ($default === 'default') {
+ // 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.
+ $recipients[$default] = empty($recipients) ? _m('No subscriptions') : _m('Select recipient:');
+ }
+
+ asort($recipients);
+
// TRANS: Dropdown label in direct notice form.
- $this->out->dropdown('to', _('To'), $mutual, null, false,
- ($this->to) ? $this->to->id : null);
+ $this->out->dropdown('to-box',
+ _m('To'),
+ $recipients,
+ null,
+ false,
+ $default);
- $this->out->element('textarea', array('class' => 'notice_data-text',
- 'cols' => 35,
- 'rows' => 4,
- 'name' => 'content'),
- ($this->content) ? $this->content : '');
+ $this->out->element('textarea',
+ ['class' => 'notice_data-text',
+ 'cols' => 35,
+ 'rows' => 4,
+ 'name' => 'content'],
+ $this->content);
- $contentLimit = Message::maxContent();
+ $contentLimit = MessageModel::maxContent();
if ($contentLimit > 0) {
$this->out->element('span',
- array('class' => 'count'),
+ ['class' => 'count'],
$contentLimit);
}
}
/**
- * Action elements
+ * 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')));
+ $this->out->element('input',
+ ['id' => 'notice_action-submit',
+ 'class' => 'submit',
+ 'name' => 'message_send',
+ 'type' => 'submit',
+ // TRANS: Button text for sending a direct notice.
+ 'value' => _m('Send button for direct notice', 'Send')]);
}
-
/**
- * Show the form
- *
- * Uses a recipe to output the form.
+ * Show 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'
- )
- );
+ $this->elementStart('div',
+ ['id' => 'input_form_direct',
+ 'class' => 'input_form current nonav']);
parent::show();
$this->elementEnd('div');
$this->elementEnd('div');
-
}
}
diff --git a/plugins/DirectMessage/lib/messagelistitem.php b/plugins/DirectMessage/lib/messagelistitem.php
index c9f4c6042b..679abc2602 100644
--- a/plugins/DirectMessage/lib/messagelistitem.php
+++ b/plugins/DirectMessage/lib/messagelistitem.php
@@ -1,112 +1,102 @@
.
+
/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
+ * GNUsocial implementation of Direct Messages
*
- * 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 .
- *
- * @category Widget
- * @package StatusNet
- * @author Evan Prodromou
- * @copyright 2011 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-if (!defined('STATUSNET')) {
- // This check helps protect against security problems;
- // your code file can't be executed directly from the web.
- exit(1);
-}
+defined('GNUSOCIAL') || die();
/**
* A single item in a message list
*
- * @category Widget
- * @package StatusNet
+ * @category Plugin
+ * @package GNUsocial
* @author Evan Prodromou
- * @copyright 2011 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link http://status.net/
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class MessageListItem extends Widget
{
- var $message;
+ protected $message;
/**
- * Constructor
+ * Constructor.
*
* @param HTMLOutputter $out Output context
- * @param Message $message Message to show
+ * @param Notice $message Message to show
*/
- function __construct($out, $message)
+ function __construct(HTMLOutputter $out, $message)
{
parent::__construct($out);
$this->message = $message;
}
/**
- * Show the widget
+ * Show the widget.
*
* @return void
*/
function show()
{
- $this->out->elementStart('li', array('class' => 'h-entry notice',
- 'id' => 'message-' . $this->message->id));
-
$profile = $this->getMessageProfile();
+ if (is_null($profile)) {
+ // null most probably because there are no attention profiles and
+ // the UI below isn't ready for that, yet.
+ return;
+ }
- $this->out->elementStart('a', array('href' => $profile->profileurl,
- 'class' => 'p-author'));
+ $this->out->elementStart('li', ['class' => 'h-entry notice',
+ 'id' => 'message-' . $this->message->getID()]);
+
+ $this->out->elementStart('a', ['href' => $profile->getUrl(),
+ '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->element('img', ['src' => $avatarUrl,
+ 'class' => 'avatar u-photo',
+ 'width' => AVATAR_STREAM_SIZE,
+ 'height' => AVATAR_STREAM_SIZE,
+ 'alt' => $profile->getBestName()]);
+ $this->out->element('span', ['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->elementStart('div', ['class' => 'e-content']);
+ $this->out->raw($this->message->getRendered());
$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;
- }
+ ['message' => $this->message->getID()]);
$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->elementStart('a', ['rel' => 'bookmark',
+ 'class' => 'timestamp',
+ 'href' => $messageurl]);
+ $dt = common_date_iso8601($this->message->getCreated());
+ $this->out->element('time',
+ ['class' => 'dt-published',
+ 'datetime' => common_date_iso8601($this->message->getCreated()),
+ // TRANS: Timestamp title (tooltip text) for NoticeListItem
+ 'title' => common_exact_date($this->message->getCreated())],
+ common_date_string($this->message->getCreated()));
$this->out->elementEnd('a');
if ($this->message->source) {
@@ -132,7 +122,7 @@ abstract class MessageListItem extends Widget
{
// A dummy array with messages. These will get extracted by xgettext and
// are used in self::showSource().
- $dummy_messages = array(
+ $dummy_messages = [
// TRANS: A possible notice source (web interface).
_m('SOURCE','web'),
// TRANS: A possible notice source (XMPP).
@@ -142,12 +132,12 @@ abstract class MessageListItem extends Widget
// TRANS: A possible notice source (OpenMicroBlogging).
_m('SOURCE','omb'),
// TRANS: A possible notice source (Application Programming Interface).
- _m('SOURCE','api'),
- );
+ _m('SOURCE','api')
+ ];
}
/**
- * Show the source of the message
+ * 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.
@@ -156,7 +146,7 @@ abstract class MessageListItem extends Widget
*
* @return void
*/
- function showSource($source)
+ function showSource(string $source)
{
$source_name = _m('SOURCE',$source);
switch ($source) {
@@ -171,8 +161,9 @@ abstract class MessageListItem extends Widget
$ns = Notice_source::getKV($source);
if ($ns) {
$this->out->elementStart('span', 'device');
- $this->out->element('a', array('href' => $ns->url,
- 'rel' => 'external'),
+ $this->out->element('a',
+ ['href' => $ns->url,
+ 'rel' => 'external'],
$ns->name);
$this->out->elementEnd('span');
} else {
@@ -184,11 +175,11 @@ abstract class MessageListItem extends Widget
}
/**
- * Return the profile to show in the message item
- *
- * Overridden in sub-classes to show sender, receiver, or whatever
+ * 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();
+ abstract function getMessageProfile(): ?Profile;
}
diff --git a/plugins/DirectMessage/lib/models/MessageModel.php b/plugins/DirectMessage/lib/models/MessageModel.php
new file mode 100644
index 0000000000..716ecc730f
--- /dev/null
+++ b/plugins/DirectMessage/lib/models/MessageModel.php
@@ -0,0 +1,148 @@
+.
+
+/**
+ * GNUsocial implementation of Direct Messages
+ *
+ * @package GNUsocial
+ * @author Mikael Nordfeldth
+ * @author Bruno Casteleiro
+ * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+
+defined('GNUSOCIAL') || die();
+
+/**
+ * Model for a direct message
+ *
+ * @category Plugin
+ * @package GNUsocial
+ * @author Bruno Casteleiro
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class MessageModel
+{
+ /**
+ * Retrieve size-limit for messages content
+ *
+ * @return int size-limit
+ */
+ public static function maxContent(): int
+ {
+ $desclimit = common_config('message', 'contentlimit');
+ // null => use global limit (distinct from 0!)
+ if (is_null($desclimit) || !is_int($desclimit)) {
+ $desclimit = common_config('site', 'textlimit');
+ }
+ return $desclimit;
+ }
+
+ /**
+ * Is message-text too long?
+ *
+ * @param string $content message-text
+ * @return bool true if too long, false otherwise
+ */
+ public static function contentTooLong(string $content): bool
+ {
+ $contentlimit = self::maxContent();
+ return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
+ }
+
+ /**
+ * Return data object of messages received by some user.
+ *
+ * @param User $to receiver
+ * @param int|null $page page limiter
+ * @return Notice data object with stream for messages
+ */
+ public static function inboxMessages(User $to, ?int $page = null)
+ {
+ $attention = new Attention();
+ $attention->selectAdd('notice_id');
+ $attention->whereAdd('profile_id = ' . $to->getID());
+
+ $ids = $attention->find() ? $attention->fetchAll('notice_id') : [];
+
+ $reply = new Reply();
+ $reply->selectAdd('notice_id');
+ $reply->whereAdd('profile_id = ' . $to->getID());
+
+ if ($reply->find()) {
+ $ids = array_unique(
+ array_merge($ids, $reply->fetchAll('notice_id'))
+ );
+ } else if (empty($ids)) {
+ return null;
+ }
+
+ $message = new Notice();
+
+ $message->whereAdd('scope = ' . NOTICE::MESSAGE_SCOPE);
+ $message->whereAddIn('id', $ids, 'int');
+ $message->orderBy('created DESC, id DESC');
+
+ if (!is_null($page) && $page >= 0) {
+ $page = ($page == 0) ? 1 : $page;
+ $message->limit(($page - 1) * MESSAGES_PER_PAGE,
+ MESSAGES_PER_PAGE + 1);
+ }
+
+ return $message->find() ? $message : null;
+ }
+
+ /**
+ * Return data object of messages sent by some user.
+ *
+ * @param User $from sender
+ * @param int|null $page page limiter
+ * @return Notice data object with stream for messages
+ */
+ public static function outboxMessages(User $from, ?int $page = null)
+ {
+ $message = new Notice();
+
+ $message->profile_id = $from->getID();
+ $message->whereAdd('scope = ' . NOTICE::MESSAGE_SCOPE);
+ $message->orderBy('created DESC, id DESC');
+
+ if (!is_null($page) && $page >= 0) {
+ $page = ($page == 0) ? 1 : $page;
+ $message->limit(($page - 1) * MESSAGES_PER_PAGE,
+ MESSAGES_PER_PAGE + 1);
+ }
+
+ return $message->find() ? $message : null;
+ }
+
+ /**
+ * Save a new message.
+ *
+ * @param Profile $from sender
+ * @param string $content message-text
+ * @param string $source message's source
+ * @return Notice stored message
+ */
+ public static function saveNew(Profile $from, string $content, string $source = 'web'): Notice
+ {
+ return Notice::saveNew($from->getID(),
+ $content,
+ $source,
+ ['distribute' => false, // using events to handle remote distribution
+ 'scope' => NOTICE::MESSAGE_SCOPE]);
+ }
+}
diff --git a/plugins/DirectMessage/locale/en_GB/LC_MESSAGES/DirectMessage.po b/plugins/DirectMessage/locale/en_GB/LC_MESSAGES/DirectMessage.po
index 7cd2025bac..2cbcec9157 100644
--- a/plugins/DirectMessage/locale/en_GB/LC_MESSAGES/DirectMessage.po
+++ b/plugins/DirectMessage/locale/en_GB/LC_MESSAGES/DirectMessage.po
@@ -32,8 +32,8 @@ msgstr "Message"
#. TRANS: Plugin description.
#: DirectMessagePlugin.php:168
-msgid "Direct Message to other local users (broken out of core)."
-msgstr "Direct Message to other local users (broken out of core)."
+msgid "Direct Message to other local users."
+msgstr "Direct Message to other local users."
#. TRANS: Form validation error displayed when message content is too long.
#. TRANS: %d is the maximum number of characters for a message.
@@ -68,7 +68,7 @@ msgstr[1] "Message too long - maximum is %1$d characters, you sent %2$d."
#. TRANS: Button text for sending a direct notice.
#: lib/messageform.php:175
-msgctxt "Send button for sending notice"
+msgctxt "Send button for direct notice"
msgid "Send"
msgstr "Send"