MDL-48933 core_message: New dialog to send a message to a user

This commit is contained in:
Frederic Massart 2015-01-15 10:53:09 +08:00
parent 4c27f52d91
commit cf4a17cb9b
17 changed files with 1972 additions and 10 deletions

76
message/ajax.php Normal file
View file

@ -0,0 +1,76 @@
<?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/>.
/**
* Message ajax.
*
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('AJAX_SCRIPT', true);
require('../config.php');
require_once($CFG->libdir . '/filelib.php');
require_once(__DIR__ . '/lib.php');
// Only real logged in users.
require_login(null, false, null, true, true);
if (isguestuser()) {
throw new require_login_exception();
}
// Messaging needs to be enabled.
if (empty($CFG->messaging)) {
throw new moodle_exception('disabled', 'core_message');
}
require_sesskey();
$action = optional_param('action', null, PARAM_ALPHA);
$response = null;
switch ($action) {
// Sending a message.
case 'sendmessage':
require_capability('moodle/site:sendmessage', context_system::instance());
$userid = required_param('userid', PARAM_INT);
if (empty($userid) || isguestuser($userid) || $userid == $USER->id) {
// Cannot send messags to self, nobody or a guest.
throw new coding_exception('Invalid user to send the message to');
}
$message = required_param('message', PARAM_RAW);
$user2 = core_user::get_user($userid);
$messageid = message_post_message($USER, $user2, $message, FORMAT_MOODLE);
if (!$messageid) {
throw new moodle_exception('errorwhilesendingmessage', 'core_message');
}
$response = array();
break;
}
if ($response !== null) {
echo json_encode($response);
exit();
}
throw new coding_exception('Invalid request');

View file

@ -2660,3 +2660,49 @@ function message_get_messages($useridto, $useridfrom = 0, $notifications = -1, $
$messages = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
return $messages;
}
/**
* Requires the JS libraries to send a message using a dialog.
*
* @return void
*/
function message_messenger_requirejs() {
global $PAGE;
static $done = false;
if ($done) {
return;
}
$PAGE->requires->yui_module(
array('moodle-core_message-messenger'),
'Y.M.core_message.messenger.init',
array(array())
);
$PAGE->requires->strings_for_js(array(
'errorwhilesendingmessage',
'messagesent',
'messagetosend',
'sendingmessage',
'sendmessage',
'viewconversation',
), 'core_message');
$PAGE->requires->string_for_js('error', 'core');
$done = true;
}
/**
* Returns the attributes to place on a link to open the 'Send message' dialog.
*
* @param object $user User object.
* @return void
*/
function message_messenger_sendmessage_link_params($user) {
return array(
'data-trigger' => 'core_message-messenger::sendmessage',
'data-fullname' => fullname($user),
'data-userid' => $user->id,
'role' => 'button'
);
}

View file

