MDL-54687 core_message: refactored JS

- Created smaller AMD modules to handle specific tasks.
- Used JS events to notify AMD modules when an action has happened.
- Used data-* selectors, rather than CSS selectors (and removed
  any unused CSS selectors).
- Minor changes to behaviour when sending and deleting messages.
- Added JSDocs.
- General improvements to JS.
This commit is contained in:
Mark Nelson 2016-07-07 14:40:41 +08:00
parent f7775c9a0c
commit e237d2bd39
14 changed files with 858 additions and 391 deletions

View File

@ -0,0 +1,59 @@
// 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/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([], function() {
/**
* Actions class.
*
* @param {Messagearea} messageArea The messaging area object.
*/
function Actions(messageArea) {
this.messageArea = messageArea;
this._init();
}
/** @type {Messagearea} The messaging area object. */
Actions.prototype.messageArea = null;
/**
* Initialise the event listeners.
*
* @private
*/
Actions.prototype._init = function() {
this.messageArea.onDelegateEvent('click', "[data-action='choose-messages-to-delete']",
this._chooseMessagesToDelete.bind(this));
};
/**
* Handles when we have selected to delete messages.
*
* @private
*/
Actions.prototype._chooseMessagesToDelete = function() {
this.messageArea.trigger('choose-messages-to-delete');
};
return Actions;
});

169
message/amd/src/contacts.js Normal file
View File

@ -0,0 +1,169 @@
// 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/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'],
function($, ajax, templates, notification) {
/**
* Contacts class.
*
* @param {Messagearea} messageArea The messaging area object.
*/
function Contacts(messageArea) {
this.messageArea = messageArea;
this._init();
}
/** @type {Messagearea} The messaging area object. */
Contacts.prototype.messageArea = null;
/**
* Initialise the event listeners.
*
* @private
*/
Contacts.prototype._init = function() {
this.messageArea.onCustomEvent('conversations-selected', this._viewConversations.bind(this));
this.messageArea.onCustomEvent('contacts-selected', this._viewContacts.bind(this));
this.messageArea.onCustomEvent('messages-deleted', this._viewConversations.bind(this));
this.messageArea.onCustomEvent('message-send', this._viewConversationsWithUserSelected.bind(this));
this.messageArea.onCustomEvent('message-sent', this._viewConversationsWithUserSelected.bind(this));
this.messageArea.onCustomEvent('contact-removed', this._removeContact.bind(this));
this.messageArea.onCustomEvent('contact-added', this._viewContacts.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='view-contact-msg']", this._viewConversation.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='view-contact-profile']", this._viewContact.bind(this));
};
/**
* Handles viewing the list of conversations.
*
* @private
*/
Contacts.prototype._viewConversations = function() {
return this._loadContactArea('core_message_data_for_messagearea_conversations');
};
/**
* Handles viewing the list of conversations and selecting a user.
*
* @param {Event} event The message sent event
* @param {int} userid The id of the user who the message was sent to
* @returns {Promise} The promise resolved when the message sent after actions are done
* @private
*/
Contacts.prototype._viewConversationsWithUserSelected = function(event, userid) {
return this._viewConversations().then(function() {
this._setSelectedUser(userid);
}.bind(this));
};
/**
* Handles viewing the list of contacts.
*
* @private
*/
Contacts.prototype._viewContacts = function() {
return this._loadContactArea('core_message_data_for_messagearea_contacts');
};
/**
* Handles viewing a particular conversation.
*
* @param {Event} event
* @private
*/
Contacts.prototype._viewConversation = function(event) {
var userid = $(event.currentTarget).data('userid');
this._setSelectedUser(userid);
this.messageArea.trigger('conversation-selected', userid);
};
/**
* Handles viewing a particular contact.
*
* @param {Event} event
* @private
*/
Contacts.prototype._viewContact = function(event) {
var userid = $(event.currentTarget).data('userid');
this._setSelectedUser(userid);
this.messageArea.trigger('contact-selected', userid);
};
/**
* Handles loading the contact area.
*
* @param {String} webservice The web service to call
* @returns {Promise} The promise resolved when the contact area has been rendered
* @private
*/
Contacts.prototype._loadContactArea = function(webservice) {
// Show loading template.
templates.render('core_message/loading', {}).done(function(html, js) {
templates.replaceNodeContents("[data-region='contacts']", html, js);
});
// Call the web service to return the data we want to view.
var promises = ajax.call([{
methodname: webservice,
args: {
userid: this.messageArea.getCurrentUserId()
}
}]);
// After the request render the contacts area.
return promises[0].then(function(data) {
// We have the data - lets re-render the template with it.
return templates.render('core_message/contacts', data);
}).then(function(html, js) {
templates.replaceNodeContents("[data-region='contacts-area']", html, js);
}).fail(notification.exception);
};
/**
* Handles removing a contact from the list.
*
* @param {Event} event
* @param {int} userid
* @private
*/
Contacts.prototype._removeContact = function(event, userid) {
this.messageArea.find("[data-region='contact'][data-userid='" + userid + "']").remove();
};
/**
* Handles selecting a contact in the list.
*
* @param {int} userid
* @private
*/
Contacts.prototype._setSelectedUser = function(userid) {
// Remove the 'selected' class from any other contact.
this.messageArea.find("[data-region='contact']").removeClass('selected');
// Set the tab for the user to selected.
this.messageArea.find("[data-region='contact'][data-userid='" + userid + "']").addClass('selected');
};
return Contacts;
}
);

