Merge branch 'MDL-63915_master' of git://github.com/markn86/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 27 Feb 2019 04:08:09 +0000 (12:08 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 27 Feb 2019 04:08:09 +0000 (12:08 +0800)
84 files changed:
lang/en/message.php
lib/outputrenderers.php
message/amd/build/message_area.min.js [deleted file]
message/amd/build/message_area_actions.min.js [deleted file]
message/amd/build/message_area_contacts.min.js [deleted file]
message/amd/build/message_area_events.min.js [deleted file]
message/amd/build/message_area_messages.min.js [deleted file]
message/amd/build/message_area_profile.min.js [deleted file]
message/amd/build/message_area_search.min.js [deleted file]
message/amd/build/message_area_tabs.min.js [deleted file]
message/amd/build/message_drawer.min.js
message/amd/build/message_drawer_router.min.js
message/amd/build/message_drawer_view_contact.min.js
message/amd/build/message_drawer_view_contacts.min.js
message/amd/build/message_drawer_view_contacts_section_contacts.min.js
message/amd/build/message_drawer_view_conversation.min.js
message/amd/build/message_drawer_view_group_info.min.js
message/amd/build/message_drawer_view_overview.min.js
message/amd/build/message_drawer_view_overview_section.min.js
message/amd/build/message_drawer_view_search.min.js
message/amd/build/message_drawer_view_settings.min.js
message/amd/build/message_preferences.min.js [deleted file]
message/amd/build/message_user_button.min.js [new file with mode: 0644]
message/amd/src/message_area.js [deleted file]
message/amd/src/message_area_actions.js [deleted file]
message/amd/src/message_area_contacts.js [deleted file]
message/amd/src/message_area_events.js [deleted file]
message/amd/src/message_area_messages.js [deleted file]
message/amd/src/message_area_profile.js [deleted file]
message/amd/src/message_area_search.js [deleted file]
message/amd/src/message_area_tabs.js [deleted file]
message/amd/src/message_drawer.js
message/amd/src/message_drawer_router.js
message/amd/src/message_drawer_view_contact.js
message/amd/src/message_drawer_view_contacts.js
message/amd/src/message_drawer_view_contacts_section_contacts.js
message/amd/src/message_drawer_view_conversation.js
message/amd/src/message_drawer_view_group_info.js
message/amd/src/message_drawer_view_overview.js
message/amd/src/message_drawer_view_overview_section.js
message/amd/src/message_drawer_view_search.js
message/amd/src/message_drawer_view_settings.js
message/amd/src/message_preferences.js [deleted file]
message/amd/src/message_user_button.js [new file with mode: 0644]
message/classes/api.php
message/classes/helper.php
message/classes/output/messagearea/contact.php
message/classes/output/messagearea/contacts.php
message/classes/output/messagearea/message.php
message/classes/output/messagearea/message_area.php
message/classes/output/messagearea/message_search_results.php [deleted file]
message/classes/output/messagearea/messages.php
message/classes/output/messagearea/profile.php
message/classes/output/messagearea/user_search_results.php
message/classes/output/renderer.php [deleted file]
message/externallib.php
message/index.php
message/lib.php
message/templates/message_area.mustache [deleted file]
message/templates/message_area_contact.mustache [deleted file]
message/templates/message_area_contacts_area.mustache [deleted file]
message/templates/message_area_message.mustache [deleted file]
message/templates/message_area_message_search_results.mustache [deleted file]
message/templates/message_area_messages.mustache [deleted file]
message/templates/message_area_messages_area.mustache [deleted file]
message/templates/message_area_profile.mustache [deleted file]
message/templates/message_area_response.mustache [deleted file]
message/templates/message_area_user_search_results.mustache [deleted file]
message/templates/message_drawer.mustache
message/templates/message_drawer_view_overview_body.mustache
message/templates/message_drawer_view_overview_footer.mustache [moved from message/templates/message_area_contacts.mustache with 56% similarity]
message/templates/message_drawer_view_overview_section.mustache
message/templates/message_index.mustache [new file with mode: 0644]
message/templates/message_preferences.mustache [deleted file]
message/templates/message_preferences_component.mustache [deleted file]
message/templates/message_preferences_notification_processor.mustache [deleted file]
message/upgrade.txt
theme/boost/scss/moodle/message.scss
theme/boost/style/moodle.css
theme/bootstrapbase/less/moodle/message.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/templates/core_message/message_drawer.mustache
theme/bootstrapbase/templates/core_message/message_drawer_view_overview_section.mustache
theme/bootstrapbase/templates/core_message/message_index.mustache [new file with mode: 0644]

index e47c095..38c9faa 100644 (file)
@@ -242,7 +242,6 @@ $string['userisblockingyou'] = 'This user has blocked you from sending messages
 $string['userisblockingyounoncontact'] = '{$a} only accepts messages from their contacts.';
 $string['userwouldliketocontactyou'] = '{$a} would like to contact you';
 $string['viewfullnotification'] = 'View full notification';
-$string['viewinganotherusersmessagearea'] = 'You are viewing another user\'s message area.';
 $string['viewmessageswith'] = 'View messages with {$a}';
 $string['viewnotificationresource'] = 'Go to: {$a}';
 $string['viewunreadmessageswith'] = 'View unread messages with {$a}';
index c4e5da5..17f0d7e 100644 (file)
@@ -4098,7 +4098,7 @@ EOD;
                             'title' => get_string('message', 'message'),
                             'url' => new moodle_url('/message/index.php', array('id' => $user->id)),
                             'image' => 'message',