@ -0,0 +1,564 @@
YUI.add('moodle-core_message-messenger', function (Y, NAME) {
// 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/>.
/**
* Messenger constants.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var CSS = {},
SELECTORS = {};
// 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/>.
/**
* Messenger manager.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
SELECTORS.MANAGER = {
SENDMESSAGE: '[data-trigger="core_message-messenger::sendmessage"]'
};
/**
* Messenger manager.
*
* @namespace M.core_message.messenger
* @class MANAGER
* @constructor
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').Manager = Y.extend(MANAGER, Y.Base, {
_sendMessageDialog: null,
_events: [],
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._setEvents();
},
/**
* Sending a message.
*
* @method sendMessage
* @param {Number} userid The user ID to send a message to.
* @param {String} fullname The fullname of the user.
* @param {EventFacade} e The event triggered, when any it should be passed to the dialog.
*/
sendMessage: function(userid, fullname, e) {
var Klass;
if (!this._sendMessageDialog) {
Klass = Y.namespace('M.core_message.messenger.sendMessage');
this._sendMessageDialog = new Klass({
url: this.get('url')
});
}
this._sendMessageDialog.prepareForUser(userid, fullname);
this._sendMessageDialog.show(e);
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
var captureEvent = function(e) {
var target = e.currentTarget,
userid = parseInt(target.getData('userid'), 10),
fullname = target.getData('fullname');
if (!userid || !fullname) {
return;
}
// Pass the validation before preventing defaults.
e.preventDefault();
this.sendMessage(userid, fullname, e);
};
this._events.push(Y.delegate('click', captureEvent, 'body', SELECTORS.MANAGER.SENDMESSAGE, this));
this._events.push(Y.one(Y.config.doc).delegate('key', captureEvent, 'space', SELECTORS.MANAGER.SENDMESSAGE, this));
}
}, {
NAME: 'core_message-messenger-manager',
ATTRS: {
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default M.cfg.wwwroot + '/message/ajax.php'
* @type String
*/
url: {
readonly: true,
value: M.cfg.wwwroot + '/message/ajax.php'
}
}
});
var MANAGERINST;
Y.namespace('M.core_message.messenger').init = function(config) {
if (!MANAGERINST) {
// Prevent duplicates of the manager if this function is called more than once.
MANAGERINST = new MANAGER(config);
}
return MANAGERINST;
};
// 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/>.
/**
* Send message dialog.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
CSS.SENDMSGDIALOG = {
ACCESSHIDE: 'accesshide',
ACTIONS: 'message-actions',
FOOTER: 'message-footer',
HIDDEN: 'hidden',
HISTORYLINK: 'message-history',
INPUT: 'message-input',
INPUTAREA: 'message-area',
NOTICE: 'message-notice',
NOTICEAREA: 'message-notice-area',
PREFIX: 'core_message-messenger-sendmessage',
SENDBTN: 'message-send',
WRAPPER: 'message-wrapper'
};
SELECTORS.SENDMSGDIALOG = {
FORM: 'form',
HISTORYLINK: '.message-history',
INPUT: '.message-input',
NOTICE: '.message-notice div',
NOTICEAREA: '.message-notice-area',
SENDBTN: '.message-send'
};
/**
* Send message dialog.
*
* @namespace M.core_message
* @class SENDMSGDIALOG
* @constructor
*/
var SENDMSGDIALOG = function() {
SENDMSGDIALOG.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').sendMessage = Y.extend(SENDMSGDIALOG, M.core.dialogue, {
_bb: null,
_sendLock: false,
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._bb = this.get('boundingBox');
// Prepare the content area.
tpl = Y.Handlebars.compile(
'<form action="#">' +
'<div class="{{CSS.INPUTAREA}}">' +
'<label class="{{CSS.ACCESSHIDE}}" for="{{id}}">{{labelStr}}</label>' +
'<textarea class="{{CSS.INPUT}}" id="{{id}}"></textarea>' +
'<div class="{{CSS.NOTICEAREA}}" style="display: none;" aria-live="assertive">' +
'<div class="{{CSS.NOTICE}}"><div></div></div>' +
'</div>' +
'</div>' +
'<div class="{{CSS.ACTIONS}}">' +
'<input type="submit" value="{{sendStr}}" class="{{CSS.SENDBTN}}">' +
'<a href="#" class="{{CSS.HISTORYLINK}}">{{viewHistoryStr}}</a>' +
'<div style="clear: both;"></div>' +
'</div>' +
'</form>'
);
content = Y.Node.create(
tpl({
CSS: CSS.SENDMSGDIALOG,
id: Y.guid(),
labelStr: M.util.get_string('messagetosend', 'core_message'),
loadingIcon: M.util.image_url('i/loading', 'moodle'),
sendStr: M.util.get_string('sendmessage', 'core_message'),
viewHistoryStr: M.util.get_string('viewconversation', 'core_message')
})
);
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
// Use standard dialogue class name. This removes the default styling of the footer.
this._bb.one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content');
// Set the events listeners.
this._setEvents();
},
/**
* Prepare the dialog for a user.
*
* @method prepareForUser
* @param {Number} userid The user ID.
* @param {String} fullname The user full name.
*/
prepareForUser: function(userid, fullname) {
var title;
this.set('userid', userid);
this.set('fullname', fullname);
// Prepare the title.
title = Y.Node.create('<h1>' + Y.Escape.html(fullname) + '</h1>');
this.setStdModContent(Y.WidgetStdMod.HEADER, title, Y.WidgetStdMod.REPLACE);
// Update the link to the conversation.
this._bb.one(SELECTORS.SENDMSGDIALOG.HISTORYLINK)
.set('href', M.cfg.wwwroot + '/message/index.php?id=' + this.get('userid'));
// Set the content as empty and lock send.
this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).set('value', '');
},
/**
* Send the message to the user.
*
* @method sendMessage
* @param {String} message The message to be sent.
*/
sendMessage: function(message) {
if (this._sendLock) {
// Do not proceed if the lock is active.
return;
}
if (!message || !this._validateMessage(message)) {
// Do not send falsy messages.
return;
}
// Actually send the message.
this._ioSend = Y.io(this.get('url'), {
method: 'POST',
data: build_querystring({
sesskey: M.cfg.sesskey,
action: 'sendmessage',
userid: this.get('userid'),
message: message
}),
on: {
start: function() {
var img = '<img alt="" role="presentation" src="' + M.util.image_url('i/loading_small', 'moodle') + '">';
this.setSendLock(true);
this.showNotice(img + ' ' + M.util.get_string('sendingmessage', 'core_message'));
},
success: function(id, response) {
var data = null;
try {
data = Y.JSON.parse(response.responseText);
if (data.error) {
this.hideNotice();
new M.core.ajaxException(data);
return;
}
} catch (e) {
this.hideNotice();
new M.core.exception(e);
return;
}
// Show a success message.
this.showNotice(M.util.get_string('messagesent', 'core_message'));
// Hide the dialog.
Y.later(1300, this, function() {
this.hideNotice();
this.hide();
});
},
failure: function() {
this.hideNotice();
new M.core.alert({
title: M.util.get_string('error', 'core'),
message: M.util.get_string('errorwhilesendingmessage', 'core_message')
});
},
complete: function() {
this.setSendLock(false);
}
},
context: this
});
},
/**
* Show a notice.
*
* @method hideNotice.
*/
hideNotice: function() {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).hide();
},
/**
* Show a notice.
*
* @param {String} html String to show.
* @method showNotice.
*/
showNotice: function(html) {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICE).setHTML(html);
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).show();
},
/**
* Set the send lock.
*
* We do not lock the send button because that would cause a focus change on screenreaders
* which then conflicts with the aria-live region reading out that we are sending a message.
*
* @method setSendLock
* @param {Boolean} lock When true, enables the lock.
*/
setSendLock: function(lock) {
if (lock) {
this._sendLock = true;
} else {
this._sendLock = false;
}
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
// Form submit.
this._bb.one(SELECTORS.SENDMSGDIALOG.FORM).on('submit', function(e) {
var message = this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).get('value');
e.preventDefault();
this.sendMessage(message);
}, this);
},
/**
* Validates a message.
*
* @method _validateMessage
* @param {String} message A message to be validated.
*/
_validateMessage: function(message) {
var trimmed;
if (!message) {
return false;
}
// Basic validation.
trimmed = message.replace(' ', '')
.replace('&nbsp;', '')
.replace(/(<br\s*\/?>(<\/br\s*\/?>)?)+/, '')
.trim();
return trimmed.length > 1;
}
}, {
NAME: 'core_message-messenger-sendmessage',
CSS_PREFIX: CSS.SENDMSGDIALOG.PREFIX,
ATTRS: {
/**
* Fullname of the user.
*
* @attribute fullname
* @default ''
* @type String
*/
fullname: {
validator: Y.Lang.isString,
value: ''
},
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default null
* @type String
*/
url: {
validator: Y.Lang.isString,
value: null
},
/**
* User ID this dialog interacts with.
*
* @attribute userid
* @default 0
* @type Number
*/
userid: {
validator: Y.Lang.isNumber,
value: 0
}
}
});
Y.Base.modifyAttrs(Y.namespace('M.core_message.messenger.sendMessage'), {
/**
* List of extra classes.
*
* @attribute extraClasses
* @default ['core_message-messenger-sendmessage']
* @type Array
*/
extraClasses: {
value: ['core_message-messenger-sendmessage']
},
/**
* Whether to focus on the target that caused the Widget to be shown.
*
* @attribute focusOnPreviousTargetAfterHide
* @default true
* @type Node
*/
focusOnPreviousTargetAfterHide: {
value: true
},
/**
*
* Width.
*
* @attribute width
* @default '260px'
* @type String|Number
*/
width: {
value: '360px'
},
/**
* Boolean indicating whether or not the Widget is visible.
*
* @attribute visible
* @default false
* @type Boolean
*/
visible: {
value: false
},
/**
* Whether the widget should be modal or not.
*
* @attribute modal
* @type Boolean
* @default true
*/
modal: {
value: true
},
/**
* Whether the widget should be draggable or not.
*
* @attribute draggable
* @type Boolean
* @default false
*/
draggable: {
value: false
},
/**
* Whether to display the dialogue centrally on the screen.
*
* @attribute center
* @type Boolean
* @default false
*/
center: {
value : true
}
});
}, '@VERSION@', {
"requires": [
"escape",
"handlebars",
"io-base",
"moodle-core-notification-ajaxexception",
"moodle-core-notification-alert",
"moodle-core-notification-dialogue",
"moodle-core-notification-exception"
]
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,564 @@
YUI.add('moodle-core_message-messenger', function (Y, NAME) {
// 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/>.
/**
* Messenger constants.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var CSS = {},
SELECTORS = {};
// 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/>.
/**
* Messenger manager.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
SELECTORS.MANAGER = {
SENDMESSAGE: '[data-trigger="core_message-messenger::sendmessage"]'
};
/**
* Messenger manager.
*
* @namespace M.core_message.messenger
* @class MANAGER
* @constructor
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').Manager = Y.extend(MANAGER, Y.Base, {
_sendMessageDialog: null,
_events: [],
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._setEvents();
},
/**
* Sending a message.
*
* @method sendMessage
* @param {Number} userid The user ID to send a message to.
* @param {String} fullname The fullname of the user.
* @param {EventFacade} e The event triggered, when any it should be passed to the dialog.
*/
sendMessage: function(userid, fullname, e) {
var Klass;
if (!this._sendMessageDialog) {
Klass = Y.namespace('M.core_message.messenger.sendMessage');
this._sendMessageDialog = new Klass({
url: this.get('url')
});
}
this._sendMessageDialog.prepareForUser(userid, fullname);
this._sendMessageDialog.show(e);
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
var captureEvent = function(e) {
var target = e.currentTarget,
userid = parseInt(target.getData('userid'), 10),
fullname = target.getData('fullname');
if (!userid || !fullname) {
return;
}
// Pass the validation before preventing defaults.
e.preventDefault();
this.sendMessage(userid, fullname, e);
};
this._events.push(Y.delegate('click', captureEvent, 'body', SELECTORS.MANAGER.SENDMESSAGE, this));
this._events.push(Y.one(Y.config.doc).delegate('key', captureEvent, 'space', SELECTORS.MANAGER.SENDMESSAGE, this));
}
}, {
NAME: 'core_message-messenger-manager',
ATTRS: {
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default M.cfg.wwwroot + '/message/ajax.php'
* @type String
*/
url: {
readonly: true,
value: M.cfg.wwwroot + '/message/ajax.php'
}
}
});
var MANAGERINST;
Y.namespace('M.core_message.messenger').init = function(config) {
if (!MANAGERINST) {
// Prevent duplicates of the manager if this function is called more than once.
MANAGERINST = new MANAGER(config);
}
return MANAGERINST;
};
// 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/>.
/**
* Send message dialog.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
CSS.SENDMSGDIALOG = {
ACCESSHIDE: 'accesshide',
ACTIONS: 'message-actions',
FOOTER: 'message-footer',
HIDDEN: 'hidden',
HISTORYLINK: 'message-history',
INPUT: 'message-input',
INPUTAREA: 'message-area',
NOTICE: 'message-notice',
NOTICEAREA: 'message-notice-area',
PREFIX: 'core_message-messenger-sendmessage',
SENDBTN: 'message-send',
WRAPPER: 'message-wrapper'
};
SELECTORS.SENDMSGDIALOG = {
FORM: 'form',
HISTORYLINK: '.message-history',
INPUT: '.message-input',
NOTICE: '.message-notice div',
NOTICEAREA: '.message-notice-area',
SENDBTN: '.message-send'
};
/**
* Send message dialog.
*
* @namespace M.core_message
* @class SENDMSGDIALOG
* @constructor
*/
var SENDMSGDIALOG = function() {
SENDMSGDIALOG.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').sendMessage = Y.extend(SENDMSGDIALOG, M.core.dialogue, {
_bb: null,
_sendLock: false,
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._bb = this.get('boundingBox');
// Prepare the content area.
tpl = Y.Handlebars.compile(
'<form action="#">' +
'<div class="{{CSS.INPUTAREA}}">' +
'<label class="{{CSS.ACCESSHIDE}}" for="{{id}}">{{labelStr}}</label>' +
'<textarea class="{{CSS.INPUT}}" id="{{id}}"></textarea>' +
'<div class="{{CSS.NOTICEAREA}}" style="display: none;" aria-live="assertive">' +
'<div class="{{CSS.NOTICE}}"><div></div></div>' +
'</div>' +
'</div>' +
'<div class="{{CSS.ACTIONS}}">' +
'<input type="submit" value="{{sendStr}}" class="{{CSS.SENDBTN}}">' +
'<a href="#" class="{{CSS.HISTORYLINK}}">{{viewHistoryStr}}</a>' +
'<div style="clear: both;"></div>' +
'</div>' +
'</form>'
);
content = Y.Node.create(
tpl({
CSS: CSS.SENDMSGDIALOG,
id: Y.guid(),
labelStr: M.util.get_string('messagetosend', 'core_message'),
loadingIcon: M.util.image_url('i/loading', 'moodle'),
sendStr: M.util.get_string('sendmessage', 'core_message'),
viewHistoryStr: M.util.get_string('viewconversation', 'core_message')
})
);
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
// Use standard dialogue class name. This removes the default styling of the footer.
this._bb.one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content');
// Set the events listeners.
this._setEvents();
},
/**
* Prepare the dialog for a user.
*
* @method prepareForUser
* @param {Number} userid The user ID.
* @param {String} fullname The user full name.
*/
prepareForUser: function(userid, fullname) {
var title;
this.set('userid', userid);
this.set('fullname', fullname);
// Prepare the title.
title = Y.Node.create('<h1>' + Y.Escape.html(fullname) + '</h1>');
this.setStdModContent(Y.WidgetStdMod.HEADER, title, Y.WidgetStdMod.REPLACE);
// Update the link to the conversation.
this._bb.one(SELECTORS.SENDMSGDIALOG.HISTORYLINK)
.set('href', M.cfg.wwwroot + '/message/index.php?id=' + this.get('userid'));
// Set the content as empty and lock send.
this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).set('value', '');
},
/**
* Send the message to the user.
*
* @method sendMessage
* @param {String} message The message to be sent.
*/
sendMessage: function(message) {
if (this._sendLock) {
// Do not proceed if the lock is active.
return;
}
if (!message || !this._validateMessage(message)) {
// Do not send falsy messages.
return;
}
// Actually send the message.
this._ioSend = Y.io(this.get('url'), {
method: 'POST',
data: build_querystring({
sesskey: M.cfg.sesskey,
action: 'sendmessage',
userid: this.get('userid'),
message: message
}),
on: {
start: function() {
var img = '<img alt="" role="presentation" src="' + M.util.image_url('i/loading_small', 'moodle') + '">';
this.setSendLock(true);
this.showNotice(img + ' ' + M.util.get_string('sendingmessage', 'core_message'));
},
success: function(id, response) {
var data = null;
try {
data = Y.JSON.parse(response.responseText);
if (data.error) {
this.hideNotice();
new M.core.ajaxException(data);
return;
}
} catch (e) {
this.hideNotice();
new M.core.exception(e);
return;
}
// Show a success message.
this.showNotice(M.util.get_string('messagesent', 'core_message'));
// Hide the dialog.
Y.later(1300, this, function() {
this.hideNotice();
this.hide();
});
},
failure: function() {
this.hideNotice();
new M.core.alert({
title: M.util.get_string('error', 'core'),
message: M.util.get_string('errorwhilesendingmessage', 'core_message')
});
},
complete: function() {
this.setSendLock(false);
}
},
context: this
});
},
/**
* Show a notice.
*
* @method hideNotice.
*/
hideNotice: function() {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).hide();
},
/**
* Show a notice.
*
* @param {String} html String to show.
* @method showNotice.
*/
showNotice: function(html) {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICE).setHTML(html);
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).show();
},
/**
* Set the send lock.
*
* We do not lock the send button because that would cause a focus change on screenreaders
* which then conflicts with the aria-live region reading out that we are sending a message.
*
* @method setSendLock
* @param {Boolean} lock When true, enables the lock.
*/
setSendLock: function(lock) {
if (lock) {
this._sendLock = true;
} else {
this._sendLock = false;
}
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
// Form submit.
this._bb.one(SELECTORS.SENDMSGDIALOG.FORM).on('submit', function(e) {
var message = this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).get('value');
e.preventDefault();
this.sendMessage(message);
}, this);
},
/**
* Validates a message.
*
* @method _validateMessage
* @param {String} message A message to be validated.
*/
_validateMessage: function(message) {
var trimmed;
if (!message) {
return false;
}
// Basic validation.
trimmed = message.replace(' ', '')
.replace('&nbsp;', '')
.replace(/(<br\s*\/?>(<\/br\s*\/?>)?)+/, '')
.trim();
return trimmed.length > 1;
}
}, {
NAME: 'core_message-messenger-sendmessage',
CSS_PREFIX: CSS.SENDMSGDIALOG.PREFIX,
ATTRS: {
/**
* Fullname of the user.
*
* @attribute fullname
* @default ''
* @type String
*/
fullname: {
validator: Y.Lang.isString,
value: ''
},
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default null
* @type String
*/
url: {
validator: Y.Lang.isString,
value: null
},
/**
* User ID this dialog interacts with.
*
* @attribute userid
* @default 0
* @type Number
*/
userid: {
validator: Y.Lang.isNumber,
value: 0
}
}
});
Y.Base.modifyAttrs(Y.namespace('M.core_message.messenger.sendMessage'), {
/**
* List of extra classes.
*
* @attribute extraClasses
* @default ['core_message-messenger-sendmessage']
* @type Array
*/
extraClasses: {
value: ['core_message-messenger-sendmessage']
},
/**
* Whether to focus on the target that caused the Widget to be shown.
*
* @attribute focusOnPreviousTargetAfterHide
* @default true
* @type Node
*/
focusOnPreviousTargetAfterHide: {
value: true
},
/**
*
* Width.
*
* @attribute width
* @default '260px'
* @type String|Number
*/
width: {
value: '360px'
},
/**
* Boolean indicating whether or not the Widget is visible.
*
* @attribute visible
* @default false
* @type Boolean
*/
visible: {
value: false
},
/**
* Whether the widget should be modal or not.
*
* @attribute modal
* @type Boolean
* @default true
*/
modal: {
value: true
},
/**
* Whether the widget should be draggable or not.
*
* @attribute draggable
* @type Boolean
* @default false
*/
draggable: {
value: false
},
/**
* Whether to display the dialogue centrally on the screen.
*
* @attribute center
* @type Boolean
* @default false
*/
center: {
value : true
}
});
}, '@VERSION@', {
"requires": [
"escape",
"handlebars",
"io-base",
"moodle-core-notification-ajaxexception",
"moodle-core-notification-alert",
"moodle-core-notification-dialogue",
"moodle-core-notification-exception"
]
});