View File

@ -14,388 +14,103 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This module handles toggling between the 'Conversations' and 'Contacts'
* tabs in the message area.
* 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/ajax', 'core/templates', 'core/notification', 'core/str', 'core/config'],
function($, ajax, templates, notification, str, config) {
define(['jquery', 'core_message/actions', 'core_message/contacts', 'core_message/messages',
'core_message/profile', 'core_message/tabs'],
function($, Actions, Contacts, Messages, Profile, Tabs) {
function Messagearea(selector) {
this._node = $(selector);
this._init();
}
Messagearea.prototype.maxstringlength = 60;
Messagearea.prototype.find = function(selector) {
return this._node.find(selector);
};
Messagearea.prototype._init = function() {
this._node.on('click', '.tabconversations', this._loadConversations.bind(this));
this._node.on('click', '.tabcontacts', this._loadContacts.bind(this));
this._node.on('click', '.contact-msg', this._viewMessages.bind(this));
this._node.on('click', '.contact-profile', this._viewContact.bind(this));
this._node.on('click', '.sendmessagebtn', this._sendMessage.bind(this));
this._node.on('click', '.viewprofilebtn', this._viewProfile.bind(this));
this._node.on('click', '.newmessagebtn', this._newMessage.bind(this));
this._node.on('click', '.blockcontactbtn', this._blockContact.bind(this));
this._node.on('click', '.unblockcontactbtn', this._unblockContact.bind(this));
this._node.on('click', '.addcontactbtn', this._addContact.bind(this));
this._node.on('click', '.removecontactbtn', this._removeContact.bind(this));
this._node.on('click', '.choosemessagestodeletebtn', this._chooseMessagesToDelete.bind(this));
this._node.on('click', '.deletemessagesbtn', this._deleteMessages.bind(this));
this._node.on('click', '.canceldeletemessagesbtn', this._cancelDeleteMessages.bind(this));
};
Messagearea.prototype._loadConversations = function() {
this._loadContactArea('core_message_data_for_messagearea_conversations');
};
Messagearea.prototype._loadContacts = function() {
this._loadContactArea('core_message_data_for_messagearea_contacts');
};
Messagearea.prototype._loadContactArea = function(methodname) {
// Show loading template.
templates.render('core_message/loading', {}).done(function(html) {
this.find('.contacts').empty().append(html);
}.bind(this));
// Call the web service to return the data we want to view.
var promises = ajax.call([{
methodname: methodname,
args: {
userid: this._getCurrentUserId()
}
}]);
// After the request render the contacts area.
promises[0].then(function(data) {
// We have the data - lets re-render the template with it.
return templates.render('core_message/contacts', data).then(function(html, js) {
this.find('.contacts-area').empty().append(html);
// And execute any JS that was in the template.
templates.runTemplateJS(js);
}.bind(this));
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._viewMessages = function(event) {
var userid = $(event.currentTarget).data('userid');
this._loadMessages(userid);
};
Messagearea.prototype._sendMessage = function() {
// Call the web service to save our message.
var promises = ajax.call([{
methodname: 'core_message_send_instant_messages',
args: {
messages: [
{
touserid: this.find('.messages').data('userid'),
text: this.find('#sendmessagetxt').val()
}
]
}
}]);
// Update the DOM when we get some data back.
promises[0].then(function() {
// Update the messaging area.
this._addMessageToDom();
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._addMessageToDom = function() {
// Get the variables we are going to use.
var userid = this.find('.messages').data('userid');
var text = this.find('#sendmessagetxt').val();
// 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._getCurrentUserId(),
otheruserid: userid
}
}]);
// Add the message.
promises[0].then(function(data) {
templates.render('core_message/message', data).then(function(html, js) {
this.find('.messages').append(html);
// And execute any JS that was in the template.
templates.runTemplateJS(js);
// Update the conversation on the left.
var leftmsg = text.substr(0, this.maxstringlength);
if (text.length > this.maxstringlength) {
leftmsg += " ...";
}
this.find('#contact-' + userid + ' .lastmessage').empty().append(leftmsg);
// Empty the response text area.
this.find('#sendmessagetxt').val('');
}.bind(this));
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._viewContact = function(event) {
var userid = $(event.currentTarget).data('userid');
// Show loading template.
templates.render('core_message/loading', {}).done(function(html) {
this.find('.messages-area').empty().append(html);
}.bind(this));
// Remove the 'selected' class from any other contact.
this.find('.contact').removeClass('selected');
// Set the tab for the user to selected.
this.find('#contact-' + userid).addClass('selected');
// Call the web service to return the profile.
var promises = ajax.call([{
methodname: 'core_message_data_for_messagearea_get_profile',
args: {
currentuserid: this._getCurrentUserId(),
otheruserid: userid
}
}]);
// Show the profile.
promises[0].then(function(data) {
templates.render('core_message/profile', data).then(function(html, js) {
this.find('.messages-area').empty().append(html);
// And execute any JS that was in the template.
templates.runTemplateJS(js);
}.bind(this));
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._viewProfile = function() {
var userid = this.find('.profile').data('userid');
window.location.href = config.wwwroot + '/user/profile.php?id=' + userid;
};
Messagearea.prototype._newMessage = function() {
var userid = this.find('.profile').data('userid');
this._loadMessages(userid);
};
Messagearea.prototype._blockContact = function() {
var userid = this.find('.profile').data('userid');
// Block the contact.
var promises = ajax.call([{
methodname: 'core_message_block_contacts',
args: {
userid: this._getCurrentUserId(),
userids: [
userid
]
}
}]);
// Change the button.
promises[0].then(function() {
str.get_string('unblockcontact', 'message').then(function(s) {
this._changeButton('blockcontactbtn', s, 'unblockcontactbtn', 'unblockcontactbtn-' + userid);
}.bind(this));
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._unblockContact = function() {
var userid = this.find('.profile').data('userid');
// Unblock the contact.
var promises = ajax.call([{
methodname: 'core_message_unblock_contacts',
args: {
userid: this._getCurrentUserId(),
userids: [
userid
]
}
}]);
// Change the button.
promises[0].then(function() {
str.get_string('blockcontact', 'message').then(function(s) {
this._changeButton('unblockcontactbtn', s, 'blockcontactbtn', 'blockcontactbtn-' + userid);
}.bind(this));
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._addContact = function() {
var userid = this.find('.profile').data('userid');
// Add the contact.
var promises = ajax.call([{
methodname: 'core_message_create_contacts',
args: {
userid: this._getCurrentUserId(),
userids: [
userid
]
}
}]);
// Change the button.
promises[0].then(function() {
str.get_string('removecontact', 'message').then(function(s) {
this._changeButton('addcontactbtn', s, 'removecontactbtn', 'removecontactbtn-' + userid);
}.bind(this));
// Add the contact to the contact list.
this._loadContactArea('core_message_data_for_messagearea_contacts');
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._removeContact = function() {
var userid = this.find('.profile').data('userid');
// Remove the contact.
var promises = ajax.call([{
methodname: 'core_message_delete_contacts',
args: {
userid: this._getCurrentUserId(),
userids: [
userid
]
}
}]);
// Change the button.
promises[0].then(function() {
str.get_string('addcontact', 'message').then(function(s) {
this._changeButton('removecontactbtn', s, 'addcontactbtn', 'addcontactbtn-' + userid);
}.bind(this));
// Remove the contact from the contact list.
this.find('#contact-' + userid).remove();
}.bind(this)).fail(notification.exception);
};
Messagearea.prototype._chooseMessagesToDelete = function() {
// Toggle the checkboxes.
var checkboxes = this.find('.deletemessagecheckbox').toggle();
// Check if we didn't toggle to delete the messages.
if (checkboxes.is(':hidden')) {
// Only show a response field if we are viewing the logged in user's messages.
this.find('.response').empty();
if (this._getLoggedInUserId() == this._getCurrentUserId()) {
templates.render('core_message/message_response', {}).then(function (html, js) {
this.find('.response').append(html);
templates.runTemplateJS(js);
}.bind(this));
}
} else {
templates.render('core_message/message_delete_message', {}).then(function(html, js) {
this.find('.response').empty().append(html);
templates.runTemplateJS(js);
}.bind(this));
/**
* Messagearea class.
*
* @param {String} selector The selector for the page region containing the message area.
*/
function Messagearea(selector) {
this.node = $(selector);
this._init();
}
};
Messagearea.prototype._deleteMessages = function() {
var userid = this._getCurrentUserId();
var checkboxes = this.find('.deletemessagecheckbox input:checked');
var requests = [];
var messagestoremove = [];
/** @type {jQuery} The jQuery node for the page region containing the message area. */
Messagearea.prototype.node = null;
// 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;
var message = this.find('#message-' + messageid + '' + isread);
messagestoremove.push(message);
requests.push({
methodname: 'core_message_delete_message',
args: {
messageid: messageid,
userid: userid,
read: isread
}
});
}.bind(this));
/**
* Initialise the other objects we require.
*/
Messagearea.prototype._init = function() {
new Actions(this);
new Contacts(this);
new Messages(this);
new Profile(this);
new Tabs(this);
};
ajax.call(requests)[requests.length - 1].then(function() {
// Remove the messages from the DOM.
$.each(messagestoremove, function(key, message) {
// Remove the message.
message.remove();
}.bind(this));
// 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.find('div.message[data-blocktime=\'' + blocktime + '\']').length == 0) {
this.find('div.blocktime[data-blocktime=\'' + blocktime + '\']').remove();
}
}.bind(this));
// Simple toggle the delete button action.
this._chooseMessagesToDelete();
}.bind(this), notification.exception);
};
/**
* 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);
};
Messagearea.prototype._cancelDeleteMessages = function() {
// Simple toggle the delete button action.
this._chooseMessagesToDelete();
};
/**
* 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);
};
Messagearea.prototype._loadMessages = function(userid) {
// Show loading template.
templates.render('core_message/loading', {}).done(function(html) {
this.find('.messages-area').empty().append(html);
}.bind(this));
// Remove the 'selected' class from any other contact.
this.find('.contact').removeClass('selected');
// Set the tab for the user to selected.
this.find('#contact-' + userid).addClass('selected');
// Call the web service to get our data.
var promises = ajax.call([{
methodname: 'core_message_data_for_messagearea_messages',
args: {
currentuserid: this._getCurrentUserId(),
otheruserid: userid
/**
* 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);
};
// Do stuff when we get data back.
promises[0].then(function(data) {
// We have the data - lets re-render the template with it.
return templates.render('core_message/messages', data).then(function(html, js) {
// Append the message.
this.find('.messages-area').empty().append(html);
// And execute any JS that was in the template.
templates.runTemplateJS(js);
}.bind(this));
}.bind(this)).fail(notification.exception);
};
/**
* Handles finding a node in the messaging area.
*
* @param {String} selector The selector for the node we are looking for
* @returns {jQuery} The node
*/
Messagearea.prototype.find = function(selector) {
return this.node.find(selector);
};
Messagearea.prototype._changeButton = function(oldclass, text, newclass, newid) {
var button = this.find('.' + oldclass);
button.val(text);
button.removeClass(oldclass);
button.addClass(newclass);
button.prop('id', newid);
};
/**
* Returns the ID of the logged in user.
*
* @returns {int} The user id
*/
Messagearea.prototype.getLoggedInUserId = function() {
return this.node.data('loggedinuserid');
};
Messagearea.prototype._getLoggedInUserId = function() {
return this._node.data('loggedinuserid');
};
/**
* Returns the ID of the user whose message area we are viewing.
*
* @returns {int} The user id
*/
Messagearea.prototype.getCurrentUserId = function() {
return this.node.data('userid');
};
Messagearea.prototype._getCurrentUserId = function() {
return this._node.data('userid');
};
return Messagearea;
});
return Messagearea;
}
);

242
message/amd/src/messages.js Normal file
View File

@ -0,0 +1,242 @@
// 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/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'],
function($, ajax, templates, notification) {
/**
* Messages class.
*
* @param {Messagearea} messageArea The messaging area object.
*/
function Messages(messageArea) {
this.messageArea = messageArea;
this._init();
}
/** @type {Messagearea} The messaging area object. */
Messages.prototype.messageArea = null;
/**
* Initialise the event listeners.
*
* @private
*/
Messages.prototype._init = function() {
this.messageArea.onCustomEvent('conversation-selected', this._loadMessages.bind(this));
this.messageArea.onCustomEvent('message-send', this._loadMessages.bind(this));
this.messageArea.onCustomEvent('choose-messages-to-delete', this._chooseMessagesToDelete.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='send-message']", this._sendMessage.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='delete-messages']", this._deleteMessages.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='cancel-delete-messages']",
this._cancelMessagesToDelete.bind(this));
};
/**
* Loads messages for a specific user.
*
* @param {Event} event
* @param {int} userid
* @returns {Promise} The promise resolved when the messages have been loaded.
* @private
*/
Messages.prototype._loadMessages = function(event, userid) {
// Show loading template.
templates.render('core_message/loading', {}).done(function(html, js) {
templates.replaceNodeContents("[data-region='messages-area']", html, js);
});
// Call the web service to get our data.
var promises = ajax.call([{
methodname: 'core_message_data_for_messagearea_messages',
args: {
currentuserid: this.messageArea.getCurrentUserId(),
otheruserid: userid
}
}]);
// Do stuff when we get data back.
return promises[0].then(function(data) {
// We have the data - lets re-render the template with it.
return templates.render('core_message/messages', data);
}).then(function(html, js) {
templates.replaceNodeContents("[data-region='messages-area']", html, js);
}.bind(this)).fail(notification.exception);
};
/**
* Handles sending a message.
*
* @returns {Promise} The promise resolved once the message has been sent.
* @private
*/
Messages.prototype._sendMessage = function() {
// Call the web service to save our message.
var promises = ajax.call([{
methodname: 'core_message_send_instant_messages',
args: {
messages: [
{
touserid: this._getUserId(),
text: this.messageArea.find("[data-region='send-message-txt']").val()
}
]
}
}]);
// Update the DOM when we get some data back.
return promises[0].then(function() {
// Some variables to pass to the trigger.
var userid = this._getUserId();
// Fire an event to say the message was sent.
this.messageArea.trigger('message-sent', userid);
// Update the messaging area.
this._addMessageToDom();
}.bind(this)).fail(notification.exception);
};
/**
* Handles selecting messages to delete.
*
* @returns {Promise} The promise resolved when the messages to delete have been selected.
* @private
*/
Messages.prototype._chooseMessagesToDelete = function() {
// Show the checkboxes.
this.messageArea.find("[data-region='delete-message-checkbox']").show();
// Display the confirmation message.
var responseSelector = "[data-region='messages-area'] [data-region='response']";
return templates.render('core_message/message_delete_message', {}).then(function(html, js) {
templates.replaceNodeContents(responseSelector, html, js);
});
};
/**
* Handles deleting messages.
*
* @private
*/
Messages.prototype._deleteMessages = function() {
var userid = this.messageArea.getCurrentUserId();
var checkboxes = this.messageArea.find("[data-region='delete-message-checkbox'] input:checked");
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;
var message = this.messageArea.find("[data-region='message'][data-id='" + messageid + '' + isread + "']");
messagestoremove.push(message);
requests.push({
methodname: 'core_message_delete_message',
args: {
messageid: messageid,
userid: userid,
read: isread
}
});
}.bind(this));
if (requests.length > 0) {
ajax.call(requests)[requests.length - 1].then(function() {
// Remove the messages from the DOM.
$.each(messagestoremove, function(key, message) {
// Remove the message.
message.remove();
});
// 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("[data-region='message'][data-blocktime='" + blocktime + "']").length === 0) {
this.messageArea.find("[data-region='blocktime'][data-blocktime='" + blocktime + "']").remove();
}
}.bind(this));
// Simply perform the same action as canceling to delete (hide checkboxes, replace response area etc).
this._cancelMessagesToDelete();
}.bind(this), notification.exception);
}
};
/**
* Handles canceling deleting messages.
*
* @private
*/
Messages.prototype._cancelMessagesToDelete = function() {
// Uncheck all checkboxes.
this.messageArea.find("[data-region='delete-message-checkbox'] input:checked").removeAttr('checked');
// Hide the checkboxes.
this.messageArea.find("[data-region='delete-message-checkbox']").hide();
// Remove the confirmation message.
var responseSelector = "[data-region='messages-area'] [data-region='response']";
this.messageArea.find(responseSelector).empty();
// Only show a response text area if we are viewing the logged in user's messages.
if (this.messageArea.getLoggedInUserId() == this.messageArea.getCurrentUserId()) {
return templates.render('core_message/message_response', {}).then(function(html, js) {
templates.replaceNodeContents(responseSelector, html, js);
});
}
};
/**
* Handles adding messages to the DOM.
*
* @returns {Promise} The promise resolved when the message has been added to the DOM.
* @private
*/
Messages.prototype._addMessageToDom = 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 templates.render('core_message/message', data);
}).then(function(html, js) {
templates.appendNodeContents("[data-region='messages']", html, js);
// Empty the response text area.
this.messageArea.find("[data-region='send-message-txt']").val('');
}.bind(this)).fail(notification.exception);
};
/**
* Returns the ID of the other user in the conversation.
*
* @returns {int} The user id
* @private
*/
Messages.prototype._getUserId = function() {
return this.messageArea.find("[data-region='messages']").data('userid');
};
return Messages;
}
);

214
message/amd/src/profile.js Normal file
View File

@ -0,0 +1,214 @@
// 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/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'],
function($, ajax, templates, notification, str, config) {
/**
* 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() {
this.messageArea.onCustomEvent('contact-selected', this._viewProfile.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-view']", this._viewFullProfile.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-send-message']", this._sendMessage.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-unblock-contact']", this._unblockContact.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-block-contact']", this._blockContact.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-add-contact']", this._addContact.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='profile-remove-contact']", this._removeContact.bind(this));
};
/**
* Handles viewing the profile.
*
* @param {Event} event
* @param {int} userid
* @returns {Promise} The promise resolved when the profile has been rendered
* @private
*/
Profile.prototype._viewProfile = function(event, userid) {
// Show loading template.
templates.render('core_message/loading', {}).done(function(html, js) {
templates.replaceNodeContents("[data-region='messages-area']", html, js);
});
// 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/profile', data);
}).then(function(html, js) {
templates.replaceNodeContents("[data-region='messages-area']", html, js);
}).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('message-send', this._getUserId());
};
/**
* Handles blocking the contact.
*
* @returns {Promise} The promise resolved when the contact has been blocked
* @private
*/
Profile.prototype._blockContact = function() {
var action = this._performAction('core_message_block_contacts', 'unblockcontact', 'profile-block-contact',
'profile-unblock-contact');
return action.then(function() {
this.messageArea.trigger('contact-blocked', this._getUserId());
}.bind(this));
};
/**
* Handles unblocking the contact.
*
* @returns {Promise} The promise resolved when the contact has been unblocked
* @private
*/
Profile.prototype._unblockContact = function() {
var action = this._performAction('core_message_unblock_contacts', 'blockcontact', 'profile-unblock-contact',
'profile-block-contact');
return action.then(function() {
this.messageArea.trigger('contact-unblocked', this._getUserId());
}.bind(this));
};
/**
* Handles adding the contact.
*
* @returns {Promise} The promise resolved when the contact has been added
* @private
*/
Profile.prototype._addContact = function() {
var action = this._performAction('core_message_create_contacts', 'removecontact', 'profile-add-contact',
'profile-remove-contact');
return action.then(function() {
this.messageArea.trigger('contact-added', this._getUserId());
}.bind(this));
};
/**
* Handles removing the contact.
*
* @returns {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('contact-removed', 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
* @returns {Promise} The promise resolved when the action has been performed
* @private
*/
Profile.prototype._performAction = function(service, string, oldaction, newaction) {
var promises = ajax.call([{
methodname: service,
args: {
userid: this.messageArea.getCurrentUserId(),
userids: [
this._getUserId()
]
}
}]);
return promises[0].then(function() {
return str.get_string(string, 'message');
}).then(function(s) {
this._changeButton(s, oldaction, newaction);
}.bind(this)).fail(notification.exception);
};
/**
* Changes the button 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
* @private
*/
Profile.prototype._changeButton = function(text, oldaction, newaction) {
var button = this.messageArea.find("[data-action='" + oldaction + "']");
button.val(text);
button.attr('data-action', newaction);
};
/**
* Returns the ID of the user whos profile we are viewing.
*
* @returns {int} The user ID
* @private
*/
Profile.prototype._getUserId = function() {
return this.messageArea.find("[data-region='profile']").data('userid');
};
return Profile;
}
);