-                            'linkattributes' => array('role' => 'button'),
+                            'linkattributes' => \core_message\helper::messageuser_link_params($user->id),
                             'page' => $this->page
                         ),
                         'togglecontact' => array(
@@ -4183,6 +4183,9 @@ EOD;
                     if ($button['buttontype'] === 'togglecontact') {
                         \core_message\helper::togglecontact_requirejs();
                     }
+                    if ($button['buttontype'] === 'message') {
+                        \core_message\helper::messageuser_requirejs();
+                    }
                     $image = $this->pix_icon($button['formattedimage'], $button['title'], 'moodle', array(
                         'class' => 'iconsmall',
                         'role' => 'presentation'
diff --git a/message/amd/build/message_area.min.js b/message/amd/build/message_area.min.js
deleted file mode 100644 (file)
index 288f2df..0000000
Binary files a/message/amd/build/message_area.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_actions.min.js b/message/amd/build/message_area_actions.min.js
deleted file mode 100644 (file)
index 2a0fd28..0000000
Binary files a/message/amd/build/message_area_actions.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_contacts.min.js b/message/amd/build/message_area_contacts.min.js
deleted file mode 100644 (file)
index d5078a2..0000000
Binary files a/message/amd/build/message_area_contacts.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_events.min.js b/message/amd/build/message_area_events.min.js
deleted file mode 100644 (file)
index a94af1d..0000000
Binary files a/message/amd/build/message_area_events.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_messages.min.js b/message/amd/build/message_area_messages.min.js
deleted file mode 100644 (file)
index fb627be..0000000
Binary files a/message/amd/build/message_area_messages.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_profile.min.js b/message/amd/build/message_area_profile.min.js
deleted file mode 100644 (file)
index c99cadb..0000000
Binary files a/message/amd/build/message_area_profile.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_search.min.js b/message/amd/build/message_area_search.min.js
deleted file mode 100644 (file)
index fcaaef7..0000000
Binary files a/message/amd/build/message_area_search.min.js and /dev/null differ
diff --git a/message/amd/build/message_area_tabs.min.js b/message/amd/build/message_area_tabs.min.js
deleted file mode 100644 (file)
index 9c3daf3..0000000
Binary files a/message/amd/build/message_area_tabs.min.js and /dev/null differ
index 3f57164..453da2f 100644 (file)
Binary files a/message/amd/build/message_drawer.min.js and b/message/amd/build/message_drawer.min.js differ
index c65abdd..ef9875e 100644 (file)
Binary files a/message/amd/build/message_drawer_router.min.js and b/message/amd/build/message_drawer_router.min.js differ
index bb22a09..542d416 100644 (file)
Binary files a/message/amd/build/message_drawer_view_contact.min.js and b/message/amd/build/message_drawer_view_contact.min.js differ
index 8dddba3..6d0d66e 100644 (file)
Binary files a/message/amd/build/message_drawer_view_contacts.min.js and b/message/amd/build/message_drawer_view_contacts.min.js differ
index 7035c6b..679b900 100644 (file)
Binary files a/message/amd/build/message_drawer_view_contacts_section_contacts.min.js and b/message/amd/build/message_drawer_view_contacts_section_contacts.min.js differ
index f6bd73d..8399306 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation.min.js and b/message/amd/build/message_drawer_view_conversation.min.js differ
index 7493b92..3669b74 100644 (file)
Binary files a/message/amd/build/message_drawer_view_group_info.min.js and b/message/amd/build/message_drawer_view_group_info.min.js differ
index acaa285..7ce1ee6 100644 (file)
Binary files a/message/amd/build/message_drawer_view_overview.min.js and b/message/amd/build/message_drawer_view_overview.min.js differ
index 0535a9c..0154211 100644 (file)
Binary files a/message/amd/build/message_drawer_view_overview_section.min.js and b/message/amd/build/message_drawer_view_overview_section.min.js differ
index 3b3d047..2f3881e 100644 (file)
Binary files a/message/amd/build/message_drawer_view_search.min.js and b/message/amd/build/message_drawer_view_search.min.js differ
index 5fbcdb9..063d2f6 100644 (file)
Binary files a/message/amd/build/message_drawer_view_settings.min.js and b/message/amd/build/message_drawer_view_settings.min.js differ
diff --git a/message/amd/build/message_preferences.min.js b/message/amd/build/message_preferences.min.js
deleted file mode 100644 (file)
index 54bf917..0000000
Binary files a/message/amd/build/message_preferences.min.js and /dev/null differ
diff --git a/message/amd/build/message_user_button.min.js b/message/amd/build/message_user_button.min.js
new file mode 100644 (file)
index 0000000..d422b14
Binary files /dev/null and b/message/amd/build/message_user_button.min.js differ
diff --git a/message/amd/src/message_area.js b/message/amd/src/message_area.js
deleted file mode 100644 (file)
index b9482aa..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module instantiates the functionality of the messaging area.
- *
- * @module     core_message/message_area
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core_message/message_area_contacts', 'core_message/message_area_messages',
-        'core_message/message_area_profile', 'core_message/message_area_tabs', 'core_message/message_area_search'],
-    function($, Contacts, Messages, Profile, Tabs, Search) {
-
-        /**
-         * Messagearea class.
-         *
-         * @param {String} selector The selector for the page region containing the message area.
-         * @param {int} pollmin
-         * @param {int} pollmax
-         * @param {int} polltimeout
-         */
-        function Messagearea(selector, pollmin, pollmax, polltimeout) {
-            this.node = $(selector);
-            this.pollmin = pollmin;
-            this.pollmax = pollmax;
-            this.polltimeout = polltimeout;
-            this._init();
-        }
-
-        /** @type {jQuery} The jQuery node for the page region containing the message area. */
-        Messagearea.prototype.node = null;
-
-        /** @type {int} The minimum time to poll for messages. */
-        Messagearea.prototype.pollmin = null;
-
-        /** @type {int} The maximum time to poll for messages. */
-        Messagearea.prototype.pollmax = null;
-
-        /** @type {int} The time used once we have reached the maximum polling time. */
-        Messagearea.prototype.polltimeout = null;
-
-        /**
-         * Initialise the other objects we require.
-         */
-        Messagearea.prototype._init = function() {
-            new Contacts(this);
-            new Messages(this);
-            new Profile(this);
-            new Tabs(this);
-            new Search(this);
-        };
-
-        /**
-         * Handles adding a delegate event to the messaging area node.
-         *
-         * @param {String} action The action we are listening for
-         * @param {String} selector The selector for the page we are assigning the action to
-         * @param {Function} callable The function to call when the event happens
-         */
-        Messagearea.prototype.onDelegateEvent = function(action, selector, callable) {
-            this.node.on(action, selector, callable);
-        };
-
-        /**
-         * Handles adding a custom event to the messaging area node.
-         *
-         * @param {String} action The action we are listening for
-         * @param {Function} callable The function to call when the event happens
-         */
-        Messagearea.prototype.onCustomEvent = function(action, callable) {
-            this.node.on(action, callable);
-        };
-
-        /**
-         * Handles triggering an event on the messaging area node.
-         *
-         * @param {String} event The selector for the page region containing the message area
-         * @param {Object=} data The data to pass when we trigger the event
-         */
-        Messagearea.prototype.trigger = function(event, data) {
-            if (typeof data == 'undefined') {
-                data = '';
-            }
-            this.node.trigger(event, data);
-        };
-
-        /**
-         * Handles finding a node in the messaging area.
-         *
-         * @param {String} selector The selector for the node we are looking for
-         * @return {jQuery} The node
-         */
-        Messagearea.prototype.find = function(selector) {
-            return this.node.find(selector);
-        };
-
-        /**
-         * Returns the ID of the user whose message area we are viewing.
-         *
-         * @return {int} The user id
-         */
-        Messagearea.prototype.getCurrentUserId = function() {
-            return this.node.data('userid');
-        };
-
-        /**
-         * Function to determine if we should be showing contacts initially or messages.
-         *
-         * @return {boolean} True to show contacts first, otherwise show messages.
-         */
-        Messagearea.prototype.showContactsFirst = function() {
-            return !!this.node.data('displaycontacts');
-        };
-
-        return Messagearea;
-    }
-);
diff --git a/message/amd/src/message_area_actions.js b/message/amd/src/message_area_actions.js
deleted file mode 100644 (file)
index 9504183..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * The module handles any actions we perform on the message area.
- *
- * @module     core_message/message_area_actions
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['core_message/message_area_events'], function(Events) {
-
-    /** @type {Object} The list of selectors for the message area. */
-    var SELECTORS = {
-        MESSAGES: "[data-region='messages']"
-    };
-
-    /**
-     * Actions class.
-     *
-     * @param {Messagearea} messageArea The messaging area object.
-     */
-    function Actions(messageArea) {
-        this.messageArea = messageArea;
-    }
-
-    /** @type {Messagearea} The messaging area object. */
-    Actions.prototype.messageArea = null;
-
-    /**
-     * Handles when we have selected to delete messages.
-     */
-    Actions.prototype.chooseMessagesToDelete = function() {
-        // Only fire the event if we are viewing messages.
-        if (this.messageArea.find(SELECTORS.MESSAGES).length !== 0) {
-            this.messageArea.trigger(Events.CHOOSEMESSAGESTODELETE);
-        }
-    };
-
-    return Actions;
-});
\ No newline at end of file
diff --git a/message/amd/src/message_area_contacts.js b/message/amd/src/message_area_contacts.js
deleted file mode 100644 (file)
index c2eb102..0000000
+++ /dev/null
@@ -1,711 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module handles the contacts area of the messaging area.
- *
- * @module     core_message/message_area_contacts
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/custom_interaction_events', 'core/str',
-        'core_message/message_area_events'],
-    function($, Ajax, Templates, Notification, CustomEvents, Str, Events) {
-
-        /** @type {Object} The list of selectors for the message area. */
-        var SELECTORS = {
-            CONTACT: "[data-region='contact']",
-            CONTACTICONBLOCKED: "[data-region='contact-icon-blocked']",
-            CONTACTS: "[data-region='contacts'][data-region-content='contacts']",
-            CONTACTSAREA: "[data-region='contacts-area']",
-            CONVERSATIONS: "[data-region='contacts'][data-region-content='conversations']",
-            COURSE: "[data-region='course']",
-            LASTMESSAGETEXT: "[data-region='last-message-text']",
-            LASTMESSAGEUSER: "[data-region='last-message-user']",
-            LOADINGICON: '.loading-icon',
-            MESSAGETEXT: "[data-region='message-text']",
-            MESSAGINGAREA: "[data-region='messaging-area']",
-            NOCONTACTS: "[data-region=no-contacts]",
-            SEARCHBOX: "[data-region='search-box']",
-            SEARCHRESULTSAREA: "[data-region='search-results-area']",
-            SEARCHTEXTAREA: "[data-region='search-text-area']",
-            SELECTEDVIEWCONVERSATION: "[data-action='view-contact-msg'].selected",
-            SELECTEDVIEWPROFILE: "[data-action='view-contact-profile'].selected",
-            SHOWMESSAGES: "[data-action='show-messages']",
-            VIEWCONVERSATION: "[data-action='view-contact-msg']",
-            VIEWPROFILE: "[data-action='view-contact-profile']"
-        };
-
-        /**
-         * Contacts class.
-         *
-         * @param {Messagearea} messageArea The messaging area object.
-         */
-        function Contacts(messageArea) {
-            this.messageArea = messageArea;
-            this._init();
-        }
-
-        /** @type {Boolean} checks if we are currently loading conversations */
-        Contacts.prototype._isLoadingConversations = false;
-
-        /** @type {Boolean} checks if we are currently loading contacts */
-        Contacts.prototype._isLoadingContacts = false;
-
-        /** @type {int} the number of contacts displayed */
-        Contacts.prototype._numContactsDisplayed = 0;
-
-        /** @type {int} the number of contacts to retrieve */
-        Contacts.prototype._numContactsToRetrieve = 20;
-
-        /** @type {int} the number of conversations displayed */
-        Contacts.prototype._numConversationsDisplayed = 0;
-
-        /** @type {int} the number of conversations to retrieve */
-        Contacts.prototype._numConversationsToRetrieve = 20;
-
-        /** @type {int} the number of chars of the message to show */
-        Contacts.prototype._messageLength = 60;
-
-        /** @type {Messagearea} The messaging area object. */
-        Contacts.prototype.messageArea = null;
-
-        /**
-         * Initialise the event listeners.
-         *
-         * @private
-         */
-        Contacts.prototype._init = function() {
-            CustomEvents.define(this.messageArea.node, [
-                CustomEvents.events.activate,
-                CustomEvents.events.down,
-                CustomEvents.events.up,
-            ]);
-
-            this.messageArea.onCustomEvent(Events.MESSAGESEARCHCANCELED, this._viewConversations.bind(this));
-            this.messageArea.onCustomEvent(Events.USERSSEARCHCANCELED, this._viewContacts.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTSSELECTED, this._viewContacts.bind(this));
-            this.messageArea.onCustomEvent(Events.CONVERSATIONDELETED, this._deleteConversation.bind(this));
-            this.messageArea.onCustomEvent(Events.CONVERSATIONSSELECTED, this._viewConversations.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTSSELECTED, this._viewContacts.bind(this));
-            this.messageArea.onCustomEvent(Events.MESSAGESDELETED, this._updateLastMessage.bind(this));
-            this.messageArea.onCustomEvent(Events.MESSAGESENT, this._handleMessageSent.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTREMOVED, function(e, userid) {
-                this._removeContact(SELECTORS.CONTACTS, userid);
-            }.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTADDED, function(e, userid) {
-                this._addContact(userid);
-            }.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTBLOCKED, function(e, userid) {
-                this._blockContact(userid);
-            }.bind(this));
-            this.messageArea.onCustomEvent(Events.CONTACTUNBLOCKED, function(e, userid) {
-                this._unblockContact(userid);
-            }.bind(this));
-            this.messageArea.onCustomEvent(Events.CHOOSEMESSAGESTODELETE,
-                this._startDeleting.bind(this));
-            this.messageArea.onCustomEvent(Events.CANCELDELETEMESSAGES,
-                this._stopDeleting.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWCONVERSATION,
-                this._viewConversation.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWPROFILE,
-                this._viewContact.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SHOWMESSAGES,
-                this._showMessagingArea.bind(this));
-
-            this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.CONTACT,
-                this._selectPreviousContact.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.CONTACT,
-                this._selectNextContact.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.VIEWCONVERSATION,
-                this._selectPreviousConversation.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.VIEWCONVERSATION,
-                this._selectNextConversation.bind(this));
-
-            this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.COURSE, this._selectPreviousCourse.bind());
-            this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.COURSE, this._selectNextCourse.bind());
-
-            this.messageArea.onDelegateEvent('focus', SELECTORS.SEARCHBOX, this._setSearching.bind(this));
-            this.messageArea.onDelegateEvent('blur', SELECTORS.SEARCHBOX, this._clearSearching.bind(this));
-
-            // Now enable the ability to infinitely scroll through conversations and contacts.
-            CustomEvents.define(this.messageArea.find(SELECTORS.CONVERSATIONS), [
-                CustomEvents.events.scrollBottom
-            ]);
-            CustomEvents.define(this.messageArea.find(SELECTORS.CONTACTS), [
-                CustomEvents.events.scrollBottom
-            ]);
-            this.messageArea.onDelegateEvent(CustomEvents.events.scrollBottom, SELECTORS.CONVERSATIONS,
-                this._loadConversations.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.scrollBottom, SELECTORS.CONTACTS,
-                this._loadContacts.bind(this));
-
-            if (!this.messageArea.showContactsFirst()) {
-                // Set the initial number of conversations to retrieve. Otherwise it will display no conversations.
-                this._numConversationsDisplayed = 20;
-            }
-        };
-
-        /**
-         * Turn on deleting.
-         *
-         * @private
-         */
-        Contacts.prototype._startDeleting = function() {
-            this.messageArea.find(SELECTORS.CONTACTSAREA).addClass('editing');
-        };
-
-        /**
-         * Turn off deleting.
-         *
-         * @private
-         */
-        Contacts.prototype._stopDeleting = function() {
-            this.messageArea.find(SELECTORS.CONTACTSAREA).removeClass('editing');
-        };
-
-        /**
-         * Handles viewing the list of conversations.
-         *
-         * @private
-         */
-        Contacts.prototype._viewConversations = function() {
-            // If conversations is empty then try load some.
-            if (this._numConversationsDisplayed === 0) {
-                this._loadConversations();
-            }
-
-            this.messageArea.find(SELECTORS.CONTACTS).hide();
-            this.messageArea.find(SELECTORS.CONVERSATIONS).show();
-        };
-
-        /**
-         * Handles viewing the list of contacts.
-         *
-         * @private
-         */
-        Contacts.prototype._viewContacts = function() {
-            // If contacts is empty then try load some.
-            if (this._numContactsDisplayed === 0) {
-                this._loadContacts();
-            }
-
-            this.messageArea.find(SELECTORS.CONVERSATIONS).hide();
-            this.messageArea.find(SELECTORS.CONTACTS).show();
-        };
-
-        /**
-         * Handles when a message is sent.
-         *
-         * @param {Event} event The message sent event
-         * @param {int} userid The id of the user who the message was sent to
-         * @param {String} text The message text
-         * @private
-         */
-        Contacts.prototype._handleMessageSent = function(event, userid, text) {
-            // Switch to viewing the conversations.
-            this._viewConversations();
-            // Get the user node.
-            var user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
-            // If the user has not been loaded yet, let's copy the element from contact or search panel to the conversation panel.
-            if (user.length === 0) {
-                // Let's clone the data on the contact page.
-                var usercontact = this._getUserNode(SELECTORS.CONTACTS, userid);
-                if (usercontact.length === 0) {
-                    // No luck, maybe we sent the message to a user we searched for - check search page.
-                    usercontact = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
-                }
-                if (usercontact.length == 0) {
-                    // Can't do much.
-                    return;
-                }
-                user = usercontact.clone();
-                // Change the data action attribute.
-                user.attr('data-action', 'view-contact-msg');
-                // Remove the 'no conversations' message.
-                this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
-                    SELECTORS.NOCONTACTS).remove();
-                // Increment the number of conversations displayed.
-                this._numConversationsDisplayed++;
-            }
-            // Move the contact to the top of the list.
-            user.prependTo(this.messageArea.find(SELECTORS.CONVERSATIONS));
-            // Scroll to the top.
-            this.messageArea.find(SELECTORS.CONVERSATIONS).scrollTop(0);
-            // Get the new text to show.
-            this._updateContactText(user, text, true);
-            // Ensure user is selected.
-            this._setSelectedUser("[data-userid='" + userid + "']");
-        };
-
-        /**
-         * Handles loading conversations.
-         *
-         * @return {Promise|boolean} The promise resolved when the contact area has been rendered,
-         * @private
-         */
-        Contacts.prototype._loadConversations = function() {
-            if (this._isLoadingConversations) {
-                return false;
-            }
-
-            // Tell the user we are loading items.
-            this._isLoadingConversations = true;
-
-            // Keep track of the number of contacts
-            var numberreceived = 0;
-            // Add loading icon to the end of the list.
-            return Templates.render('core/loading', {}).then(function(html, js) {
-                if (this._numConversationsDisplayed) {
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS),
-                        "<div style='text-align:center'>" + html + "</div>", js);
-                } else { // No conversations, just replace contents.
-                    Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS),
-                        "<div style='text-align:center'>" + html + "</div>", js);
-                }
-                return this._getItems('core_message_data_for_messagearea_conversations',
-                    this._numConversationsDisplayed, this._numConversationsToRetrieve);
-            }.bind(this)).then(function(data) {
-                numberreceived = data.contacts.length;
-                data.isconversation = true;
-                return Templates.render('core_message/message_area_contacts', data);
-            }).then(function(html, js) {
-                // Remove the loading icon.
-                this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
-                    SELECTORS.LOADINGICON).remove();
-                // Only append data if we got data back.
-                if (numberreceived > 0) {
-                    // Show the new content.
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS), html, js);
-                    // Increment the number of conversations displayed. We increment by the number of conversations we
-                    // asked to retrieve not by the number that was actually retrieved, see MDL-55870.
-                    this._numConversationsDisplayed += this._numConversationsToRetrieve;
-                } else if (!this._numConversationsDisplayed) {
-                    // If we didn't receive any contacts and there are currently none, then we want to show a message.
-                    Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS), html, js);
-                }
-                // Mark that we are no longer busy loading data.
-                this._isLoadingConversations = false;
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Handles loading contacts.
-         *
-         * @return {Promise|boolean} The promise resolved when the contact area has been rendered
-         * @private
-         */
-        Contacts.prototype._loadContacts = function() {
-            if (this._isLoadingContacts) {
-                return false;
-            }
-
-            // Tell the user we are loading items.
-            this._isLoadingContacts = true;
-
-            // Keep track of the number of contacts
-            var numberreceived = 0;
-            // Add loading icon to the end of the list.
-            return Templates.render('core/loading', {}).then(function(html, js) {
-                if (this._numContactsDisplayed) {
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONTACTS),
-                        "<div style='text-align:center'>" + html + "</div>", js);
-                } else { // No contacts, just replace contents.
-                    Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONTACTS),
-                        "<div style='text-align:center'>" + html + "</div>", js);
-                }
-                return this._getItems('core_message_data_for_messagearea_contacts',
-                    this._numContactsDisplayed, this._numContactsToRetrieve);
-            }.bind(this)).then(function(data) {
-                numberreceived = data.contacts.length;
-                data.isconversation = false;
-                return Templates.render('core_message/message_area_contacts', data);
-            }).then(function(html, js) {
-                // Remove the loading icon.
-                this.messageArea.find(SELECTORS.CONTACTS + " " +
-                    SELECTORS.LOADINGICON).remove();
-                // Only append data if we got data back.
-                if (numberreceived > 0) {
-                    // Show the new content.
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONTACTS), html, js);
-                    // Increment the number of contacts displayed.
-                    this._numContactsDisplayed += numberreceived;
-                } else if (!this._numContactsDisplayed) {
-                    // If we didn't receive any contacts and there are currently none, then we want to show a message.
-                    Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONTACTS), html, js);
-                }
-                // Mark that we are no longer busy loading data.
-                this._isLoadingContacts = false;
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Handles viewing a particular conversation.
-         *
-         * @param {Event} event
-         * @private
-         */
-        Contacts.prototype._viewConversation = function(event) {
-            // Cancel any deletion of messages we may have.
-            this.messageArea.trigger(Events.CANCELDELETEMESSAGES);
-
-            var userid = $(event.currentTarget).data('userid');
-            var messageid = $(event.currentTarget).data('messageid');
-            var selector = "[data-userid='" + userid + "']";
-            // If we have a specific message id then we did a search and the contact may appear in multiple
-            // places - we don't want to highlight them all.
-            if (messageid) {
-                selector = "[data-messageid='" + messageid + "']";
-            }
-
-            this._setSelectedUser(selector);
-            this.messageArea.trigger(Events.CONVERSATIONSELECTED, userid);
-            // Don't highlight the contact because the message region has changed.
-            this.messageArea.find(SELECTORS.SELECTEDVIEWPROFILE).removeClass('selected');
-            this._showMessagingArea();
-        };
-
-        /**
-         * Handles viewing a particular contact.
-         *
-         * @param {Event} event
-         * @private
-         */
-        Contacts.prototype._viewContact = function(event) {
-            // Cancel any deletion of messages we may have.
-            this.messageArea.trigger(Events.CANCELDELETEMESSAGES);
-
-            var userid = $(event.currentTarget).data('userid');
-            this._setSelectedUser("[data-userid='" + userid + "']");
-            this.messageArea.trigger(Events.CONTACTSELECTED, userid);
-            // Don't highlight the conversation because the message region has changed.
-            this.messageArea.find(SELECTORS.SELECTEDVIEWCONVERSATION).removeClass('selected');
-            this._showMessagingArea();
-        };
-
-        /**
-         * Handles returning a list of items to display.
-         *
-         * @param {String} webservice The web service to call
-         * @param {int} limitfrom
-         * @param {int} limitnum
-         * @return {Promise} The promise resolved when the contact area has been rendered
-         * @private
-         */
-        Contacts.prototype._getItems = function(webservice, limitfrom, limitnum) {
-            // Call the web service to return the data we want to view.
-            var promises = Ajax.call([{
-                methodname: webservice,
-                args: {
-                    userid: this.messageArea.getCurrentUserId(),
-                    limitfrom: limitfrom,
-                    limitnum: limitnum
-                }
-            }]);
-
-            return promises[0];
-        };
-
-        /**
-         * Handles deleting a conversation.
-         *
-         * @param {Event} event
-         * @param {int} userid The user id belonging to the messages we are deleting.
-         * @private
-         */
-        Contacts.prototype._deleteConversation = function(event, userid) {
-            // Remove the conversation.
-            this._removeContact(SELECTORS.CONVERSATIONS, userid);
-            this._numConversationsDisplayed--;
-            this._hideMessagingArea();
-            // Now we have done all the deletion we can set the flag back to false.
-            this._stopDeleting();
-        };
-
-        /**
-         * Handles updating the last message in the contact.
-         *
-         * @param {Event} event
-         * @param {int} userid The user id belonging to the messages we are deleting
-         * @param {jQuery|null} updatemessage The message we need to update the contact panel with
-         * @private
-         */
-        Contacts.prototype._updateLastMessage = function(event, userid, updatemessage) {
-            // Check if the last message needs updating.
-            if (updatemessage) {
-                var user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
-                var updatemessagetext = updatemessage.find(SELECTORS.MESSAGETEXT).text().trim();
-                var sentbyuser = false;
-                if (updatemessage.data('useridto') == userid) {
-                    // Must have been sent by the currently logged in user.
-                    sentbyuser = true;
-                }
-
-                this._updateContactText(user, updatemessagetext, sentbyuser);
-            }
-
-            // Now we have done all the deletion we can set the flag back to false.
-            this._stopDeleting();
-        };
-
-        /**
-         * Handles adding a contact to the list.
-         *
-         * @private
-         */
-        Contacts.prototype._addContact = function() {
-            this.messageArea.find(SELECTORS.CONTACTS).empty();
-            this._numContactsDisplayed = 0;
-            this._loadContacts();
-        };
-
-        /**
-         * Handles removing a contact from the list.
-         *
-         * @param {String} selector
-         * @param {int} userid
-         * @private
-         */
-        Contacts.prototype._removeContact = function(selector, userid) {
-            this._getUserNode(selector, userid).remove();
-            this._numContactsDisplayed--;
-        };
-
-        /**
-         * Handles marking a contact as blocked on the list.
-         *
-         * @param {int} userid
-         * @private
-         */
-        Contacts.prototype._blockContact = function(userid) {
-            var user = this._getUserNode(SELECTORS.CONTACTS, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
-
-            user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
-
-            user = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
-        };
-
-        /**
-         * Handles marking a contact as unblocked on the list.
-         *
-         * @param {int} userid
-         * @private
-         */
-        Contacts.prototype._unblockContact = function(userid) {
-            var user = this._getUserNode(SELECTORS.CONTACTS, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
-
-            user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
-
-            user = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
-            user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
-        };
-
-        /**
-         * Handles retrieving a user node from a list.
-         *
-         * @param {String} selector
-         * @param {int} userid
-         * @return {jQuery} The user node
-         * @private
-         */
-        Contacts.prototype._getUserNode = function(selector, userid) {
-            return this.messageArea.find(selector + " " + SELECTORS.CONTACT +
-                "[data-userid='" + userid + "']");
-        };
-
-        /**
-         * Handles selecting a contact in the list.
-         *
-         * @param {String} selector
-         * @private
-         */
-        Contacts.prototype._setSelectedUser = function(selector) {
-            // Remove the 'selected' class from any other contact.
-            this.messageArea.find(SELECTORS.CONTACT).removeClass('selected');
-            this.messageArea.find(SELECTORS.CONTACT).attr('aria-pressed', false);
-            // Set the tab for the user to selected.
-            this.messageArea.find(SELECTORS.CONTACT + selector).addClass('selected');
-            this.messageArea.find(SELECTORS.CONTACT + selector).attr('aria-pressed', true);
-        };
-
-        /**
-         * Converts a text message into the text that should be stored in the contact list
-         *
-         * @param {String} text
-         * @return {String} The altered text
-         */
-        Contacts.prototype._getContactText = function(text) {
-            if (text.length > this._messageLength) {
-                text = text.substr(0, this._messageLength - 3);
-                text += '...';
-            }
-
-            // Text node prevents script injection through HTML entities.
-            return document.createTextNode(text);
-        };
-
-        /**
-         * Handles updating the contact text.
-         *
-         * @param {jQuery} user The user to update
-         * @param {String} text The text to update the contact with
-         * @param {Boolean} sentbyuser Was it sent by the currently logged in user?
-         * @private
-         */
-        Contacts.prototype._updateContactText = function(user, text, sentbyuser) {
-            // Get the text we will display on the contact panel.
-            text = this._getContactText(text);
-            if (sentbyuser) {
-                Str.get_string('you', 'message').done(function(string) {
-                    // Ensure we display that the message is from this user.
-                    user.find(SELECTORS.LASTMESSAGEUSER).empty().append(string);
-                }).always(function() {
-                    user.find(SELECTORS.LASTMESSAGETEXT).empty().append(text);
-                });
-            } else {
-                user.find(SELECTORS.LASTMESSAGEUSER).empty();
-                user.find(SELECTORS.LASTMESSAGETEXT).empty().append(text);
-            }
-        };
-
-        /**
-         * Shifts focus to the next contact in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectNextContact = function(e, data) {
-            var contact = $(e.target).closest(SELECTORS.CONTACT);
-            var next = contact.next();
-            next.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Shifts focus to the previous contact in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectPreviousContact = function(e, data) {
-            var contact = $(e.target).closest(SELECTORS.CONTACT);
-            var previous = contact.prev();
-            previous.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Shifts focus to the next course in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectNextCourse = function(e, data) {
-            var course = $(e.target).closest(SELECTORS.COURSE);
-            course.next().focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Shifts focus to the previous course in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectPreviousCourse = function(e, data) {
-            var course = $(e.target).closest(SELECTORS.COURSE);
-            course.prev().focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Shifts focus to the next conversation in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectNextConversation = function(e, data) {
-            var conversation = $(e.target).closest(SELECTORS.VIEWCONVERSATION);
-            var next = conversation.next();
-            next.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Shifts focus to the previous conversation in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         */
-        Contacts.prototype._selectPreviousConversation = function(e, data) {
-            var conversation = $(e.target).closest(SELECTORS.VIEWCONVERSATION);
-            var previous = conversation.prev();
-            previous.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Flags the search area as seaching.
-         */
-        Contacts.prototype._setSearching = function() {
-            $(SELECTORS.SEARCHTEXTAREA).addClass('searching');
-        };
-
-        /**
-         * Flags the search area as seaching.
-         */
-        Contacts.prototype._clearSearching = function() {
-            $(SELECTORS.SEARCHTEXTAREA).removeClass('searching');
-        };
-
-        /**
-         * Make the messaging area visible.
-         */
-        Contacts.prototype._showMessagingArea = function() {
-            this.messageArea.find(SELECTORS.MESSAGINGAREA)
-                .removeClass('hide-messages')
-                .addClass('show-messages');
-        };
-
-        /**
-         * Hide the messaging area.
-         */
-        Contacts.prototype._hideMessagingArea = function() {
-            this.messageArea.find(SELECTORS.MESSAGINGAREA)
-                .removeClass('show-messages')
-                .addClass('hide-messages');
-        };
-
-        return Contacts;
-    }
-);
diff --git a/message/amd/src/message_area_events.js b/message/amd/src/message_area_events.js
deleted file mode 100644 (file)
index 0ae610d..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module defines the events that are triggered in the message area.
- *
- * @module     core_message/message_area_events
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define([], function() {
-
-    /** @type {Object} The list of events triggered in the message area. */
-    return {
-        CANCELDELETEMESSAGES: 'cancel-delete-messages',
-        CHOOSEMESSAGESTODELETE: 'choose-messages-to-delete',
-        CONTACTADDED: 'contact-added',
-        CONTACTBLOCKED: 'contact-blocked',
-        CONTACTREMOVED: 'contact-removed',
-        CONTACTSELECTED: 'contact-selected',
-        CONTACTSSELECTED: 'contacts-selected',
-        CONTACTUNBLOCKED: 'contact-unblocked',
-        CONVERSATIONDELETED: 'conversation-deleted',
-        CONVERSATIONSELECTED: 'conversation-selected',
-        CONVERSATIONSSELECTED: 'conversations-selected',
-        MESSAGESDELETED: 'messages-deleted',
-        MESSAGESEARCHCANCELED: 'message-search-canceled',
-        MESSAGESENT: 'message-sent',
-        SENDMESSAGE: 'message-send',
-        USERSSEARCHCANCELED: 'users-search-canceled'
-    };
-});
\ No newline at end of file
diff --git a/message/amd/src/message_area_messages.js b/message/amd/src/message_area_messages.js
deleted file mode 100644 (file)
index 77e6f50..0000000
+++ /dev/null
@@ -1,876 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module handles the message area of the messaging area.
- *
- * @module     core_message/message_area_messages
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/custom_interaction_events',
-        'core/auto_rows', 'core_message/message_area_actions', 'core/modal_factory', 'core/modal_events',
-        'core/str', 'core_message/message_area_events', 'core/backoff_timer'],
-    function($, Ajax, Templates, Notification, CustomEvents, AutoRows, Actions, ModalFactory,
-             ModalEvents, Str, Events, BackOffTimer) {
-
-        /** @type {int} The message area default height. */
-        var MESSAGES_AREA_DEFAULT_HEIGHT = 500;
-
-        /** @type {int} The response default height. */
-        var MESSAGES_RESPONSE_DEFAULT_HEIGHT = 50;
-
-        /** @type {Object} The list of selectors for the message area. */
-        var SELECTORS = {
-            BLOCKTIME: "[data-region='blocktime']",
-            CANCELDELETEMESSAGES: "[data-action='cancel-delete-messages']",
-            CONTACT: "[data-region='contact']",
-            CONVERSATIONS: "[data-region='contacts'][data-region-content='conversations']",
-            DELETEALLMESSAGES: "[data-action='delete-all-messages']",
-            DELETEMESSAGES: "[data-action='delete-messages']",
-            LOADINGICON: '.loading-icon',
-            MESSAGE: "[data-region='message']",
-            MESSAGERESPONSE: "[data-region='response']",
-            MESSAGES: "[data-region='messages']",
-            MESSAGESAREA: "[data-region='messages-area']",
-            MESSAGINGAREA: "[data-region='messaging-area']",
-            SENDMESSAGE: "[data-action='send-message']",
-            SENDMESSAGETEXT: "[data-region='send-message-txt']",
-            SHOWCONTACTS: "[data-action='show-contacts']",
-            STARTDELETEMESSAGES: "[data-action='start-delete-messages']",
-        };
-
-        /** @type {int} The number of milliseconds in a second. */
-        var MILLISECONDSINSEC = 1000;
-
-        /**
-         * Messages class.
-         *
-         * @param {Messagearea} messageArea The messaging area object.
-         */
-        function Messages(messageArea) {
-            this.messageArea = messageArea;
-            this._init();
-        }
-
-        /** @type {Boolean} checks if we are sending a message */
-        Messages.prototype._isSendingMessage = false;
-
-        /** @type {Boolean} checks if we are currently loading messages */
-        Messages.prototype._isLoadingMessages = false;
-
-        /** @type {int} the number of messagess displayed */
-        Messages.prototype._numMessagesDisplayed = 0;
-
-        /** @type {array} the messages displayed or about to be displayed on the page */
-        Messages.prototype._messageQueue = [];
-
-        /** @type {int} the number of messages to retrieve */
-        Messages.prototype._numMessagesToRetrieve = 20;
-
-        /** @type {Modal} the confirmation modal */
-        Messages.prototype._confirmationModal = null;
-
-        /** @type {int} the timestamp for the most recent visible message */
-        Messages.prototype._latestMessageTimestamp = 0;
-
-        /** @type {BackOffTimer} the backoff timer */
-        Messages.prototype._backoffTimer = null;
-
-        /** @type {Messagearea} The messaging area object. */
-        Messages.prototype.messageArea = null;
-
-        /**
-         * Initialise the event listeners.
-         *
-         * @private
-         */
-        Messages.prototype._init = function() {
-            CustomEvents.define(this.messageArea.node, [
-                CustomEvents.events.activate,
-                CustomEvents.events.up,
-                CustomEvents.events.down,
-                CustomEvents.events.enter,
-            ]);
-
-            // We have a responsive media query based on height that reduces this size on screens shorter than 670.
-            if ($(window).height() <= 670) {
-                MESSAGES_AREA_DEFAULT_HEIGHT = 400;
-            }
-
-            AutoRows.init(this.messageArea.node);
-
-            this.messageArea.onCustomEvent(Events.CONVERSATIONSELECTED, this._viewMessages.bind(this));
-            this.messageArea.onCustomEvent(Events.SENDMESSAGE, this._viewMessages.bind(this));
-            this.messageArea.onCustomEvent(Events.CHOOSEMESSAGESTODELETE, this._chooseMessagesToDelete.bind(this));
-            this.messageArea.onCustomEvent(Events.CANCELDELETEMESSAGES, this._hideDeleteAction.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SENDMESSAGE,
-                this._sendMessage.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.STARTDELETEMESSAGES,
-                this._startDeleting.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.DELETEMESSAGES,
-                this._deleteMessages.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.DELETEALLMESSAGES,
-                this._deleteAllMessages.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.CANCELDELETEMESSAGES,
-                this._triggerCancelMessagesToDelete.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.MESSAGE,
-                this._toggleMessage.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SHOWCONTACTS,
-                this._hideMessagingArea.bind(this));
-
-            this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.MESSAGE,
-                this._selectPreviousMessage.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.MESSAGE,
-                this._selectNextMessage.bind(this));
-
-            this.messageArea.onDelegateEvent('focus', SELECTORS.SENDMESSAGETEXT, this._setMessaging.bind(this));
-            this.messageArea.onDelegateEvent('blur', SELECTORS.SENDMESSAGETEXT, this._clearMessaging.bind(this));
-
-            $(document).on(AutoRows.events.ROW_CHANGE, this._adjustMessagesAreaHeight.bind(this));
-
-            // Check if any messages have been displayed on page load.
-            var messages = this.messageArea.find(SELECTORS.MESSAGES);
-            if (messages.length) {
-                this._addScrollEventListener(messages.find(SELECTORS.MESSAGE).length);
-                this._latestMessageTimestamp = messages.find(SELECTORS.MESSAGE + ':last').data('timecreated');
-            }
-
-            // Create a timer to poll the server for new messages.
-            this._backoffTimer = new BackOffTimer(this._loadNewMessages.bind(this),
-                BackOffTimer.getIncrementalCallback(this.messageArea.pollmin * MILLISECONDSINSEC, MILLISECONDSINSEC,
-                    this.messageArea.pollmax * MILLISECONDSINSEC, this.messageArea.polltimeout * MILLISECONDSINSEC));
-
-            // Start the timer.
-            this._backoffTimer.start();
-        };
-
-        /**
-         * View the message panel.
-         *
-         * @param {Event} event
-         * @param {int} userid
-         * @return {Promise} The promise resolved when the messages have been loaded.
-         * @private
-         */
-        Messages.prototype._viewMessages = function(event, userid) {
-            // We are viewing another user, or re-loading the panel, so set number of messages displayed to 0.
-            this._numMessagesDisplayed = 0;
-            // Stop the existing timer so we can set up the new user's messages.
-            this._backoffTimer.stop();
-            // Reset the latest timestamp when we change the messages view.
-            this._latestMessageTimestamp = 0;
-
-            // Mark all the messages as read.
-            var markMessagesAsRead = Ajax.call([{
-                methodname: 'core_message_mark_all_messages_as_read',
-                args: {
-                    useridto: this.messageArea.getCurrentUserId(),
-                    useridfrom: userid
-                }
-            }]);
-
-            // Keep track of the number of messages received.
-            var numberreceived = 0;
-            // Show loading template.
-            return Templates.render('core/loading', {}).then(function(html, js) {
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js);
-                return markMessagesAsRead[0];
-            }.bind(this)).then(function() {
-                var conversationnode = this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
-                    SELECTORS.CONTACT + "[data-userid='" + userid + "']");
-                if (conversationnode.hasClass('unread')) {
-                    // Remove the class.
-                    conversationnode.removeClass('unread');
-                    // Trigger an event letting the notification popover (and whoever else) know.
-                    $(document).trigger('messagearea:conversationselected', userid);
-                }
-                return this._getMessages(userid);
-            }.bind(this)).then(function(data) {
-                numberreceived = data.messages.length;
-                // We have the data - lets render the template with it.
-                return Templates.render('core_message/message_area_messages_area', data);
-            }).then(function(html, js) {
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js);
-                this._addScrollEventListener(numberreceived);
-                // Restart the poll timer.
-                this._backoffTimer.restart();
-                this.messageArea.find(SELECTORS.SENDMESSAGETEXT).focus();
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Loads messages while scrolling.
-         *
-         * @return {Promise|boolean} The promise resolved when the messages have been loaded.
-         * @private
-         */
-        Messages.prototype._loadMessages = function() {
-            if (this._isLoadingMessages) {
-                return false;
-            }
-
-            this._isLoadingMessages = true;
-
-            // Keep track of the number of messages received.
-            var numberreceived = 0;
-            // Show loading template.
-            return Templates.render('core/loading', {}).then(function(html, js) {
-                Templates.prependNodeContents(this.messageArea.find(SELECTORS.MESSAGES),
-                    "<div style='text-align:center'>" + html + "</div>", js);
-                return this._getMessages(this._getUserId());
-            }.bind(this)).then(function(data) {
-                numberreceived = data.messages.length;
-                // We have the data - lets render the template with it.
-                return Templates.render('core_message/message_area_messages', data);
-            }).then(function(html, js) {
-                // Remove the loading icon.
-                this.messageArea.find(SELECTORS.MESSAGES + " " +
-                    SELECTORS.LOADINGICON).remove();
-                // Check if we got something to do.
-                if (numberreceived > 0) {
-                    var newHtml = $('<div>' + html + '</div>');
-                    if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, true)) {
-                        this.messageArea.node.find(SELECTORS.BLOCKTIME + ':first').remove();
-                    }
-                    // Get height before we add the messages.
-                    var oldheight = this.messageArea.find(SELECTORS.MESSAGES)[0].scrollHeight;
-                    // Show the new content.
-                    Templates.prependNodeContents(this.messageArea.find(SELECTORS.MESSAGES), html, js);
-                    // Get height after we add the messages.
-                    var newheight = this.messageArea.find(SELECTORS.MESSAGES)[0].scrollHeight;
-                    // Make sure scroll bar is at the location before we loaded more messages.
-                    this.messageArea.find(SELECTORS.MESSAGES).scrollTop(newheight - oldheight);
-                    // Increment the number of messages displayed.
-                    this._numMessagesDisplayed += numberreceived;
-                }
-                // Mark that we are no longer busy loading data.
-                this._isLoadingMessages = false;
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Loads and renders messages newer than the most recently seen messages.
-         *
-         * @return {Promise|boolean} The promise resolved when the messages have been loaded.
-         * @private
-         */
-        Messages.prototype._loadNewMessages = function() {
-            if (this._isLoadingMessages) {
-                return false;
-            }
-
-            // If we have no user id yet then bail early.
-            if (!this._getUserId()) {
-                return false;
-            }
-
-            this._isLoadingMessages = true;
-
-            // Only scroll the message window if the user hasn't scrolled up.
-            var shouldScrollBottom = false;
-            var messages = this.messageArea.find(SELECTORS.MESSAGES);
-            if (messages.length !== 0) {
-                var scrollTop = messages.scrollTop();
-                var innerHeight = messages.innerHeight();
-                var scrollHeight = messages[0].scrollHeight;
-
-                if (scrollTop + innerHeight >= scrollHeight) {
-                    shouldScrollBottom = true;
-                }
-            }
-
-            // Keep track of the number of messages received.
-            return this._getMessages(this._getUserId(), true).then(function(data) {
-                return this._addMessagesToDom(data.messages, shouldScrollBottom);
-            }.bind(this)).always(function() {
-                // Mark that we are no longer busy loading data.
-                this._isLoadingMessages = false;
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Handles returning a list of messages to display.
-         *
-         * @param {int} userid
-         * @param {bool} fromTimestamp Load messages from the latest known timestamp
-         * @return {Promise} The promise resolved when the contact area has been rendered
-         * @private
-         */
-        Messages.prototype._getMessages = function(userid, fromTimestamp) {
-            var args = {
-                currentuserid: this.messageArea.getCurrentUserId(),
-                otheruserid: userid,
-                limitfrom: this._numMessagesDisplayed,
-                limitnum: this._numMessagesToRetrieve,
-                newest: true
-            };
-
-            // If we're trying to load new messages since the message UI was
-            // rendered. Used for ajax polling while user is on the message UI.
-            if (fromTimestamp) {
-                args.timefrom = this._latestMessageTimestamp;
-                // Remove limit and offset. We want all new messages.
-                args.limitfrom = 0;
-                args.limitnum = 0;
-            }
-
-            // Call the web service to get our data.
-            var promises = Ajax.call([{
-                methodname: 'core_message_data_for_messagearea_messages',
-                args: args,
-            }]);
-
-            // Do stuff when we get data back.
-            return promises[0].then(function(data) {
-                var messages = data.messages;
-
-                // Did we get any new messages?
-                if (messages && messages.length) {
-                    var latestMessage = messages[messages.length - 1];
-
-                    // Update our record of the latest known message for future requests.
-                    if (latestMessage.timecreated > this._latestMessageTimestamp) {
-                        // Next request should be for the second after the most recent message we've seen.
-                        this._latestMessageTimestamp = latestMessage.timecreated + 1;
-                    }
-                }
-
-                return data;
-            }.bind(this)).fail(function(ex) {
-                // Stop the timer if we received an error so that we don't keep spamming the server.
-                this._backoffTimer.stop();
-                Notification.exception(ex);
-            }.bind(this));
-        };
-
-        /**
-         * Handles sending a message.
-         *
-         * @return {Promise|boolean} The promise resolved once the message has been sent.
-         * @private
-         */
-        Messages.prototype._sendMessage = function() {
-            var element = this.messageArea.find(SELECTORS.SENDMESSAGETEXT);
-            var text = element.val().trim();
-
-            // Do not do anything if it is empty.
-            if (text === '') {
-                return false;
-            }
-
-            // If we are sending a message, don't do anything, be patient!
-            if (this._isSendingMessage) {
-                return false;
-            }
-
-            // Ok, mark that we are sending a message.
-            this._isSendingMessage = true;
-
-            // Call the web service to save our message.
-            var promises = Ajax.call([{
-                methodname: 'core_message_send_instant_messages',
-                args: {
-                    messages: [
-                        {
-                            touserid: this._getUserId(),
-                            text: text
-                        }
-                    ]
-                }
-            }]);
-
-            element.prop('disabled', true);
-
-            // Update the DOM when we get some data back.
-            return promises[0].then(function(response) {
-                if (response.length < 0) {
-                    // Even errors should return valid data.
-                    throw new Error('Invalid response');
-                }
-                if (response[0].errormessage) {
-                    throw new Error(response[0].errormessage);
-                }
-                // Fire an event to say the message was sent.
-                this.messageArea.trigger(Events.MESSAGESENT, [this._getUserId(), text]);
-                // Update the messaging area.
-                return this._addLastMessageToDom();
-            }.bind(this)).then(function() {
-                // Ok, we are no longer sending a message.
-                this._isSendingMessage = false;
-            }.bind(this)).always(function() {
-                element.prop('disabled', false);
-                element.focus();
-            }).fail(Notification.exception);
-        };
-
-        /**
-         * Handles selecting messages to delete.
-         *
-         * @private
-         */
-        Messages.prototype._chooseMessagesToDelete = function() {
-            this.messageArea.find(SELECTORS.MESSAGESAREA).addClass('editing');
-            this.messageArea.find(SELECTORS.MESSAGE)
-                .attr('role', 'checkbox')
-                .attr('aria-checked', 'false');
-        };
-
-        /**
-         * Handles deleting messages.
-         *
-         * @private
-         */
-        Messages.prototype._deleteMessages = function() {
-            var userid = this.messageArea.getCurrentUserId();
-            var checkboxes = this.messageArea.find(SELECTORS.MESSAGE + "[aria-checked='true']");
-            var requests = [];
-            var messagestoremove = [];
-
-            // Go through all the checked checkboxes and prepare them for deletion.
-            checkboxes.each(function(id, element) {
-                var node = $(element);
-                var messageid = node.data('messageid');
-                var isread = node.data('messageread') ? 1 : 0;
-                messagestoremove.push(node);
-                requests.push({
-                    methodname: 'core_message_delete_message',
-                    args: {
-                        messageid: messageid,
-                        userid: userid,
-                        read: isread
-                    }
-                });
-            });
-            if (requests.length > 0) {
-                $.when(Ajax.call(requests)).then(function() {
-                    // Store the last message on the page, and the last message being deleted.
-                    var updatemessage = null;
-                    var messages = this.messageArea.find(SELECTORS.MESSAGE);
-                    var lastmessage = messages.last();
-                    var lastremovedmessage = messagestoremove[messagestoremove.length - 1];
-                    // Remove the messages from the DOM.
-                    $.each(messagestoremove, function(key, message) {
-                        // Remove the message.
-                        message.remove();
-                    });
-                    // If the last message was deleted then we need to provide the new last message.
-                    if (lastmessage.data('id') === lastremovedmessage.data('id')) {
-                        updatemessage = this.messageArea.find(SELECTORS.MESSAGE).last();
-                    }
-                    // Now we have removed all the messages from the DOM lets remove any block times we may need to as well.
-                    $.each(messagestoremove, function(key, message) {
-                        // First - let's make sure there are no more messages in that time block.
-                        var blocktime = message.data('blocktime');
-                        if (this.messageArea.find(SELECTORS.MESSAGE +
-                            "[data-blocktime='" + blocktime + "']").length === 0) {
-                            this.messageArea.find(SELECTORS.BLOCKTIME +
-                                "[data-blocktime='" + blocktime + "']").remove();
-                        }
-                    }.bind(this));
-
-                    // If there are no messages at all, then remove conversation panel.
-                    if (this.messageArea.find(SELECTORS.MESSAGE).length === 0) {
-                        this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
-                            SELECTORS.CONTACT + "[data-userid='" + this._getUserId() + "']").remove();
-                    }
-
-                    // Trigger event letting other modules know messages were deleted.
-                    this.messageArea.trigger(Events.MESSAGESDELETED, [this._getUserId(), updatemessage]);
-                }.bind(this)).catch(Notification.exception);
-            } else {
-                // Trigger event letting other modules know messages were deleted.
-                this.messageArea.trigger(Events.MESSAGESDELETED, this._getUserId());
-            }
-
-            // Hide the items responsible for deleting messages.
-            this._hideDeleteAction();
-        };
-
-        /**
-         * Handles adding a scrolling event listener.
-         *
-         * @param {int} numberreceived The number of messages received
-         * @private
-         */
-        Messages.prototype._addScrollEventListener = function(numberreceived) {
-            // Scroll to the bottom.
-            this._scrollBottom();
-            // Set the number of messages displayed.
-            this._numMessagesDisplayed = numberreceived;
-            // Now enable the ability to infinitely scroll through messages.
-            CustomEvents.define(this.messageArea.find(SELECTORS.MESSAGES), [
-                CustomEvents.events.scrollTop
-            ]);
-            // Assign the event for scrolling.
-            this.messageArea.onCustomEvent(CustomEvents.events.scrollTop, this._loadMessages.bind(this));
-        };
-
-        /**
-         * Handles deleting a conversation.
-         *
-         * @private
-         */
-        Messages.prototype._deleteAllMessages = function() {
-            if (this._confirmationModal) {
-                // Just show the existing modal.
-                this._confirmationModal.show();
-                return;
-            }
-
-            var stringsPromise = Str.get_strings([
-                {key: 'confirm'},
-                {key: 'deleteallconfirm', component: 'message'},
-                {key: 'delete'}
-            ]);
-            var deleteModalPromise = ModalFactory.create(
-                {
-                    type: ModalFactory.types.SAVE_CANCEL
-                },
-                this.messageArea.find(SELECTORS.DELETEALLMESSAGES)
-            );
-
-            $.when(stringsPromise, deleteModalPromise).then(function(s, modal) {
-                modal.setTitle(s[0]);
-                modal.setBody(s[1]);
-                modal.setSaveButtonText(s[2]);
-
-                this._confirmationModal = modal;
-                // Only delete the conversation if the user agreed in the confirmation modal.
-                modal.getRoot().on(ModalEvents.save, function() {
-                    var otherUserId = this._getUserId();
-                    var request = {
-                        methodname: 'core_message_delete_conversation',
-                        args: {
-                            userid: this.messageArea.getCurrentUserId(),
-                            otheruserid: otherUserId
-                        }
-                    };
-
-                    // Delete the conversation.
-                    Ajax.call([request])[0].then(function() {
-                        // Clear the message area.
-                        this.messageArea.find(SELECTORS.MESSAGESAREA).empty();
-                        // Let the app know a conversation was deleted.
-                        this.messageArea.trigger(Events.CONVERSATIONDELETED, otherUserId);
-                        this._hideDeleteAction();
-                    }.bind(this)).catch(Notification.exception);
-                }.bind(this));
-
-                // Display the confirmation.
-                modal.show();
-            }.bind(this)).catch(Notification.exception);
-        };
-
-        /**
-         * Handles hiding the delete checkboxes and replacing the response area.
-         *
-         * @private
-         */
-        Messages.prototype._hideDeleteAction = function() {
-            this.messageArea.find(SELECTORS.MESSAGE)
-                .removeAttr('role')
-                .removeAttr('aria-checked');
-            this.messageArea.find(SELECTORS.MESSAGESAREA).removeClass('editing');
-        };
-
-        /**
-         * Triggers the CANCELDELETEMESSAGES event.
-         *
-         * @private
-         */
-        Messages.prototype._triggerCancelMessagesToDelete = function() {
-            // Trigger event letting other modules know message deletion was canceled.
-            this.messageArea.trigger(Events.CANCELDELETEMESSAGES);
-        };
-
-        /**
-         * Handles adding messages to the DOM.
-         *
-         * @param {array} messages An array of messages to be added to the DOM.
-         * @param {boolean} shouldScrollBottom True will scroll to the bottom of the message window and show the new messages.
-         * @return {Promise} The promise resolved when the messages have been added to the DOM.
-         * @private
-         */
-        Messages.prototype._addMessagesToDom = function(messages, shouldScrollBottom) {
-            var numberreceived = 0;
-            var messagesArea = this.messageArea.find(SELECTORS.MESSAGES);
-            messages = messages.filter(function(message) {
-                var id = "" + message.id + message.isread;
-                // If the message is already queued to be rendered, remove from the list of messages.
-                if (this._messageQueue[id]) {
-                    return false;
-                }
-                // Filter out any messages already rendered.
-                var result = messagesArea.find(SELECTORS.MESSAGE + '[data-id="' + id + '"]');
-                // Any message we are rendering should go in the messageQueue.
-                if (!result.length) {
-                    this._messageQueue[id] = true;
-                }
-                return !result.length;
-            }.bind(this));
-            numberreceived = messages.length;
-            // We have the data - lets render the template with it.
-            return Templates.render('core_message/message_area_messages', {messages: messages}).then(function(html, js) {
-                // Check if we got something to do.
-                if (numberreceived > 0) {
-                    var newHtml = $('<div>' + html + '</div>');
-                    if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, false)) {
-                        newHtml.find(SELECTORS.BLOCKTIME + ':first').remove();
-                    }
-                    // Show the new content.
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.MESSAGES), newHtml, js);
-                    // Scroll the new message into view.
-                    if (shouldScrollBottom) {
-                        this._scrollBottom();
-                    }
-                    // Increment the number of messages displayed.
-                    this._numMessagesDisplayed += numberreceived;
-                    // Reset the poll timer because the user may be active.
-                    this._backoffTimer.restart();
-                }
-            }.bind(this));
-        };
-
-        /**
-         * Handles adding the last message to the DOM.
-         *
-         * @return {Promise} The promise resolved when the message has been added to the DOM.
-         * @private
-         */
-        Messages.prototype._addLastMessageToDom = function() {
-            // Call the web service to return how the message should look.
-            var promises = Ajax.call([{
-                methodname: 'core_message_data_for_messagearea_get_most_recent_message',
-                args: {
-                    currentuserid: this.messageArea.getCurrentUserId(),
-                    otheruserid: this._getUserId()
-                }
-            }]);
-
-            // Add the message.
-            return promises[0].then(function(data) {
-                return this._addMessagesToDom([data], true);
-            }.bind(this)).always(function() {
-                // Empty the response text area.text
-                this.messageArea.find(SELECTORS.SENDMESSAGETEXT).val('').trigger('input');
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Returns the ID of the other user in the conversation.
-         *
-         * @return {int} The user id
-         * @private
-         */
-        Messages.prototype._getUserId = function() {
-            return this.messageArea.find(SELECTORS.MESSAGES).data('userid');
-        };
-
-        /**
-         * Scrolls to the bottom of the messages.
-         *
-         * @private
-         */
-        Messages.prototype._scrollBottom = function() {
-            // Scroll to the bottom.
-            var messages = this.messageArea.find(SELECTORS.MESSAGES);
-            if (messages.length !== 0) {
-                messages.scrollTop(messages[0].scrollHeight);
-            }
-        };
-
-        /**
-         * Select the previous message in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Extra event data
-         * @private
-         */
-        Messages.prototype._selectPreviousMessage = function(e, data) {
-            var currentMessage = $(e.target).closest(SELECTORS.MESSAGE);
-
-            do {
-                currentMessage = currentMessage.prev();
-            } while (currentMessage.length && !currentMessage.is(SELECTORS.MESSAGE));
-
-            currentMessage.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Select the next message in the list.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Extra event data
-         * @private
-         */
-        Messages.prototype._selectNextMessage = function(e, data) {
-            var currentMessage = $(e.target).closest(SELECTORS.MESSAGE);
-
-            do {
-                currentMessage = currentMessage.next();
-            } while (currentMessage.length && !currentMessage.is(SELECTORS.MESSAGE));
-
-            currentMessage.focus();
-
-            data.originalEvent.preventDefault();
-            data.originalEvent.stopPropagation();
-        };
-
-        /**
-         * Flag the response area as messaging.
-         *
-         * @param {event} e The jquery event
-         * @private
-         */
-        Messages.prototype._setMessaging = function(e) {
-            $(e.target).closest(SELECTORS.MESSAGERESPONSE).addClass('messaging');
-        };
-
-        /**
-         * Clear the response area as messaging flag.
-         *
-         * @param {event} e The jquery event
-         * @private
-         */
-        Messages.prototype._clearMessaging = function(e) {
-            $(e.target).closest(SELECTORS.MESSAGERESPONSE).removeClass('messaging');
-        };
-
-        /**
-         * Turn on delete message mode.
-         *
-         * @param {event} e The jquery event
-         * @private
-         */
-        Messages.prototype._startDeleting = function(e) {
-            var actions = new Actions(this.messageArea);
-            actions.chooseMessagesToDelete();
-
-            e.preventDefault();
-        };
-
-        /**
-         * Check if the message area is in editing mode.
-         *
-         * @return {bool}
-         * @private
-         */
-        Messages.prototype._isEditing = function() {
-            return this.messageArea.find(SELECTORS.MESSAGESAREA).hasClass('editing');
-        };
-
-        /**
-         * Check or uncheck the message if the message area is in editing mode.
-         *
-         * @param {event} e The jquery event
-         * @private
-         */
-        Messages.prototype._toggleMessage = function(e) {
-            if (!this._isEditing()) {
-                return;
-            }
-
-            var message = $(e.target).closest(SELECTORS.MESSAGE);
-
-            if (message.attr('aria-checked') === 'true') {
-                message.attr('aria-checked', 'false');
-            } else {
-                message.attr('aria-checked', 'true');
-            }
-        };
-
-        /**
-         * Adjust the height of the messages area to match the changed height of
-         * the response area.
-         *
-         * @private
-         */
-        Messages.prototype._adjustMessagesAreaHeight = function() {
-            var messagesArea = this.messageArea.find(SELECTORS.MESSAGES);
-            var messagesResponse = this.messageArea.find(SELECTORS.MESSAGERESPONSE);
-
-            var currentMessageResponseHeight = messagesResponse.outerHeight();
-            var diffResponseHeight = currentMessageResponseHeight - MESSAGES_RESPONSE_DEFAULT_HEIGHT;
-            var newMessagesAreaHeight = MESSAGES_AREA_DEFAULT_HEIGHT - diffResponseHeight;
-
-            messagesArea.outerHeight(newMessagesAreaHeight);
-        };
-
-        /**
-         * Handle the event that triggers sending a message from the messages area.
-         *
-         * @param {event} e The jquery event
-         * @param {object} data Additional event data
-         * @private
-         */
-        Messages.prototype._sendMessageHandler = function(e, data) {
-            data.originalEvent.preventDefault();
-
-            this._sendMessage();
-        };
-
-        /**
-         * Hide the messaging area. This only applies on smaller screen resolutions.
-         *
-         * @private
-         */
-        Messages.prototype._hideMessagingArea = function() {
-            this.messageArea.find(SELECTORS.MESSAGINGAREA)
-                .removeClass('show-messages')
-                .addClass('hide-messages');
-        };
-
-        /**
-         * Checks if a day separator needs to be removed.
-         *
-         * Example - scrolling up and loading previous messages that belong to the
-         * same day as the last message that was previously shown, meaning we can
-         * remove the original separator.
-         *
-         * @param {jQuery} domHtml The HTML in the DOM.
-         * @param {jQuery} newHtml The HTML to compare to the DOM
-         * @param {boolean} loadingPreviousMessages Are we loading previous messages?
-         * @return {boolean}
-         * @private
-         */
-        Messages.prototype._hasMatchingBlockTime = function(domHtml, newHtml, loadingPreviousMessages) {
-            var blockTime, blockTimePos, newBlockTime, newBlockTimePos;
-
-            if (loadingPreviousMessages) {
-                blockTimePos = ':first';
-                newBlockTimePos = ':last';
-            } else {
-                blockTimePos = ':last';
-                newBlockTimePos = ':first';
-            }
-
-            blockTime = domHtml.find(SELECTORS.BLOCKTIME + blockTimePos);
-            newBlockTime = newHtml.find(SELECTORS.BLOCKTIME + newBlockTimePos);
-
-            if (blockTime.length && newBlockTime.length) {
-                return blockTime.data('blocktime') == newBlockTime.data('blocktime');
-            }
-
-            return false;
-        };
-
-        return Messages;
-    }
-);
diff --git a/message/amd/src/message_area_profile.js b/message/amd/src/message_area_profile.js
deleted file mode 100644 (file)
index eb73dcb..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module handles the profile area of the messaging area.
- *
- * @module     core_message/message_area_profile
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/config',
-        'core/custom_interaction_events', 'core_message/message_area_events'],
-    function($, Ajax, Templates, Notification, Str, Config, CustomEvents, Events) {
-
-        /** @type {Object} The list of selectors for the message area. */
-        var SELECTORS = {
-            PROFILE: "[data-region='profile']",
-            PROFILEADDCONTACT: "[data-action='profile-add-contact']",
-            PROFILEBLOCKCONTACT: "[data-action='profile-block-contact']",
-            PROFILEREMOVECONTACT: "[data-action='profile-remove-contact']",
-            PROFILESENDMESSAGE: "[data-action='profile-send-message']",
-            PROFILEUNBLOCKCONTACT: "[data-action='profile-unblock-contact']",
-            PROFILEVIEW: "[data-action='profile-view']",
-            SHOWCONTACTS: "[data-action='show-contacts']",
-            MESSAGESAREA: "[data-region='messages-area']",
-            MESSAGINGAREA: "[data-region='messaging-area']"
-        };
-
-        /**
-         * Profile class.
-         *
-         * @param {Messagearea} messageArea The messaging area object.
-         */
-        function Profile(messageArea) {
-            this.messageArea = messageArea;
-            this._init();
-        }
-
-        /** @type {Messagearea} The messaging area object. */
-        Profile.prototype.messageArea = null;
-
-        /**
-         * Initialise the event listeners.
-         *
-         * @private
-         */
-        Profile.prototype._init = function() {
-            CustomEvents.define(this.messageArea.node, [
-                CustomEvents.events.activate
-            ]);
-
-            this.messageArea.onCustomEvent(Events.CONTACTSELECTED, this._viewProfile.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILEVIEW,
-                function(e, data) {
-                    this._viewFullProfile();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILESENDMESSAGE,
-                function(e, data) {
-                    this._sendMessage();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILEUNBLOCKCONTACT,
-                function(e, data) {
-                    this._unblockContact();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILEBLOCKCONTACT,
-                function(e, data) {
-                    this._blockContact();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILEADDCONTACT,
-                function(e, data) {
-                    this._addContact();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.PROFILEREMOVECONTACT,
-                function(e, data) {
-                    this._removeContact();
-                    data.originalEvent.preventDefault();
-                }.bind(this));
-            this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SHOWCONTACTS,
-                this._hideMessagingArea.bind(this));
-        };
-
-        /**
-         * Handles viewing the profile.
-         *
-         * @param {Event} event
-         * @param {int} userid
-         * @return {Promise} The promise resolved when the profile has been rendered
-         * @private
-         */
-        Profile.prototype._viewProfile = function(event, userid) {
-            // Show loading template.
-            Templates.render('core/loading', {}).done(function(html, js) {
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js);
-            }.bind(this));
-
-            // Call the web service to return the profile.
-            var promises = Ajax.call([{
-                methodname: 'core_message_data_for_messagearea_get_profile',
-                args: {
-                    currentuserid: this.messageArea.getCurrentUserId(),
-                    otheruserid: userid
-                }
-            }]);
-
-            // Show the profile.
-            return promises[0].then(function(data) {
-                return Templates.render('core_message/message_area_profile', data);
-            }).then(function(html, js) {
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js);
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Handles viewing the user's full profile.
-         *
-         * @private
-         */
-        Profile.prototype._viewFullProfile = function() {
-            window.location.href = Config.wwwroot + '/user/profile.php?id=' + this._getUserId();
-        };
-
-        /**
-         * Handles viewing the messages with the user.
-         *
-         * @private
-         */
-        Profile.prototype._sendMessage = function() {
-            this.messageArea.trigger(Events.SENDMESSAGE, this._getUserId());
-        };
-
-        /**
-         * Handles blocking the contact.
-         *
-         * @return {Promise} The promise resolved when the contact has been blocked
-         * @private
-         */
-        Profile.prototype._blockContact = function() {
-            var action = this._performAction('core_message_block_user', 'unblockcontact', 'profile-block-contact',
-                'profile-unblock-contact', '');
-            return action.then(function() {
-                this.messageArea.trigger(Events.CONTACTBLOCKED, this._getUserId());
-            }.bind(this));
-        };
-
-        /**
-         * Handles unblocking the contact.
-         *
-         * @return {Promise} The promise resolved when the contact has been unblocked
-         * @private
-         */
-        Profile.prototype._unblockContact = function() {
-            var action = this._performAction('core_message_unblock_user', 'blockcontact', 'profile-unblock-contact',
-                'profile-block-contact', 'danger');
-            return action.then(function() {
-                this.messageArea.trigger(Events.CONTACTUNBLOCKED, this._getUserId());
-            }.bind(this));
-        };
-
-        /**
-         * Handles adding the contact.
-         *
-         * @return {Promise} The promise resolved when the contact has been added
-         * @private
-         */
-        Profile.prototype._addContact = function() {
-            var action = this._performAction('core_message_create_contact_request', 'removecontact', 'profile-add-contact',
-                'profile-remove-contact', 'danger');
-            return action.then(function() {
-                this.messageArea.trigger(Events.CONTACTADDED, this._getUserId());
-            }.bind(this));
-        };
-
-        /**
-         * Handles removing the contact.
-         *
-         * @return {Promise} The promise resolved when the contact has been removed
-         * @private
-         */
-        Profile.prototype._removeContact = function() {
-            var action = this._performAction('core_message_delete_contacts', 'addcontact', 'profile-remove-contact',
-                'profile-add-contact', '');
-            return action.then(function() {
-                this.messageArea.trigger(Events.CONTACTREMOVED, this._getUserId());
-            }.bind(this));
-        };
-
-        /**
-         * Helper function to perform actions on the profile page.
-         *
-         * @param {String} service The web service to call.
-         * @param {String} string The string to change the button value to
-         * @param {String} oldaction The data-action of the button
-         * @param {string} newaction The data-action to change the button to
-         * @param {String} newclass The CSS class we want to add
-         * @return {Promise} The promise resolved when the action has been performed
-         * @private
-         */
-        Profile.prototype._performAction = function(service, string, oldaction, newaction, newclass) {
-            // This is a temporary hack as we are getting rid of this UI.
-            var userargs = '';
-            switch (service) {
-                case 'core_message_block_user':
-                    userargs = {
-                        userid: this.messageArea.getCurrentUserId(),
-                        blockeduserid: this._getUserId()
-                    };
-                    break;
-                case 'core_message_unblock_user':
-                    userargs = {
-                        userid: this.messageArea.getCurrentUserId(),
-                        unblockeduserid: this._getUserId()
-                    };
-                    break;
-                case 'core_message_create_contact_request':
-                    userargs = {
-                        userid: this.messageArea.getCurrentUserId(),
-                        requesteduserid: this._getUserId()
-                    };
-                    break;
-                default:
-                    userargs = {
-                        userid: this.messageArea.getCurrentUserId(),
-                        userids: [
-                            this._getUserId()
-                        ]
-                    };
-            }
-
-
-            var promises = Ajax.call([{
-                methodname: service,
-                args: userargs
-            }]);
-
-            return promises[0].then(function() {
-                return Str.get_string(string, 'message');
-            }).then(function(s) {
-                this._changeText(s, oldaction, newaction, newclass);
-            }.bind(this)).fail(Notification.exception);
-        };
-
-        /**
-         * Changes the text in the profile area.
-         *
-         * @param {String} text The string to change the button value to
-         * @param {string} oldaction The data-action of the button
-         * @param {string} newaction The data-action to change the button to
-         * @param {String} newclass The CSS class we want to add
-         * @private
-         */
-        Profile.prototype._changeText = function(text, oldaction, newaction, newclass) {
-            var anchor = this.messageArea.find("[data-action='" + oldaction + "']");
-            // Change the text.
-            anchor.text(text);
-            // Remove any class.
-            anchor.removeClass();
-            // Add the class if there is one.
-            if (newclass) {
-                anchor.addClass(newclass);
-            }
-
-            anchor.attr('data-action', newaction);
-        };
-
-        /**
-         * Returns the ID of the user whos profile we are viewing.
-         *
-         * @return {int} The user ID
-         * @private
-         */
-        Profile.prototype._getUserId = function() {
-            return this.messageArea.find(SELECTORS.PROFILE).data('userid');
-        };
-
-        /**
-         * Hide the messaging area. This only applies on smaller screen resolutions.
-         */
-        Profile.prototype._hideMessagingArea = function() {
-            this.messageArea.find(SELECTORS.MESSAGINGAREA)
-                .removeClass('show-messages')
-                .addClass('hide-messages');
-        };
-
-        return Profile;
-    }
-);
diff --git a/message/amd/src/message_area_search.js b/message/amd/src/message_area_search.js
deleted file mode 100644 (file)
index 46960d7..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * The module handles searching contacts.
- *
- * @module     core_message/message_area_search
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/custom_interaction_events',
-        'core_message/message_area_events'],
-    function($, Ajax, Templates, Notification, Str, CustomEvents, Events) {
-
-    /** @type {Object} The list of selectors for the message area. */
-    var SELECTORS = {
-        CONTACTS: "[data-region='contacts'][data-region-content='contacts']",
-        CONTACTSAREA: "[data-region='contacts-area']",
-        CONVERSATIONS: "[data-region='contacts'][data-region-content='conversations']",
-        DELETESEARCHFILTER: "[data-region='search-filter-area']",
-        LOADINGICON: '.loading-icon',
-        SEARCHBOX: "[data-region='search-box']",
-        SEARCHFILTER: "[data-region='search-filter']",
-        SEARCHFILTERAREA: "[data-region='search-filter-area']",
-        SEARCHRESULTSAREA: "[data-region='search-results-area']",
-        SEARCHTEXTAREA: "[data-region='search-text-area']",
-        SEARCHUSERSINCOURSE: "[data-action='search-users-in-course']",
-    };
-
-    /**
-     * Search class.
-     *
-     * @param {Messagearea} messageArea The messaging area object.
-     */
-    function Search(messageArea) {
-        this.messageArea = messageArea;
-        this._init();
-    }
-
-    /** @type {Messagearea} The messaging area object. */
-    Search.prototype.messageArea = null;
-
-    /** @type {String} The area we are searching in. */
-    Search.prototype._searchArea = null;
-
-    /** @type {String} The id of the course we are searching in (if any). */
-    Search.prototype._courseid = null;
-
-    /** @type {Boolean} checks if we are currently loading  */
-    Search.prototype._isLoading = false;
-
-    /** @type {String} The number of messages displayed. */
-    Search.prototype._numMessagesDisplayed = 0;
-
-    /** @type {String} The number of messages to retrieve. */
-    Search.prototype._numMessagesToRetrieve = 20;
-
-    /** @type {String} The number of users displayed. */
-    Search.prototype._numUsersDisplayed = 0;
-
-    /** @type {String} The number of users to retrieve. */
-    Search.prototype._numUsersToRetrieve = 20;
-
-    /** @type {Array} The type of available search areas. **/
-    Search.prototype._searchAreas = {
-        MESSAGES: 'messages',
-        USERS: 'users',
-        USERSINCOURSE: 'usersincourse'
-    };
-
-    /** @type {int} The timeout before performing an ajax search */
-    Search.prototype._requestTimeout = null;
-
-    /**
-     * Initialise the event listeners.
-     *
-     * @private
-     */
-    Search.prototype._init = function() {
-        // Handle searching for text.
-        this.messageArea.find(SELECTORS.SEARCHTEXTAREA).on('input', this._searchRequest.bind(this));
-
-        // Handle clicking on a course in the list of users.
-        this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SEARCHUSERSINCOURSE, function(e) {
-            this._setFilter($(e.currentTarget).html());
-            this._setPlaceholderText('searchforuser');
-            this._clearSearchArea();
-            this._searchArea = this._searchAreas.USERSINCOURSE;
-            this._courseid = $(e.currentTarget).data('courseid');
-            this._searchUsersInCourse();
-            this.messageArea.find(SELECTORS.SEARCHBOX).focus();
-        }.bind(this));
-
-        // Handle deleting the search filter.
-        this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.DELETESEARCHFILTER, function() {
-            this._hideSearchResults();
-            // Filter has been removed, so we don't want to be searching in a course anymore.
-            this._searchArea = this._searchAreas.USERS;
-            this._setPlaceholderText('searchforuserorcourse');
-            // Go back the contacts.
-            this.messageArea.trigger(Events.USERSSEARCHCANCELED);
-            this.messageArea.find(SELECTORS.SEARCHBOX).focus();
-        }.bind(this));
-
-        // Handle events that occur outside this module.
-        this.messageArea.onCustomEvent(Events.CONVERSATIONSSELECTED, function() {
-            this._hideSearchResults();
-            this._searchArea = this._searchAreas.MESSAGES;
-            this._setPlaceholderText('searchmessages');
-        }.bind(this));
-        this.messageArea.onCustomEvent(Events.CONTACTSSELECTED, function() {
-            this._hideSearchResults();
-            this._searchArea = this._searchAreas.USERS;
-            this._setPlaceholderText('searchforuserorcourse');
-        }.bind(this));
-        this.messageArea.onCustomEvent(Events.MESSAGESENT, function() {
-            this._hideSearchResults();
-            this._searchArea = this._searchAreas.MESSAGES;
-            this._setPlaceholderText('searchmessages');
-        }.bind(this));
-
-        // Event listeners for scrolling through messages and users in courses.
-        CustomEvents.define(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), [
-            CustomEvents.events.scrollBottom
-        ]);
-        this.messageArea.onDelegateEvent(CustomEvents.events.scrollBottom, SELECTORS.SEARCHRESULTSAREA,
-            function() {
-                if (this._searchArea == this._searchAreas.MESSAGES) {
-                    this._searchMessages();
-                } else if (this._searchArea == this._searchAreas.USERSINCOURSE) {
-                    this._searchUsersInCourse();
-                }
-            }.bind(this)
-        );
-
-        // Set the initial search area.
-        this._searchArea = (this.messageArea.showContactsFirst()) ? this._searchAreas.USERS : this._searchAreas.MESSAGES;
-    };
-
-    /**
-     * Handles when search requests are sent.
-     *
-     * @private
-     */
-    Search.prototype._searchRequest = function() {
-        var str = this.messageArea.find(SELECTORS.SEARCHTEXTAREA + ' input').val();
-
-        if (this._requestTimeout) {
-            clearTimeout(this._requestTimeout);
-        }
-
-        if (str.trim() === '') {
-            // If nothing we being searched then we need to display the usual data.
-            if (this._searchArea == this._searchAreas.MESSAGES) {
-                this._hideSearchResults();
-                this.messageArea.trigger(Events.MESSAGESEARCHCANCELED);
-            } else if (this._searchArea == this._searchAreas.USERS) {
-                this._hideSearchResults();
-                this.messageArea.trigger(Events.USERSSEARCHCANCELED);
-            } else if (this._searchArea == this._searchAreas.USERSINCOURSE) {
-                // We are still searching in a course, so need to list all the users again.
-                this._clearSearchArea();
-                this._searchUsersInCourse();
-            }
-            return;
-        }
-
-        this.messageArea.find(SELECTORS.CONVERSATIONS).hide();
-        this.messageArea.find(SELECTORS.CONTACTS).hide();
-        this.messageArea.find(SELECTORS.SEARCHRESULTSAREA).show();
-
-        if (this._searchArea == this._searchAreas.MESSAGES) {
-            this._requestTimeout = setTimeout(function() {
-                this._clearSearchArea();
-                this._numMessagesDisplayed = 0;
-                this._searchMessages();
-            }.bind(this), 300);
-        } else if (this._searchArea == this._searchAreas.USERSINCOURSE) {
-            this._requestTimeout = setTimeout(function() {
-                this._clearSearchArea();
-                this._numUsersDisplayed = 0;
-                this._searchUsersInCourse();
-            }.bind(this), 300);
-        } else { // Must be searching for users and courses
-            this._requestTimeout = setTimeout(function() {
-                this._clearSearchArea();
-                this._numUsersDisplayed = 0;
-                this._searchUsers();
-            }.bind(this), 300);
-        }
-    };
-
-    /**
-     * Handles searching for messages.
-     *
-     * @private
-     * @return {Promise|boolean} The promise resolved when the search area has been rendered
-     */
-    Search.prototype._searchMessages = function() {
-        if (this._isLoading) {
-            return false;
-        }
-
-        var str = this.messageArea.find(SELECTORS.SEARCHBOX).val();
-
-        // Tell the user we are loading items.
-        this._isLoading = true;
-
-        // Call the web service to get our data.
-        var promises = Ajax.call([{
-            methodname: 'core_message_data_for_messagearea_search_messages',
-            args: {
-                userid: this.messageArea.getCurrentUserId(),
-                search: str,
-                limitfrom: this._numMessagesDisplayed,
-                limitnum: this._numMessagesToRetrieve
-            }
-        }]);
-
-        // Keep track of the number of messages
-        var numberreceived = 0;
-        // Add loading icon to the end of the list.
-        return Templates.render('core/loading', {}).then(function(html, js) {
-            Templates.appendNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA),
-                "<div style='text-align:center'>" + html + "</div>", js);
-            return promises[0];
-        }.bind(this)).then(function(data) {
-            numberreceived = data.contacts.length;
-            return Templates.render('core_message/message_area_message_search_results', data);
-        }).then(function(html, js) {
-            // Remove the loading icon.
-            this.messageArea.find(SELECTORS.SEARCHRESULTSAREA + " " +
-                SELECTORS.LOADINGICON).remove();
-            // Only append data if we got data back.
-            if (numberreceived > 0) {
-                // Show the new content.
-                Templates.appendNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), html, js);
-                // Increment the number of contacts displayed.
-                this._numMessagesDisplayed += numberreceived;
-            } else if (this._numMessagesDisplayed == 0) { // Must have nothing to begin with.
-                // Replace the new content.
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), html, js);
-            }
-            // Mark that we are no longer busy loading data.
-            this._isLoading = false;
-        }.bind(this)).fail(Notification.exception);
-    };
-
-    /**
-     * Handles searching for users.
-     *
-     * @private
-     * @return {Promise} The promise resolved when the search area has been rendered
-     */
-    Search.prototype._searchUsers = function() {
-        var str = this.messageArea.find(SELECTORS.SEARCHBOX).val();
-
-        // Call the web service to get our data.
-        var promises = Ajax.call([{
-            methodname: 'core_message_data_for_messagearea_search_users',
-            args: {
-                userid: this.messageArea.getCurrentUserId(),
-                search: str,
-                limitnum: this._numUsersToRetrieve
-            }
-        }]);
-
-        // Perform the search and replace the content.
-        return Templates.render('core/loading', {}).then(function(html, js) {
-            Templates.replaceNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA),
-                "<div style='text-align:center'>" + html + "</div>", js);
-            return promises[0];
-        }.bind(this)).then(function(data) {
-            if (data.contacts.length > 0) {
-                data.hascontacts = true;
-            }
-            if (data.courses.length > 0) {
-                data.hascourses = true;
-            }
-            if (data.noncontacts.length > 0) {
-                data.hasnoncontacts = true;
-            }
-            return Templates.render('core_message/message_area_user_search_results', data);
-        }).then(function(html, js) {
-            Templates.replaceNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), html, js);
-        }.bind(this)).fail(Notification.exception);
-    };
-
-    /**
-     * Handles searching for users in a course.
-     *
-     * @private
-     * @return {Promise|boolean} The promise resolved when the search area has been rendered
-     */
-    Search.prototype._searchUsersInCourse = function() {
-        if (this._isLoading) {
-            return false;
-        }
-
-        var str = this.messageArea.find(SELECTORS.SEARCHBOX).val();
-
-        // Tell the user we are loading items.
-        this._isLoading = true;
-
-        // Call the web service to get our data.
-        var promises = Ajax.call([{
-            methodname: 'core_message_data_for_messagearea_search_users_in_course',
-            args: {
-                userid: this.messageArea.getCurrentUserId(),
-                courseid: this._courseid,
-                search: str,
-                limitfrom: this._numUsersDisplayed,
-                limitnum: this._numUsersToRetrieve
-            }
-        }]);
-
-        // Keep track of the number of contacts
-        var numberreceived = 0;
-        // Add loading icon to the end of the list.
-        return Templates.render('core/loading', {}).then(function(html, js) {
-            Templates.appendNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA),
-                "<div style='text-align:center'>" + html + "</div>", js);
-            return promises[0];
-        }.bind(this)).then(function(data) {
-            numberreceived = data.contacts.length;
-            if (numberreceived > 0) {
-                data.hascontacts = true;
-            }
-            return Templates.render('core_message/message_area_user_search_results', data);
-        }).then(function(html, js) {
-            // Remove the loading icon.
-            this.messageArea.find(SELECTORS.SEARCHRESULTSAREA + " " +
-                SELECTORS.LOADINGICON).remove();
-            // Only append data if we got data back.
-            if (numberreceived > 0) {
-                // Show the new content.
-                Templates.appendNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), html, js);
-                // Increment the number of contacts displayed.
-                this._numUsersDisplayed += numberreceived;
-            } else if (this._numUsersDisplayed == 0) { // Must have nothing to begin with.
-                // Replace the new content.
-                Templates.replaceNodeContents(this.messageArea.find(SELECTORS.SEARCHRESULTSAREA), html, js);
-            }
-            // Mark that we are no longer busy loading data.
-            this._isLoading = false;
-        }.bind(this)).fail(Notification.exception);
-    };
-
-    /**
-     * Sets placeholder text for search input.
-     *
-     * @private
-     * @param {String} text The placeholder text
-     * @return {Promise} The promise resolved when the placeholder text has been set
-     */
-    Search.prototype._setPlaceholderText = function(text) {
-        return Str.get_string(text, 'message').then(function(s) {
-            this.messageArea.find(SELECTORS.SEARCHTEXTAREA + ' input').attr('placeholder', s);
-        }.bind(this));
-    };
-
-    /**
-     * Sets filter for search input.
-     *
-     * @private
-     * @param {String} text The filter text
-     */
-    Search.prototype._setFilter = function(text) {
-        this.messageArea.find(SELECTORS.SEARCHBOX).val('');
-        this.messageArea.find(SELECTORS.CONTACTSAREA).addClass('searchfilter');
-        this.messageArea.find(SELECTORS.SEARCHFILTERAREA).show();
-        this.messageArea.find(SELECTORS.SEARCHFILTER).html(text);
-        Str.get_string('removecoursefilter', 'message', text).then(function(languagestring) {
-            this.messageArea.find(SELECTORS.SEARCHFILTERAREA).attr('aria-label', languagestring);
-        }.bind(this)).catch(Notification.exception);
-    };
-
-    /**
-     * Hides filter for search input.
-     *
-     * @private
-     */
-    Search.prototype._clearFilters = function() {
-        this.messageArea.find(SELECTORS.CONTACTSAREA).removeClass('searchfilter');
-        this.messageArea.find(SELECTORS.SEARCHFILTER).empty();
-        this.messageArea.find(SELECTORS.SEARCHFILTERAREA).hide();
-        this.messageArea.find(SELECTORS.SEARCHFILTERAREA).removeAttr('aria-label');
-    };
-
-    /**
-     * Handles clearing the search area.
-     *
-     * @private
-     */
-    Search.prototype._clearSearchArea = function() {
-        this.messageArea.find(SELECTORS.SEARCHRESULTSAREA).empty();
-    };
-
-    /**
-     * Handles hiding the search area.
-     *
-     * @private
-     */
-    Search.prototype._hideSearchResults = function() {
-        this._clearFilters();
-        this.messageArea.find(SELECTORS.SEARCHTEXTAREA + ' input').val('');
-        this._clearSearchArea();
-        this.messageArea.find(SELECTORS.SEARCHRESULTSAREA).hide();
-    };
-
-    return Search;
-});
diff --git a/message/amd/src/message_area_tabs.js b/message/amd/src/message_area_tabs.js
deleted file mode 100644 (file)
index b0bcca8..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This module handles the tabs of the messaging area.
- *
- * @module     core_message/message_area_tabs
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['core/custom_interaction_events', 'core_message/message_area_events'], function(CustomEvents, Events) {
-
-    /** @type {Object} The list of selectors for the message area. */
-    var SELECTORS = {
-        ACTIVECONTACTSTAB: "[data-region='contacts-area'] [role='tab'][aria-selected='true']",
-        CONTACTSPANELS: "[data-region='contacts']",
-        VIEWCONTACTS: "[data-action='contacts-view']",
-        VIEWCONVERSATIONS: "[data-action='conversations-view']"
-    };
-
-    /**
-     * Tabs class.
-     *
-     * @param {Messagearea} messageArea The messaging area object.
-     */
-    function Tabs(messageArea) {
-        this.messageArea = messageArea;
-        this._init();
-    }
-
-    /** @type {Messagearea} The messaging area object. */
-    Tabs.prototype.messageArea = null;
-
-    /**
-     * Initialise the event listeners.
-     *
-     * @private
-     */
-    Tabs.prototype._init = function() {
-        CustomEvents.define(this.messageArea.node, [
-            CustomEvents.events.activate,
-            CustomEvents.events.up,
-            CustomEvents.events.down,
-            CustomEvents.events.next,
-            CustomEvents.events.previous,
-            CustomEvents.events.ctrlPageUp,
-            CustomEvents.events.ctrlPageDown,
-        ]);
-
-        this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWCONVERSATIONS,
-                this._viewConversations.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWCONTACTS,
-                this._viewContacts.bind(this));
-
-        // Change to the other tab if any arrow keys are pressed, since there are only two tabs.
-        this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.VIEWCONVERSATIONS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.VIEWCONVERSATIONS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.next, SELECTORS.VIEWCONVERSATIONS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.previous, SELECTORS.VIEWCONVERSATIONS,
-                this._toggleTabs.bind(this));
-        // Change to the other tab if any arrow keys are pressed, since there are only two tabs.
-        this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.VIEWCONTACTS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.VIEWCONTACTS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.next, SELECTORS.VIEWCONTACTS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.previous, SELECTORS.VIEWCONTACTS,
-                this._toggleTabs.bind(this));
-        // Tab panel keyboard handling.
-        this.messageArea.onDelegateEvent(CustomEvents.events.ctrlPageUp, SELECTORS.CONTACTSPANELS,
-                this._toggleTabs.bind(this));
-        this.messageArea.onDelegateEvent(CustomEvents.events.ctrlPageDown, SELECTORS.CONTACTSPANELS,
-                this._toggleTabs.bind(this));
-
-        this.messageArea.onCustomEvent(Events.MESSAGESENT, function() {
-            this._selectTab(SELECTORS.VIEWCONVERSATIONS, SELECTORS.VIEWCONTACTS);
-        }.bind(this));
-    };
-
-    /**
-     * Handles when the conversation tab is selected.
-     *
-     * @private
-     */
-    Tabs.prototype._viewConversations = function() {
-        this.messageArea.trigger(Events.CONVERSATIONSSELECTED);
-        this._selectTab(SELECTORS.VIEWCONVERSATIONS, SELECTORS.VIEWCONTACTS);
-    };
-
-    /**
-     * Handles when the contacts tab is selected.
-     *
-     * @private
-     */
-    Tabs.prototype._viewContacts = function() {
-        this.messageArea.trigger(Events.CONTACTSSELECTED);
-        this._selectTab(SELECTORS.VIEWCONTACTS, SELECTORS.VIEWCONVERSATIONS);
-    };
-
-    /**
-     * Sets a tab to selected.
-     *
-     * @param {String} tabselect The name of the tab to select
-     * @param {String} tabdeselect The name of the tab to deselect
-     * @private
-     */
-    Tabs.prototype._selectTab = function(tabselect, tabdeselect) {
-        tabdeselect = this.messageArea.find(tabdeselect);
-        tabdeselect.removeClass('selected');
-        tabdeselect.attr('aria-selected', 'false');
-        tabdeselect.attr('tabindex', '-1');
-
-        tabselect = this.messageArea.find(tabselect);
-        tabselect.addClass('selected');
-        tabselect.attr('aria-selected', 'true');
-        tabselect.attr('tabindex', '0');
-    };
-
-    /**
-     * Change to the inactive tab.
-     *
-     * @param {event} e The javascript event
-     * @param {object} data The additional event data
-     * @private
-     */
-    Tabs.prototype._toggleTabs = function(e, data) {
-        var activeTab = this.messageArea.find(SELECTORS.ACTIVECONTACTSTAB);
-
-        if (activeTab.is(SELECTORS.VIEWCONVERSATIONS)) {
-            this._viewContacts();
-        } else {
-            this._viewConversations();
-        }
-
-        this.messageArea.find(SELECTORS.ACTIVECONTACTSTAB).focus();
-
-        e.preventDefault();
-        e.stopPropagation();
-        data.originalEvent.preventDefault();
-        data.originalEvent.stopPropagation();
-    };
-
-    return Tabs;
-});
index 7ea6a2d..8823ac0 100644 (file)
@@ -70,21 +70,24 @@ function(
     /**
      * Get elements for route.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {Object} root The message drawer container.
      * @param {string} selector The route container.
      *
      * @return {array} elements Found route container objects.
     */
-    var getElementsForRoute = function(root, selector) {
+    var getParametersForRoute = function(namespace, root, selector) {
         var candidates = root.children();
         var header = candidates.filter(SELECTORS.HEADER_CONTAINER).find(selector);
         var body = candidates.filter(SELECTORS.BODY_CONTAINER).find(selector);
         var footer = candidates.filter(SELECTORS.FOOTER_CONTAINER).find(selector);
-        var elements = [header, body, footer].filter(function(element) {
-            return element.length;
-        });
 
-        return elements;
+        return [
+            namespace,
+            header.length ? header : null,
+            body.length ? body : null,
+            footer.length ? footer : null
+        ];
     };
 
     var routes = [
@@ -94,28 +97,30 @@ function(
         [Routes.VIEW_GROUP_INFO, SELECTORS.VIEW_GROUP_INFO, ViewGroupInfo.show, ViewGroupInfo.description],
         [Routes.VIEW_OVERVIEW, SELECTORS.VIEW_OVERVIEW, ViewOverview.show, ViewOverview.description],
         [Routes.VIEW_SEARCH, SELECTORS.VIEW_SEARCH, ViewSearch.show, ViewSearch.description],
-        [Routes.VIEW_SETTINGS, SELECTORS.VIEW_SETTINGS, ViewSettings.show, ViewSettings.description],
+        [Routes.VIEW_SETTINGS, SELECTORS.VIEW_SETTINGS, ViewSettings.show, ViewSettings.description]
     ];
 
     /**
      * Create routes.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {Object} root The message drawer container.
      */
-    var createRoutes = function(root) {
+    var createRoutes = function(namespace, root) {
         routes.forEach(function(route) {
-            Router.add(route[0], getElementsForRoute(root, route[1]), route[2], route[3]);
+            Router.add(namespace, route[0], getParametersForRoute(namespace, root, route[1]), route[2], route[3]);
         });
     };
 
     /**
      * Show the message drawer.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} root The message drawer container.
      */
-    var show = function(root) {
+    var show = function(namespace, root) {
         if (!root.attr('data-shown')) {
-            Router.go(Routes.VIEW_OVERVIEW);
+            Router.go(namespace, Routes.VIEW_OVERVIEW);
             root.attr('data-shown', true);
         }
 
@@ -148,9 +153,11 @@ function(
     /**
      * Listen to and handle events for routing, showing and hiding the message drawer.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} root The message drawer container.
+     * @param {bool} alwaysVisible Is this messaging app always shown?
      */
-    var registerEventListeners = function(root) {
+    var registerEventListeners = function(namespace, root, alwaysVisible) {
         CustomEvents.define(root, [CustomEvents.events.activate]);
         var paramRegex = /^data-route-param-?(\d*)$/;
 
@@ -186,7 +193,7 @@ function(
             var params = paramAttributes.map(function(attribute) {
                 return attribute.nodeValue;
             });
-            var routeParams = [route].concat(params);
+            var routeParams = [namespace, route].concat(params);
 
             Router.go.apply(null, routeParams);
 
@@ -194,40 +201,42 @@ function(
         });
 
         root.on(CustomEvents.events.activate, SELECTORS.ROUTES_BACK, function(e, data) {
-            Router.back();
+            Router.back(namespace);
 
             data.originalEvent.preventDefault();
         });
 
-        PubSub.subscribe(Events.SHOW, function() {
-            show(root);
-        });
-
-        PubSub.subscribe(Events.HIDE, function() {
-            hide(root);
-        });
+        if (!alwaysVisible) {
+            PubSub.subscribe(Events.SHOW, function() {
+                show(namespace, root);
+            });
 
-        PubSub.subscribe(Events.TOGGLE_VISIBILITY, function() {
-            if (isVisible(root)) {
+            PubSub.subscribe(Events.HIDE, function() {
                 hide(root);
-            } else {
-                show(root);
-            }
-        });
+            });
+
+            PubSub.subscribe(Events.TOGGLE_VISIBILITY, function() {
+                if (isVisible(root)) {
+                    hide(root);
+                } else {
+                    show(namespace, root);
+                }
+            });
+        }
 
         PubSub.subscribe(Events.SHOW_CONVERSATION, function(conversationId) {
-            show(root);
-            Router.go(Routes.VIEW_CONVERSATION, conversationId);
+            show(namespace, root);
+            Router.go(namespace, Routes.VIEW_CONVERSATION, conversationId);
         });
 
         PubSub.subscribe(Events.CREATE_CONVERSATION_WITH_USER, function(userId) {
-            show(root);
-            Router.go(Routes.VIEW_CONVERSATION, null, 'create', userId);
+            show(namespace, root);
+            Router.go(namespace, Routes.VIEW_CONVERSATION, null, 'create', userId);
         });
 
         PubSub.subscribe(Events.SHOW_SETTINGS, function() {
-            show(root);
-            Router.go(Routes.VIEW_SETTINGS);
+            show(namespace, root);
+            Router.go(namespace, Routes.VIEW_SETTINGS);
         });
 
         PubSub.subscribe(Events.PREFERENCES_UPDATED, function(preferences) {
@@ -247,11 +256,25 @@ function(
      * Initialise the message drawer.
      *
      * @param {Object} root The message drawer container.
+     * @param {String} uniqueId Unique identifier for the Routes
+     * @param {bool} alwaysVisible Should we show the app now, or wait for the user?
+     * @param {int} sendToUser Should we message someone now?
+     * @param {int} conversationId The value of the conversation id, null if none
      */
-    var init = function(root) {
+    var init = function(root, uniqueId, alwaysVisible, sendToUser, conversationId) {
         root = $(root);
-        createRoutes(root);
-        registerEventListeners(root);
+        createRoutes(uniqueId, root);
+        registerEventListeners(uniqueId, root, alwaysVisible);
+        if (alwaysVisible) {
+            show(uniqueId, root);
+            if (sendToUser) {
+                if (conversationId) {
+                    Router.go(uniqueId, Routes.VIEW_CONVERSATION, conversationId);
+                } else {
+                    Router.go(uniqueId, Routes.VIEW_CONVERSATION, null, 'create', sendToUser);
+                }
+            }
+        }
     };
 
     return {
index a6be5ab..5134ee6 100644 (file)
@@ -41,8 +41,8 @@ function(
     /* @var {object} routes Message drawer route elements and callbacks. */
     var routes = {};
 
-    /* @var {array} history Store for route objects history. */
-    var history = [];
+    /* @var {object} history Store for route objects history. */
+    var history = {};
 
     var SELECTORS = {
         CAN_RECEIVE_FOCUS: 'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',
@@ -52,40 +52,52 @@ function(
     /**
      * Add a route.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {string} route Route config name.
-     * @param {array} elements Route container objects.
+     * @param {array} parameters Route parameters.
      * @param {callback} onGo Route initialization function.
      * @param {callback} getDescription Route initialization function.
      */
-    var add = function(route, elements, onGo, getDescription) {
-        routes[route] = {
-            elements: elements,
-            onGo: onGo,
-            getDescription: getDescription
-        };
+    var add = function(namespace, route, parameters, onGo, getDescription) {
+        if (!routes[namespace]) {
+            routes[namespace] = [];
+        }
+
+        routes[namespace][route] =
+            {
+                parameters: parameters,
+                onGo: onGo,
+                getDescription: getDescription
+            };
     };
 
     /**
      * Go to a defined route and run the route callbacks.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {string} newRoute Route config name.
      * @return {object} record Current route record with route config name and parameters.
      */
-    var changeRoute = function(newRoute) {
+    var changeRoute = function(namespace, newRoute) {
         var newConfig;
         // Get the rest of the arguments, if any.
-        var args = [].slice.call(arguments, 1);
+        var args = [].slice.call(arguments, 2);
         var renderPromise = $.Deferred().resolve().promise();
 
-        Object.keys(routes).forEach(function(route) {
-            var config = routes[route];
+        Object.keys(routes[namespace]).forEach(function(route) {
+            var config = routes[namespace][route];
             var isMatch = route === newRoute;
 
             if (isMatch) {
                 newConfig = config;
             }
 
-            config.elements.forEach(function(element) {
+            config.parameters.forEach(function(element) {
+                // Some parameters may be null, or not an element.
+                if (typeof element !== 'object' || element === null) {
+                    return;
+                }
+
                 element.removeClass('previous');
 
                 if (isMatch) {
@@ -100,12 +112,23 @@ function(
 
         if (newConfig) {
             if (newConfig.onGo) {
-                renderPromise = newConfig.onGo.apply(undefined, newConfig.elements.concat(args));
+                renderPromise = newConfig.onGo.apply(undefined, newConfig.parameters.concat(args));
                 var currentFocusElement = $(document.activeElement);
                 var hasFocus = false;
+                var firstFocusable = null;
 
-                for (var i = 0; i < newConfig.elements.length; i++) {
-                    var element = newConfig.elements[i];
+                // No need to start at 0 as we know that is the namespace.
+                for (var i = 1; i < newConfig.parameters.length; i++) {
+                    var element = newConfig.parameters[i];
+
+                    // Some parameters may be null, or not an element.
+                    if (typeof element !== 'object' || element === null) {
+                        continue;
+                    }
+
+                    if (!firstFocusable) {
+                        firstFocusable = element;
+                    }
 
                     if (element.has(currentFocusElement).length) {
                         hasFocus = true;
@@ -116,7 +139,7 @@ function(
                 if (!hasFocus) {
                     // This page doesn't have focus yet so focus the first focusable
                     // element in the new view.
-                    newConfig.elements[0].find(SELECTORS.CAN_RECEIVE_FOCUS).filter(':visible').first().focus();
+                    firstFocusable.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(':visible').first().focus();
                 }
             }
         }
@@ -135,18 +158,23 @@ function(
     /**
      * Go to a defined route and store the route history.
      *
-     * @param {string} newRoute Route config name.
+     * @param {String} namespace Unique identifier for the Routes
      * @return {object} record Current route record with route config name and parameters.
      */
-    var go = function() {
+    var go = function(namespace) {
         var currentFocusElement = $(document.activeElement);
         var record = changeRoute.apply(null, arguments);
         var inHistory = false;
+
+        if (!history[namespace]) {
+            history[namespace] = [];
+        }
+
         // History stores a unique list of routes. Check to see if the new route
         // is already in the history, if it is then forget all history after it.
         // This ensures there are no duplicate routes in history and that it represents
-        // a linear path of routes (it never stores something like [foo, bar, foo])/
-        history = history.reduce(function(carry, previous) {
+        // a linear path of routes (it never stores something like [foo, bar, foo])).
+        history[namespace] = history[namespace].reduce(function(carry, previous) {
             if (previous.route === record.route) {
                 inHistory = true;
             }
@@ -158,20 +186,29 @@ function(
             return carry;
         }, []);
 
-        var previousRecord = history.length ? history[history.length - 1] : null;
+        var historylength = history[namespace].length;
+        var previousRecord = historylength ? history[namespace][historylength - 1] : null;
 
         if (previousRecord) {
-            var prevConfig = routes[previousRecord.route];
-            prevConfig.elements.forEach(function(element) {
-                element.addClass('previous');
-            });
+            var prevConfig = routes[namespace][previousRecord.route];
+            var elements = prevConfig.parameters;
+
+            // The first one will be the namespace, skip it.
+            for (var i = 1; i < elements.length; i++) {
+                // Some parameters may be null, or not an element.
+                if (typeof elements[i] !== 'object' || elements[i] === null) {
+                    continue;
+                }
+
+                elements[i].addClass('previous');
+            }
 
             previousRecord.focusElement = currentFocusElement;
 
             if (prevConfig.getDescription) {
                 // If the route has a description then set it on the back button for
                 // the new page we're displaying.
-                prevConfig.getDescription.apply(null, prevConfig.elements.concat(previousRecord.params))
+                prevConfig.getDescription.apply(null, prevConfig.parameters.concat(previousRecord.params))
                     .then(function(description) {
                         return Str.get_string('backto', 'core_message', description);
                     })
@@ -180,12 +217,15 @@ function(
                         // that the back button is visible.
                         return record.renderPromise.then(function() {
                             // Find the elements for the new route we displayed.
-                            routes[record.route].elements.forEach(function(element) {
+                            routes[namespace][record.route].parameters.forEach(function(element) {
+                                // Some parameters may be null, or not an element.
+                                if (typeof element !== 'object' || !element) {
+                                    return;
+                                }
+
                                 // Update the aria label for the back button.
                                 element.find(SELECTORS.ROUTES_BACK).attr('aria-label', label);
                             });
-
-                            return;
                         });
                     })
                     .catch(function() {
@@ -193,23 +233,24 @@ function(
                     });
             }
         }
-
-        history.push(record);
+        history[namespace].push(record);
         return record;
     };
 
     /**
      * Go back to the previous route record stored in history.
+     *
+     * @param {String} namespace Unique identifier for the Routes
      */
-    var back = function() {
-        if (history.length) {
+    var back = function(namespace) {
+        if (history[namespace].length) {
             // Remove the current route.
-            history.pop();
-            var previous = history.pop();
+            history[namespace].pop();
+            var previous = history[namespace].pop();
 
             if (previous) {
                 // If we have a previous route then show it.
-                go.apply(undefined, [previous.route].concat(previous.params));
+                go.apply(undefined, [namespace, previous.route].concat(previous.params));
                 // Delay the focus 50 milliseconds otherwise it doesn't correctly
                 // focus the element for some reason...
                 window.setTimeout(function() {
index ffae0df..141c56a 100644 (file)
@@ -68,12 +68,15 @@ function(
     /**
      * Setup the contact page.
      *
-     * @param {Object} root Contact container element.
+     * @param {string} namespace The route namespace.
+     * @param {Object} header Contact header element.
+     * @param {Object} body Contact body container element.
+     * @param {Object} footer Contact footer container element.
      * @param {Object} contact The contact object.
      * @returns {Object} jQuery promise
      */
-    var show = function(root, contact) {
-        root = $(root);
+    var show = function(namespace, header, body, footer, contact) {
+        var root = $(body);
 
         getContentContainer(root).empty();
         return render(root, contact);
index b3821ab..f64898a 100644 (file)
@@ -123,11 +123,12 @@ function(
     /**
      * Setup the contact page.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} header Contacts header container element.
      * @param {Object} body Contacts body container element.
      * @return {Object} jQuery promise
      */
-    var show = function(header, body) {
+    var show = function(namespace, header, body) {
         body = $(body);
 
         if (!body.attr('data-contacts-init')) {
index 84c0f51..17f8260 100644 (file)
@@ -42,7 +42,7 @@ function(
 ) {
 
     var limit = 100;
-    var offset = 0;
+    var initialOffset = 0;
 
     var SELECTORS = {
         BLOCK_ICON_CONTAINER: '[data-region="block-icon-container"]',
@@ -87,28 +87,29 @@ function(
     /**
      * Load the user contacts and call the renderer.
      *
-     * @param {Object} listRoot The lazy loaded list root element
-     * @param {Integer} userId The logged in user id.
-     * @return {Object} jQuery promise
+     * @param {Number} offset The offset to use for loading contacts
+     * @return {Function} the callback.
      */
-    var load = function(listRoot, userId) {
-        return MessageRepository.getContacts(userId, (limit + 1), offset)
-            .then(function(result) {
-                return result;
-            })
-            .then(function(contacts) {
-                if (contacts.length > limit) {
-                    contacts.pop();
-                } else {
-                    LazyLoadList.setLoadedAll(listRoot, true);
-                }
-                return contacts;
-            })
-            .then(function(contacts) {
-                offset = offset + limit;
-                return contacts;
-            })
-            .catch(Notification.exception);
+    var getLoadFunction = function(offset) {
+        return function(listRoot, userId) {
+            return MessageRepository.getContacts(userId, (limit + 1), offset)
+                .then(function(result) {
+                    return result;
+                })
+                .then(function(contacts) {
+                    if (contacts.length > limit) {
+                        contacts.pop();
+                    } else {
+                        LazyLoadList.setLoadedAll(listRoot, true);
+                    }
+                    return contacts;
+                })
+                .then(function(contacts) {
+                    offset = offset + limit;
+                    return contacts;
+                })
+                .catch(Notification.exception);
+        };
     };
 
     /**
@@ -191,7 +192,7 @@ function(
         }
 
         // The root element is already the lazy loaded list root.
-        LazyLoadList.show(root, load, render);
+        LazyLoadList.show(root, getLoadFunction(initialOffset), render);
     };
 
     return {
index 9c99eba..486ee7c 100644 (file)
@@ -104,6 +104,9 @@ function(
     // This is the render function which will be generated when this module is
     // first called. See generateRenderFunction for details.
     var render = null;
+    // The list of renderers that have been registered to render
+    // this conversation. See generateRenderFunction for details.
+    var renderers = [];
 
     var NEWEST_FIRST = Constants.NEWEST_MESSAGES_FIRST;
     var LOAD_MESSAGE_LIMIT = Constants.LOAD_MESSAGE_LIMIT;
@@ -1049,15 +1052,33 @@ function(
      * @param  {Object} header The conversation header container element.
      * @param  {Object} body The conversation body container element.
      * @param  {Object} footer The conversation footer container element.
+     * @param  {Bool} isNewConversation Has someone else already initialised a conversation?
      * @return {Promise} Renderer promise.
      */
-    var generateRenderFunction = function(header, body, footer) {
+    var generateRenderFunction = function(header, body, footer, isNewConversation) {
+        var rendererFunc = function(patch) {
+            return Renderer.render(header, body, footer, patch);
+        };
+
+        if (!isNewConversation) {
+            // Looks like someone got here before us! We'd better update our
+            // UI to make sure it matches.
+            var initialState = StateManager.buildInitialState(viewState.midnight, viewState.loggedInUserId, viewState.id);
+            var syncPatch = Patcher.buildPatch(initialState, viewState);
+            rendererFunc(syncPatch);
+        }
+
+        renderers.push(rendererFunc);
+
         return function(newState) {
             var patch = Patcher.buildPatch(viewState, newState);
             // This is a great place to add in some console logging if you need
             // to debug something. You can log the current state, the next state,
             // and the generated patch and see exactly what will be updated.
-            return Renderer.render(header, body, footer, patch)
+            var renderPromises = renderers.map(function(renderFunc) {
+                return renderFunc(patch);
+            });
+            return $.when.apply(null, renderPromises)
                 .then(function() {
                     viewState = newState;
                     if (newState.id) {
@@ -1154,14 +1175,16 @@ function(
     /**
      * Show the view contact page.
      *
-     * @param {Object} e Element this event handler is called on.
-     * @param {Object} data Data for this event.
+     * @param {String} namespace Unique identifier for the Routes
+     * @return {Function} View contact handler.
      */
-    var handleViewContact = function(e, data) {
-        var otherUserId = getOtherUserId();
-        var otherUser = viewState.members[otherUserId];
-        MessageDrawerRouter.go(MessageDrawerRoutes.VIEW_CONTACT, otherUser);
-        data.originalEvent.preventDefault();
+    var generateHandleViewContact = function(namespace) {
+        return function(e, data) {
+            var otherUserId = getOtherUserId();
+            var otherUser = viewState.members[otherUserId];
+            MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONTACT, otherUser);
+            data.originalEvent.preventDefault();
+        };
     };
 
     /**
@@ -1187,68 +1210,71 @@ function(
     };
 
     /**
-     * Show the view contact page.
+     * Show the view group info page.
      *
-     * @param {Object} e Element this event handler is called on.
-     * @param {Object} data Data for this event.
+     * @param {String} namespace Unique identifier for the Routes
+     * @return {Function} View group info handler.
      */
-    var handleViewGroupInfo = function(e, data) {
-        MessageDrawerRouter.go(
-            MessageDrawerRoutes.VIEW_GROUP_INFO,
-            {
-                id: viewState.id,
-                name: viewState.name,
-                subname: viewState.subname,
-                imageUrl: viewState.imageUrl,
-                totalMemberCount: viewState.totalMemberCount
-            },
-            viewState.loggedInUserId
-        );
-        data.originalEvent.preventDefault();
+    var generateHandleViewGroupInfo = function(namespace) {
+        return function(e, data) {
+            MessageDrawerRouter.go(
+                namespace,
+                MessageDrawerRoutes.VIEW_GROUP_INFO,
+                {
+                    id: viewState.id,
+                    name: viewState.name,
+                    subname: viewState.subname,
+                    imageUrl: viewState.imageUrl,
+                    totalMemberCount: viewState.totalMemberCount
+                },
+                viewState.loggedInUserId
+            );
+            data.originalEvent.preventDefault();
+        };
     };
 
-    var headerActivateHandlers = [
-        [SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)],
-        [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
-        [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
-        [SELECTORS.ACTION_REQUEST_REMOVE_CONTACT, generateConfirmActionHandler(requestRemoveContact)],
-        [SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION, generateConfirmActionHandler(requestDeleteConversation)],
-        [SELECTORS.ACTION_CANCEL_EDIT_MODE, handleCancelEditMode],
-        [SELECTORS.ACTION_VIEW_CONTACT, handleViewContact],
-        [SELECTORS.ACTION_VIEW_GROUP_INFO, handleViewGroupInfo],
-        [SELECTORS.ACTION_CONFIRM_FAVOURITE, handleSetFavourite],
-        [SELECTORS.ACTION_CONFIRM_UNFAVOURITE, handleUnsetFavourite],
-    ];
-    var bodyActivateHandlers = [
-        [SELECTORS.ACTION_CANCEL_CONFIRM, generateConfirmActionHandler(cancelRequest)],
-        [SELECTORS.ACTION_CONFIRM_BLOCK, generateConfirmActionHandler(blockUser)],
-        [SELECTORS.ACTION_CONFIRM_UNBLOCK, generateConfirmActionHandler(unblockUser)],
-        [SELECTORS.ACTION_CONFIRM_ADD_CONTACT, generateConfirmActionHandler(addContact)],
-        [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT, generateConfirmActionHandler(removeContact)],
-        [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(deleteSelectedMessages)],
-        [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION, generateConfirmActionHandler(deleteConversation)],
-        [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
-        [SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],
-        [SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],
-        [SELECTORS.MESSAGE, handleSelectMessage]
-    ];
-    var footerActivateHandlers = [
-        [SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],
-        [SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(requestDeleteSelectedMessages)],
-        [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
-        [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
-    ];
-
     /**
      * Listen to, and handle events for conversations.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} header Conversation header container element.
      * @param {Object} body Conversation body container element.
      * @param {Object} footer Conversation footer container element.
      */
-    var registerEventListeners = function(header, body, footer) {
+    var registerEventListeners = function(namespace, header, body, footer) {
         var isLoadingMoreMessages = false;
         var messagesContainer = getMessagesContainer(body);
+        var headerActivateHandlers = [
+            [SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)],
+            [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
+            [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
+            [SELECTORS.ACTION_REQUEST_REMOVE_CONTACT, generateConfirmActionHandler(requestRemoveContact)],
+            [SELECTORS.ACTION_REQUEST_DELETE_CONVERSATION, generateConfirmActionHandler(requestDeleteConversation)],
+            [SELECTORS.ACTION_CANCEL_EDIT_MODE, handleCancelEditMode],
+            [SELECTORS.ACTION_VIEW_CONTACT, generateHandleViewContact(namespace)],
+            [SELECTORS.ACTION_VIEW_GROUP_INFO, generateHandleViewGroupInfo(namespace)],
+            [SELECTORS.ACTION_CONFIRM_FAVOURITE, handleSetFavourite],
+            [SELECTORS.ACTION_CONFIRM_UNFAVOURITE, handleUnsetFavourite],
+        ];
+        var bodyActivateHandlers = [
+            [SELECTORS.ACTION_CANCEL_CONFIRM, generateConfirmActionHandler(cancelRequest)],
+            [SELECTORS.ACTION_CONFIRM_BLOCK, generateConfirmActionHandler(blockUser)],
+            [SELECTORS.ACTION_CONFIRM_UNBLOCK, generateConfirmActionHandler(unblockUser)],
+            [SELECTORS.ACTION_CONFIRM_ADD_CONTACT, generateConfirmActionHandler(addContact)],
+            [SELECTORS.ACTION_CONFIRM_REMOVE_CONTACT, generateConfirmActionHandler(removeContact)],
+            [SELECTORS.ACTION_CONFIRM_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(deleteSelectedMessages)],
+            [SELECTORS.ACTION_CONFIRM_DELETE_CONVERSATION, generateConfirmActionHandler(deleteConversation)],
+            [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
+            [SELECTORS.ACTION_ACCEPT_CONTACT_REQUEST, generateConfirmActionHandler(acceptContactRequest)],
+            [SELECTORS.ACTION_DECLINE_CONTACT_REQUEST, generateConfirmActionHandler(declineContactRequest)],
+            [SELECTORS.MESSAGE, handleSelectMessage]
+        ];
+        var footerActivateHandlers = [
+            [SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],
+            [SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(requestDeleteSelectedMessages)],
+            [SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
+            [SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
+        ];
 
         AutoRows.init(footer);
 
@@ -1503,6 +1529,7 @@ function(
      * 2.) A conversation id with no action or other user id (e.g. from the contacts page)
      * 3.) No conversation/id with an action and other other user id. (e.g. from contact page)
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} header Conversation header container element.
      * @param {Object} body Conversation body container element.
      * @param {Object} footer Conversation footer container element.
@@ -1511,7 +1538,7 @@ function(
      * @param {Number} otherUserId The other user id for a private conversation
      * @return {Object} jQuery promise
      */
-    var show = function(header, body, footer, conversationOrId, action, otherUserId) {
+    var show = function(namespace, header, body, footer, conversationOrId, action, otherUserId) {
         var conversation = null;
         var conversationId = null;
 
@@ -1531,14 +1558,6 @@ function(
             conversationId = getCachedPrivateConversationIdFromUserId(otherUserId);
         }
 
-        if (!body.attr('data-init')) {
-            // Generate the render function to bind the header, body, and footer
-            // elements to it so that we don't need to pass them around this module.
-            render = generateRenderFunction(header, body, footer);
-            registerEventListeners(header, body, footer);
-            body.attr('data-init', true);
-        }
-
         // This is a new conversation if:
         // 1. We don't already have a state
         // 2. The given conversation doesn't match the one currently loaded
@@ -1548,6 +1567,15 @@ function(
         //    with to viewing a different user that they also haven't initialised a
         //    conversation with.
         var isNewConversation = !viewState || (viewState.id != conversationId) || (otherUserId && otherUserId != getOtherUserId());
+
+        if (!body.attr('data-init')) {
+            // Generate the render function to bind the header, body, and footer
+            // elements to it so that we don't need to pass them around this module.
+            render = generateRenderFunction(header, body, footer, isNewConversation);
+            registerEventListeners(namespace, header, body, footer);
+            body.attr('data-init', true);
+        }
+
         if (isNewConversation) {
             // Reset all of the states back to the beginning if we're loading a new
             // conversation.
index a9e721a..3fd9215 100644 (file)
@@ -134,13 +134,16 @@ function(
     /**
      * Setup the contact page.
      *
-     * @param {Object} root Contact container element.
+     * @param {string} namespace The route namespace.
+     * @param {Object} header Contact header container element.
+     * @param {Object} body Contact body container element.
+     * @param {Object} footer Contact body container element.
      * @param {Number} conversation The conversation
      * @param {Number} loggedInUserId The logged in user id
      * @return {Object} jQuery promise
      */
-    var show = function(root, conversation, loggedInUserId) {
-        root = $(root);
+    var show = function(namespace, header, body, footer, conversation, loggedInUserId) {
+        var root = $(body);
 
         getContentContainer(root).empty();
         return render(root, conversation, loggedInUserId)
index e214fca..74f4d71 100644 (file)
@@ -186,18 +186,19 @@ function(
     /**
      * Listen to, and handle event in the overview header.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {Object} header Conversation header container element.
      */
-    var registerEventListeners = function(header) {
+    var registerEventListeners = function(namespace, header) {
         var searchInput = getSearchInput(header);
         var ignoredKeys = [KeyCodes.tab, KeyCodes.shift, KeyCodes.ctrl, KeyCodes.alt];
 
         searchInput.on('click', function() {
-            Router.go(Routes.VIEW_SEARCH);
+            Router.go(namespace, Routes.VIEW_SEARCH);
         });
         searchInput.on('keydown', function(e) {
             if (ignoredKeys.indexOf(e.keyCode) < 0 && e.key != 'Meta') {
-                Router.go(Routes.VIEW_SEARCH);
+                Router.go(namespace, Routes.VIEW_SEARCH);
             }
         });
 
@@ -208,13 +209,14 @@ function(
     /**
      * Setup the overview page.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {Object} header Overview header container element.
      * @param {Object} body Overview body container element.
      * @return {Object} jQuery promise
      */
-    var show = function(header, body) {
+    var show = function(namespace, header, body) {
         if (!header.attr('data-init')) {
-            registerEventListeners(header);
+            registerEventListeners(namespace, header);
             header.attr('data-init', true);
         }
 
@@ -242,7 +244,8 @@ function(
                 return filterCountsByType(result.unread, sectionType);
             });
 
-            Section.show(sectionRoot, sectionType, includeFavourites, totalCountPromise, unreadCountPromise);
+            Section.show(namespace, null, sectionRoot, null, sectionType, includeFavourites,
+                totalCountPromise, unreadCountPromise);
         });
 
         return allCounts.then(function(result) {
index 45e5f76..8e39af3 100644 (file)
@@ -212,7 +212,7 @@ function(
      * Build the callback to load conversations.
      *
      * @param  {Number} type The conversation type.
-     * @param  {Bool} includeFavourites Include/exclude favourites.
+     * @param  {bool} includeFavourites Include/exclude favourites.
      * @param  {Number} offset Result offset
      * @return {Function}
      */
@@ -483,12 +483,13 @@ function(
     /**
      * Listen to, and handle events in this section.
      *
+     * @param {String} namespace Unique identifier for the Routes
      * @param {Object} root The section container element.
      * @param {Function} loadCallback The callback to load items.
      * @param {Number} type The conversation type for this section
-     * @param {Bool} includeFavourites If this section includes favourites
+     * @param {bool} includeFavourites If this section includes favourites
      */
-    var registerEventListeners = function(root, loadCallback, type, includeFavourites) {
+    var registerEventListeners = function(namespace, root, loadCallback, type, includeFavourites) {
         var listRoot = LazyLoadList.getRoot(root);
 
         // Set the minimum height of the section to the height of the toggle. This
@@ -586,7 +587,7 @@ function(
             var conversationElement = $(e.target).closest(SELECTORS.CONVERSATION);
             var conversationId = conversationElement.attr('data-conversation-id');
             var conversation = loadedConversationsById[conversationId];
-            MessageDrawerRouter.go(MessageDrawerRoutes.VIEW_CONVERSATION, conversation);
+            MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONVERSATION, conversation);
 
             data.originalEvent.preventDefault();
         });
@@ -595,18 +596,21 @@ function(
     /**
      * Setup the section.
      *
-     * @param {Object} root The section container element.
+     * @param {String} namespace Unique identifier for the Routes
+     * @param {Object} header The header container element.
+     * @param {Object} body The section container element.
+     * @param {Object} footer The footer container element.
      * @param {Number} type The conversation type for this section
-     * @param {Bool} includeFavourites If this section includes favourites
+     * @param {bool} includeFavourites If this section includes favourites
      * @param {Object} totalCountPromise Resolves wth the total conversations count
      * @param {Object} unreadCountPromise Resolves wth the unread conversations count
      */
-    var show = function(root, type, includeFavourites, totalCountPromise, unreadCountPromise) {
-        root = $(root);
+    var show = function(namespace, header, body, footer, type, includeFavourites, totalCountPromise, unreadCountPromise) {
+        var root = $(body);
 
         if (!root.attr('data-init')) {
             var loadCallback = getLoadCallback(type, includeFavourites, 0);
-            registerEventListeners(root, loadCallback, type, includeFavourites);
+            registerEventListeners(namespace, root, loadCallback, type, includeFavourites);
 
             if (isVisible(root)) {
                 setExpanded(root);
index 8293d66..8db0aa7 100644 (file)
@@ -935,11 +935,12 @@ function(
     /**
      * Setup the search page.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} header Contacts header container element.
      * @param {Object} body Contacts body container element.
      * @return {Object} jQuery promise
      */
-    var show = function(header, body) {
+    var show = function(namespace, header, body) {
         if (!body.attr('data-init')) {
             registerEventListeners(header, body);
             body.attr('data-init', true);
index 8146bbd..6abe7ef 100644 (file)
@@ -264,12 +264,14 @@ function(
      * Initialise the settings page by adding event listeners to
      * the checkboxes.
      *
+     * @param {string} namespace The route namespace.
      * @param {Object} header The settings header element.
      * @param {Object} body The settings body element.
+     * @param {Object} footer The footer body element.
      * @param {Number} loggedInUserId The logged in user id.
      * @return {Object} jQuery promise
      */
-    var show = function(header, body, loggedInUserId) {
+    var show = function(namespace, header, body, footer, loggedInUserId) {
         if (!body.attr('data-init')) {
             init(body, loggedInUserId);
             body.attr('data-init', true);
diff --git a/message/amd/src/message_preferences.js b/message/amd/src/message_preferences.js
deleted file mode 100644 (file)
index c5944b6..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Controls the message preference page.
- *
- * @module     core_message/message_preferences
- * @class      message_preferences
- * @package    message
- * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core/ajax', 'core/notification',
-        'core_message/message_notification_preference', 'core/custom_interaction_events'],
-        function($, Ajax, Notification, MessageNotificationPreference, CustomEvents) {
-
-    var SELECTORS = {
-        PREFERENCE: '[data-state]',
-        PREFERENCES_CONTAINER: '[data-region="preferences-container"]',
-        CONTACTABLE_PRIVACY_CONTAINER: '[data-region="privacy-setting-container"]',
-    };
-
-    /**
-     * Constructor for the MessagePreferences.
-     *
-     * @param {object} element The root element for the message preferences
-     */
-    var MessagePreferences = function(element) {
-        this.root = $(element);
-        this.userId = this.root.find(SELECTORS.PREFERENCES_CONTAINER).attr('data-user-id');
-
-        this.registerEventListeners();
-    };
-
-    /**
-     * Check if the preferences have been disabled on this page.
-     *
-     * @method preferencesDisabled
-     * @return {bool}
-     */
-    MessagePreferences.prototype.preferencesDisabled = function() {
-        return this.root.find(SELECTORS.PREFERENCES_CONTAINER).hasClass('disabled');
-    };
-
-    /**
-     * Update the contactable privacy user preference in the DOM and
-     * send a request to update on the server.
-     *
-     * @return {Promise}
-     * @method saveContactablePrivacySetting
-     */
-    MessagePreferences.prototype.saveContactablePrivacySetting = function() {
-        var container = this.root.find(SELECTORS.CONTACTABLE_PRIVACY_CONTAINER);
-        var value = $("input[type='radio']:checked").val();
-
-        if (container.hasClass('loading')) {
-            return $.Deferred().resolve();
-        }
-
-        container.addClass('loading');
-
-        var request = {
-            methodname: 'core_user_update_user_preferences',
-            args: {
-                userid: this.userId,
-                preferences: [
-                    {
-                        type: container.attr('data-preference-key'),
-                        value: value,
-                    }
-                ]
-            }
-        };
-
-        return Ajax.call([request])[0]
-            .fail(Notification.exception)
-            .always(function() {
-                container.removeClass('loading');
-            });
-    };
-
-    /**
-     * Create all of the event listeners for the message preferences page.
-     *
-     * @method registerEventListeners
-     */
-    MessagePreferences.prototype.registerEventListeners = function() {
-        CustomEvents.define(this.root, [
-            CustomEvents.events.activate
-        ]);
-
-        this.root.on('change', function(e) {
-            // Add listener for privacy setting radio buttons change.
-            if (e.target.name == 'message_blocknoncontacts') {
-                this.saveContactablePrivacySetting();
-            } else {
-                // Add listener for processor preferences.
-                if (!this.preferencesDisabled()) {
-                    var preferencesContainer = $(e.target).closest(SELECTORS.PREFERENCES_CONTAINER);
-                    var preferenceElement = $(e.target).closest(SELECTORS.PREFERENCE);
-                    var messagePreference = new MessageNotificationPreference(preferencesContainer, this.userId);
-
-                    preferenceElement.addClass('loading');
-                    messagePreference.save().always(function() {
-                        preferenceElement.removeClass('loading');
-                    });
-                }
-            }
-        }.bind(this));
-    };
-
-    return MessagePreferences;
-});
diff --git a/message/amd/src/message_user_button.js b/message/amd/src/message_user_button.js
new file mode 100644 (file)
index 0000000..bedfe91
--- /dev/null
@@ -0,0 +1,73 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Module to message a user from their profile page.
+ *
+ * @module     core_message/message_user_button
+ * @copyright  2019 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/custom_interaction_events', 'core_message/message_drawer_helper'],
+    function($, CustomEvents, MessageDrawerHelper) {
+
+        /**
+         * Get the id for the user being messaged.
+         *
+         * @param {object} element jQuery object for the button
+         * @return {int}
+         */
+        var getUserId = function(element) {
+            return parseInt(element.attr('data-userid'));
+        };
+
+        /**
+         * Returns the conversation id, 0 if none.
+         *
+         * @param {object} element jQuery object for the button
+         * @return {int}
+         */
+        var getConversationId = function(element) {
+            return parseInt(element.attr('data-conversationid'));
+        };
+
+        /**
+         * Handles opening the messaging drawer to send a
+         * message to a given user.
+         *
+         * @method enhance
+         * @param {object} element jQuery object for the button
+         */
+        var send = function(element) {
+            element = $(element);
+
+            CustomEvents.define(element, [CustomEvents.events.activate]);
+
+            element.on(CustomEvents.events.activate, function(e, data) {
+                var conversationid = getConversationId(element);
+                if (conversationid) {
+                    MessageDrawerHelper.showConversation(conversationid);
+                } else {
+                    MessageDrawerHelper.createConversationWithUser(getUserId(element));
+                }
+                e.preventDefault();
+                data.originalEvent.preventDefault();
+            });
+        };
+
+        return /** @alias module:core_message/message_user_button */ {
+            send: send
+        };
+    });
index b033693..b15a97e 100644 (file)
@@ -151,10 +151,9 @@ class api {
     /**
      * Handles searching for user in a particular course in the message area.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * But we are deprecating data_for_messagearea_search_users_in_course external function.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_search_users_in_course.
+     * Followup: MDL-63261
      *
      * @param int $userid The user id doing the searching
      * @param int $courseid The id of the course we are searching in
@@ -197,10 +196,9 @@ class api {
     /**
      * Handles searching for user in the message area.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * But we are deprecating data_for_messagearea_search_users external function.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_search_users.
+     * Followup: MDL-63261
      *
      * @param int $userid The user id doing the searching
      * @param string $search The string the user is searching
@@ -1029,9 +1027,9 @@ class api {
     /**
      * Returns the contacts to display in the contacts area.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_contacts.
+     * Followup: MDL-63261
      *
      * @param int $userid The user id
      * @param int $limitfrom
@@ -1199,9 +1197,9 @@ class api {
     /**
      * Returns the messages to display in the message area.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_messages.
+     * Followup: MDL-63261
      *
      * @param int $userid the current user
      * @param int $otheruserid the other user
@@ -1280,9 +1278,9 @@ class api {
     /**
      * Returns the most recent message between two users.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_get_most_recent_message.
+     * Followup: MDL-63261
      *
      * @param int $userid the current user
      * @param int $otheruserid the other user
@@ -1325,9 +1323,9 @@ class api {
     /**
      * Returns the profile information for a contact for a user.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web service goes through final deprecation.
+     * The related web service is data_for_messagearea_get_profile.
+     * Followup: MDL-63261
      *
      * @param int $userid The user id
      * @param int $otheruserid The id of the user whose profile we want to view.
index 0bbdde7..9dbb3a9 100644 (file)
@@ -37,9 +37,9 @@ class helper {
     /**
      * Helper function to retrieve the messages between two users
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web services go through final deprecation.
+     * The related web services are data_for_messagearea_messages AND data_for_messagearea_get_most_recent_message.
+     * Followup: MDL-63261
      *
      * @param int $userid the current user
      * @param int $otheruserid the other user
@@ -232,9 +232,9 @@ class helper {
     /**
      * Helper function to return an array of messages.
      *
-     * TODO: This function should be removed once the new group messaging UI is in place and the old messaging UI is removed.
-     * For now we are not removing/deprecating this function for backwards compatibility with messaging UI.
-     * Followup: MDL-63915
+     * TODO: This function should be removed once the related web services go through final deprecation.
+     * The related web services are data_for_messagearea_messages AND data_for_messagearea_get_most_recent_message.
+     * Followup: MDL-63261
      *
      * @param int $userid
      * @param array $messages
@@ -435,6 +435,40 @@ class helper {
         return $params;
     }
 
+    /**
+     * Requires the JS libraries for the message user button.
+     *
+     * @return void
+     */
+    public static function messageuser_requirejs() {
+        global $PAGE;
+
+        static $done = false;
+        if ($done) {
+            return;
+        }
+
+        $PAGE->requires->js_call_amd('core_message/message_user_button', 'send', array('#message-user-button'));
+        $done = true;
+    }
+
+    /**
+     * Returns the attributes to place on the message user button.
+     *
+     * @param int $useridto
+     * @return array
+     */
+    public static function messageuser_link_params(int $useridto) : array {
+        global $USER;
+
+        return [
+            'id' => 'message-user-button',
+            'role' => 'button',
+            'data-conversationid' => api::get_conversation_between_users([$USER->id, $useridto]),
+            'data-userid' => $useridto,
+        ];
+    }
+
     /**
      * Returns the conversation hash between users for easy look-ups in the DB.
      *
@@ -604,6 +638,10 @@ class helper {
     /**
      * Backwards compatibility formatter, transforming the new output of get_conversations() into the old format.
      *
+     * TODO: This function should be removed once the related web services go through final deprecation.
+     * The related web services are data_for_messagearea_conversations.
+     * Followup: MDL-63261
+     *
      * @param array $conversations the array of conversations, which must come from get_conversations().
      * @return array the array of conversations, formatted in the legacy style.
      */
@@ -638,4 +676,88 @@ class helper {
         }
         return $tmp;
     }
+
+    /**
+     * Renders the messaging widget.
+     *
+     * @param bool $isdrawer Are we are rendering the drawer or is this on a full page?
+     * @param int|null $sendtouser The ID of the user we want to send a message to
+     * @param int|null $conversationid The ID of the conversation we want to load
+     * @return string The HTML.
+     */
+    public static function render_messaging_widget(bool $isdrawer, int $sendtouser = null, int $conversationid = null) {
+        global $USER, $CFG, $PAGE;
+
+        // Early bail out conditions.
+        if (empty($CFG->messaging) || !isloggedin() || isguestuser() || user_not_fully_set_up($USER) ||
+            get_user_preferences('auth_forcepasswordchange') ||
+            (!$USER->policyagreed && !is_siteadmin() &&
+                ($manager = new \core_privacy\local\sitepolicy\manager()) && $manager->is_defined())) {
+            return '';
+        }
+
+        $renderer = $PAGE->get_renderer('core');
+        $requestcount = \core_message\api::get_received_contact_requests_count($USER->id);
+        $contactscount = \core_message\api::count_contacts($USER->id);
+
+        $choices = [];
+        $choices[] = [
+            'value' => \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS,
+            'text' => get_string('contactableprivacy_onlycontacts', 'message')
+        ];
+        $choices[] = [
+            'value' => \core_message\api::MESSAGE_PRIVACY_COURSEMEMBER,
+            'text' => get_string('contactableprivacy_coursemember', 'message')
+        ];
+        if (!empty($CFG->messagingallusers)) {
+            // Add the MESSAGE_PRIVACY_SITE option when site-wide messaging between users is enabled.
+            $choices[] = [
+                'value' => \core_message\api::MESSAGE_PRIVACY_SITE,
+                'text' => get_string('contactableprivacy_site', 'message')
+            ];
+        }
+
+        // Enter to send.
+        $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $USER);
+
+        if ($isdrawer) {
+            $template = 'core_message/message_drawer';
+            $messageurl = new \moodle_url('/message/index.php');
+        } else {
+            $template = 'core_message/message_index';
+            $messageurl = null;
+        }
+
+        $templatecontext = [
+            'contactrequestcount' => $requestcount,
+            'loggedinuser' => [
+                'id' => $USER->id,
+                'midnight' => usergetmidnight(time())
+            ],
+            'contacts' => [
+                'sectioncontacts' => [
+                    'placeholders' => array_fill(0, $contactscount > 50 ? 50 : $contactscount, true)
+                ],
+                'sectionrequests' => [
+                    'placeholders' => array_fill(0, $requestcount > 50 ? 50 : $requestcount, true)
+                ],
+            ],
+            'settings' => [
+                'privacy' => $choices,
+                'entertosend' => $entertosend
+            ],
+            'overview' => [
+                'messageurl' => $messageurl
+            ],
+            'sendtouser' => false,
+            'conversationid' => false
+        ];
+
+        if ($sendtouser) {
+            $templatecontext['sendtouser'] = $sendtouser;
+            $templatecontext['conversationid'] = $conversationid;
+        }
+
+        return $renderer->render_from_template($template, $templatecontext);
+    }
 }
index 364940c..ab01237 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare a contact for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index db276d6..785d9a9 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare the contacts for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 5fc7e05..93177e0 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare a message for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index b5cb3bf..9d2e05d 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare the message area for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
diff --git a/message/classes/output/messagearea/message_search_results.php b/message/classes/output/messagearea/message_search_results.php
deleted file mode 100644 (file)
index 85c47c0..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Contains class used to display message search results.
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_message\output\messagearea;
-
-defined('MOODLE_INTERNAL') || die();
-
-use renderable;
-use templatable;
-
-/**
- * Class used to display message search results.
- *
- * @package   core_message
- * @copyright 2016 Mark Nelson <markn@moodle.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class message_search_results implements templatable, renderable {
-
-    /**
-     * @var array The list of contacts.
-     */
-    public $contacts;
-
-    /**
-     * Constructor.
-     *
-     * @param array $contacts
-     */
-    public function __construct($contacts) {
-        $this->contacts = $contacts;
-    }
-
-    public function export_for_template(\renderer_base $output) {
-        $data = new \stdClass();
-        $data->contacts = array();
-        foreach ($this->contacts as $contact) {
-            $contact = new contact($contact);
-            $data->contacts[] = $contact->export_for_template($output);
-        }
-
-        return $data;
-    }
-}
\ No newline at end of file
index 7d98e7b..ce8e75c 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare the messages for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index cbeb9ee..151bba1 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to prepare a profile for display.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 622c629..ac66358 100644 (file)
@@ -17,6 +17,9 @@
 /**
  * Contains class used to display user search results.
  *
+ * TODO: This file should be removed once the related web services go through final deprecation.
+ * Followup: MDL-63261
+ *
  * @package   core_message
  * @copyright 2016 Mark Nelson <markn@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
diff --git a/message/classes/output/renderer.php b/message/classes/output/renderer.php
deleted file mode 100644 (file)
index 0659064..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Contains class used to render the message area.
- *
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-namespace core_message\output;
-
-defined('MOODLE_INTERNAL') || die();
-
-use plugin_renderer_base;
-
-/**
- * Renderer class for the message area.
- *
- * @package    core_message
- * @copyright  2016 Mark Nelson <markn@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class renderer extends plugin_renderer_base {
-
-    /**
-     * Renders the message area.
-     *
-     * Defer to template.
-     *
-     * @param \core_message\output\messagearea\message_area $page
-     * @return string html for the page
-     */
-    public function render_message_area(\core_message\output\messagearea\message_area $page) {
-        $data = $page->export_for_template($this);
-        return parent::render_from_template('core_message/message_area', $data);
-    }
-}
\ No newline at end of file
index 588a4d4..a779b98 100644 (file)
@@ -1250,10 +1250,6 @@ class core_message_external extends external_api {
      *
      * @deprecated since 3.6
      *
-     * NOTE: We are deprecating this function but not search_users_in_course API function for backwards compatibility
-     * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed.
-     * Followup: MDL-63915
-     *
      * @param int $userid The id of the user who is performing the search
      * @param int $courseid The id of the course
      * @param string $search The string being searched
@@ -1351,10 +1347,6 @@ class core_message_external extends external_api {
      *
      * @deprecated since 3.6
      *
-     * NOTE: We are deprecating this function but not search_users API function for backwards compatibility
-     * with messaging UI. But should be removed once new group messaging UI is in place and old messaging UI is removed.
-     * Followup: MDL-63915
-     *
      * @param int $userid The id of the user who is performing the search
      * @param string $search The string being searched
      * @param int $limitnum
@@ -1538,7 +1530,7 @@ class core_message_external extends external_api {
      * @since 3.2
      */
     public static function data_for_messagearea_search_messages($userid, $search, $limitfrom = 0, $limitnum = 0) {
-        global $CFG, $PAGE, $USER;
+        global $CFG, $USER;
 
         // Check if messaging is enabled.
         if (empty($CFG->messaging)) {
@@ -1567,10 +1559,38 @@ class core_message_external extends external_api {
             $params['limitfrom'],
             $params['limitnum']
         );
-        $results = new \core_message\output\messagearea\message_search_results($messages);
 
-        $renderer = $PAGE->get_renderer('core_message');
-        return $results->export_for_template($renderer);
+        $data = new \stdClass();
+        $data->contacts = [];
+        foreach ($messages as $message) {
+            $contact = new \stdClass();
+            $contact->userid = $message->userid;
+            $contact->fullname = $message->fullname;
+            $contact->profileimageurl = $message->profileimageurl;
+            $contact->profileimageurlsmall = $message->profileimageurlsmall;
+            $contact->messageid = $message->messageid;
+            $contact->ismessaging = $message->ismessaging;
+            $contact->sentfromcurrentuser = false;
+            if ($message->lastmessage) {
+                if ($message->userid !== $message->useridfrom) {
+                    $contact->sentfromcurrentuser = true;
+                }
+                $contact->lastmessage = shorten_text($message->lastmessage, 60);
+            } else {
+                $contact->lastmessage = null;
+            }
+            $contact->lastmessagedate = $message->lastmessagedate;
+            $contact->showonlinestatus = is_null($message->isonline) ? false : true;
+            $contact->isonline = $message->isonline;
+            $contact->isblocked = $message->isblocked;
+            $contact->isread = $message->isread;
+            $contact->unreadcount = $message->unreadcount;
+            $contact->conversationid = $message->conversationid;
+
+            $data->contacts[] = $contact;
+        }
+
+        return $data;
     }
 
     /**
index 090381d..5883633 100644 (file)
@@ -36,61 +36,35 @@ if (empty($CFG->messaging)) {
 
 // The id of the user we want to view messages from.
 $id = optional_param('id', 0, PARAM_INT);
+// It's possible a user may come from a link where these parameters are specified.
+// We no longer support viewing another user's messaging area (that can be achieved
+// via the 'Log-in as' feature). The 'user2' value takes preference over 'id'.
+$userid = optional_param('user2', $id, PARAM_INT);
 
-// It's possible for someone with the right capabilities to view a conversation between two other users. For BC
-// we are going to accept other URL parameters to figure this out.
-$user1id = optional_param('user1', $USER->id, PARAM_INT);
-$user2id = optional_param('user2', $id, PARAM_INT);
-$contactsfirst = optional_param('contactsfirst', 0, PARAM_INT);
-
-$url = new moodle_url('/message/index.php');
-if ($id) {
-    $url->param('id', $id);
-} else {
-    if ($user1id) {
-        $url->param('user1', $user1id);
-    }
-    if ($user2id) {
-        $url->param('user2', $user2id);
-    }
-    if ($contactsfirst) {
-        $url->param('contactsfirst', $contactsfirst);
-    }
+if (!core_user::is_real_user($userid)) {
+    $userid = null;
 }
-$PAGE->set_url($url);
 
-$user1 = null;
-$currentuser = true;
-if ($user1id != $USER->id) {
-    $user1 = core_user::get_user($user1id, '*', MUST_EXIST);
-    $currentuser = false;
-} else {
-    $user1 = $USER;
-}
-
-$user2 = null;
-if (!empty($user2id)) {
-    $user2 = core_user::get_user($user2id, '*', MUST_EXIST);
+if ($userid) {
+    $recipient = new stdClass();
+    $recipient->id = $userid;
+    if (!\core_message\api::can_post_message($recipient)) {
+        throw new moodle_exception('Can not contact user');
+    }
 }
 
-$user2realuser = !empty($user2) && core_user::is_real_user($user2->id);
-$systemcontext = context_system::instance();
-if ($currentuser === false && !has_capability('moodle/site:readallmessages', $systemcontext)) {
-    print_error('accessdenied', 'admin');
+$url = new moodle_url('/message/index.php');
+if ($userid) {
+    $url->param('id', $userid);
 }
-
-$PAGE->set_context(context_user::instance($user1->id));
+$PAGE->set_url($url);
+$PAGE->set_context(context_user::instance($USER->id));
 $PAGE->set_pagelayout('standard');
+
 $strmessages = get_string('messages', 'message');
-if ($user2realuser) {
-    $user2fullname = fullname($user2);
 
-    $PAGE->set_title("$strmessages: $user2fullname");
-    $PAGE->set_heading("$strmessages: $user2fullname");
-} else {
-    $PAGE->set_title("{$SITE->shortname}: $strmessages");
-    $PAGE->set_heading("{$SITE->shortname}: $strmessages");
-}
+$PAGE->set_title("$strmessages");
+$PAGE->set_heading("$strmessages");
 
 // Remove the user node from the main navigation for this page.
 $usernode = $PAGE->navigation->find('users', null);
@@ -99,152 +73,19 @@ $usernode->remove();
 $settings = $PAGE->settingsnav->find('messages', null);
 $settings->make_active();
 
-if ($currentuser) {
-    // We're in the pprocess of deprecating this page however we haven't replaced the functionality
-    // for the admin (or user with correct capabilities) to view other user's conversations. For the
-    // time being this page will simply open the message drawer unless it's the admin user case just
-    // mentioned. In that case we will render the old UI for backwards compatibility.
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('messages', 'message'));
-    $conversationid = empty($user2id) ? null : \core_message\api::get_conversation_between_users([$USER->id, $user2id]);
-    if (empty($conversationid) && !empty($user2id)) {
-        $PAGE->requires->js_call_amd('core_message/message_drawer_helper', 'createConversationWithUser', [$user2id]);
-    } else if (!empty($conversationid)) {
-        $PAGE->requires->js_call_amd('core_message/message_drawer_helper', 'showConversation', [$conversationid]);
-    } else {
-        $PAGE->requires->js_call_amd('core_message/message_drawer_helper', 'show');
-    }
-    echo $OUTPUT->footer();
-    exit();
-}
-
-// The only time we should get here is if it's an admin type user viewing another user's messages.
-
-// Get the renderer and the information we are going to be use.
-$renderer = $PAGE->get_renderer('core_message');
-$requestedconversation = false;
-if ($contactsfirst) {
-    $conversations = \core_message\api::get_contacts($user1->id, 0, 20);
-} else {
-    $conversations = \core_message\api::get_conversations($user1->id, 0, 20);
-
-    // Format the conversations in the legacy style, as the get_conversations method has since been changed.
-    $conversations = \core_message\helper::get_conversations_legacy_formatter($conversations);
-}
-$messages = [];
-if (!$user2realuser) {
-    // If there are conversations, but the user has not chosen a particular one, then render the most recent one.
-    $user2 = new stdClass();
-    $user2->id = null;
-    if (!empty($conversations)) {
-        $contact = reset($conversations);
-        $user2->id = $contact->userid;
-    }
-} else {
-    // The user has specifically requested to see a conversation. Add the flag to
-    // the context so that we can render the messaging app appropriately - this is
-    // used for smaller screens as it allows the UI to be responsive.
-    $requestedconversation = true;
+// Check if there is an existing conversation with the supplied user (if there is one).
+$conversationid = null;
+if ($userid) {
+    $conversationid = \core_message\api::get_conversation_between_users([$USER->id, $userid]);
 }
 
-// Mark the conversation as read.
-if (!empty($user2->id)) {
-    $hasbeenreadallmessages = false;
-    if ($currentuser && isset($conversations[$user2->id])) {
-        // Mark the conversation we are loading as read.
-        if ($conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id])) {
-            \core_message\api::mark_all_messages_as_read($user1->id, $conversationid);
-            $hasbeenreadallmessages = true;
-        }
-
-        // Ensure the UI knows it's read as well.
-        $conversations[$user2->id]->isread = 1;
-    }
-
-    // Get the conversationid.
-    if (!isset($conversationid)) {
-        if (!$conversationid = \core_message\api::get_conversation_between_users([$user1->id, $user2->id])) {
-            // If the individual conversationid doesn't exist, create it.
-            $conversation = \core_message\api::create_conversation(
-                \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
-                [$user1->id, $user2->id]
-            );
-            $conversationid = $conversation->id;
-        }
-    }
-
-    $convmessages = \core_message\api::get_conversation_messages($user1->id, $conversationid, 0, 20, 'timecreated DESC');
-    $messages = [];
-    if (!empty($convmessages)) {
-        $messages = $convmessages['messages'];
-
-        // Parse the messages to add missing fields for backward compatibility.
-        $messages = array_reverse($messages);
-        // Keeps track of the last day, month and year combo we were viewing.
-        $day = '';
-        $month = '';
-        $year = '';
-        foreach ($messages as $message) {
-            // Add useridto.
-            if (empty($message->useridto)) {
-                if ($message->useridfrom == $user1->id) {
-                    $message->useridto = $user2->id;
-                } else {
-                    $message->useridto = $user1->id;
-                }
-            }
-
-            // Add currentuserid.
-            $message->currentuserid = $USER->id;
-
-            // Check if we are now viewing a different block period.
-            $message->displayblocktime = false;
-            $date = usergetdate($message->timecreated);
-            if ($day != $date['mday'] || $month != $date['month'] || $year != $date['year']) {
-                $day = $date['mday'];
-                $month = $date['month'];
-                $year = $date['year'];
-                $message->displayblocktime = true;
-                $message->blocktime = userdate($message->timecreated, get_string('strftimedaydate'));
-            }
-
-            // We don't have this information here so, for now, we leave an empty value or the current time.
-            // This is a temporary solution because a new UI is being built in MDL-63303.
-            $message->timeread = 0;
-            if ($hasbeenreadallmessages && $message->useridfrom != $user1->id) {
-                // As all the messages sent by the other user have been marked as read previously, we will change
-                // timeread to the current time to avoid the last message will be duplicated after calling to the
-                // core_message_data_for_messagearea_messages via javascript.
-                // We only need to change that to the other user, because for the current user, messages are always
-                // marked as unread.
-                $message->timeread = time();
-            }
-        }
-    }
-}
-
-$pollmin = !empty($CFG->messagingminpoll) ? $CFG->messagingminpoll : MESSAGE_DEFAULT_MIN_POLL_IN_SECONDS;
-$pollmax = !empty($CFG->messagingmaxpoll) ? $CFG->messagingmaxpoll : MESSAGE_DEFAULT_MAX_POLL_IN_SECONDS;
-$polltimeout = !empty($CFG->messagingtimeoutpoll) ? $CFG->messagingtimeoutpoll : MESSAGE_DEFAULT_TIMEOUT_POLL_IN_SECONDS;
-$messagearea = new \core_message\output\messagearea\message_area($user1->id, $user2->id, $conversations, $messages,
-        $requestedconversation, $contactsfirst, $pollmin, $pollmax, $polltimeout);
-
-// Now the page contents.
 echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('messages', 'message'));
-
 // Display a message if the messages have not been migrated yet.
-if (!get_user_preferences('core_message_migrate_data', false, $user1id)) {
+if (!get_user_preferences('core_message_migrate_data', false)) {
     $notify = new \core\output\notification(get_string('messagingdatahasnotbeenmigrated', 'message'),
         \core\output\notification::NOTIFY_WARNING);
     echo $OUTPUT->render($notify);
 }
-
-// Display a message that the user is viewing someone else's messages.
-if (!$currentuser) {
-    $notify = new \core\output\notification(get_string('viewinganotherusersmessagearea', 'message'),
-        \core\output\notification::NOTIFY_WARNING);
-    echo $OUTPUT->render($notify);
-}
-echo $renderer->render($messagearea);
+echo $OUTPUT->heading(get_string('messages', 'message'));
+echo \core_message\helper::render_messaging_widget(false, $userid, $conversationid);
 echo $OUTPUT->footer();
index 5ed7fff..759a50f 100644 (file)
@@ -804,63 +804,10 @@ function core_message_render_navbar_output(\renderer_base $renderer) {
 }
 
 /**
- * Render the message drawer to be included in the top of the body of
- * each page.
+ * Render the message drawer to be included in the top of the body of each page.
  *
  * @return string HTML
  */
 function core_message_standard_after_main_region_html() {
-    global $USER, $CFG, $PAGE;
-
-    // Early bail out conditions.
-    if (empty($CFG->messaging) || !isloggedin() || isguestuser() || user_not_fully_set_up($USER) ||
-        get_user_preferences('auth_forcepasswordchange') ||
-        (!$USER->policyagreed && !is_siteadmin() &&
-            ($manager = new \core_privacy\local\sitepolicy\manager()) && $manager->is_defined())) {
-        return '';
-    }
-
-    $renderer = $PAGE->get_renderer('core');
-    $requestcount = \core_message\api::get_received_contact_requests_count($USER->id);
-    $contactscount = \core_message\api::count_contacts($USER->id);
-
-    $choices = [];
-    $choices[] = [
-        'value' => \core_message\api::MESSAGE_PRIVACY_ONLYCONTACTS,
-        'text' => get_string('contactableprivacy_onlycontacts', 'message')
-    ];
-    $choices[] = [
-        'value' => \core_message\api::MESSAGE_PRIVACY_COURSEMEMBER,
-        'text' => get_string('contactableprivacy_coursemember', 'message')
-    ];
-    if (!empty($CFG->messagingallusers)) {
-        // Add the MESSAGE_PRIVACY_SITE option when site-wide messaging between users is enabled.
-        $choices[] = [
-            'value' => \core_message\api::MESSAGE_PRIVACY_SITE,
-            'text' => get_string('contactableprivacy_site', 'message')
-        ];
-    }
-
-    // Enter to send.
-    $entertosend = get_user_preferences('message_entertosend', $CFG->messagingdefaultpressenter, $USER);
-
-    return $renderer->render_from_template('core_message/message_drawer', [
-        'contactrequestcount' => $requestcount,
-        'loggedinuser' => [
-            'id' => $USER->id,
-            'midnight' => usergetmidnight(time())
-        ],
-        'contacts' => [
-            'sectioncontacts' => [
-                'placeholders' => array_fill(0, $contactscount > 50 ? 50 : $contactscount, true)
-            ],
-            'sectionrequests' => [
-                'placeholders' => array_fill(0, $requestcount > 50 ? 50 : $requestcount, true)
-            ],
-        ],
-        'settings' => [
-            'privacy' => $choices,
-            'entertosend' => $entertosend
-        ]
-    ]);
+    return \core_message\helper::render_messaging_widget(true, null, null);
 }
diff --git a/message/templates/message_area.mustache b/message/templates/message_area.mustache
deleted file mode 100644 (file)
index 1a6cbe7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-<div class="messaging-area-container" data-userid="{{userid}}" data-displaycontacts="{{contactsfirst}}">
-    <div class="messaging-area {{#requestedconversation}}show-messages{{/requestedconversation}}
-            {{^requestedconversation}}hide-messages{{/requestedconversation}}" data-region="messaging-area">
-        <div class="contacts-area" data-region="contacts-area" role="tablist">
-            {{#contacts}}
-                {{> core_message/message_area_contacts_area }}
-            {{/contacts}}
-        </div>
-        <div class="messages-area" data-region="messages-area" role="log">
-            {{#messages}}
-                {{> core_message/message_area_messages_area }}
-            {{/messages}}
-        </div>
-    </div>
-</div>
-{{#js}}
-    require(['core_message/message_area'],
-        function(Messagearea) {
-            new Messagearea('.messaging-area-container', {{pollmin}}, {{pollmax}}, {{polltimeout}});
-        }
-    );
-{{/js}}
diff --git a/message/templates/message_area_contact.mustache b/message/templates/message_area_contact.mustache
deleted file mode 100644 (file)
index 2ca50f2..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-<div class="contact {{#selected}}selected{{/selected}} {{#lastmessage}}{{^isread}}unread{{/isread}}{{/lastmessage}}"
-     data-action="view-contact-msg"
-     data-userid="{{userid}}" data-messageid="{{#messageid}}{{.}}{{isread}}{{/messageid}}" data-region="contact" role="button"
-     aria-pressed="{{#selected}}true{{/selected}}{{^selected}}false{{/selected}}" tabindex="0">
-    <div class="picture">
-        <img src="{{profileimageurl}}" alt="" />
-    </div>
-    <div class="information">
-        <div class="name">
-            {{fullname}}
-            {{#showonlinestatus}}
-            <div {{#isonline}}class="d-inline status online"{{/isonline}}{{^isonline}}class="hidden"{{/isonline}}>
-                <span class="online-text" data-region="contact-icon-online">
-                    {{#pix}} t/online, core, {{#str}} online, message {{/str}} {{/pix}}
-                </span>
-            </div>
-            {{/showonlinestatus}}
-            <span {{^isblocked}}class="hidden"{{/isblocked}} data-region="contact-icon-blocked">
-                {{#pix}} t/block, core, {{#str}} contactblocked, message {{/str}} {{/pix}}
-            </span>
-        </div>
-        <p class="lastmessage" data-region="last-message-area">
-            <span data-region="last-message-user">
-                {{#sentfromcurrentuser}}{{#str}}you, message{{/str}}{{/sentfromcurrentuser}}
-            </span>
-            <span data-region="last-message-text">
-                {{#lastmessage}}
-                    {{.}}
-                {{/lastmessage}}
-            </span>
-        </p>
-    </div>
-    <div class="unread-count-container">
-        <span data-region="unread-count" class="badge badge-important">{{unreadcount}}</span>
-    </div>
-</div>
diff --git a/message/templates/message_area_contacts_area.mustache b/message/templates/message_area_contacts_area.mustache
deleted file mode 100644 (file)
index b368a0c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-<div class="searchtextarea" data-region="search-text-area">
-    <label class="accesshide" for="searchtext">{{#str}}search{{/str}}</label>
-    <input data-region="search-box" type="text" id="searchtext" placeholder="{{#contactsfirst}} {{#str}}searchforuserorcourse, message{{/str}} {{/contactsfirst}} {{^contactsfirst}} {{#str}}searchmessages, message{{/str}} {{/contactsfirst}}">
-    <div data-region="search-filter-area" class="searchfilterarea" style="display:none" role="button" tabindex="0">
-        <div data-region="search-filter" class="searchfilter"></div>
-        <div data-action="search-filter-delete" class="searchfilterdelete">{{#pix}}t/delete{{/pix}}</div>
-    </div>
-</div>
-{{#contactsfirst}}
-<div class="contacts" data-region="contacts" data-region-content="conversations" style="display:none;" role="tabpanel" id="conversations-tab-panel"></div>
-<div class="contacts" data-region="contacts" data-region-content="contacts" role="tabpanel" id="contacts-tab-panel">
-    {{> core_message/message_area_contacts }}
-</div>
-{{/contactsfirst}}
-{{^contactsfirst}}
-<div class="contacts" data-region="contacts" data-region-content="conversations" role="tabpanel" id="conversations-tab-panel">
-    {{> core_message/message_area_contacts }}
-</div>
-<div class="contacts" data-region="contacts" data-region-content="contacts" style="display:none;" role="tabpanel" id="contacts-tab-panel"></div>
-{{/contactsfirst}}
-{{! Hidden divs to load the other tab and search panels via JS when appropriate. }}
-<div class="contacts searcharea" data-region="search-results-area" style="display:none;"></div>
-<div class="tabs" role="tablist">
-    <div class="tab tabconversations {{^contactsfirst}}selected{{/contactsfirst}} " data-action="conversations-view" role="tab" aria-controls="conversations-tab-panel" aria-selected="{{^contactsfirst}}true{{/contactsfirst}}{{#contactsfirst}}false{{/contactsfirst}}" tabindex="0">
-        <div class="tabimage">{{#pix}}t/message, moodle{{/pix}}</div>
-        <div>{{#str}}messages, message{{/str}}</div>
-    </div>
-    <div class="tab tabcontacts {{#contactsfirst}}selected{{/contactsfirst}}" data-action="contacts-view" role="tab" aria-controls="contacts-tab-panel" aria-selected="{{#contactsfirst}}true{{/contactsfirst}}{{^contactsfirst}}false{{/contactsfirst}}" tabindex="-1">
-        <div class="tabimage">{{#pix}}i/cohort, moodle{{/pix}}</div>
-        <div>{{#str}}contacts, message{{/str}}</div>
-    </div>
-</div>
diff --git a/message/templates/message_area_message.mustache b/message/templates/message_area_message.mustache
deleted file mode 100644 (file)
index c04eba9..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{#displayblocktime}}
-    <div class="blocktime" data-region="blocktime" data-blocktime="{{blocktime}}" data-id="{{id}}{{isread}}">
-        {{blocktime}}
-    </div>
-{{/displayblocktime}}
-<div class="message d-flex row-fluid"
-    data-region="message"
-    data-blocktime="{{blocktime}}"
-    data-id="{{id}}{{isread}}"
-    data-useridto="{{useridto}}"
-    data-useridfrom="{{useridfrom}}"
-    data-messageid="{{id}}"
-    data-messageread="{{isread}}"
-    data-timecreated="{{timecreated}}"
-    tabindex="0">
-
-    <div class="content {{position}}">
-        <span class="text" data-region="message-text">
-            {{{text}}}
-        </span>
-        <span class="timesent">
-            {{timesent}}
-        </span>
-    </div>
-</div>
diff --git a/message/templates/message_area_message_search_results.mustache b/message/templates/message_area_message_search_results.mustache
deleted file mode 100644 (file)
index 7f6f5b8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{#contacts}}
-    {{> core_message/message_area_contact }}
-{{/contacts}}
-{{^contacts}}
-    <div class="noresults">{{#str}}noresults{{/str}}</div>
-{{/contacts}}
diff --git a/message/templates/message_area_messages.mustache b/message/templates/message_area_messages.mustache
deleted file mode 100644 (file)
index 433fc7e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{#messages}}
-    {{> core_message/message_area_message }}
-{{/messages}}
diff --git a/message/templates/message_area_messages_area.mustache b/message/templates/message_area_messages_area.mustache
deleted file mode 100644 (file)
index 34b2ee4..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{!
-    @template core_message/message_area_messages_area
-
-    Messages area template.
-
-    Classes required for JS:
-    * none
-
-    Data attributes required for JS:
-    * none
-
-    Context variables required for this template:
-    * isonline - boolean
-    * isblocked - boolean
-    * otheruserid - int
-    * otheruserfullname - string
-    * messages - array of messages
-
-    Example context (json):
-    {
-        "isonline": true,
-        "isblocked": true,
-        "otheruserid": 1,
-        "otheruserfullname": "Sam Student",
-        "messages": [
-            {
-                "text": "Hello there!"
-            }
-        ]
-    }
-}}
-{{#otheruserid}}
-<div class="messages-header">
-    <div class="view-toggle btn-container">
-        <button class="btn btn-link" data-action="show-contacts">&lt; {{#str}} messages, message {{/str}}</button>
-    </div>
-    <div class="delete-all btn-container">
-        <button class="btn btn-link" data-action="delete-all-messages" aria-label="{{#str}} deleteallmessages, message {{/str}}">
-            {{#str}} deleteall {{/str}}
-        </button>
-    </div>
-    <div class="name-container">
-        <div class="name">
-            <button class="btn btn-link" data-action="view-contact-profile" data-userid="{{otheruserid}}">{{otheruserfullname}}</button>
-            {{#isblocked}}
-                <span data-region="contact-icon-blocked">
-                    {{#pix}} t/block, core, {{#str}} contactblocked, message {{/str}} {{/pix}}
-                </span>
-            {{/isblocked}}
-        </div>
-        {{#showonlinestatus}}
-            <div class="status {{#isonline}}online{{/isonline}}">
-                <span class="offline-text">{{#str}} offline, message {{/str}}</span>
-                <span class="online-text">{{#str}} online, message {{/str}}</span>
-            </div>
-        {{/showonlinestatus}}
-    </div>
-    <div class="delete-instructions">
-        {{#str}} selectmessagestodelete, message {{/str}}
-    </div>
-    <div class="actions" data-region="messages-header-actions">
-        <button class="btn btn-link messages-delete" data-action="start-delete-messages"
-                aria-label="{{#str}} editmessages, message {{/str}}">{{#str}}edit{{/str}}</button>
-        <button class="btn btn-link cancel-messages-delete" data-action="cancel-delete-messages"
-                aria-label="{{#str}} canceledit, message {{/str}}">{{#str}}cancel{{/str}}</button>
-    </div>
-</div>
-{{/otheruserid}}
-{{#contactsfirst}}
-<div class="messages-header">
-    <div class="name-container">
-        <div class="name">
-            {{#str}}newmessagesearch, message{{/str}}
-        </div>
-    </div>
-</div>
-{{/contactsfirst}}
-<div class="messages" data-region="messages" data-userid="{{otheruserid}}">
-    {{> core_message/message_area_messages }}
-</div>
-{{#otheruserid}}
-<div class="response" data-region="response">
-    {{#iscurrentuser}}
-        <div class="message-box">
-            {{> core_message/message_area_response }}
-        </div>
-    {{/iscurrentuser}}
-    <div class="delete-confirmation">
-        <button class="btn btn-link confirm" data-action="delete-messages">{{#str}} deleteselectedmessages, message {{/str}}</button>
-    </div>
-</div>
-{{/otheruserid}}
diff --git a/message/templates/message_area_profile.mustache b/message/templates/message_area_profile.mustache
deleted file mode 100644 (file)
index 1a706aa..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-<div class="profile-header">
-    <div class="view-toggle btn-container">
-        <button class="btn btn-link show-contacts" data-action="show-contacts">&lt; {{#str}} contacts, message {{/str}}</button>
-    </div>
-</div>
-<div class="profile" data-userid="{{userid}}" data-region="profile">
-    <div class="user-container">
-        <a data-action="profile-view" href="#">
-            <img class="profile-picture" src="{{profileimageurl}}"
-                 alt="{{#str}}pictureof, moodle, {{fullname}}{{/str}}"
-                 title="{{#str}}pictureof, moodle, {{fullname}}{{/str}}" />
-        </a>
-        <div class="name-container">
-            <div class="name">{{fullname}}</div>
-            {{#showonlinestatus}}
-                <div class="status {{#isonline}}online{{/isonline}}">
-                    <div class="online-text">{{#str}} online, message {{/str}}</div>
-                    <div class="offline-text">{{#str}} offline, message {{/str}}</div>
-                </div>
-            {{/showonlinestatus}}
-        </div>
-        <ul class="information">
-            {{#email}}<li><div class="name">{{#str}} email {{/str}}</div><div class="value">{{.}}</div></li>{{/email}}
-            {{#country}}<li><div class="name">{{#str}} country {{/str}}</div><div class="value">{{country}}</div></li>{{/country}}
-            {{#city}}<li><div class="name">{{#str}} city {{/str}}</div><div class="value">{{city}}</div></li>{{/city}}
-        </ul>
-    </div>
-    <div class="actions">
-        <div class="separator">
-            <a data-action="profile-send-message" href="#">{{#str}}sendmessage, message{{/str}}</a>
-        </div>
-        <div class="separator">
-            {{#isblocked}}
-                <a data-action="profile-unblock-contact" href="#">{{#str}}unblockcontact, message{{/str}}</a>
-            {{/isblocked}}
-            {{^isblocked}}
-                <a class="danger" data-action="profile-block-contact" href="#">{{#str}}blockcontact, message{{/str}}</a>
-            {{/isblocked}}
-        </div>
-        <div class="separator">
-            {{#iscontact}}
-                <a class="danger" data-action="profile-remove-contact" href="#">{{#str}}removecontact, message{{/str}}</a>
-            {{/iscontact}}
-            {{^iscontact}}
-                <a data-action="profile-add-contact" href="#">{{#str}}addcontact, message{{/str}}</a>
-            {{/iscontact}}
-        </div>
-    </div>
-</div>
diff --git a/message/templates/message_area_response.mustache b/message/templates/message_area_response.mustache
deleted file mode 100644 (file)
index f7de545..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-<div class="message-text-container">
-    <textarea data-region="send-message-txt"
-        rows="1"
-        data-auto-rows
-        data-max-rows="5"
-        role="textbox"
-        aria-label="{{#str}} writeamessage, message {{/str}}"
-        placeholder="{{#str}} writeamessage, message {{/str}}"></textarea>
-</div>
-<div class="send-button-container">
-    <button class="btn btn-link" data-action="send-message">{{#str}}send, message{{/str}}</button>
-</div>
diff --git a/message/templates/message_area_user_search_results.mustache b/message/templates/message_area_user_search_results.mustache
deleted file mode 100644 (file)
index b395512..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{#hascontacts}}
-    {{#contacts}}
-        {{> core_message/message_area_contact }}
-    {{/contacts}}
-{{/hascontacts}}
-{{#hascourses}}
-    <div class="heading">{{#str}}courses{{/str}}</div>
-    {{#courses}}
-        <div class="course" data-action="search-users-in-course" data-courseid="{{id}}" data-region="course" role="button" tabindex="0">
-            {{fullname}}
-        </div>
-    {{/courses}}
-{{/hascourses}}
-{{#hasnoncontacts}}
-    <div class="heading">{{#str}}noncontacts, message{{/str}}</div>
-    {{#noncontacts}}
-        {{> core_message/message_area_contact }}
-    {{/noncontacts}}
-{{/hasnoncontacts}}
-{{^hascontacts}}
-    {{^hascourses}}
-        {{^hasnoncontacts}}
-            <div class="noresults">{{#str}}noresults{{/str}}</div>
-        {{/hasnoncontacts}}
-    {{/hascourses}}
-{{/hascontacts}}
index a75d721..1371003 100644 (file)
@@ -36,7 +36,7 @@
 
 <div
     id="message-drawer-{{uniqid}}"
-    class="message-drawer bg-light hidden"
+    class="message-app drawer bg-light hidden"
     aria-expanded="false"
     aria-hidden="true"
     data-region="message-drawer"
     </div>
     <div class="footer-container position-relative" data-region="footer-container">
         {{> core_message/message_drawer_view_conversation_footer }}
+        {{> core_message/message_drawer_view_overview_footer }}
     </div>
 </div>
 {{#js}}
 require(['jquery', 'core_message/message_drawer'], function($, MessageDrawer) {
     var root = $('#message-drawer-{{uniqid}}');
-    MessageDrawer.init(root);
+    MessageDrawer.init(root, '{{uniqid}}', false);
 });
 {{/js}}
index 8ba2939..9ea7637 100644 (file)
@@ -34,7 +34,7 @@
 
 }}
 <div class="h-100 view-overview-body" aria-hidden="false" data-region="view-overview" data-user-id="{{loggedinuser.id}}">
-    <div id="message-drawer-view-overview-container" class="d-flex flex-column h-100" style="overflow-y: auto">
+    <div id="message-drawer-view-overview-container-{{uniqid}}" class="d-flex flex-column h-100" style="overflow-y: auto">
             {{> core_message/message_drawer_view_overview_section_favourites }}
             {{> core_message/message_drawer_view_overview_section_group_messages }}
             {{> core_message/message_drawer_view_overview_section_messages }}
     You should have received a copy of the GNU General Public License
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
-{{#contacts}}
-    {{> core_message/message_area_contact }}
-{{/contacts}}
-{{^contacts}}
-    <div class="nocontacts" data-region="no-contacts">
-        {{#isconversation}}
-            {{#str}}nomessages, message{{/str}}
-        {{/isconversation}}
-        {{^isconversation}}
-            {{#str}}nocontacts, message{{/str}}
-        {{/isconversation}}
+{{!
+    @template core_message/message_drawer_view_overview_message_page_link
+
+    This template will render the body of the overview section of the message drawer.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * All data attributes are required
+
+    Example context (json):
+    {}
+
+}}
+{{#overview.messageurl}}
+    <div data-region="view-overview" class="text-center">
+        <a href="{{overview.messageurl}}">
+            {{#str}} seeall, core_message {{/str}}
+        </a>
     </div>
-{{/contacts}}
+{{/overview.messageurl}}
\ No newline at end of file
index 26835b3..7b21ec1 100644 (file)
@@ -43,9 +43,9 @@
         <button
             class="btn btn-link w-100 text-left p-2 d-flex align-items-center overview-section-toggle {{^expanded}}collapsed{{/expanded}}"
             data-toggle="collapse"
-            data-target="#{{$region}}{{/region}}-target"
+            data-target="#{{$region}}{{/region}}-target-{{uniqid}}"
             aria-expanded="{{#expanded}}true{{/expanded}}{{^expanded}}false{{/expanded}}"
-            aria-controls="{{$region}}{{/region}}-target"
+            aria-controls="{{$region}}{{/region}}-target-{{uniqid}}"
         >
             <span class="collapsed-icon-container">
                 {{#pix}} t/collapsedcaret, core {{/pix}}
@@ -68,9 +68,9 @@
     {{< core_message/message_drawer_lazy_load_list }}
         {{$rootclasses}}collapse border-bottom {{#expanded}}show{{/expanded}}{{/rootclasses}}
         {{$rootattributes}}
-            id="{{$region}}{{/region}}-target"
+            id="{{$region}}{{/region}}-target-{{uniqid}}"
             aria-labelledby="{{$region}}{{/region}}-toggle"
-            data-parent="#message-drawer-view-overview-container"
+            data-parent="#message-drawer-view-overview-container-{{uniqid}}"
         {{/rootattributes}}
     {{/ core_message/message_drawer_lazy_load_list }}
 </div>
diff --git a/message/templates/message_index.mustache b/message/templates/message_index.mustache
new file mode 100644 (file)
index 0000000..4c98838
--- /dev/null
@@ -0,0 +1,70 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core_message/message_drawer
+
+    This template will render the message drawer.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * All data attributes are required
+
+    Context variables required for this template:
+    * userid The logged in user id
+    * urls The URLs for the popover
+
+    Example context (json):
+    {}
+
+}}
+<div
+        id="message-index-{{uniqid}}"
+        class="message-app main bg-light border"
+        aria-expanded="false"
+        aria-hidden="true"
+        data-region="message-index"
+        role="region"
+>
+    <div class="header-container position-relative" data-region="header-container">
+        {{> core_message/message_drawer_view_contacts_header }}
+        {{> core_message/message_drawer_view_conversation_header }}
+        {{> core_message/message_drawer_view_overview_header }}
+        {{> core_message/message_drawer_view_search_header }}
+        {{> core_message/message_drawer_view_settings_header }}
+    </div>
+    <div class="body-container position-relative" data-region="body-container">
+        {{> core_message/message_drawer_view_contact_body }}
+        {{> core_message/message_drawer_view_contacts_body }}
+        {{> core_message/message_drawer_view_conversation_body }}
+        {{> core_message/message_drawer_view_group_info_body }}
+        {{> core_message/message_drawer_view_overview_body }}
+        {{> core_message/message_drawer_view_search_body }}
+        {{> core_message/message_drawer_view_settings_body }}
+    </div>
+    <div class="footer-container position-relative" data-region="footer-container">
+        {{> core_message/message_drawer_view_conversation_footer }}
+        {{> core_message/message_drawer_view_overview_footer }}
+    </div>
+</div>
+{{#js}}
+    require(['jquery', 'core_message/message_drawer'], function($, MessageDrawer) {
+    var root = $('#message-index-{{uniqid}}');
+    MessageDrawer.init(root, '{{uniqid}}', true, {{sendtouser}}, {{conversationid}});
+    });
+{{/js}}
\ No newline at end of file
diff --git a/message/templates/message_preferences.mustache b/message/templates/message_preferences.mustache
deleted file mode 100644 (file)
index ebe1c40..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{!
-    @template core_message/message_preferences
-
-    The message preferences page
-
-    Classes required for JS:
-    * None
-
-    Data attibutes required for JS:
-    * All data attributes are required
-
-    Context variables required for this template:
-    * userid The logged in user id
-    * disableall If the user has disabled notifications
-    * components The list of notification components
-    * privacychoices The choice options for the contactable privacy setting
-
-    Example context (json):
-    {
-        "userid": 1,
-        "disableall": 0,
-        "components": [
-            {
-                "notifications": [
-                    {
-                        "displayname": "Notices about minor problems",
-                        "preferencekey": "message_provider_moodle_notices",
-                        "onlinehelphtml": "<p>some help HTML</p>",
-                        "offlinehelphtml": "<p>some help HTML</p>",
-                        "processors": [
-                            {
-                                "displayname": "Popup notification",
-                                "name": "popup",
-                                "locked": 0,
-                                "userconfigured": 1,
-                                "loggedin": {
-                                    "name": "loggedin",
-                                    "displayname": "When I'm logged in",
-                                    "checked": 0,
-                                    "disableall": 0
-                                },
-                                "loggedoff": {
-                                    "name": "loggedoff",
-                                    "displayname": "When I'm offline",
-                                    "checked": 0,
-                                    "disableall": 0
-                                }
-                            }
-                        ]
-                    }
-                ]
-            }
-        ],
-        "privacychoices": [
-            {
-                "value": 1,
-                "text": "My contacts only",
-                "checked": 0
-            },
-            {
-                "value": 2,
-                "text": "Anyone within courses I am a member of",
-                "checked": 1
-            }
-        ]
-    }
-}}
-<div class="preferences-page-container" data-region="preferences-page-container">
-    <h2>{{#str}} messagepreferences, message {{/str}}</h2>
-    <div class="privacy-setting-container"
-         data-user-id="{{userid}}"
-         data-region="privacy-setting-container"
-         data-preference-key="message_blocknoncontacts">
-        <p>{{#str}} contactableprivacy, message {{/str}}</p>
-       {{#privacychoices}}
-        <input id="action-selection-option-{{value}}"
-               type="radio"
-               name="message_blocknoncontacts"
-               value="{{value}}"
-               {{#checked}}checked="checked"{{/checked}}/>
-        <label for="action-selection-option-{{value}}">{{text}}</label>
-        <br>
-       {{/privacychoices}}
-    </div><br>
-    <div class="preferences-container {{#disableall}}disabled{{/disableall}}"
-        data-user-id="{{userid}}"
-        data-region="preferences-container">
-        <table class="table table-hover preference-table" data-region="preference-table">
-            <tbody>
-                {{#components}}
-                    {{> message/message_preferences_component }}
-                {{/components}}
-            </tbody>
-        </table>
-    </div>
-</div>
-{{#js}}
-require(['jquery', 'core_message/message_preferences'],
-    function($, MessagePreferences) {
-
-    new MessagePreferences($('[data-region="preferences-page-container"]'));
-});
-{{/js}}
diff --git a/message/templates/message_preferences_component.mustache b/message/templates/message_preferences_component.mustache
deleted file mode 100644 (file)
index aeac026..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{!
-    @template core_message/message_preferences_component
-
-    The message preferences page
-
-    Classes required for JS:
-    * None
-
-    Data attibutes required for JS:
-    * All data attributes are required
-
-    Context variables required for this template:
-    * notifications The list of notifications
-
-    Example context (json):
-    {
-        "notifications": [
-            {
-                "displayname": "Notices about minor problems",
-                "preferencekey": "message_provider_moodle_notices",
-                "onlinehelphtml": "<p>some help HTML</p>",
-                "offlinehelphtml": "<p>some help HTML</p>",
-                "processors": [
-                    {
-                        "displayname": "Popup notification",
-                        "name": "popup",
-                        "locked": 0,
-                        "userconfigured": 1,
-                        "loggedin": {
-                            "name": "loggedin",
-                            "displayname": "When I'm logged in",
-                            "checked": 0,
-                            "disableall": 0
-                        },
-                        "loggedoff": {
-                            "name": "loggedoff",
-                            "displayname": "When I'm offline",
-                            "checked": 0,
-                            "disableall": 0
-                        }
-                    }
-                ]
-            }
-        ]
-    }
-}}
-{{#notifications}}
-    <tr data-preference-key="{{preferencekey}}">
-        <th>{{displayname}}</th>
-        <td class="align-bottom">
-            <div class="container-fluid">
-                <div class="row-fluid">
-                    <div class="span6 col-xs-6">
-                        {{#str}} loggedin, message {{/str}}
-                        {{#onlinehelphtml}}{{{.}}}{{/onlinehelphtml}}
-                    </div>
-                    <div class="span6 col-xs-6">
-                        {{#str}} loggedoff, message {{/str}}
-                        {{#offlinehelphtml}}{{{.}}}{{/offlinehelphtml}}
-                    </div>
-                </div>
-            </div>
-        </td>
-    </tr>
-    {{#processors}}
-        {{> message/message_preferences_notification_processor }}
-    {{/processors}}
-{{/notifications}}
diff --git a/message/templates/message_preferences_notification_processor.mustache b/message/templates/message_preferences_notification_processor.mustache
deleted file mode 100644 (file)
index 43c38ef..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-{{!
-    This file is part of Moodle - http://moodle.org/
-
-    Moodle is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Moodle 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-}}
-{{!
-    @template core_message/message_preferences_notification_processor
-
-    The message preferences page
-
-    Classes required for JS:
-    * None
-
-    Data attibutes required for JS:
-    * All data attributes are required
-
-    Context variables required for this template:
-    * displayname   The display name of the processor
-    * name          The name of the processor
-    * locked        Whether the processor is locked
-    * loggedin      The logged in settings
-    * loggedoff     The logged off settings
-
-    Example context (json):
-    {
-        "displayname": "Notices about minor problems",
-        "preferencekey": "message_provider_moodle_notices",
-        "processors": [
-            {
-                "displayname": "Popup notification",
-                "name": "popup",
-                "locked": 0,
-                "userconfigured": 1,
-                "loggedin": {
-                    "name": "loggedin",
-                    "displayname": "When I'm logged in",
-                    "checked": 0,
-                    "disableall": 0
-                },
-                "loggedoff": {
-                    "name": "loggedoff",
-                    "displayname": "When I'm offline",
-                    "checked": 0,
-                    "disableall": 0
-                }
-            }
-        ]
-    }
-}}
-<tr class="preference-row" data-region="preference-row" data-preference-key="{{preferencekey}}">
-    <td class="preference-name">{{displayname}}</td>
-    <td {{^userconfigured}}class="disabled"{{/userconfigured}} data-processor-name="{{name}}">
-        {{#locked}}
-            <div class="dimmed_text">{{lockedmessage}}</div>
-        {{/locked}}
-        {{^locked}}
-            <div class="disabled-message">{{#str}} disabled, question {{/str}}</div>
-            <form>
-                <div class="container-fluid">
-                    <div class="row-fluid">
-                        <div class="span6 col-xs-6">
-                            {{#loggedin}}
-                                {{< core/hover_tooltip }}
-                                    {{$anchor}}
-                                        <label class="preference-state"
-                                            title="{{displayname}}"
-                                            data-state="{{name}}">
-
-                                            <span class="accesshide">{{displayname}}</span>
-                                            <input type="checkbox"
-                                                tabindex="-1"
-                                                class="accesshide"
-                                                {{#checked}}checked{{/checked}}
-                                                {{#disableall}}disabled{{/disableall}} />
-                                            <div class="preference-state-status-container" tabindex="0">
-                                                <span class="on-text">{{#str}} on, message {{/str}}</span>
-                                                <span class="off-text">{{#str}} off, message {{/str}}</span>
-                                                {{> core/loading }}
-                                            </div>
-                                        </label>
-                                    {{/anchor}}
-                                    {{$tooltip}}{{displayname}}{{/tooltip}}
-                                {{/ core/hover_tooltip }}
-                            {{/loggedin}}
-                        </div>
-                        <div class="span6 col-xs-6">
-                            {{#loggedoff}}
-                                {{< core/hover_tooltip }}
-                                    {{$anchor}}
-                                        <label class="preference-state"
-                                            title="{{displayname}}"
-                                            data-state="{{name}}">
-
-                                            <span class="accesshide">{{displayname}}</span>
-                                            <input type="checkbox"
-                                                tabindex="-1"
-                                                class="accesshide"
-                                                {{#checked}}checked{{/checked}}
-                                                {{#disableall}}disabled{{/disableall}} />
-                                            <div class="preference-state-status-container" tabindex="0">
-                                                <span class="on-text">{{#str}} on, message {{/str}}</span>
-                                                <span class="off-text">{{#str}} off, message {{/str}}</span>
-                                                {{> core/loading }}
-                                            </div>
-                                        </label>
-                                    {{/anchor}}
-                                    {{$tooltip}}{{displayname}}{{/tooltip}}
-                                {{/ core/hover_tooltip }}
-                            {{/loggedoff}}
-                        </div>
-                    </div>
-                </div>
-            </form>
-        {{/locked}}
-    </td>
-</tr>
index cc7ee4f..d220d00 100644 (file)
@@ -1,6 +1,15 @@
 This files describes API changes in /message/ messaging system,
 information provided here is intended especially for developers.
 
+=== 3.7 ===
+
+* The message/index.php page used to support viewing another user's messages (if you had the right capabilities) by
+  altering the URL and adding the parameters 'user1' and 'user2'. There were only some very rare occurrences where you
+  could access a URL generated with these parameters (eg. log report). It was decided to stop supporting this
+  functionality and remove all the legacy code (see MDL-63915).
+  Note - It's still possible to view another user's messages if you have the right capabilities and are able to
+  'log-in as' them.
+
 === 3.6 ===
 
 * The following functions have been finally deprecated and can not be used anymore:
index 3005682..28055ec 100644 (file)
     display: none;
 }
 
-.messaging-area-container {
-    margin-bottom: 30px;
-
-    .status {
-        .online-text {
-            display: none;
-            color: #7d7;
-        }
-
-        .offline-text {
-            color: #ff6961;
-        }
-
-        &.online {
-            .online-text {
-                display: inherit;
-            }
-            .offline-text {
-                display: none;
-            }
-        }
-    }
-
-    a,
-    .btn.btn-link {
-        color: #4f94cd;
-    }
-
-    .messaging-area {
-        border: 1px solid #e3e3e3;
-        clear: both;
-
-        img {
-            max-width: 100%;
-        }
-
-        .contacts-area {
-            border-right: 1px solid #e3e3e3;
-            height: 600px;
-            @media (max-height: 670px) {
-                height: 500px;
-            }
-            width: 280px;
-            display: inline-block;
-            box-sizing: border-box;
-
-            &.searchfilter {
-
-                .searchtextarea {
-                    height: 80px;
-                }
-
-                .searcharea {
-                    height: 470px;
-                }
-            }
-
-            .searchtextarea {
-                padding: 5px;
-                text-align: center;
-                height: 50px;
-                box-sizing: border-box;
-                line-height: 50px;
-                background-color: #fff;
-                transition: background-color linear 0.2s;
-
-                input {
-                    height: 28px;
-                    line-height: 20px;
-                    margin-bottom: 10px;
-                    vertical-align: middle;
-                    padding: 4px 6px;
-                    background-color: #f5f5f5;
-                    border: 0;
-                    width: 90%;
-                    box-shadow: none;
-                    transition: background-color linear 0.2s;
-
-                    &:focus {
-                        box-shadow: none;
-                    }
-                }
-
-                &.searching {
-                    background-color: #f5f5f5;
-                    transition: background-color linear 0.2s;
-
-                    input {
-                        background-color: #fff;
-                        transition: background-color linear 0.2s;
-                    }
-                }
-
-                .searchfilterarea {
-                    line-height: 20px;
-                    cursor: pointer;
-
-                    .searchfilter {
-                        float: left;
-                    }
-
-                    .searchfilterdelete {
-                        float: left;
-                        margin-left: 5px;
-                    }
-                }
-            }
-
-            .searcharea {
-
-                .heading {
-                    text-align: center;
-                    border-top: 1px solid black;
-                    border-bottom: 1px solid black;
-                    font-size: 14px;
-                    font-weight: bold;
-                }
-
-                .course {
-                    text-align: center;
-
-                    &:hover {
-                        background-color: #4f94cd;
-                        color: #fff;
-                        border: none;
-                        cursor: pointer;
-                    }
-                }
-
-                .noresults {
-                    padding-top: 20px;
-                    text-align: center;
-                }
-            }
-
-            .contacts {
-                height: 500px;
-                @media (max-height: 670px) {
-                    height: 400px;
-                }
-                overflow-y: auto;
-                -webkit-overflow-scrolling: touch;
-
-                .nocontacts {
-                    padding-top: 20px;
-                    text-align: center;
-                }
-
-                .contact {
-                    height: 66px;
-                    cursor: pointer;
-                    border-bottom: 1px solid #e3e3e3;
-                    box-sizing: border-box;
-
-                    &.unread {
-                        background-color: #f1f1f1;
-
-                        .picture {
-                            border-color: #f1f1f1;
-                        }
-
-                        .information {
-                            width: 60%;
-                        }
-
-                        .unread-count-container {
-                            display: inline-block;
-                            width: 15%;
-                            float: left;
-                        }
-                    }
-
-                    &:hover {
-                        @include setSelectedContact();
-                        background-color: #79b5e6;
-                    }
-
-                    &.selected {
-                        @include setSelectedContact();
-                    }
-
-                    .picture {
-                        line-height: 66px;
-                        text-align: center;
-                        height: 66px;
-                        border-bottom: 1px solid #fff;
-                        width: 25%;
-                        float: left;
-                        display: inline-block;
-                        box-sizing: border-box;
-
-                        img {
-                            border-radius: 50%;
-                            height: 54px;
-                        }
-                    }
-
-                    .information {
-                        padding: 6px 0;
-                        height: 66px;
-                        width: 75%;
-                        float: left;
-                        display: inline-block;
-                        box-sizing: border-box;
-
-                        .name {
-                            font-weight: bold;
-
-                            img {
-                                vertical-align: baseline;
-                            }
-                        }
-
-                        .lastmessage {
-                            word-wrap: break-word;
-                            margin: 0;
-                            height: 40px;
-                            line-height: 17px;
-                            overflow: hidden;
-                            text-overflow: ellipsis;
-                            white-space: nowrap;
-                            color: #a1a1a1;
-                            padding-right: 10px;
-                        }
-                    }
-
-                    .unread-count-container {
-                        display: none;
-                        line-height: 66px;
-                        text-align: center;
-                        box-sizing: border-box;
-                    }
-                }
-            }
-
-            @mixin setSelectedTab() {
-                color: #4f94cd;
-            }
-
-            .tabs {
-                border-top: 1px solid #e3e3e3;
-                height: 50px;
-                box-sizing: border-box;
-
-                .tab {
-                    cursor: pointer;
-                    height: 100%;
-                    background-color: #f5f5f5;
-                    margin: 0;
-                    width: 50%;
-                    text-align: center;
-                    float: left;
-
-                    &:hover {
-                        @include setSelectedTab();
-                    }
-
-                    .tabimage {
-                        height: 30px;
-                        line-height: 30px;
-
-                        img {
-                            height: 20px;
-                        }
-                    }
-                }
-
-                .tab.selected {
-                    @include setSelectedTab();
-                }
-            }
-        }
-
-        .messages-area {
-            width: calc(100% - 280px);
-            height: 600px;
-            @media (max-height: 670px) {
-                height: 500px;
-            }
-            box-sizing: border-box;
-            margin: 0;
-            position: relative;
-            float: right;
-
-            .btn-container {
-                position: absolute;
-                top: 0;
-                left: 0;
-                padding-left: 15px;
-                font-weight: normal;
-
-                &.view-toggle {
-                    display: none;
-                }
-
-                &.delete-all {
-                    display: none;
-                }
-            }
-
-            .profile-header {
-                height: 50px;
-                line-height: 50px;
-                display: none;
-
-                .btn-container {
-                    display: block;
-
-                    .btn-link {
-                        padding: 0;
-                        line-height: inherit;
-                    }
-                }
-            }
-
-            .profile {
-                padding: 30px;
-                font-size: 16px;
-                height: 600px;
-                @media (max-height: 670px) {
-                    height: 500px;
-                }
-                box-sizing: border-box;
-                overflow-y: auto;
-                -webkit-overflow-scrolling: touch;
-
-                .user-container {
-                    height: 100px;
-
-                    .profile-picture {
-                        border-radius: 50%;
-                        max-height: 100px;
-                        display: inline-block;
-                    }
-
-                    .name-container {
-                        display: inline-block;
-                        vertical-align: top;
-                        margin-top: 20px;
-                        margin-left: 10px;
-
-                        .name {
-                            font-weight: bold;
-                            display: block;
-                        }
-
-                        .status {
-                            display: block;
-                            font-size: 14px;
-                        }
-                    }
-                }
-
-                .information {
-                    margin: 0;
-                    display: inline-block;
-                    float: right;
-                    margin-top: 20px;
-                    font-size: 14px;
-                    list-style: none;
-
-                    .name {
-                        display: inline-block;
-                        font-weight: bold;
-                        text-align: right;
-                        margin-right: 10px;
-                    }
-
-                    .value {
-                        display: inline-block;
-                    }
-                }
-
-                .actions {
-                    padding-top: 80px;
-
-                    .separator {
-                        border-bottom: 1px solid #e3e3e3;
-                        margin-bottom: 20px;
-                        padding-bottom: 5px;
-
-                        a {
-                            &.danger {
-                                color: #ff6961;
-                            }
-                        }
-                    }
-                }
-            }
-
-            .messages-header {
-                height: 50px;
-                font-weight: bold;
-                line-height: 50px;
-                box-sizing: border-box;
-                border-bottom: 1px solid #e3e3e3;
-                text-align: center;
-                position: relative;
-
-                .btn-link {
-                    padding: 0;
-                    line-height: inherit;
-                }
-
-                .delete-instructions {
-                    display: none;
-                }
-
-                .name-container {
-                    padding-top: 9px;
-                    display: inline-block;
-
-                    .name {
-                        line-height: 20px;
-                    }
-
-                    .status {
-                        line-height: 12px;
-                        font-size: 12px;
-                        font-weight: normal;
-                    }
-                }
-
-                .actions {
-                    position: absolute;
-                    top: 0;
-                    right: 0;
-                    padding-right: 15px;
-                    font-weight: normal;
-
-                    .cancel-messages-delete {
-                        display: none;
-                    }
-                }
-            }
-
-            .messages {
-                height: 500px;
-                @media (max-height: 670px) {
-                    height: 400px;
-                }
-                overflow-y: auto;
-                overflow-x: hidden;
-                -webkit-overflow-scrolling: touch;
-                box-sizing: border-box;
-                padding: 20px;
-
-                .blocktime {
-                    clear: both;
-                    text-align: center;
-                    color: #a1a1a1;
-                    font-size: 12px;
-                    margin: 5px 0;
-                }
-
-                .message {
-
-                    .deletemessagecheckbox {
-                        display: none;
-                        text-align: center;
-                        padding-top: 5px;
-                    }
-
-                    .content {
-                        border: 1px solid #e3e3e3;
-                        padding: 10px;
-                        margin-bottom: 5px;
-                        font-size: 12px;
-                        word-wrap: break-word;
-                        max-width: 55%;
-                        position: relative;
-
-                        .text {
-                            display: block;
-
-                            p {
-                                margin: 0;
-                            }
-                        }
-
-                        .timesent {
-                            font-size: 10px;
-                            color: #a1a1a1;
-                            float: right;
-                        }
-                    }
-
-                    .content.left {
-                        margin-left: auto;
-                    }
-
-                    .content.right {
-                        margin-right: auto;
-                    }
-                }
-            }
-
-            .response {
-                display: table;
-                padding: 10px 10px 9px;
-                position: absolute;
-                bottom: 0;
-                left: 0;
-                width: 100%;
-                line-height: 28px;
-                text-align: center;
-                border-top: 1px solid #e3e3e3;
-                box-sizing: border-box;
-                background-color: #fff;
-                transition: background-color linear 0.2s;
-
-                .delete-confirmation {
-                    display: none;
-
-                    .btn.btn-link.confirm {
-                        border: 1px solid #4f94cd;
-                        height: 30px;
-
-                        &:hover {
-                            background-color: #4f94cd;
-                            color: #fff;
-                            text-shadow: none;
-                        }
-                    }
-                }
-
-                .message-box {
-                    display: table-row;
-
-                    .message-text-container {
-                        display: table-cell;
-
-                        textarea {
-                            line-height: 20px;
-                            padding: 4px 6px;
-                            vertical-align: middle;
-                            width: 100%;
-                            margin: 0;
-                            resize: none;
-                            border: none;
-                            box-shadow: none;
-                            box-sizing: border-box;
-                            background-color: #f5f5f5;
-                            transition: background-color linear 0.2s;
-
-                            &:focus {
-                                box-shadow: none;
-                            }
-                        }
-                    }
-
-                    .send-button-container {
-                        display: table-cell;
-                        width: 1px;
-
-                        button {
-                            height: 30px;
-                        }
-                    }
-                }
-
-                &.messaging {
-                    background-color: #f5f5f5;
-                    transition: background-color linear 0.2s;
-
-                    .message-box {
-                        .message-text-container {
-                            textarea {
-                                background-color: #fff;
-                                transition: background-color linear 0.2s;
-                            }
-                        }
-                    }
-                }
-            }
-
-            &.editing {
-                .messages-header {
-                    .btn-container {
-                        &.view-all {
-                            display: none;
-                        }
-
-                        &.delete-all {
-                            display: block;
-                        }
-                    }
-
-                    .delete-instructions {
-                        display: block;
-                    }
-
-                    .name-container {
-                        display: none;
-                    }
-
-                    .actions {
-                        .messages-delete {
-                            display: none;
-                        }
-
-                        .cancel-messages-delete {
-                            display: block;
-                        }
-                    }
-                }
-
-                .messages {
-                    .message {
-                        cursor: pointer;
-
-                        &[aria-checked="true"] {
-                            .content {
-                                background-color: #4f94cd;
-                                border-color: #4f94cd;
-                                color: #fff;
-
-                                .timesent {
-                                    color: #fff;
-                                }
-                            }
-                        }
-
-                        &[aria-checked="false"] {
-                            .content {
-                                &:hover {
-                                    background-color: #79b5e6;
-                                    color: #fff;
-
-                                    .timesent {
-                                        color: #fff;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-
-                .response {
-                    .delete-confirmation {
-                        display: block;
-                    }
-
-                    .message-box {
-                        display: none;
-                    }
-                }
-            }
-        }
-    }
-}
-
 .preferences-container {
     .container-fluid {
         padding: 0;
 }
 
 @media (max-width: 979px) {
-    .messaging-area-container {
-        .messaging-area {
-            position: relative;
-            overflow: hidden;
-            height: 600px;
-            @media (max-height: 670px) {
-                height: 500px;
-            }
-
-            .messages-area {
-                .messages-header {
-                    .btn-container {
-                        &.view-toggle {
-                            display: block;
-                        }
-
-                        &.delete-all {
-                            display: none;
-                        }
-                    }
-                }
-
-                .profile-header {
-                    display: block;
-                }
-
-                .profile {
-                    height: 550px;
-                }
-
-                &.editing {
-                    .messages-header {
-                        .btn-container {
-                            &.view-toggle {
-                                display: none;
-                            }
-
-