View file

@ -0,0 +1,12 @@
{
"name": "moodle-core_message-messenger",
"builds": {
"moodle-core_message-messenger": {
"jsfiles": [
"constants.js",
"manager.js",
"sendmessage.js"
]
}
}
}

View file

@ -0,0 +1,26 @@
// 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/>.
/**
* Messenger constants.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var CSS = {},
SELECTORS = {};

123
message/yui/src/messenger/js/manager.js vendored Normal file
View file

@ -0,0 +1,123 @@
// 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/>.
/**
* Messenger manager.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
SELECTORS.MANAGER = {
SENDMESSAGE: '[data-trigger="core_message-messenger::sendmessage"]'
};
/**
* Messenger manager.
*
* @namespace M.core_message.messenger
* @class MANAGER
* @constructor
*/
var MANAGER = function() {
MANAGER.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').Manager = Y.extend(MANAGER, Y.Base, {
_sendMessageDialog: null,
_events: [],
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._setEvents();
},
/**
* Sending a message.
*
* @method sendMessage
* @param {Number} userid The user ID to send a message to.
* @param {String} fullname The fullname of the user.
* @param {EventFacade} e The event triggered, when any it should be passed to the dialog.
*/
sendMessage: function(userid, fullname, e) {
var Klass;
if (!this._sendMessageDialog) {
Klass = Y.namespace('M.core_message.messenger.sendMessage');
this._sendMessageDialog = new Klass({
url: this.get('url')
});
}
this._sendMessageDialog.prepareForUser(userid, fullname);
this._sendMessageDialog.show(e);
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
var captureEvent = function(e) {
var target = e.currentTarget,
userid = parseInt(target.getData('userid'), 10),
fullname = target.getData('fullname');
if (!userid || !fullname) {
return;
}
// Pass the validation before preventing defaults.
e.preventDefault();
this.sendMessage(userid, fullname, e);
};
this._events.push(Y.delegate('click', captureEvent, 'body', SELECTORS.MANAGER.SENDMESSAGE, this));
this._events.push(Y.one(Y.config.doc).delegate('key', captureEvent, 'space', SELECTORS.MANAGER.SENDMESSAGE, this));
}
}, {
NAME: 'core_message-messenger-manager',
ATTRS: {
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default M.cfg.wwwroot + '/message/ajax.php'
* @type String
*/
url: {
readonly: true,
value: M.cfg.wwwroot + '/message/ajax.php'
}
}
});
var MANAGERINST;
Y.namespace('M.core_message.messenger').init = function(config) {
if (!MANAGERINST) {
// Prevent duplicates of the manager if this function is called more than once.
MANAGERINST = new MANAGER(config);
}
return MANAGERINST;
};