68
message/amd/src/tabs.js Normal file
View File

@ -0,0 +1,68 @@
// 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/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([], function() {
/**
* 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() {
this.messageArea.onDelegateEvent('click', "[data-action='conversations-view']", this._viewConversations.bind(this));
this.messageArea.onDelegateEvent('click', "[data-action='contacts-view']", this._viewContacts.bind(this));
};
/**
* Handles when the conversation tab is selected.
*
* @private
*/
Tabs.prototype._viewConversations = function() {
this.messageArea.trigger('conversations-selected');
};
/**
* Handles when the contacts tab is selected.
*
* @private
*/
Tabs.prototype._viewContacts = function() {
this.messageArea.trigger('contacts-selected');
};
return Tabs;
});

View File

@ -1,4 +1,4 @@
<div id="contact-{{userid}}" class="row-fluid contact {{#lastmessage}}contact-msg{{/lastmessage}} {{^lastmessage}}contact-profile{{/lastmessage}} {{#selected}}selected{{/selected}}" data-userid="{{userid}}">
<div class="row-fluid contact {{#selected}}selected{{/selected}}" data-action="{{#lastmessage}}view-contact-msg{{/lastmessage}}{{^lastmessage}}view-contact-profile{{/lastmessage}}" data-userid="{{userid}}" data-region="contact">
<div class="span2 picture">
<img height="64" src="{{profileimageurl}}" alt="" />
</div>

