Merge branch 'MDL-71610-master' of git://github.com/bmbrands/moodle

This commit is contained in:
Ilya Tregubov 2021-09-29 15:40:39 +02:00
commit 34a0ba3232
110 changed files with 769 additions and 265 deletions

View file

@ -8836,7 +8836,7 @@ function admin_externalpage_setup($section, $extrabutton = '', array $extraurlpa
$visiblepathtosection = array_reverse($extpage->visiblepath);
if ($PAGE->user_allowed_editing()) {
if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
if ($PAGE->user_is_editing()) {
$caption = get_string('blockseditoff');
$url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));

2
lib/amd/build/edit_switch.min.js vendored Normal file
View file

@ -0,0 +1,2 @@
define ("core/edit_switch",["exports","core/ajax","core/event_dispatcher","core/notification"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=a.eventTypes=void 0;var e=function(a,c){return(0,b.call)([{methodname:"core_change_editmode",args:{context:a,setmode:c}}])[0]},f=function(a){if(a.checked){a.setAttribute("aria-checked",!0)}else{a.setAttribute("aria-checked",!1)}var b=h(a,a.checked);if(!b.defaultPrevented){window.location=a.dataset.pageurl}},g={editModeSet:"core/edit_switch/editModeSet"};a.eventTypes=g;var h=function(a,b){return(0,c.dispatchEvent)(g.editModeSet,{editMode:b},a,{cancelable:!0})};a.init=function init(a){var b=document.getElementById(a);b.addEventListener("change",function(){e(b.dataset.context,b.checked).then(function(a){if(a.success){f(b)}else{b.checked=!1}}).catch(d.exception)})}});
//# sourceMappingURL=edit_switch.min.js.map

File diff suppressed because one or more lines are too long

121
lib/amd/src/edit_switch.js Normal file
View file

@ -0,0 +1,121 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Controls the edit switch.
*
* @module core/edit_switch
* @copyright 2021 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {call as fetchMany} from 'core/ajax';
import {dispatchEvent} from 'core/event_dispatcher';
import {exception as displayException} from 'core/notification';
/**
* Change the Edit mode.
*
* @param {number} context The contextid that editing is being set for
* @param {bool} setmode Whether editing is set or not
* @return {Promise} Resolved with an array file the stored file url.
*/
const setEditMode = (context, setmode) => fetchMany([{
methodname: 'core_change_editmode',
args: {
context,
setmode,
},
}])[0];
/**
* Toggle the edit switch
*
* @method
* @protected
* @param {HTMLElement} editSwitch
*/
const toggleEditSwitch = editSwitch => {
if (editSwitch.checked) {
editSwitch.setAttribute('aria-checked', true);
} else {
editSwitch.setAttribute('aria-checked', false);
}
const event = notifyEditModeSet(editSwitch, editSwitch.checked);
if (!event.defaultPrevented) {
window.location = editSwitch.dataset.pageurl;
}
};
/**
* Names of events for core/edit_switch.
*
* @static
* @property {String} editModeSet See {@link event:core/edit_switch/editModeSet}
*/
export const eventTypes = {
/**
* An event triggered when the edit mode toggled.
*
* @event core/edit_switch/editModeSet
* @type {CustomEvent}
* @property {HTMLElement} target The switch used to toggle the edit mode
* @property {object} detail
* @property {bool} detail.editMode
*/
editModeSet: 'core/edit_switch/editModeSet',
};
/**
* Dispatch the editModeSet event after changing the edit mode.
*
* This event is cancelable.
*
* The default action is to reload the page after toggling the edit mode.
*
* @method
* @protected
* @param {HTMLElement} container
* @param {bool} editMode
* @returns {CustomEvent}
*/
const notifyEditModeSet = (container, editMode) => dispatchEvent(
eventTypes.editModeSet,
{editMode},
container,
{cancelable: true}
);
/**
* Add the eventlistener for the editswitch.
*
* @param {string} editingSwitchId The id of the editing switch to listen for
*/
export const init = editingSwitchId => {
const editSwitch = document.getElementById(editingSwitchId);
editSwitch.addEventListener('change', () => {
setEditMode(editSwitch.dataset.context, editSwitch.checked)
.then(result => {
if (result.success) {
toggleEditSwitch(editSwitch);
} else {
editSwitch.checked = false;
}
return;
})
.catch(displayException);
});
};

View file

@ -134,7 +134,7 @@ trait behat_session_trait {
}
// How much we will be waiting for the element to appear.
if (!$timeout) {
if ($timeout === false) {
$timeout = self::get_timeout();
$microsleep = false;
} else {
@ -339,7 +339,7 @@ trait behat_session_trait {
protected function spin($lambda, $args = false, $timeout = false, $exception = false, $microsleep = false) {
// Using default timeout which is pretty high.
if (!$timeout) {
if ($timeout === false) {
$timeout = self::get_timeout();
}

103
lib/classes/external/editmode.php vendored Normal file
View file

@ -0,0 +1,103 @@
<?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/>.
/**
* A web service to load the mapping of moodle pix names to fontawesome icon names.
*
* @package core
* @category external
* @copyright 2021 Bas Brands <bas@sonsbeekmedia.nl>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\external;
use external_api;
use external_function_parameters;
use external_single_structure;
use external_value;
/**
* Web service to change the edit mode.
*
* @package core
* @copyright 2021 Bas Brands <bas@sonsbeekmedia.nl>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class editmode extends external_api {
/**
* Description of the parameters suitable for the `change_editmode` function.
*
* @return external_function_parameters
*/
public static function change_editmode_parameters(): external_function_parameters {
return new external_function_parameters(
[
'setmode' => new external_value(PARAM_BOOL, 'Set edit mode to'),
'context' => new external_value(PARAM_INT, 'Page context id')
]
);
}
/**
* Save the image and return any warnings and the new image url
*
* @param bool $setmode the current edit mode
* @param int $contextid the current page context id
* @return array the new edit mode.
*/
public static function change_editmode(bool $setmode, int $contextid): array {
global $USER, $PAGE;
$params = self::validate_parameters(
self::change_editmode_parameters(),
[
'setmode' => $setmode,
'context' => $contextid
]
);
$context = \context_helper::instance_by_id($params['context']);
self::validate_context($context);
$PAGE->set_context($context);
if ($context->id === \context_user::instance($USER->id)->id) {
$PAGE->set_blocks_editing_capability('moodle/my:manageblocks');
}
$success = false;
if ($PAGE->user_allowed_editing()) {
$USER->editing = $setmode;
$success = true;
}
return ['success' => $success];
}
/**
* Description of the return value for the `change_editmode` function.
*
* @return external_single_structure
*/
public static function change_editmode_returns(): external_single_structure {
$keys = [
'success' => new external_value(PARAM_BOOL, 'The edit mode was changed', VALUE_REQUIRED),
];
return new external_single_structure($keys, 'editmode');
}
}

View file

@ -2785,7 +2785,13 @@ $functions = array(
'type' => 'read',
'ajax' => true,
],
'core_change_editmode' => [
'classname' => 'core\external\editmode',
'methodname' => 'change_editmode',
'description' => 'Change the editing mode',
'type' => 'write',
'ajax' => true,
],
);
$services = array(

View file

@ -2484,7 +2484,7 @@ class grade_item extends grade_object {
global $USER;
// Determine which display type to use for this average
if (isset($USER->gradeediting) && array_key_exists($this->courseid, $USER->gradeediting) && $USER->gradeediting[$this->courseid]) {
if (isset($USER->editing) && $USER->editing) {
$displaytype = GRADE_DISPLAY_TYPE_REAL;
} else if ($rangesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // no ==0 here, please resave report and user prefs

View file

@ -5434,7 +5434,7 @@ class settings_navigation extends navigation_node {
}
$frontpage->id = 'frontpagesettings';
if ($this->page->user_allowed_editing()) {
if ($this->page->user_allowed_editing() && !$this->page->theme->haseditswitch) {
// Add the turn on/off settings
$url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));

View file

@ -754,7 +754,7 @@ class theme_config {
'rendererfactory', 'csspostprocess', 'editor_sheets', 'editor_scss', 'rarrow', 'larrow', 'uarrow', 'darrow',
'hidefromselector', 'doctype', 'yuicssmodules', 'blockrtlmanipulations', 'blockrendermethod',
'scss', 'extrascsscallback', 'prescsscallback', 'csstreepostprocessor', 'addblockposition',
'iconsystem', 'precompiledcsscallback');
'iconsystem', 'precompiledcsscallback', 'haseditswitch');
foreach ($config as $key=>$value) {
if (in_array($key, $configurable)) {

View file

@ -2748,6 +2748,9 @@ EOD;
*/
public function edit_button(moodle_url $url) {
if ($this->page->theme->haseditswitch == true) {
return;
}
$url->param('sesskey', sesskey());
if ($this->page->user_is_editing()) {
$url->param('edit', 'off');
@ -2760,6 +2763,27 @@ EOD;
return $this->single_button($url, $editstring);
}
/**
* Create a navbar switch for toggling editing mode.
*
* @return string Html containing the edit switch
*/
public function edit_switch() {
if ($this->page->user_allowed_editing()) {
$temp = (object) [
'legacyseturl' => (new moodle_url('/editmode.php'))->out(false),
'pagecontextid' => $this->page->context->id,
'pageurl' => $this->page->url,
'sesskey' => sesskey(),
];
if ($this->page->user_is_editing()) {
$temp->checked = true;
}
return $this->render_from_template('core/editswitch', $temp);
}
}
/**
* Returns HTML to display a simple button to close a window
*

View file

@ -0,0 +1,63 @@
{{!
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 theme_boost/editswitch
This template renders the top navbar.
Example context (json):
{
"url": "http://localhost/",
"sesskey": "sesskey",
"edit": 1,
"adminedit": true,
"checked": "",
"string": "Edit on"
}
}}
<form action="{{{legacyseturl}}}" method="post" class="d-flex align-items-center editmode-switch-form">
<div class="custom-control custom-control-right custom-switch text-nowrap">
<input type="checkbox"{{!
}} name="setmode"{{!
}}{{#checked}}{{!
}} aria-checked="true" checked{{!
}}{{/checked}}{{!
}}{{^checked}}{{!
}} aria-checked="false"{{!
}}{{/checked}}{{!
}} class="custom-control-input"{{!
}} id="{{uniqid}}-editingswitch"{{!
}} data-context="{{{pagecontextid}}}"{{!
}} data-pageurl="{{{pageurl}}}"{{!
}}>
<label class="custom-control-label" for="{{uniqid}}-editingswitch">
{{#str}} editmode {{/str}}
</label>
<input type="hidden" name="sesskey" value="{{{sesskey}}}">
<input type="hidden" name="pageurl" value="{{{pageurl}}}">
<input type="hidden" name="context" value="{{{pagecontextid}}}">
<noscript>
<input type="submit" value="{{#str}}setmode, core{{/str}}">
</noscript>
</div>
</form>
{{#js}}
require(['core/edit_switch'], function(editSwitch) {
editSwitch.init('{{uniqid}}-editingswitch');
});
{{/js}}

View file

@ -833,33 +833,38 @@ class behat_navigation extends behat_base {
/**
* Open the course homepage with editing mode enabled.
*
* @Given /^I am on "(?P<coursefullname_string>(?:[^"]|\\")*)" course homepage with editing mode on$/
* @throws coding_exception
* @param string $coursefullname The course full name of the course.
* @return void
*/
public function i_am_on_course_homepage_with_editing_mode_on($coursefullname) {
$this->i_am_on_course_homepage_with_editing_mode_set_to($coursefullname, 'on');
}
/**
* Open the course homepage with editing mode set to either on, or off.
*
* @Given I am on :coursefullname course homepage with editing mode :onoroff
* @throws coding_exception
* @param string $coursefullname The course full name of the course.
* @param string $onoroff Whehter to switch editing on, or off.
*/
public function i_am_on_course_homepage_with_editing_mode_set_to(string $coursefullname, string $onoroff): void {
global $DB;
$course = $DB->get_record("course", array("fullname" => $coursefullname), 'id', MUST_EXIST);
$url = new moodle_url('/course/view.php', ['id' => $course->id]);
if ($this->running_javascript() && $sesskey = $this->get_sesskey()) {
// Javascript is running so it is possible to grab the session ket and jump straight to editing mode.
$url->param('edit', 1);
$url->param('sesskey', $sesskey);
$this->execute('behat_general::i_visit', [$url]);
return;
}
// Visit the course page.
$this->execute('behat_general::i_visit', [$url]);
try {
$this->execute("behat_forms::press_button", get_string('turneditingon'));
} catch (Exception $e) {
$this->execute("behat_navigation::i_navigate_to_in_current_page_administration", [get_string('turneditingon')]);
switch ($onoroff) {
case 'on':
$this->execute('behat_navigation::i_turn_editing_mode_on');
break;
case 'off':
$this->execute('behat_navigation::i_turn_editing_mode_off');
break;
default:
throw new \coding_exception("Unknown editing mode '{$onoroff}'. Accepted values are 'on' and 'off'");
}
}
@ -1103,7 +1108,6 @@ class behat_navigation extends behat_base {
$this->execute('behat_general::i_visit', [$url]);
}
/**
* First checks to see if we are on this page via the breadcrumb. If not we then attempt to follow the link name given.
*
@ -1193,4 +1197,54 @@ class behat_navigation extends behat_base {
"//div[contains(concat(' ', @class, ' '), ' dropdown-menu ')]" .
"//div[contains(concat(' ', @class, ' '), ' submenu ')][@aria-label='" . $submenuname . "']";
}
/**
* Returns whether the user can edit the current page.
*
* @return bool
*/
protected function is_editing_on() {
$body = $this->find('xpath', "//body", false, false, 0);
return $body->hasClass('editing');
}
/**
* Turns editing mode on.
* @Given I switch editing mode on
* @Given I turn editing mode on
*/
public function i_turn_editing_mode_on() {
$this->execute('behat_forms::i_set_the_field_to', [get_string('editmode'), 1]);
if (!$this->running_javascript()) {
$this->execute('behat_general::i_click_on', [
get_string('setmode', 'core'),
'button',
]);
}
if (!$this->is_editing_on()) {
throw new ExpectationException('The edit mode could not be turned on', $this->getSession());
}
}
/**
* Turns editing mode off.
* @Given I switch editing mode off
* @Given I turn editing mode off
*/
public function i_turn_editing_mode_off() {
$this->execute('behat_forms::i_set_the_field_to', [get_string('editmode'), 0]);
if (!$this->running_javascript()) {
$this->execute('behat_general::i_click_on', [
get_string('setmode', 'core'),
'button',
]);
}
if ($this->is_editing_on()) {
throw new ExpectationException('The edit mode could not be turned off', $this->getSession());
}
}
}

View file

@ -44,15 +44,15 @@ Feature: Context freezing apply to child contexts
And I click on "Continue" "button"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa1b"
Then "Add a new discussion topic" "link" should exist
When I am on "courseaa2" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should exist
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
Then "Add a new discussion topic" "link" should exist
@ -61,15 +61,15 @@ Feature: Context freezing apply to child contexts
And I follow "faa1"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa1b"
Then "Add a new discussion topic" "link" should exist
When I am on "courseaa2" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should exist
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
And "Add a new discussion topic" "link" should exist
@ -89,23 +89,23 @@ Feature: Context freezing apply to child contexts
Scenario: Freeze course should freeze all children
Given I am on the "courseaa1" "Course" page logged in as "admin"
And I should see "Turn editing on"
And "Set mode" "button" should exist
When I follow "Freeze this context"
And I click on "Continue" "button"
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
And "Unfreeze this context" "link" should exist in current page administration
When I follow "faa1b"
Then "Add a new discussion topic" "link" should not exist
And "Unfreeze this context" "link" should not exist in current page administration
When I am on "courseaa2" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should exist
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
Then "Add a new discussion topic" "link" should exist
@ -114,15 +114,15 @@ Feature: Context freezing apply to child contexts
And I follow "faa1"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
When I follow "faa1b"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa2" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should exist
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
Then "Add a new discussion topic" "link" should exist
@ -146,39 +146,39 @@ Feature: Context freezing apply to child contexts
And I click on "managecontextlock" action for "cata" in management category listing
And I click on "Continue" "button"
And I am on "courseaa1" course homepage
And I should not see "Turn editing on"
And "Set mode" "button" should not exist
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
And "Unfreeze this context" "link" should not exist in current page administration
When I follow "faa1b"
Then "Add a new discussion topic" "link" should not exist
And "Unfreeze this context" "link" should not exist in current page administration
When I am on "courseaa2" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should not exist
And "Unfreeze this context" "link" should not exist in current page administration
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
Then "Add a new discussion topic" "link" should exist
And I log out
When I am on the "courseaa1" "Course" page logged in as "teacher"
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
And I follow "faa1"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa1" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
When I follow "faa1b"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseaa2" course homepage
Then I should not see "Turn editing on"
Then "Set mode" "button" should not exist
When I follow "faa2"
Then "Add a new discussion topic" "link" should not exist
When I am on "courseb" course homepage
Then I should see "Turn editing on"
Then "Set mode" "button" should exist
When I follow "fb"
Then "Add a new discussion topic" "link" should exist

View file

@ -0,0 +1,79 @@
@core @turn_edit_mode_on @javascript
Feature: Turn editing mode on
Users should be able to turn editing mode on and off
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I turn editing mode off
And I log out
Scenario: Edit mode on page Gradebook
Given the following "activities" exist:
| activity | course | idnumber | name | intro |
| assign | C1 | assign1 | Test Assignment 1 | Test Assignment 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
And "Edit assign Test Assignment 1" "link" should exist
And I turn editing mode off
Then "Edit assign Test Assignment 1" "link" should not exist
Scenario: Edit mode on page Course
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I turn editing mode on
And I should see "Add an activity or resource"
And I turn editing mode off
Then I should not see "Add an activity or resource"
Scenario: Edit mode on page Homepage
Given I log in as "admin"
And I am on site homepage
And I turn editing mode on
And I should see "Add an activity or resource"
And I turn editing mode off
Then I should not see "Add an activity or resource"
Scenario: Edit mode on page Default profile
Given I log in as "admin"
And I navigate to "Appearance > Default profile page" in site administration
And I turn editing mode on
And I should see "Add a block"
And I turn editing mode off
Then I should not see "Add a block"
Scenario: Edit mode on page Profile
Given I log in as "admin"
And I follow "View profile"
And I turn editing mode on
And I should see "Add a block"
And I turn editing mode off
Then I should not see "Add a block"
Scenario: Edit mode on page Default dashboard
Given I log in as "admin"
And I navigate to "Appearance > Default Dashboard page" in site administration
And I turn editing mode on
And I should see "Add a block"
And I turn editing mode off
Then I should not see "Add a block"
Scenario: Edit mode on page Dashboard
And I log in as "teacher1"
And I turn editing mode on
And I should see "Add a block"
Then I turn editing mode off
Then I should not see "Add a block"