View file

@ -0,0 +1,400 @@
// 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/>.
/**
* Send message dialog.
*
* @module moodle-core_message-messenger
* @package core_message
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
CSS.SENDMSGDIALOG = {
ACCESSHIDE: 'accesshide',
ACTIONS: 'message-actions',
FOOTER: 'message-footer',
HIDDEN: 'hidden',
HISTORYLINK: 'message-history',
INPUT: 'message-input',
INPUTAREA: 'message-area',
NOTICE: 'message-notice',
NOTICEAREA: 'message-notice-area',
PREFIX: 'core_message-messenger-sendmessage',
SENDBTN: 'message-send',
WRAPPER: 'message-wrapper'
};
SELECTORS.SENDMSGDIALOG = {
FORM: 'form',
HISTORYLINK: '.message-history',
INPUT: '.message-input',
NOTICE: '.message-notice div',
NOTICEAREA: '.message-notice-area',
SENDBTN: '.message-send'
};
/**
* Send message dialog.
*
* @namespace M.core_message
* @class SENDMSGDIALOG
* @constructor
*/
var SENDMSGDIALOG = function() {
SENDMSGDIALOG.superclass.constructor.apply(this, arguments);
};
Y.namespace('M.core_message.messenger').sendMessage = Y.extend(SENDMSGDIALOG, M.core.dialogue, {
_bb: null,
_sendLock: false,
/**
* Initializer.
*
* @method initializer
*/
initializer: function() {
this._bb = this.get('boundingBox');
// Prepare the content area.
tpl = Y.Handlebars.compile(
'<form action="#">' +
'<div class="{{CSS.INPUTAREA}}">' +
'<label class="{{CSS.ACCESSHIDE}}" for="{{id}}">{{labelStr}}</label>' +
'<textarea class="{{CSS.INPUT}}" id="{{id}}"></textarea>' +
'<div class="{{CSS.NOTICEAREA}}" style="display: none;" aria-live="assertive">' +
'<div class="{{CSS.NOTICE}}"><div></div></div>' +
'</div>' +
'</div>' +
'<div class="{{CSS.ACTIONS}}">' +
'<input type="submit" value="{{sendStr}}" class="{{CSS.SENDBTN}}">' +
'<a href="#" class="{{CSS.HISTORYLINK}}">{{viewHistoryStr}}</a>' +
'<div style="clear: both;"></div>' +
'</div>' +
'</form>'
);
content = Y.Node.create(
tpl({
CSS: CSS.SENDMSGDIALOG,
id: Y.guid(),
labelStr: M.util.get_string('messagetosend', 'core_message'),
loadingIcon: M.util.image_url('i/loading', 'moodle'),
sendStr: M.util.get_string('sendmessage', 'core_message'),
viewHistoryStr: M.util.get_string('viewconversation', 'core_message')
})
);
this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE);
// Use standard dialogue class name. This removes the default styling of the footer.
this._bb.one('.moodle-dialogue-wrap').addClass('moodle-dialogue-content');
// Set the events listeners.
this._setEvents();
},
/**
* Prepare the dialog for a user.
*
* @method prepareForUser
* @param {Number} userid The user ID.
* @param {String} fullname The user full name.
*/
prepareForUser: function(userid, fullname) {
var title;
this.set('userid', userid);
this.set('fullname', fullname);
// Prepare the title.
title = Y.Node.create('<h1>' + Y.Escape.html(fullname) + '</h1>');
this.setStdModContent(Y.WidgetStdMod.HEADER, title, Y.WidgetStdMod.REPLACE);
// Update the link to the conversation.
this._bb.one(SELECTORS.SENDMSGDIALOG.HISTORYLINK)
.set('href', M.cfg.wwwroot + '/message/index.php?id=' + this.get('userid'));
// Set the content as empty and lock send.
this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).set('value', '');
},
/**
* Send the message to the user.
*
* @method sendMessage
* @param {String} message The message to be sent.
*/
sendMessage: function(message) {
if (this._sendLock) {
// Do not proceed if the lock is active.
return;
}
if (!message || !this._validateMessage(message)) {
// Do not send falsy messages.
return;
}
// Actually send the message.
this._ioSend = Y.io(this.get('url'), {
method: 'POST',
data: build_querystring({
sesskey: M.cfg.sesskey,
action: 'sendmessage',
userid: this.get('userid'),
message: message
}),
on: {
start: function() {
var img = '<img alt="" role="presentation" src="' + M.util.image_url('i/loading_small', 'moodle') + '">';
this.setSendLock(true);
this.showNotice(img + ' ' + M.util.get_string('sendingmessage', 'core_message'));
},
success: function(id, response) {
var data = null;
try {
data = Y.JSON.parse(response.responseText);
if (data.error) {
this.hideNotice();
new M.core.ajaxException(data);
return;
}
} catch (e) {
this.hideNotice();
new M.core.exception(e);
return;
}
// Show a success message.
this.showNotice(M.util.get_string('messagesent', 'core_message'));
// Hide the dialog.
Y.later(1300, this, function() {
this.hideNotice();
this.hide();
});
},
failure: function() {
this.hideNotice();
new M.core.alert({
title: M.util.get_string('error', 'core'),
message: M.util.get_string('errorwhilesendingmessage', 'core_message')
});
},
complete: function() {
this.setSendLock(false);
}
},
context: this
});
},
/**
* Show a notice.
*
* @method hideNotice.
*/
hideNotice: function() {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).hide();
},
/**
* Show a notice.
*
* @param {String} html String to show.
* @method showNotice.
*/
showNotice: function(html) {
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICE).setHTML(html);
this._bb.one(SELECTORS.SENDMSGDIALOG.NOTICEAREA).show();
},
/**
* Set the send lock.
*
* We do not lock the send button because that would cause a focus change on screenreaders
* which then conflicts with the aria-live region reading out that we are sending a message.
*
* @method setSendLock
* @param {Boolean} lock When true, enables the lock.
*/
setSendLock: function(lock) {
if (lock) {
this._sendLock = true;
} else {
this._sendLock = false;
}
},
/**
* Register the events.
*
* @method _setEvents.
*/
_setEvents: function() {
// Form submit.
this._bb.one(SELECTORS.SENDMSGDIALOG.FORM).on('submit', function(e) {
var message = this._bb.one(SELECTORS.SENDMSGDIALOG.INPUT).get('value');
e.preventDefault();
this.sendMessage(message);
}, this);
},
/**
* Validates a message.
*
* @method _validateMessage
* @param {String} message A message to be validated.
*/
_validateMessage: function(message) {
var trimmed;
if (!message) {
return false;
}
// Basic validation.
trimmed = message.replace(' ', '')
.replace('&nbsp;', '')
.replace(/(<br\s*\/?>(<\/br\s*\/?>)?)+/, '')
.trim();
return trimmed.length > 1;
}
}, {
NAME: 'core_message-messenger-sendmessage',
CSS_PREFIX: CSS.SENDMSGDIALOG.PREFIX,
ATTRS: {
/**
* Fullname of the user.
*
* @attribute fullname
* @default ''
* @type String
*/
fullname: {
validator: Y.Lang.isString,
value: ''
},
/**
* URL to the message Ajax actions.
*
* @attribute url
* @default null
* @type String
*/
url: {
validator: Y.Lang.isString,
value: null
},
/**
* User ID this dialog interacts with.
*
* @attribute userid
* @default 0
* @type Number
*/
userid: {
validator: Y.Lang.isNumber,
value: 0
}
}
});
Y.Base.modifyAttrs(Y.namespace('M.core_message.messenger.sendMessage'), {
/**
* List of extra classes.
*
* @attribute extraClasses
* @default ['core_message-messenger-sendmessage']
* @type Array
*/
extraClasses: {
value: ['core_message-messenger-sendmessage']
},
/**
* Whether to focus on the target that caused the Widget to be shown.
*
* @attribute focusOnPreviousTargetAfterHide
* @default true
* @type Node
*/
focusOnPreviousTargetAfterHide: {
value: true
},
/**
*
* Width.
*
* @attribute width
* @default '260px'
* @type String|Number
*/
width: {
value: '360px'
},
/**
* Boolean indicating whether or not the Widget is visible.
*
* @attribute visible
* @default false
* @type Boolean
*/
visible: {
value: false
},
/**
* Whether the widget should be modal or not.
*
* @attribute modal
* @type Boolean
* @default true
*/
modal: {
value: true
},
/**
* Whether the widget should be draggable or not.
*
* @attribute draggable
* @type Boolean
* @default false
*/
draggable: {
value: false
},
/**
* Whether to display the dialogue centrally on the screen.
*
* @attribute center
* @type Boolean
* @default false
*/
center: {
value : true
}
});

View file

@ -0,0 +1,13 @@
{
"moodle-core_message-messenger": {
"requires": [
"escape",
"handlebars",
"io-base",
"moodle-core-notification-ajaxexception",
"moodle-core-notification-alert",
"moodle-core-notification-dialogue",
"moodle-core-notification-exception"
]
}
}