View File

@ -4,17 +4,17 @@
<input type="text" id="filter{{uniqid}}" placeholder="{{#str}}search{{/str}}" value="{{search}}">
</form>
</div>
<div class="contacts">
<div class="contacts" data-region="contacts">
{{#contacts}}
{{> core_message/contact }}
{{/contacts}}
</div>
<div class="row-fluid tabs">
<div class="span6 tab tabconversations {{#conversationsselected}}selected{{/conversationsselected}}">
<div class="span6 tab tabconversations {{#conversationsselected}}selected{{/conversationsselected}}" data-action="conversations-view">
<div>{{#pix}}t/message, moodle{{/pix}}</div>
<div>{{#str}}conversations, message{{/str}}</div>
</div>
<div class="span6 tab tabcontacts {{^conversationsselected}}selected{{/conversationsselected}}">
<div class="span6 tab tabcontacts {{^conversationsselected}}selected{{/conversationsselected}}" data-action="contacts-view">
<div>{{#pix}}i/cohort, moodle{{/pix}}</div>
<div>{{#str}}contacts, message{{/str}}</div>
</div>

View File

@ -1,10 +1,10 @@
{{#displayblocktime}}
<div id="blocktime-{{id}}{{isread}}" class="blocktime" data-blocktime="{{blocktime}}">
<div class="blocktime" data-region="blocktime" data-blocktime="{{blocktime}}" data-id="{{id}}{{isread}}">
{{blocktime}}
</div>
{{/displayblocktime}}
<div id="message-{{id}}{{isread}}" class="message row-fluid" data-blocktime="{{blocktime}}">
<div class="span1 deletemessagecheckbox">
<div class="message row-fluid" data-region="message" data-blocktime="{{blocktime}}" data-id="{{id}}{{isread}}">
<div class="span1 deletemessagecheckbox" data-region="delete-message-checkbox">
<input type="checkbox" data-messageid="{{id}}" data-messageread="{{isread}}"/>
</div>
<div class="span11 content {{position}}">

View File

@ -1,13 +1,13 @@
<div class="messaging-area-all" data-loggedinuserid="{{loggedinuserid}}" data-userid="{{userid}}">
<input class="choosemessagestodeletebtn" type="button" value="{{#str}}delete{{/str}}"/>
<input type="button" value="{{#str}}delete{{/str}}" data-action="choose-messages-to-delete"/>
<div class="messaging-area">
<div class="row-fluid">
<div class="span4 contacts-area">
<div class="span4 contacts-area" data-region="contacts-area">
{{#contacts}}
{{> core_message/contacts }}
{{/contacts}}
</div>
<div class="span8 messages-area">
<div class="span8 messages-area" data-region="messages-area">
{{#messages}}
{{> core_message/messages }}
{{/messages}}

View File

@ -1,3 +1,3 @@
<strong>{{#str}}selectmessagestodelete, message{{/str}}</strong>
<input class="canceldeletemessagesbtn" type="button" value="{{#str}}cancel{{/str}}" />
<input class="deletemessagesbtn" type="button" value="{{#str}}delete{{/str}}" />
<input type="button" value="{{#str}}cancel{{/str}}" data-action="cancel-delete-messages"/>
<input type="button" value="{{#str}}delete{{/str}}" data-action="delete-messages"/>

View File

@ -1,2 +1,2 @@
<textarea id="sendmessagetxt"></textarea>
<input class="sendmessagebtn" type="button" value="{{#str}}send, message{{/str}}" />
<textarea data-region="send-message-txt"></textarea>
<input type="button" value="{{#str}}send, message{{/str}}" data-action="send-message" />

View File

@ -1,4 +1,4 @@
<div class="messages" data-userid="{{otheruserid}}">
<div class="messages" data-region="messages" data-userid="{{otheruserid}}">
<div class="name">
{{otheruserfullname}}
</div>
@ -6,7 +6,7 @@
{{> core_message/message }}
{{/messages}}
</div>
<div class="response">
<div class="response" data-region="response">
{{#iscurrentuser}}
{{> core_message/message_response }}
{{/iscurrentuser}}

View File

@ -1,4 +1,4 @@
<div class="row-fluid profile" data-userid="{{otheruserid}}">
<div class="row-fluid profile" data-userid="{{otheruserid}}" data-region="profile">
<div class="row-fluid picture">
<div class="span1">
<img height="64" src="{{profileimageurl}}" alt="" />
@ -18,30 +18,30 @@
</div>
<div class="row-fluid actions">
<div class="span3">
<input class="viewprofilebtn" type="button" value="{{#str}}viewprofile{{/str}}"/>
<input type="button" value="{{#str}}viewprofile{{/str}}" data-action="profile-view"/>
</div>
<div class="span3">
{{#iscurrentuser}}
<input class="newmessagebtn" type="button" value="{{#str}}sendmessage, message{{/str}}"/>
<input type="button" value="{{#str}}sendmessage, message{{/str}}" data-action="profile-send-message"/>
{{/iscurrentuser}}
{{^iscurrentuser}}
<input class="newmessagebtn" type="button" value="{{#str}}viewconversation, message{{/str}}"/>
<input type="button" value="{{#str}}viewconversation, message{{/str}}" data-action="profile-send-message"/>
{{/iscurrentuser}}
</div>
<div class="span3">
{{#isblocked}}
<input class="unblockcontactbtn" type="button" value="{{#str}}unblockcontact, message{{/str}}"/>
<input type="button" value="{{#str}}unblockcontact, message{{/str}}" data-action="profile-unblock-contact"/>
{{/isblocked}}
{{^isblocked}}
<input class="blockcontactbtn" type="button" value="{{#str}}blockcontact, message{{/str}}"/>
<input type="button" value="{{#str}}blockcontact, message{{/str}}" data-action="profile-block-contact"/>
{{/isblocked}}
</div>
<div class="span3">
{{#iscontact}}
<input class="removecontactbtn" type="button" value="{{#str}}removecontact, message{{/str}}"/>
<input type="button" value="{{#str}}removecontact, message{{/str}}" data-action="profile-remove-contact"/>
{{/iscontact}}
{{^iscontact}}
<input class="addcontactbtn" type="button" value="{{#str}}addcontact, message{{/str}}"/>
<input type="button" value="{{#str}}addcontact, message{{/str}}" data-action="profile-add-contact"/>
{{/iscontact}}
</div>
</div>