From 12d8c7b2227ba509da49a590220949358e8242b2 Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Tue, 21 Jun 2016 03:32:06 +0000 Subject: [PATCH] MDL-54700 message: added toggle contact button to user profile --- lang/en/message.php | 2 + lang/en/moodle.php | 1 + lib/db/services.php | 2 + lib/outputcomponents.php | 8 +- lib/outputrenderers.php | 22 +- message/amd/src/toggle_contact_button.js | 195 ++++++++++++++++++ message/lib.php | 36 ++++ message/templates/add_contact_button.mustache | 29 +++ .../templates/remove_contact_button.mustache | 29 +++ theme/bootstrapbase/less/moodle/user.less | 16 ++ theme/bootstrapbase/style/moodle.css | 13 ++ 11 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 message/amd/src/toggle_contact_button.js create mode 100644 message/templates/add_contact_button.mustache create mode 100644 message/templates/remove_contact_button.mustache diff --git a/lang/en/message.php b/lang/en/message.php index d7157625698..629220d8dbc 100644 --- a/lang/en/message.php +++ b/lang/en/message.php @@ -25,6 +25,7 @@ $string['addcontact'] = 'Add contact'; $string['addsomecontacts'] = 'To send a message to someone, or to add a shortcut for them on this page, use the search tab above.'; $string['addsomecontactsincoming'] = 'These messages are from people who are not in your contact list. To add them to your contacts, click the "Add contact" icon next to their name.'; +$string['addtoyourcontacts'] = 'Add to your contacts'; $string['ago'] = '{$a} ago'; $string['ajax_gui'] = 'Ajax chat room'; $string['allmine'] = 'All messages to me or from me'; @@ -126,6 +127,7 @@ $string['providerstag'] = 'Source'; $string['recent'] = 'Recent'; $string['readmessages'] = '{$a} read messages'; $string['removecontact'] = 'Remove contact'; +$string['removefromyourcontacts'] = 'Remove from your contacts'; $string['savemysettings'] = 'Save my settings'; $string['search'] = 'Search'; $string['searchforperson'] = 'Search for a person'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 632356e229b..b7334d7fddf 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1052,6 +1052,7 @@ $string['list'] = 'List'; $string['listfiles'] = 'List of files in {$a}'; $string['listofallpeople'] = 'List of all people'; $string['listofcourses'] = 'List of courses'; +$string['loading'] = 'Loading'; $string['loadinghelp'] = 'Loading...'; $string['local'] = 'Local'; $string['localplugins'] = 'Local plugins'; diff --git a/lib/db/services.php b/lib/db/services.php index 795ebba85ef..7c4af78057f 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -612,6 +612,7 @@ $functions = array( 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + 'ajax' => true, ), 'core_message_delete_contacts' => array( 'classname' => 'core_message_external', @@ -621,6 +622,7 @@ $functions = array( 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + 'ajax' => true, ), 'core_message_delete_conversation' => array( 'classname' => 'core_message_external', diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index 1217fb043c8..cd906973449 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -3684,9 +3684,15 @@ class context_header implements renderable { $this->additionalbuttons[$buttontype]['formattedimage'] = $button['image']; } } + + if (isset($button['linkattributes']['class'])) { + $class = $button['linkattributes']['class'] . ' btn'; + } else { + $class = 'btn'; + } // Add the bootstrap 'btn' class for formatting. $this->additionalbuttons[$buttontype]['linkattributes'] = array_merge($button['linkattributes'], - array('class' => 'btn')); + array('class' => $class)); } } } diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index fc95c5d5eb2..60974e4ecbb 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -4092,6 +4092,10 @@ EOD; $imagedata = $this->user_picture($user, array('size' => 100)); // Check to see if we should be displaying a message button. if (!empty($CFG->messaging) && $USER->id != $user->id && has_capability('moodle/site:sendmessage', $context)) { + $iscontact = !empty(message_get_contact($user->id)); + $contacttitle = $iscontact ? 'removefromyourcontacts' : 'addtoyourcontacts'; + $contacturlaction = $iscontact ? 'removecontact' : 'addcontact'; + $contactimage = $iscontact ? 'removecontact' : 'addcontact'; $userbuttons = array( 'messages' => array( 'buttontype' => 'message', @@ -4100,8 +4104,22 @@ EOD; 'image' => 'message', 'linkattributes' => message_messenger_sendmessage_link_params($user), 'page' => $this->page - ) + ), + 'togglecontact' => array( + 'buttontype' => 'togglecontact', + 'title' => get_string($contacttitle, 'message'), + 'url' => new moodle_url('/message/index.php', array( + 'user1' => $USER->id, + 'user2' => $user->id, + $contacturlaction => $user->id, + 'sesskey' => sesskey()) + ), + 'image' => $contactimage, + 'linkattributes' => message_togglecontact_link_params($user, $iscontact), + 'page' => $this->page + ), ); + $this->page->requires->string_for_js('changesmadereallygoaway', 'moodle'); } } @@ -4160,6 +4178,8 @@ EOD; // Include js for messaging. if ($button['buttontype'] === 'message') { message_messenger_requirejs(); + } else if ($button['buttontype'] === 'togglecontact') { + message_togglecontact_requirejs(); } $image = $this->pix_icon($button['formattedimage'], $button['title'], 'moodle', array( 'class' => 'iconsmall', diff --git a/message/amd/src/toggle_contact_button.js b/message/amd/src/toggle_contact_button.js new file mode 100644 index 00000000000..30c0303363b --- /dev/null +++ b/message/amd/src/toggle_contact_button.js @@ -0,0 +1,195 @@ +// 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 . + +/** + * Module to add/remove contact using ajax. + * + * @module message/toggle_contact_button + * @class toggle_contact_button + * @package message + * @copyright 2016 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 3.2 + */ +define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/custom_interaction_events'], + function($, ajax, templates, notification, customEvents) { + + /** + * Check the state of the element, if the current user is a contact or not. + * + * @method isContact + * @param element jQuery object for the button + * @return bool + */ + var isContact = function(element) { + return element.attr('data-is-contact') == '1'; + }; + + /** + * Record that the user is a contact. + * + * @method setContact + * @param element jQuery object for the button + */ + var setContact = function(element) { + element.attr('data-is-contact', '1'); + }; + + /** + * Record that the user is not a contact. + * + * @method setNotContact + * @param element jQuery object for the button + */ + var setNotContact = function(element) { + element.attr('data-is-contact', '0'); + }; + + /** + * Get the id for the user being viewed. + * + * @method getUserId + * @param element jQuery object for the button + * @return int + */ + var getUserId = function(element) { + return element.attr('data-userid'); + }; + + /** + * Check if this element is currently loading. + * + * @method isLoading + * @param element jQuery object for the button + * @return bool + */ + var isLoading = function(element) { + return element.hasClass('loading') || element.attr('disabled'); + }; + + /** + * Sends an ajax request to the server and handles the element state + * while the request is being performed. + * + * @method sendRequest + * @param element jQuery object for the button + * @param request request hash to send + * @return jQuery promise + */ + var sendRequest = function(element, request) { + if (isLoading(element)) { + return $.Deferred(); + } + + element.addClass('loading'); + element.attr('disabled', 'disabled'); + + return ajax.call([request])[0] + .fail(notification.exception) + .always(function() { + element.removeClass('loading'); + element.removeAttr('disabled'); + }); + }; + + /** + * Send a request to the server to add the current user as + * a contact. The contents of the button are changed to the + * remove contact button upon success. + * + * @method addContact + * @param element jQuery object for the button + */ + var addContact = function(element) { + if (isLoading(element)) { + return; + } + + var request = { + methodname: 'core_message_create_contacts', + args: { + userids: [getUserId(element)], + } + }; + sendRequest(element, request).done(function() { + setContact(element); + templates.render('message/remove_contact_button', {}).done(function(html, js) { + templates.replaceNodeContents(element, html, js); + }); + }); + }; + + /** + * Send a request to the server to remove the current user as + * a contact. The contents of the button are changed to the + * add contact button upon success. + * + * @method removeContact + * @param element jQuery object for the button + */ + var removeContact = function(element) { + if (isLoading(element)) { + return; + } + + var request = { + methodname: 'core_message_delete_contacts', + args: { + userids: [getUserId(element)], + } + }; + + sendRequest(element, request).done(function() { + setNotContact(element); + templates.render('message/add_contact_button', {}).done(function(html, js) { + templates.replaceNodeContents(element, html, js); + }); + }); + }; + + /** + * Enhances the given element with a loading gif and event handles to make + * ajax requests to add or remove a contact where appropriate. + * + * @method enhance + * @param element jQuery object for the button + */ + var enhance = function(element) { + element = $(element); + + if (!element.children('.loading-icon').length) { + // Add the loading gif if it isn't already there. + templates.render('core/loading', {}).done(function(html, js) { + element.append(html, js); + }); + } + + customEvents.define(element, [customEvents.events.activate]); + + element.on(customEvents.events.activate, function(e, data) { + if (isContact(element)) { + removeContact(element); + } else { + addContact(element); + } + e.preventDefault(); + data.originalEvent.preventDefault(); + }); + }; + + return /** @alias module:message/toggle_contact_button */ { + enhance: enhance + }; +}); diff --git a/message/lib.php b/message/lib.php index b73176f8983..40fda1106e6 100644 --- a/message/lib.php +++ b/message/lib.php @@ -1805,6 +1805,23 @@ function message_messenger_requirejs() { $done = true; } +/** + * Requires the JS libraries for the toggle contact button. + * + * @return void + */ +function message_togglecontact_requirejs() { + global $PAGE; + + static $done = false; + if ($done) { + return; + } + + $PAGE->requires->js_call_amd('message/toggle_contact_button', 'enhance', array('#toggle-contact-button')); + $done = true; +} + /** * Returns the attributes to place on a link to open the 'Send message' dialog. * @@ -1828,6 +1845,25 @@ function message_messenger_sendmessage_link_params($user) { return $params; } +/** + * Returns the attributes to place on a contact button. + * + * @param object $user User object. + * @param bool $iscontact + * @return void + */ +function message_togglecontact_link_params($user, $iscontact = false) { + $params = array( + 'data-userid' => $user->id, + 'data-is-contact' => $iscontact, + 'id' => 'toggle-contact-button', + 'role' => 'button', + 'class' => 'ajax-contact-button', + ); + + return $params; +} + /** * Determines if a user is permitted to send another user a private message. * If no sender is provided then it defaults to the logged in user. diff --git a/message/templates/add_contact_button.mustache b/message/templates/add_contact_button.mustache new file mode 100644 index 00000000000..5df410f476e --- /dev/null +++ b/message/templates/add_contact_button.mustache @@ -0,0 +1,29 @@ +{{! + 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 . +}} +{{! + @template core/add_contact_button + + Template for the contents of the add contact button on the user's profile page. + + Context variables required for this template: + * +}} + + {{#pix}} t/addcontact, core {{/pix}} + {{#str}} addtoyourcontacts, message {{/str}} + +{{> core/loading }} diff --git a/message/templates/remove_contact_button.mustache b/message/templates/remove_contact_button.mustache new file mode 100644 index 00000000000..6d8fec25e1f --- /dev/null +++ b/message/templates/remove_contact_button.mustache @@ -0,0 +1,29 @@ +{{! + 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 . +}} +{{! + @template core/add_contact_button + + Template for the contents of the add contact button on the user's profile page. + + Context variables required for this template: + * +}} + + {{#pix}} t/removecontact, core {{/pix}} + {{#str}} removefromyourcontacts, message {{/str}} + +{{> core/loading }} diff --git a/theme/bootstrapbase/less/moodle/user.less b/theme/bootstrapbase/less/moodle/user.less index 9b7bbc210df..9d646f00f46 100644 --- a/theme/bootstrapbase/less/moodle/user.less +++ b/theme/bootstrapbase/less/moodle/user.less @@ -52,6 +52,22 @@ text-align: right; } } +.ajax-contact-button { + height: 31px; + box-sizing: border-box; + + &.loading { + > *:not(.loading-icon) { + display: none; + } + .loading-icon { + display: block; + } + } + .loading-icon { + display: none; + } +} @media (max-width: 480px) { .userprofile .profile_tree { diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index 6394a5400c3..f322a7eeb8c 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -6691,6 +6691,19 @@ body.path-question-type .mform fieldset.hidden { .path-user .node_category .viewmore { text-align: right; } +.ajax-contact-button { + height: 31px; + box-sizing: border-box; +} +.ajax-contact-button.loading > *:not(.loading-icon) { + display: none; +} +.ajax-contact-button.loading .loading-icon { + display: block; +} +.ajax-contact-button .loading-icon { + display: none; +} @media (max-width: 480px) { .userprofile .profile_tree { /** Display the profile on one column on phones. */