mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
Merge branch 'MDL-68076-master-4' of git://github.com/rezaies/moodle
This commit is contained in:
commit
2edcba39a2
@ -20,6 +20,9 @@ $moodleservices = new admin_settingpage('moodleservices', new lang_string('moodl
|
||||
'admin'));
|
||||
$ADMIN->add('root', $moodleservices);
|
||||
|
||||
$userfeedback = new admin_settingpage('userfeedback', new lang_string('feedbacksettings', 'admin'));
|
||||
$ADMIN->add('root', $userfeedback);
|
||||
|
||||
if ($hassiteconfig) {
|
||||
$optionalsubsystems = new admin_settingpage('optionalsubsystems', new lang_string('advancedfeatures', 'admin'));
|
||||
$ADMIN->add('root', $optionalsubsystems);
|
||||
|
49
admin/settings/userfeedback.php
Normal file
49
admin/settings/userfeedback.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains call to feedback settings
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if ($hassiteconfig) {
|
||||
|
||||
$userfeedback->add(new admin_setting_configcheckbox('enableuserfeedback',
|
||||
new lang_string('enableuserfeedback', 'admin'),
|
||||
new lang_string('enableuserfeedback_desc', 'admin'), 1, 1, 0));
|
||||
|
||||
$options = [
|
||||
core_userfeedback::REMIND_AFTER_UPGRADE => new lang_string('userfeedbackafterupgrade', 'admin'),
|
||||
core_userfeedback::REMIND_PERIODICALLY => new lang_string('userfeedbackperiodically', 'admin'),
|
||||
core_userfeedback::REMIND_NEVER => new lang_string('never'),
|
||||
];
|
||||
$userfeedback->add(new admin_setting_configselect('userfeedback_nextreminder',
|
||||
new lang_string('userfeedbacknextreminder', 'admin'),
|
||||
new lang_string('userfeedbacknextreminder_desc', 'admin'), 1, $options));
|
||||
$userfeedback->hide_if('userfeedback_nextreminder', 'enableuserfeedback');
|
||||
|
||||
$userfeedback->add(new admin_setting_configtext('userfeedback_remindafter',
|
||||
new lang_string('userfeedbackremindafter', 'admin'),
|
||||
new lang_string('userfeedbackremindafter_desc', 'admin'), 90, PARAM_INT));
|
||||
$userfeedback->hide_if('userfeedback_remindafter', 'enableuserfeedback');
|
||||
$userfeedback->hide_if('userfeedback_remindafter', 'userfeedback_nextreminder', 'eq', 3);
|
||||
|
||||
}
|
@ -546,6 +546,8 @@ $string['enablerssfeeds'] = 'Enable RSS feeds';
|
||||
$string['enablesearchareas'] = 'Enable search areas';
|
||||
$string['enablestats'] = 'Enable statistics';
|
||||
$string['enabletrusttext'] = 'Enable trusted content';
|
||||
$string['enableuserfeedback'] = 'Enable feedback about Moodle';
|
||||
$string['enableuserfeedback_desc'] = 'If enabled, a \'Give feedback\' link is displayed in a Dashboard alert and in the footer for users to give feedback about the Moodle LMS to Moodle HQ. The Dashboard alert also has a \'Remind me later\' option.';
|
||||
$string['enablewebservices'] = 'Enable web services';
|
||||
$string['enablewsdocumentation'] = 'Web services documentation';
|
||||
$string['enrolinstancedefaults'] = 'Enrolment instance defaults';
|
||||
@ -578,6 +580,7 @@ $string['experimentalsettings'] = 'Experimental settings';
|
||||
$string['extendedusernamechars'] = 'Allow extended characters in usernames';
|
||||
$string['extramemorylimit'] = 'Extra PHP memory limit';
|
||||
$string['fatalsessionautostart'] = '<p>Serious configuration error detected, please notify server administrator.</p><p> To operate properly, Moodle requires that administrator changes PHP settings.</p><p><code>session.auto_start</code> must be set to <code>off</code>.</p><p>This setting is controlled by editing <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file on the server.</p>';
|
||||
$string['feedbacksettings'] = 'Feedback settings';
|
||||
$string['filescleanupperiod'] = 'Clean up trash pool files';
|
||||
$string['filescleanupperiod_help'] = 'How often trash pool files are deleted. These are files that are associated with a context that no longer exists, for example when a course is deleted. Please note: This setting can result in missing files in a course which is backed up, deleted and then restored if the setting \'Include files\' (backup_auto_files) in \'Automated backup settings\' is disabled.';
|
||||
$string['fileconversioncleanuptask'] = 'Cleanup of temporary records for file conversions.';
|
||||
@ -1413,6 +1416,12 @@ $string['useexternalyui'] = 'Use online YUI libraries';
|
||||
$string['user'] = 'User';
|
||||
$string['userbulk'] = 'Bulk user actions';
|
||||
$string['userbulkdownload'] = 'Export users as';
|
||||
$string['userfeedbackafterupgrade'] = 'After every major upgrade';
|
||||
$string['userfeedbacknextreminder'] = 'Next feedback reminder';
|
||||
$string['userfeedbacknextreminder_desc'] = 'When should we ask the user to give feedback again?';
|
||||
$string['userfeedbackperiodically'] = 'Periodically';
|
||||
$string['userfeedbackremindafter'] = 'Show reminder after';
|
||||
$string['userfeedbackremindafter_desc'] = 'Remind users again to give feedback after the given number of days.';
|
||||
$string['userimagesdisabled'] = 'Profile user images are disabled';
|
||||
$string['userlist'] = 'Browse list of users';
|
||||
$string['userdefaultpreferences'] = 'User default preferences';
|
||||
|
@ -225,6 +225,9 @@ $string['bycourseorder'] = 'By course order';
|
||||
$string['byname'] = 'by {$a}';
|
||||
$string['bypassed'] = 'Bypassed';
|
||||
$string['cachecontrols'] = 'Cache controls';
|
||||
$string['calltofeedback'] = 'Moodle HQ would like your feedback on the Moodle LMS.';
|
||||
$string['calltofeedback_give'] = 'Give feedback';
|
||||
$string['calltofeedback_remind'] = 'Remind me later';
|
||||
$string['cancel'] = 'Cancel';
|
||||
$string['cancelled'] = 'Cancelled';
|
||||
$string['categories'] = 'Course categories';
|
||||
@ -802,6 +805,8 @@ $string['eventsearchresultsviewed'] = 'Search results viewed';
|
||||
$string['eventunknownlogged'] = 'Unknown event';
|
||||
$string['eventusercreated'] = 'User created';
|
||||
$string['eventuserdeleted'] = 'User deleted';
|
||||
$string['eventuserfeedbackgiven'] = 'Feedback link clicked';
|
||||
$string['eventuserfeedbackremind'] = 'Remind me later feedback link clicked';
|
||||
$string['eventuserlistviewed'] = 'User list viewed';
|
||||
$string['eventuserloggedout'] = 'User logged out';
|
||||
$string['eventuserpasswordpolicyfailed'] = 'User password failed password policy';
|
||||
|
2
lib/amd/build/userfeedback.min.js
vendored
Normal file
2
lib/amd/build/userfeedback.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core/userfeedback",["exports","core/ajax","core/notification"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.registerEventListeners=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}var f={regions:{root:"[data-region=\"core/userfeedback\"]"},actions:{}};f.actions.give="".concat(f.regions.root," [data-action=\"give\"]");f.actions.remind="".concat(f.regions.root," [data-action=\"remind\"]");a.registerEventListeners=function registerEventListeners(){document.addEventListener("click",function(a){var b=a.target.closest(f.actions.give);if(b){a.preventDefault();g().then(function(){return i(b)}).then(h).catch(c.default.exception)}var d=a.target.closest(f.actions.remind);if(d){a.preventDefault();Promise.resolve(d).then(i).then(h).catch(c.default.exception)}})};var g=function(){return b.default.call([{methodname:"core_get_userfeedback_url",args:{contextid:M.cfg.contextid}}])[0].then(function(a){if(!window.open(a)){throw new Error("Unable to open popup")}})},h=function(a){if(a.dataset.record){return b.default.call([{methodname:"core_create_userfeedback_action_record",args:{action:a.dataset.action,contextid:M.cfg.contextid}}])[0]}return Promise.resolve()},i=function(a){if(a.dataset.hide){a.closest(f.regions.root).remove()}return a}});
|
||||
//# sourceMappingURL=userfeedback.min.js.map
|
1
lib/amd/build/userfeedback.min.js.map
Normal file
1
lib/amd/build/userfeedback.min.js.map
Normal file
File diff suppressed because one or more lines are too long
115
lib/amd/src/userfeedback.js
Normal file
115
lib/amd/src/userfeedback.js
Normal file
@ -0,0 +1,115 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Handle clicking on action links of the feedback alert.
|
||||
*
|
||||
* @module core/cta_feedback
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
import Notification from 'core/notification';
|
||||
|
||||
const Selectors = {
|
||||
regions: {
|
||||
root: '[data-region="core/userfeedback"]',
|
||||
},
|
||||
actions: {},
|
||||
};
|
||||
Selectors.actions.give = `${Selectors.regions.root} [data-action="give"]`;
|
||||
Selectors.actions.remind = `${Selectors.regions.root} [data-action="remind"]`;
|
||||
|
||||
/**
|
||||
* Attach the necessary event handlers to the action links
|
||||
*/
|
||||
export const registerEventListeners = () => {
|
||||
document.addEventListener('click', e => {
|
||||
const giveAction = e.target.closest(Selectors.actions.give);
|
||||
if (giveAction) {
|
||||
e.preventDefault();
|
||||
|
||||
giveFeedback()
|
||||
.then(() => hideRoot(giveAction))
|
||||
.then(recordAction)
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
|
||||
const remindAction = e.target.closest(Selectors.actions.remind);
|
||||
if (remindAction) {
|
||||
e.preventDefault();
|
||||
|
||||
Promise.resolve(remindAction)
|
||||
.then(hideRoot)
|
||||
.then(recordAction)
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* The action function that is called when users choose to give feedback.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const giveFeedback = () => {
|
||||
return Ajax.call([{
|
||||
methodname: 'core_get_userfeedback_url',
|
||||
args: {
|
||||
contextid: M.cfg.contextid,
|
||||
}
|
||||
}])[0]
|
||||
.then(url => {
|
||||
if (!window.open(url)) {
|
||||
throw new Error('Unable to open popup');
|
||||
}
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the action that the user took.
|
||||
*
|
||||
* @param {HTMLElement} clickedItem The action element that the user chose.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const recordAction = clickedItem => {
|
||||
if (clickedItem.dataset.record) {
|
||||
return Ajax.call([{
|
||||
methodname: 'core_create_userfeedback_action_record',
|
||||
args: {
|
||||
action: clickedItem.dataset.action,
|
||||
contextid: M.cfg.contextid,
|
||||
}
|
||||
}])[0];
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the root node of the CTA notification.
|
||||
*
|
||||
* @param {HTMLElement} clickedItem The action element that the user chose.
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
const hideRoot = clickedItem => {
|
||||
if (clickedItem.dataset.hide) {
|
||||
clickedItem.closest(Selectors.regions.root).remove();
|
||||
}
|
||||
|
||||
return clickedItem;
|
||||
};
|
63
lib/classes/event/userfeedback_give.php
Normal file
63
lib/classes/event/userfeedback_give.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Feedback given.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core\event;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class userfeedback_give
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class userfeedback_give extends base {
|
||||
|
||||
/**
|
||||
* Initialise required event data properties.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'r';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' clicked on the give feedback link";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventuserfeedbackgiven');
|
||||
}
|
||||
|
||||
}
|
63
lib/classes/event/userfeedback_remind.php
Normal file
63
lib/classes/event/userfeedback_remind.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Feedback remind.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core\event;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class userfeedback_remind
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class userfeedback_remind extends base {
|
||||
|
||||
/**
|
||||
* Initialise required event data properties.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'r';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' clicked on the remind later to feedback link";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventuserfeedbackremind');
|
||||
}
|
||||
|
||||
}
|
81
lib/classes/external/userfeedback/generate_url.php
vendored
Normal file
81
lib/classes/external/userfeedback/generate_url.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* External API to generate and return the URL of the feedback site.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\external\userfeedback;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_value;
|
||||
use external_single_structure;
|
||||
use external_multiple_structure;
|
||||
|
||||
/**
|
||||
* The external API to generate and return the feedback url.
|
||||
*
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class generate_url extends external_api {
|
||||
/**
|
||||
* Returns description of parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters([
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id of the page the user is in'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and return the URL of the feedback site
|
||||
*
|
||||
* @param int $contextid The context id
|
||||
* @return \stdClass
|
||||
*/
|
||||
public static function execute(int $contextid) {
|
||||
global $PAGE;
|
||||
|
||||
external_api::validate_parameters(self::execute_parameters(), ['contextid' => $contextid]);
|
||||
|
||||
$context = \context::instance_by_id($contextid);
|
||||
self::validate_context($context);
|
||||
$PAGE->set_context($context);
|
||||
|
||||
return \core_userfeedback::make_link()->out(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_value
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return new external_value(PARAM_URL, 'Feedback site\'s URL');
|
||||
}
|
||||
}
|
95
lib/classes/external/userfeedback/record_action.php
vendored
Normal file
95
lib/classes/external/userfeedback/record_action.php
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* External API to record users action on the feedback notification.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\external\userfeedback;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_value;
|
||||
|
||||
/**
|
||||
* The external API to record users action on the feedback notification.
|
||||
*
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class record_action extends external_api {
|
||||
/**
|
||||
* Returns description of parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters([
|
||||
'action' => new external_value(PARAM_ALPHA, 'The action taken by user'),
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id of the page the user is in'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record users action to the feedback CTA
|
||||
*
|
||||
* @param string $action The action the user took
|
||||
* @param int $contextid The context id
|
||||
* @throws \invalid_parameter_exception
|
||||
*/
|
||||
public static function execute(string $action, int $contextid) {
|
||||
external_api::validate_parameters(self::execute_parameters(), [
|
||||
'action' => $action,
|
||||
'contextid' => $contextid,
|
||||
]);
|
||||
|
||||
$context = \context::instance_by_id($contextid);
|
||||
self::validate_context($context);
|
||||
|
||||
switch ($action) {
|
||||
case 'give':
|
||||
set_user_preference('core_userfeedback_give', time());
|
||||
$event = \core\event\userfeedback_give::create(['context' => $context]);
|
||||
$event->trigger();
|
||||
break;
|
||||
case 'remind':
|
||||
set_user_preference('core_userfeedback_remind', time());
|
||||
$event = \core\event\userfeedback_remind::create(['context' => $context]);
|
||||
$event->trigger();
|
||||
break;
|
||||
default:
|
||||
throw new \invalid_parameter_exception('Invalid value for action parameter (value: ' . $action . '),' .
|
||||
'allowed values are: give,remind');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -96,6 +96,51 @@ class notification {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $icon The icon to use. Required keys are 'pix' and 'component'.
|
||||
* @param string $message The message to display.
|
||||
* @param array $actions An array of action links
|
||||
* @param string $region Optional region name
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public static function add_call_to_action(array $icon, string $message, array $actions, string $region = ''): void {
|
||||
global $OUTPUT, $PAGE;
|
||||
|
||||
$context = new stdClass();
|
||||
$context->icon = $icon;
|
||||
$context->message = $message;
|
||||
$context->region = $region;
|
||||
|
||||
$context->actions = array_map(function($action) {
|
||||
$data = [];
|
||||
foreach ($action['data'] as $name => $value) {
|
||||
$data[] = ['name' => $name, 'value' => $value];
|
||||
}
|
||||
$action['data'] = $data;
|
||||
|
||||
return $action;
|
||||
}, $actions);
|
||||
|
||||
$notification = $OUTPUT->render_from_template('core/local/notification/cta', $context);
|
||||
|
||||
if ($PAGE && $PAGE->state === \moodle_page::STATE_IN_BODY) {
|
||||
$id = uniqid();
|
||||
echo \html_writer::span($notification, '', ['id' => $id]);
|
||||
echo \html_writer::script(
|
||||
"(function() {" .
|
||||
"var notificationHolder = document.getElementById('user-notifications');" .
|
||||
"if (!notificationHolder) { return; }" .
|
||||
"var thisNotification = document.getElementById('{$id}');" .
|
||||
"if (!thisNotification) { return; }" .
|
||||
"notificationHolder.insertBefore(thisNotification.firstChild, notificationHolder.firstChild);" .
|
||||
"thisNotification.remove();" .
|
||||
"})();"
|
||||
);
|
||||
} else {
|
||||
throw new \coding_exception('You are calling add_call_to_action() either too early or too late.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all of the notifications in the stack and clear the stack.
|
||||
*
|
||||
|
@ -197,6 +197,7 @@ class icon_system_fontawesome extends icon_system_font {
|
||||
'core:i/backup' => 'fa-file-zip-o',
|
||||
'core:i/badge' => 'fa-shield',
|
||||
'core:i/breadcrumbdivider' => 'fa-angle-right',
|
||||
'core:i/bullhorn' => 'fa-bullhorn',
|
||||
'core:i/calc' => 'fa-calculator',
|
||||
'core:i/calendar' => 'fa-calendar',
|
||||
'core:i/calendareventdescription' => 'fa-align-left',
|
||||
|
169
lib/classes/userfeedback.php
Normal file
169
lib/classes/userfeedback.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains the core_userfeedback class
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* This Class contains helper functions for user feedback functionality.
|
||||
*
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_userfeedback {
|
||||
/**
|
||||
* @var int Ask user to give feedback a few days after each major upgrade.
|
||||
*/
|
||||
public const REMIND_AFTER_UPGRADE = 1;
|
||||
|
||||
/**
|
||||
* @var int Ask user to give feedback periodically.
|
||||
*/
|
||||
public const REMIND_PERIODICALLY = 2;
|
||||
|
||||
/**
|
||||
* @var int Do not ask user to give feedback.
|
||||
*/
|
||||
public const REMIND_NEVER = 3;
|
||||
|
||||
/**
|
||||
* Displays the feedback reminder block.
|
||||
*/
|
||||
public static function print_reminder_block(): void {
|
||||
global $PAGE;
|
||||
|
||||
static $jscalled = false;
|
||||
|
||||
$actions = [
|
||||
[
|
||||
'title' => get_string('calltofeedback_give'),
|
||||
'url' => '#',
|
||||
'data' => [
|
||||
'action' => 'give',
|
||||
'record' => 1,
|
||||
'hide' => 1,
|
||||
],
|
||||
],
|
||||
[
|
||||
'title' => get_string('calltofeedback_remind'),
|
||||
'url' => '#',
|
||||
'data' => [
|
||||
'action' => 'remind',
|
||||
'record' => 1,
|
||||
'hide' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
$icon = [
|
||||
'pix' => 'i/bullhorn',
|
||||
'component' => 'core'
|
||||
];
|
||||
|
||||
\core\notification::add_call_to_action($icon, get_string('calltofeedback'), $actions, 'core/userfeedback');
|
||||
|
||||
if (!$jscalled) {
|
||||
$jscalled = true;
|
||||
// Calling the following more than once will register event listeners twice.
|
||||
$PAGE->requires->js_call_amd('core/userfeedback', 'registerEventListeners');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the feedback reminder block should be shown or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function should_display_reminder(): bool {
|
||||
global $CFG;
|
||||
|
||||
if ($CFG->enableuserfeedback && isloggedin() && !isguestuser()) {
|
||||
$give = get_user_preferences('core_userfeedback_give');
|
||||
$remind = get_user_preferences('core_userfeedback_remind');
|
||||
|
||||
$lastactiontime = max($give ?: 0, $remind ?: 0);
|
||||
|
||||
switch ($CFG->userfeedback_nextreminder) {
|
||||
case self::REMIND_AFTER_UPGRADE:
|
||||
$lastupgrade = self::last_major_upgrade_time();
|
||||
if ($lastupgrade >= $lastactiontime) {
|
||||
return $lastupgrade + ($CFG->userfeedback_remindafter * DAYSECS) < time();
|
||||
}
|
||||
break;
|
||||
case self::REMIND_PERIODICALLY:
|
||||
return $lastactiontime + ($CFG->userfeedback_remindafter * DAYSECS) < time();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and return the URL of the feedback site
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public static function make_link(): moodle_url {
|
||||
global $CFG, $PAGE;
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
$baseurl = $CFG->userfeedback_url ?? 'https://feedback.moodle.org/lms';
|
||||
$lang = clean_param(current_language(), PARAM_LANG); // Avoid breaking WS because of incorrect package langs.
|
||||
$moodleurl = $CFG->wwwroot;
|
||||
$moodleversion = $CFG->release;
|
||||
$theme = $PAGE->theme->name;
|
||||
$themeversion = get_component_version('theme_' . $theme);
|
||||
|
||||
$url = new moodle_url($baseurl, [
|
||||
'lang' => $lang,
|
||||
'moodle_url' => $moodleurl,
|
||||
'moodle_version' => $moodleversion,
|
||||
'theme' => $theme,
|
||||
'theme_version' => $themeversion,
|
||||
'newtest' => 'Y', // Respondents might be using the same device/browser to fill out the survey.
|
||||
// The newtest param resets the session.
|
||||
]);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last major upgrade time
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function last_major_upgrade_time(): int {
|
||||
global $DB;
|
||||
|
||||
$targetversioncast = $DB->sql_cast_char2real('targetversion');
|
||||
$versioncast = $DB->sql_cast_char2real('version');
|
||||
|
||||
// A time difference more than 3 months has to be a core upgrade.
|
||||
$time = $DB->get_field_sql("SELECT timemodified
|
||||
FROM {upgrade_log}
|
||||
WHERE plugin = 'core' AND $targetversioncast - $versioncast > 30000
|
||||
ORDER BY timemodified DESC
|
||||
LIMIT 1");
|
||||
|
||||
return (int)$time;
|
||||
}
|
||||
}
|
@ -2800,6 +2800,25 @@ $functions = array(
|
||||
'ajax' => 'true',
|
||||
'capabilities' => 'moodle/contentbank:manageowncontent',
|
||||
],
|
||||
'core_create_userfeedback_action_record' => [
|
||||
'classname' => 'core\external\userfeedback\record_action',
|
||||
'methodname' => 'execute',
|
||||
'classpath' => '',
|
||||
'description' => 'Record the action that the user takes in the user feedback notification for future use.',
|
||||
'type' => 'write',
|
||||
'ajax' => 'true',
|
||||
'capabilities' => '',
|
||||
],
|
||||
'core_get_userfeedback_url' => [
|
||||
'classname' => 'core\external\userfeedback\generate_url',
|
||||
'methodname' => 'execute',
|
||||
'classpath' => '',
|
||||
'description' => 'Generate a dynamic URL for the external user feedback site.' .
|
||||
' The URL includes some parameters to pre-fill the user feedback form.',
|
||||
'type' => 'read',
|
||||
'ajax' => 'true',
|
||||
'capabilities' => '',
|
||||
],
|
||||
);
|
||||
|
||||
$services = array(
|
||||
|
@ -835,6 +835,12 @@ class core_renderer extends renderer_base {
|
||||
}
|
||||
}
|
||||
|
||||
if (isloggedin() && !isguestuser()) {
|
||||
$output .= html_writer::div(
|
||||
$this->render_from_template('core/userfeedback_footer_link', ['url' => core_userfeedback::make_link()->out(false)])
|
||||
);
|
||||
}
|
||||
|
||||
// This function is normally called from a layout.php file in {@link core_renderer::header()}
|
||||
// but some of the content won't be known until later, so we return a placeholder
|
||||
// for now. This will be replaced with the real content in {@link core_renderer::footer()}.
|
||||
|
68
lib/templates/local/notification/cta.mustache
Normal file
68
lib/templates/local/notification/cta.mustache
Normal 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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core/local/notification/cta
|
||||
|
||||
Moodle cta notification template.
|
||||
|
||||
The purpose of this template is to render a call to action notification.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* message A cleaned string (use clean_text()) to display.
|
||||
* extraclasses Additional classes to apply to the notification.
|
||||
* actions List of action links.
|
||||
* icon An icon.pix and icon.componrnt for the icon to be displauyed as the icon of CTA notification.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"message": "What do you think about Moodle?",
|
||||
"actions": [
|
||||
{
|
||||
"title": "Give feedback",
|
||||
"url": "#",
|
||||
"data": [
|
||||
{"name": "action", "value": "give"},
|
||||
{"name": "contextid", "value": "3"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
<div class="cta alert alert-primary alert-block fade in {{ extraclasses }}" {{# region }}data-region="{{ region }}"{{/ region}}>
|
||||
<div class="media">
|
||||
<div class="mr-2 icon-size-5">
|
||||
{{# pix }} {{ icon.pix }}, {{ icon.component }} {{/ pix }}
|
||||
</div>
|
||||
<div class="media-body align-self-center">
|
||||
{{{ message }}}<br>
|
||||
{{# actions }}
|
||||
<a href="{{ url }}" class="link-underline aalink"
|
||||
{{# data }}
|
||||
data-{{ name }}="{{ value }}"
|
||||
{{/ data }}
|
||||
>{{ title }}</a>
|
||||
{{/ actions }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
27
lib/templates/userfeedback_footer_link.mustache
Normal file
27
lib/templates/userfeedback_footer_link.mustache
Normal file
@ -0,0 +1,27 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core/userfeedback_footer_link
|
||||
|
||||
Adds the give feedback link.
|
||||
|
||||
Example context (json):
|
||||
{}
|
||||
}}
|
||||
|
||||
{{# pix }} i/bullhorn, core {{/ pix }}
|
||||
<a href="{{url}}" target="_blank">{{#str}}calltofeedback_give{{/str}}</a>
|
118
lib/tests/external/userfeedback/generate_url_test.php
vendored
Normal file
118
lib/tests/external/userfeedback/generate_url_test.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* External functions test for generate_url.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\external\userfeedback;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use externallib_advanced_testcase;
|
||||
use context_system;
|
||||
use context_course;
|
||||
use external_api;
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
/**
|
||||
* Class generate_url_testcase
|
||||
*
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass generate_url
|
||||
*/
|
||||
class generate_url_testcase extends externallib_advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of generate_url().
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_record_action_system() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$context = context_system::instance();
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
// Call the WS and check the requested data is returned as expected.
|
||||
$result = generate_url::execute($context->id);
|
||||
$result = external_api::clean_returnvalue(generate_url::execute_returns(), $result);
|
||||
|
||||
$this->assertStringStartsWith('https://feedback.moodle.org/lms', $result);
|
||||
$this->assertStringContainsString('?lang=en', $result);
|
||||
$this->assertStringContainsString('&moodle_url=https%3A%2F%2Fwww.example.com%2Fmoodle', $result);
|
||||
$this->assertStringContainsString('&theme=boost', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of generate_url() in a course with a course theme.
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_record_action_course_theme() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Enable course themes.
|
||||
set_config('allowcoursethemes', 1);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['theme' => 'classic']);
|
||||
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
// Call the WS and check the requested data is returned as expected.
|
||||
$result = generate_url::execute($context->id);
|
||||
$result = external_api::clean_returnvalue(generate_url::execute_returns(), $result);
|
||||
|
||||
$this->assertStringContainsString('&theme=classic', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of generate_url() when a custom feedback url is set.
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_record_action_custom_feedback_url() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Enable course themes.
|
||||
set_config('userfeedback_url', 'https://feedback.moodle.org/abc');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$context = context_system::instance();
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
// Call the WS and check the requested data is returned as expected.
|
||||
$result = generate_url::execute($context->id);
|
||||
$result = external_api::clean_returnvalue(generate_url::execute_returns(), $result);
|
||||
|
||||
$this->assertStringStartsWith('https://feedback.moodle.org/abc', $result);
|
||||
}
|
||||
}
|
88
lib/tests/external/userfeedback/record_action_test.php
vendored
Normal file
88
lib/tests/external/userfeedback/record_action_test.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* External functions test for record_action.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\external\userfeedback;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use externallib_advanced_testcase;
|
||||
use context_system;
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
/**
|
||||
* Class record_action_testcase
|
||||
*
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass record_action
|
||||
*/
|
||||
class record_action_testcase extends externallib_advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for test_record_action.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function record_action_provider() {
|
||||
return [
|
||||
'give action' => ['give'],
|
||||
'remind action' => ['remind'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of record_action().
|
||||
*
|
||||
* @dataProvider record_action_provider
|
||||
* @param string $action The action taken by the user
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_record_action(string $action) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$context = context_system::instance();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$eventsink = $this->redirectEvents();
|
||||
|
||||
$now = time();
|
||||
|
||||
// Call the WS and check the action is recorded as expected.
|
||||
$result = record_action::execute($action, $context->id);
|
||||
$this->assertNull($result);
|
||||
|
||||
$preference = get_user_preferences('core_userfeedback_' . $action);
|
||||
$this->assertGreaterThanOrEqual($now, $preference);
|
||||
|
||||
$events = $eventsink->get_events();
|
||||
$this->assertCount(1, $events);
|
||||
$this->assertInstanceOf('\core\event\userfeedback_' . $action, $events[0]);
|
||||
$eventsink->clear();
|
||||
}
|
||||
}
|
@ -163,6 +163,10 @@ if (empty($CFG->forcedefaultmymoodle) && $PAGE->user_allowed_editing()) {
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if (core_userfeedback::should_display_reminder()) {
|
||||
core_userfeedback::print_reminder_block();
|
||||
}
|
||||
|
||||
echo $OUTPUT->custom_block_region('content');
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
2
pix/i/bullhorn.svg
Normal file
2
pix/i/bullhorn.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1664 640q53 0 90.5 37.5t37.5 90.5-37.5 90.5-90.5 37.5v384q0 52-38 90t-90 38q-417-347-812-380-58 19-91 66t-31 100.5 40 92.5q-20 33-23 65.5t6 58 33.5 55 48 50 61.5 50.5q-29 58-111.5 83t-168.5 11.5-132-55.5q-7-23-29.5-87.5t-32-94.5-23-89-15-101 3.5-98.5 22-110.5h-122q-66 0-113-47t-47-113v-192q0-66 47-113t113-47h480q435 0 896-384 52 0 90 38t38 90v384zm-128 604v-954q-394 302-768 343v270q377 42 768 341z" fill="#999"/></svg>
|
After Width: | Height: | Size: 561 B |
@ -2647,4 +2647,22 @@ $picker-emojis-per-row: 7 !default;
|
||||
position: relative;
|
||||
z-index: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link-underline {
|
||||
text-decoration: underline;
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.alert.cta {
|
||||
.icon {
|
||||
padding: 0.3rem;
|
||||
&.fa {
|
||||
border-radius: 50%;
|
||||
border-style: solid;
|
||||
border-width: 0.125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11814,6 +11814,18 @@ body.h5p-embed .h5pmessages {
|
||||
position: relative;
|
||||
z-index: inherit; } }
|
||||
|
||||
.link-underline {
|
||||
text-decoration: underline; }
|
||||
.link-underline:focus {
|
||||
text-decoration: none; }
|
||||
|
||||
.alert.cta .icon {
|
||||
padding: 0.3rem; }
|
||||
.alert.cta .icon.fa {
|
||||
border-radius: 50%;
|
||||
border-style: solid;
|
||||
border-width: 0.125rem; }
|
||||
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
|
@ -12028,6 +12028,18 @@ body.h5p-embed .h5pmessages {
|
||||
position: relative;
|
||||
z-index: inherit; } }
|
||||
|
||||
.link-underline {
|
||||
text-decoration: underline; }
|
||||
.link-underline:focus {
|
||||
text-decoration: none; }
|
||||
|
||||
.alert.cta .icon {
|
||||
padding: 0.3rem; }
|
||||
.alert.cta .icon.fa {
|
||||
border-radius: 50%;
|
||||
border-style: solid;
|
||||
border-width: 0.125rem; }
|
||||
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2020060200.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2020060400.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '3.9dev+ (Build: 20200602)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user