Merge branch 'wip-MDL-56586-master-2' of git://github.com/marinaglancy/moodle

This commit is contained in:
Dan Poltawski 2016-11-24 09:30:29 +00:00
commit 304e8a8d0c
24 changed files with 481 additions and 91 deletions

View File

@ -29,7 +29,7 @@ define("MAX_USERS_TO_LIST_PER_ROLE", 10);
$contextid = required_param('contextid', PARAM_INT);
$roleid = optional_param('roleid', 0, PARAM_INT);
$returnto = optional_param('return', null, PARAM_ALPHANUMEXT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
list($context, $course, $cm) = get_context_info_array($contextid);
@ -53,7 +53,13 @@ if ($course) {
// Security.
require_login($course, false, $cm);
require_capability('moodle/role:assign', $context);
$PAGE->set_url($url);
navigation_node::override_active_url($url);
$pageurl = new moodle_url($url);
if ($returnurl) {
$pageurl->param('returnurl', $returnurl);
}
$PAGE->set_url($pageurl);
$PAGE->set_context($context);
$contextname = $context->get_context_name();
@ -141,6 +147,10 @@ if (!empty($user) && ($user->id != $USER->id)) {
}
$PAGE->set_pagelayout('admin');
if ($context->contextlevel == CONTEXT_BLOCK) {
// Do not show blocks when changing block's settings, it is confusing.
$PAGE->blocks->show_only_fake_blocks(true);
}
$PAGE->set_title($title);
switch ($context->contextlevel) {
@ -186,9 +196,6 @@ if ($roleid) {
// Print the form.
$assignurl = new moodle_url($PAGE->url, array('roleid'=>$roleid));
if ($returnto !== null) {
$assignurl->param('return', $returnto);
}
?>
<form id="assignform" method="post" action="<?php echo $assignurl ?>"><div>
<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
@ -235,18 +242,10 @@ if ($roleid) {
// Print a form to swap roles, and a link back to the all roles list.
echo '<div class="backlink">';
$newroleurl = new moodle_url($PAGE->url);
if ($returnto !== null) {
$newroleurl->param('return', $returnto);
}
$select = new single_select($newroleurl, 'roleid', $nameswithcounts, $roleid, null);
$select = new single_select($PAGE->url, 'roleid', $nameswithcounts, $roleid, null);
$select->label = get_string('assignanotherrole', 'core_role');
echo $OUTPUT->render($select);
$backurl = new moodle_url('/admin/roles/assign.php', array('contextid' => $contextid));
if ($returnto !== null) {
$backurl->param('return', $returnto);
}
echo '<p><a href="' . $backurl->out() . '">' . get_string('backtoallroles', 'core_role') . '</a></p>';
echo '<p><a href="' . $PAGE->url . '">' . get_string('backtoallroles', 'core_role') . '</a></p>';
echo '</div>';
} else if (empty($assignableroles)) {
@ -284,9 +283,6 @@ if ($roleid) {
}
} else if ($assigncounts[$roleid] > MAX_USERS_TO_LIST_PER_ROLE) {
$assignurl = new moodle_url($PAGE->url, array('roleid'=>$roleid));
if ($returnto !== null) {
$assignurl->param('return', $returnto);
}
$roleholdernames[$roleid] = '<a href="'.$assignurl.'">'.$strmorethanmax.'</a>';
} else {
$roleholdernames[$roleid] = '';
@ -307,9 +303,6 @@ if ($roleid) {
foreach ($assignableroles as $roleid => $rolename) {
$description = format_string($DB->get_field('role', 'description', array('id'=>$roleid)));
$assignurl = new moodle_url($PAGE->url, array('roleid'=>$roleid));
if ($returnto !== null) {
$assignurl->param('return', $returnto);
}
$row = array('<a href="'.$assignurl.'">'.$rolename.'</a>',
$description, $assigncounts[$roleid]);
if ($showroleholders) {
@ -322,8 +315,8 @@ if ($roleid) {
if ($context->contextlevel > CONTEXT_USER) {
if ($context->contextlevel === CONTEXT_COURSECAT && $returnto === 'management') {
$url = new moodle_url('/course/management.php', array('categoryid' => $context->instanceid));
if ($returnurl) {
$url = new moodle_url($returnurl);
} else {
$url = $context->get_url();
}

View File

@ -25,6 +25,7 @@
require_once(__DIR__ . '/../../config.php');
$contextid = required_param('contextid', PARAM_INT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
list($context, $course, $cm) = get_context_info_array($contextid);
@ -49,7 +50,13 @@ require_login($course, false, $cm);
if (!has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:manage'), $context)) {
print_error('nopermissions', 'error', '', get_string('checkpermissions', 'core_role'));
}
$PAGE->set_url($url);
navigation_node::override_active_url($url);
$pageurl = new moodle_url($url);
if ($returnurl) {
$pageurl->param('returnurl', $returnurl);
}
$PAGE->set_url($pageurl);
if ($context->contextlevel == CONTEXT_USER and $USER->id != $context->instanceid) {
$PAGE->navbar->includesettingsbase = true;
@ -75,6 +82,10 @@ $userselector->set_rows(20);
$title = get_string('checkpermissionsin', 'core_role', $contextname);
$PAGE->set_pagelayout('admin');
if ($context->contextlevel == CONTEXT_BLOCK) {
// Do not show blocks when changing block's settings, it is confusing.
$PAGE->blocks->show_only_fake_blocks(true);
}
$PAGE->set_title($title);
switch ($context->contextlevel) {
@ -156,16 +167,7 @@ if (!is_null($reportuser)) {
// Show UI for choosing a user to report on.
echo $OUTPUT->box_start('generalbox boxwidthnormal boxaligncenter', 'chooseuser');
echo '<form method="get" action="' . $CFG->wwwroot . '/' . $CFG->admin . '/roles/check.php" >';
// Hidden fields.
echo '<input type="hidden" name="contextid" value="' . $context->id . '" />';
if (!empty($user->id)) {
echo '<input type="hidden" name="userid" value="' . $user->id . '" />';
}
if ($isfrontpage) {
echo '<input type="hidden" name="courseid" value="' . $courseid . '" />';
}
echo '<form method="post" action="' . $PAGE->url . '" >';
// User selector.
echo $OUTPUT->heading('<label for="reportuser">' . $selectheading . '</label>', 3);
@ -180,7 +182,12 @@ echo $OUTPUT->box_end();
// Appropriate back link.
if ($context->contextlevel > CONTEXT_USER) {
echo html_writer::start_tag('div', array('class'=>'backlink'));
echo html_writer::tag('a', get_string('backto', '', $contextname), array('href'=>$context->get_url()));
if ($returnurl) {
$backurl = new moodle_url($returnurl);
} else {
$backurl = $context->get_url();
}
echo html_writer::link($backurl, get_string('backto', '', $contextname));
echo html_writer::end_tag('div');
}

View File

@ -33,7 +33,7 @@ $prevent = optional_param('prevent', 0, PARAM_BOOL);
$allow = optional_param('allow', 0, PARAM_BOOL);
$unprohibit = optional_param('unprohibit', 0, PARAM_BOOL);
$prohibit = optional_param('prohibit', 0, PARAM_BOOL);
$return = optional_param('return', null, PARAM_ALPHANUMEXT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
list($context, $course, $cm) = get_context_info_array($contextid);
@ -56,7 +56,13 @@ if ($course) {
// Security first.
require_login($course, false, $cm);
require_capability('moodle/role:review', $context);
$PAGE->set_url($url);
navigation_node::override_active_url($url);
$pageurl = new moodle_url($url);
if ($returnurl) {
$pageurl->param('returnurl', $returnurl);
}
$PAGE->set_url($pageurl);
if ($context->contextlevel == CONTEXT_USER and $USER->id != $context->instanceid) {
$PAGE->navbar->includesettingsbase = true;
@ -85,6 +91,11 @@ $straction = get_string('permissions', 'core_role'); // Used by tabs.php.
$currenttab = 'permissions';
$PAGE->set_pagelayout('admin');
if ($context->contextlevel == CONTEXT_BLOCK) {
// Do not show blocks when changing block's settings, it is confusing.
$PAGE->blocks->show_only_fake_blocks(true);
}
$PAGE->set_title($title);
switch ($context->contextlevel) {
case CONTEXT_SYSTEM:
@ -219,8 +230,8 @@ echo $OUTPUT->box_end();
if ($context->contextlevel > CONTEXT_USER) {
if ($context->contextlevel === CONTEXT_COURSECAT && $return === 'management') {
$url = new moodle_url('/course/management.php', array('categoryid' => $context->instanceid));
if ($returnurl) {
$url = new moodle_url($returnurl);
} else {
$url = $context->get_url();
}

View File

@ -83,16 +83,19 @@ Scenario: Block should select current activity by default
When I add the "Activity results" block
And I configure the "Activity results" block
Then the field "id_config_activitygradeitemid" matches value "Test assignment 1"
And I press "Cancel"
And I follow "Course 1"
And I follow "Test assignment 2"
And I add the "Activity results" block
And I configure the "Activity results" block
And the field "id_config_activitygradeitemid" matches value "Test assignment 2"
And I press "Cancel"
And I follow "Course 1"
And I follow "Test assignment 3"
And I add the "Activity results" block
And I configure the "Activity results" block
And the field "id_config_activitygradeitemid" matches value "Test assignment 3"
And I press "Cancel"
And I follow "Course 1"
And I follow "Test page name"
And I add the "Activity results" block

View File

@ -168,6 +168,7 @@ class helper {
* @return array
*/
public static function get_category_listitem_actions(\coursecat $category) {
global $PAGE;
$baseurl = new \moodle_url('/course/management.php', array('categoryid' => $category->id, 'sesskey' => \sesskey()));
$actions = array();
// Edit.
@ -249,11 +250,11 @@ class helper {
);
}
// Roles.
// Assign roles.
if ($category->can_review_roles()) {
$actions['assignroles'] = array(
'url' => new \moodle_url('/admin/roles/assign.php', array('contextid' => $category->get_context()->id,
'return' => 'management')),
'returnurl' => $PAGE->url->out_as_local_url(false))),
'icon' => new \pix_icon('t/assignroles', new \lang_string('assignroles', 'role')),
'string' => new \lang_string('assignroles', 'role')
);
@ -263,12 +264,22 @@ class helper {
if ($category->can_review_permissions()) {
$actions['permissions'] = array(
'url' => new \moodle_url('/admin/roles/permissions.php', array('contextid' => $category->get_context()->id,
'return' => 'management')),
'returnurl' => $PAGE->url->out_as_local_url(false))),
'icon' => new \pix_icon('i/permissions', new \lang_string('permissions', 'role')),
'string' => new \lang_string('permissions', 'role')
);
}
// Check permissions.
if ($category->can_review_permissions()) {
$actions['checkroles'] = array(
'url' => new \moodle_url('/admin/roles/check.php', array('contextid' => $category->get_context()->id,
'returnurl' => $PAGE->url->out_as_local_url(false))),
'icon' => new \pix_icon('i/checkpermissions', new \lang_string('checkpermissions', 'role')),
'string' => new \lang_string('checkpermissions', 'role')
);
}
// Cohorts.
if ($category->can_review_cohorts()) {
$actions['cohorts'] = array(

View File

@ -1088,8 +1088,11 @@ class core_course_management_helper_test extends advanced_testcase {
* Tests the fetching of actions for a category.
*/
public function test_get_category_listitem_actions() {
global $PAGE;
$this->resetAfterTest(true);
$PAGE->set_url(new moodle_url('/course/management.php'));
$generator = $this->getDataGenerator();
$category = $generator->create_category();
$context = context_system::instance();

1
lib/amd/build/addblockmodal.min.js vendored Normal file
View File

@ -0,0 +1 @@
define(["jquery","core/modal_factory","core/templates","core/str","core/notification"],function(a,b,c,d,e){return{init:function(f){var g=a("[data-key=addblock]");d.get_string("addblock").done(function(e){var h=[];a.each(f.blocks,function(a,b){h[h.length]={key:"pluginname",component:"block_"+b}});var i=[];d.get_strings(h).done(function(d){a.each(d,function(a,b){i[i.length]={name:f.blocks[a],title:b}}),f.blocks=i,b.create({title:e,body:c.render("core/add_block_body",f),type:"CANCEL"},g)})}).fail(e.exception)}}});

1
lib/amd/build/modal_cancel.min.js vendored Normal file
View File

@ -0,0 +1 @@
define(["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events"],function(a,b,c,d,e){var f={CANCEL_BUTTON:'[data-action="cancel"]'},g=function(a){d.call(this,a),this.getFooter().find(f.CANCEL_BUTTON).length||b.exception({message:"No cancel button found"})};return g.prototype=Object.create(d.prototype),g.prototype.constructor=g,g.prototype.setFooter=function(){b.exception({message:"Can not change the footer of a cancel modal"})},g.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this),this.getModal().on(c.events.activate,f.CANCEL_BUTTON,function(b,c){var d=a.Event(e.cancel);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this))},g});

View File

@ -1 +1 @@
define(["jquery","core/modal_events","core/modal","core/modal_save_cancel","core/modal_confirm","core/templates","core/notification","core/custom_interaction_events"],function(a,b,c,d,e,f,g,h){var i={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CONFIRM:"core/modal_confirm"},j={DEFAULT:c,SAVE_CANCEL:d,CONFIRM:e},k={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CONFIRM:"CONFIRM"},l=function(a,c){"undefined"!=typeof c&&(h.define(c,[h.events.activate]),c.on(h.events.activate,function(){a.show()}),a.getRoot().on(b.hidden,function(){c.focus()}))},m=function(b,c,d){c=a(c);var e=j[b],f=new e(c);return l(f,d),f},n=function(b,c){var d=i[b];return f.render(d,{}).then(function(d){var e=a(d);return m(b,e,c)}).fail(g.exception)},o=function(a,b){var c=a.type||k.DEFAULT,d=!!a.large;return k[c]||(c=k.DEFAULT),n(c,b).then(function(b){return"undefined"!=typeof a.title&&b.setTitle(a.title),"undefined"!=typeof a.body&&b.setBody(a.body),"undefined"!=typeof a.footer&&b.setFooter(a.footer),d&&b.setLarge(),b})};return{create:o,types:k}});
define(["jquery","core/modal_events","core/modal","core/modal_save_cancel","core/modal_confirm","core/modal_cancel","core/templates","core/notification","core/custom_interaction_events"],function(a,b,c,d,e,f,g,h,i){var j={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CONFIRM:"core/modal_confirm",CANCEL:"core/modal_cancel"},k={DEFAULT:c,SAVE_CANCEL:d,CONFIRM:e,CANCEL:f},l={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CONFIRM:"CONFIRM",CANCEL:"CANCEL"},m=function(a,c){"undefined"!=typeof c&&(i.define(c,[i.events.activate]),c.on(i.events.activate,function(b,c){a.show(),c.originalEvent.preventDefault()}),a.getRoot().on(b.hidden,function(){c.focus()}))},n=function(b,c,d){c=a(c);var e=k[b],f=new e(c);return m(f,d),f},o=function(b,c){var d=j[b];return g.render(d,{}).then(function(d){var e=a(d);return n(b,e,c)}).fail(h.exception)},p=function(a,b){var c=a.type||l.DEFAULT,d=!!a.large;return l[c]||(c=l.DEFAULT),o(c,b).then(function(b){return"undefined"!=typeof a.title&&b.setTitle(a.title),"undefined"!=typeof a.body&&b.setBody(a.body),"undefined"!=typeof a.footer&&b.setFooter(a.footer),d&&b.setLarge(),b})};return{create:p,types:l}});

View File

@ -0,0 +1,64 @@
// 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/>.
/**
* Show an add block modal instead of doing it on a separate page.
*
* @module core/addblockmodal
* @class addblockmodal
* @package core
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/modal_factory', 'core/templates', 'core/str', 'core/notification'],
function($, ModalFactory, Templates, Str, Notification) {
return /** @alias module:core/addblockmodal */ {
/**
* Global init function for this module.
*
* @method init
* @param {Object} context The template context for rendering this modal body.
*/
init: function(context) {
var addblocklink = $('[data-key=addblock]');
// We need the fetch the names of the blocks. It was too much to send in the page.
Str.get_string('addblock').done(function(title) {
var titlerequests = [];
$.each(context.blocks, function(index, key) {
titlerequests[titlerequests.length] = {key: 'pluginname', component: 'block_' + key};
});
var blocks = [];
Str.get_strings(titlerequests).done(function(titles) {
$.each(titles, function(index, title) {
blocks[blocks.length] = {name: context.blocks[index], title: title};
});
context.blocks = blocks;
ModalFactory.create({
title: title,
body: Templates.render('core/add_block_body', context),
type: 'CANCEL',
}, addblocklink);
});
}).fail(Notification.exception);
}
};
});

View File

@ -0,0 +1,77 @@
// 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/>.
/**
* Contain the logic for the cancel modal.
*
* @module core/modal_cancel
* @class modal_cancel
* @package core
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_events'],
function($, Notification, CustomEvents, Modal, ModalEvents) {
var SELECTORS = {
CANCEL_BUTTON: '[data-action="cancel"]',
};
/**
* Constructor for the Modal.
*
* @param {object} root The root jQuery element for the modal
*/
var ModalCancel = function(root) {
Modal.call(this, root);
if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {
Notification.exception({message: 'No cancel button found'});
}
};
ModalCancel.prototype = Object.create(Modal.prototype);
ModalCancel.prototype.constructor = ModalCancel;
/**
* Override parent implementation to prevent changing the footer content.
*/
ModalCancel.prototype.setFooter = function() {
Notification.exception({message: 'Can not change the footer of a cancel modal'});
return;
};
/**
* Set up all of the event handling for the modal.
*
* @method registerEventListeners
*/
ModalCancel.prototype.registerEventListeners = function() {
// Apply parent event listeners.
Modal.prototype.registerEventListeners.call(this);
this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {
var cancelEvent = $.Event(ModalEvents.cancel);
this.getRoot().trigger(cancelEvent, this);
if (!cancelEvent.isDefaultPrevented()) {
this.hide();
data.originalEvent.preventDefault();
}
}.bind(this));
};
return ModalCancel;
});

View File

@ -22,15 +22,16 @@
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/modal_events', 'core/modal', 'core/modal_save_cancel', 'core/modal_confirm',
define(['jquery', 'core/modal_events', 'core/modal', 'core/modal_save_cancel', 'core/modal_confirm', 'core/modal_cancel',
'core/templates', 'core/notification', 'core/custom_interaction_events'],
function($, ModalEvents, Modal, ModalSaveCancel, ModalConfirm, Templates, Notification, CustomEvents) {
function($, ModalEvents, Modal, ModalSaveCancel, ModalConfirm, ModalCancel, Templates, Notification, CustomEvents) {
// The templates for each type of modal.
var TEMPLATES = {
DEFAULT: 'core/modal',
SAVE_CANCEL: 'core/modal_save_cancel',
CONFIRM: 'core/modal_confirm',
CANCEL: 'core/modal_cancel',
};
// The JS classes for each type of modal.
@ -38,6 +39,7 @@ define(['jquery', 'core/modal_events', 'core/modal', 'core/modal_save_cancel', '
DEFAULT: Modal,
SAVE_CANCEL: ModalSaveCancel,
CONFIRM: ModalConfirm,
CANCEL: ModalCancel,
};
// The available types of modals.
@ -45,6 +47,7 @@ define(['jquery', 'core/modal_events', 'core/modal', 'core/modal_save_cancel', '
DEFAULT: 'DEFAULT',
SAVE_CANCEL: 'SAVE_CANCEL',
CONFIRM: 'CONFIRM',
CANCEL: 'CANCEL',
};
/**
@ -58,8 +61,9 @@ define(['jquery', 'core/modal_events', 'core/modal', 'core/modal_save_cancel', '
var setUpTrigger = function(modal, triggerElement) {
if (typeof triggerElement != 'undefined') {
CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
triggerElement.on(CustomEvents.events.activate, function() {
triggerElement.on(CustomEvents.events.activate, function(e, data) {
modal.show();
data.originalEvent.preventDefault();
});
modal.getRoot().on(ModalEvents.hidden, function() {

View File

@ -42,6 +42,14 @@ define('BUI_CONTEXTS_ENTIRE_SITE', 2);
define('BUI_CONTEXTS_CURRENT', 0);
define('BUI_CONTEXTS_CURRENT_SUBS', 1);
// Position of "Add block" control, to be used in theme config as a value for $THEME->addblockposition:
// - default: as a fake block that is displayed in editing mode
// - flatnav: "Add block" item in the flat navigation drawer in editing mode
// - custom: none of the above, theme will take care of displaying the control.
define('BLOCK_ADDBLOCK_POSITION_DEFAULT', 0);
define('BLOCK_ADDBLOCK_POSITION_FLATNAV', 1);
define('BLOCK_ADDBLOCK_POSITION_CUSTOM', -1);
/**
* Exception thrown when someone tried to do something with a block that does
* not exist on a page.
@ -216,10 +224,12 @@ class block_manager {
($bi->instance_allow_multiple() || !$this->is_block_present($block->name)) &&
blocks_name_allowed_in_format($block->name, $pageformat) &&
$bi->user_can_addto($this->page)) {
$block->title = $bi->get_title();
$this->addableblocks[$block->name] = $block;
}
}
core_collator::asort_objects_by_property($this->addableblocks, 'title');
return $this->addableblocks;
}
@ -1141,7 +1151,8 @@ class block_manager {
$contents = $this->extracontent[$region];
}
$contents = array_merge($contents, $this->create_block_contents($this->blockinstances[$region], $output, $region));
if ($region == $this->defaultregion) {
if (($region == $this->defaultregion) && (!isset($this->page->theme->addblockposition) ||
$this->page->theme->addblockposition == BLOCK_ADDBLOCK_POSITION_DEFAULT)) {
$addblockui = block_add_block_ui($this->page, $output);
if ($addblockui) {
$contents[] = $addblockui;
@ -1210,36 +1221,39 @@ class block_manager {
$controls[] = new action_menu_link_secondary($url, $icon, $str, $attributes);
}
// Display either "Assign roles" or "Permissions" or "Change permissions" icon (whichever first is available).
$rolesurl = null;
// Assign roles.
if (get_assignable_roles($block->context, ROLENAME_SHORT)) {
$rolesurl = new moodle_url('/admin/roles/assign.php', array('contextid' => $block->context->id));
$rolesurl = new moodle_url('/admin/roles/assign.php', array('contextid' => $block->context->id,
'returnurl' => $this->page->url->out_as_local_url()));
$str = new lang_string('assignrolesinblock', 'block', $blocktitle);
$icon = 'i/assignroles';
} else if (has_capability('moodle/role:review', $block->context) or get_overridable_roles($block->context)) {
$rolesurl = new moodle_url('/admin/roles/permissions.php', array('contextid' => $block->context->id));
$str = get_string('permissions', 'role');
$icon = 'i/permissions';
} else if (has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:assign'), $block->context)) {
$rolesurl = new moodle_url('/admin/roles/check.php', array('contextid' => $block->context->id));
$str = get_string('checkpermissions', 'role');
$icon = 'i/checkpermissions';
}
if ($rolesurl) {
// TODO: please note it is sloppy to pass urls through page parameters!!
// it is shortened because some web servers (e.g. IIS by default) give
// a 'security' error if you try to pass a full URL as a GET parameter in another URL.
$return = $this->page->url->out(false);
$return = str_replace($CFG->wwwroot . '/', '', $return);
$rolesurl->param('returnurl', $return);
$controls[] = new action_menu_link_secondary(
$rolesurl,
new pix_icon($icon, $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str,
array('class' => 'editing_roles')
new pix_icon('i/assignroles', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str, array('class' => 'editing_assignroles')
);
}
// Permissions.
if (has_capability('moodle/role:review', $block->context) or get_overridable_roles($block->context)) {
$rolesurl = new moodle_url('/admin/roles/permissions.php', array('contextid' => $block->context->id,
'returnurl' => $this->page->url->out_as_local_url()));
$str = get_string('permissions', 'role');
$controls[] = new action_menu_link_secondary(
$rolesurl,
new pix_icon('i/permissions', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str, array('class' => 'editing_permissions')
);
}
// Change permissions.
if (has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:assign'), $block->context)) {
$rolesurl = new moodle_url('/admin/roles/check.php', array('contextid' => $block->context->id,
'returnurl' => $this->page->url->out_as_local_url()));
$str = get_string('checkpermissions', 'role');
$controls[] = new action_menu_link_secondary(
$rolesurl,
new pix_icon('i/checkpermissions', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str, array('class' => 'editing_checkroles')
);
}
@ -1286,8 +1300,10 @@ class block_manager {
* @return boolean true if anything was done. False if not.
*/
public function process_url_add() {
global $CFG, $PAGE, $OUTPUT;
$blocktype = optional_param('bui_addblock', null, PARAM_PLUGIN);
if (!$blocktype) {
if ($blocktype === null) {
return false;
}
@ -1297,7 +1313,54 @@ class block_manager {
throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('addblock'));
}
if (!array_key_exists($blocktype, $this->get_addable_blocks())) {
$addableblocks = $this->get_addable_blocks();
if ($blocktype === '') {
// Display add block selection.
$addpage = new moodle_page();
$addpage->set_pagelayout('admin');
$addpage->blocks->show_only_fake_blocks(true);
$addpage->set_course($this->page->course);
$addpage->set_context($this->page->context);
if ($this->page->cm) {
$addpage->set_cm($this->page->cm);
}
$addpagebase = str_replace($CFG->wwwroot . '/', '/', $this->page->url->out_omit_querystring());
$addpageparams = $this->page->url->params();
$addpage->set_url($addpagebase, $addpageparams);
$addpage->set_block_actions_done();
// At this point we are going to display the block selector, overwrite global $PAGE ready for this.
$PAGE = $addpage;
// Some functions use $OUTPUT so we need to replace that too.
$OUTPUT = $addpage->get_renderer('core');
$site = get_site();
$straddblock = get_string('addblock');
$PAGE->navbar->add($straddblock);
$PAGE->set_title($straddblock);
$PAGE->set_heading($site->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($straddblock);
if (!$addableblocks) {
echo $OUTPUT->box(get_string('noblockstoaddhere'));
echo $OUTPUT->container($OUTPUT->action_link($addpage->url, get_string('back')), 'm-x-3 m-b-1');
} else {
$url = new moodle_url($addpage->url, array('sesskey' => sesskey()));
echo $OUTPUT->render_from_template('core/add_block_body',
['blocks' => array_values($addableblocks),
'url' => '?' . $url->get_query_string(false)]);
echo $OUTPUT->container($OUTPUT->action_link($addpage->url, get_string('cancel')), 'm-x-3 m-b-1');
}
echo $OUTPUT->footer();
// Make sure that nothing else happens after we have displayed this form.
exit;
}
if (!array_key_exists($blocktype, $addableblocks)) {
throw new moodle_exception('cannotaddthisblocktype', '', $this->page->url->out(), $blocktype);
}
@ -1332,6 +1395,7 @@ class block_manager {
if (!$confirmdelete) {
$deletepage = new moodle_page();
$deletepage->set_pagelayout('admin');
$deletepage->blocks->show_only_fake_blocks(true);
$deletepage->set_course($this->page->course);
$deletepage->set_context($this->page->context);
if ($this->page->cm) {
@ -1452,6 +1516,7 @@ class block_manager {
$editpage = new moodle_page();
$editpage->set_pagelayout('admin');
$editpage->blocks->show_only_fake_blocks(true);
$editpage->set_course($this->page->course);
//$editpage->set_context($block->context);
$editpage->set_context($this->page->context);
@ -2072,12 +2137,8 @@ function block_add_block_ui($page, $output) {
$menu = array();
foreach ($missingblocks as $block) {
$blockobject = block_instance($block->name);
if ($blockobject !== false && $blockobject->user_can_addto($page)) {
$menu[$block->name] = $blockobject->get_title();
}
$menu[$block->name] = $block->title;
}
core_collator::asort($menu);
$actionurl = new moodle_url($page->url, array('sesskey'=>sesskey()));
$select = new single_select($actionurl, 'bui_addblock', $menu, null, array(''=>get_string('adddots')), 'add_block');

View File

@ -3764,6 +3764,25 @@ class flat_navigation extends navigation_node_collection {
$flat->key = 'sitesettings';
$this->add($flat);
}
// Add-a-block in editing mode.
if (isset($this->page->theme->addblockposition) &&
$this->page->theme->addblockposition == BLOCK_ADDBLOCK_POSITION_FLATNAV &&
$PAGE->user_is_editing() && $PAGE->user_can_edit_blocks() &&
($addable = $PAGE->blocks->get_addable_blocks())) {
$url = new moodle_url($PAGE->url, ['bui_addblock' => '', 'sesskey' => sesskey()]);
$addablock = navigation_node::create(get_string('addblock'), $url);
$flat = new flat_navigation_node($addablock, 0);
$flat->set_showdivider(true);
$flat->key = 'addblock';
$this->add($flat);
$blocks = [];
foreach ($addable as $block) {
$blocks[] = $block->name;
}
$params = array('blocks' => $blocks, 'url' => '?' . $url->get_query_string(false));
$PAGE->requires->js_call_amd('core/addblockmodal', 'init', array($params));
}
}
}

View File

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

View File

@ -0,0 +1,37 @@
{{!
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/add_block_body
Template for the body of the add block modal.
Context variables required for this template:
* blocks - list of blocks containing name and title
* url - base url for links back to same page.
Example context (json):
{
"blocks" : [ { "name": "test", "title": "Test block" } ],
"url" : "?a=b"
}
}}
<div class="list-group">
{{#blocks}}
<a href="{{url}}&amp;bui_addblock={{name}}" class="list-group-item list-group-item-action">{{title}}</a>
{{/blocks}}
</div>

View File

@ -0,0 +1,45 @@
{{!
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/modal_save_cancel
Moodle modal template with one cancel button.
The purpose of this template is to render a modal.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* title A cleaned string (use clean_text()) to display.
* body HTML content for the boday
Example context (json):
{
"title": "Example cancel modal",
"body": "Some example content for the body"
}
}}
{{< core/modal }}
{{$footer}}
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel {{/str}}</button>
{{/footer}}
{{/ core/modal }}

View File

@ -40,6 +40,6 @@
{{$body}}{{#str}} areyousure {{/str}}{{/body}}
{{$footer}}
<button type="button" class="btn btn-primary" data-action="yes">{{#str}} yes {{/str}}</button>
<button type="button" class="btn" data-action="no">{{#str}} no {{/str}}</button>
<button type="button" class="btn btn-secondary" data-action="no">{{#str}} no {{/str}}</button>
{{/footer}}
{{/ core/modal }}

View File

@ -41,6 +41,6 @@
{{< core/modal }}
{{$footer}}
<button type="button" class="btn btn-primary" data-action="save">{{#str}} savechanges {{/str}}</button>
<button type="button" class="btn" data-action="cancel">{{#str}} cancel {{/str}}</button>
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel {{/str}}</button>
{{/footer}}
{{/ core/modal }}

View File

@ -152,3 +152,4 @@ $THEME->prescsscallback = 'theme_boost_get_pre_scss';
$THEME->yuicssmodules = array();
$THEME->rendererfactory = 'theme_overridden_renderer_factory';
$THEME->undeletableblocktypes = '';
$THEME->addblockposition = BLOCK_ADDBLOCK_POSITION_FLATNAV;

View File

@ -62,7 +62,7 @@
<nav class="list-group m-t-1">
{{/showdivider}}
{{#action}}
<a class="list-group-item list-group-item-action {{#isactive}}font-weight-bold{{/isactive}}" href="{{{action}}}">
<a class="list-group-item list-group-item-action {{#isactive}}font-weight-bold{{/isactive}}" href="{{{action}}}" data-key="{{key}}">
<div class="m-l-{{get_indent}}">
{{#is_section}}
<div class="media">
@ -79,7 +79,7 @@
</a>
{{/action}}
{{^action}}
<div class="list-group-item">
<div class="list-group-item" data-key="{{key}}">
<div class="m-l-{{get_indent}}">
{{#is_section}}
{{#pix}}i/folder{{/pix}}

View File

@ -38,14 +38,12 @@ require_once(__DIR__ . '/../../../../blocks/tests/behat/behat_blocks.php');
class behat_theme_boost_behat_blocks extends behat_blocks {
public function i_add_the_block($blockname) {
$this->execute('behat_forms::i_set_the_field_to',
array("bui_addblock", $this->escape($blockname))
);
$this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('addblock'));
// If we are running without javascript we need to submit the form.
if (!$this->running_javascript()) {
$this->execute('behat_general::i_click_on_in_the',
array("Go", "button", "Add a block", "block"));
$this->execute('behat_general::i_click_on_in_the', [$blockname, 'link', '#region-main', 'css_element']);
} else {
$this->execute('behat_general::i_click_on_in_the', [$blockname, 'link', '.modal-body', 'css_element']);
}
}

View File

@ -76,4 +76,56 @@ class behat_theme_boost_behat_navigation extends behat_navigation {
return $node;
}
/**
* Opens the flat navigation drawer if it is not already open
*
* @When /^I open flat navigation drawer$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function i_open_flat_navigation_drawer() {
if (!$this->running_javascript()) {
// Navigation drawer is always open without JS.
return;
}
$xpath = "//button[contains(@data-action,'toggle-drawer')]";
$node = $this->find('xpath', $xpath);
$expanded = $node->getAttribute('aria-expanded');
if ($expanded === 'false') {
$node->click();
$this->wait_for_pending_js();
}
}
/**
* Closes the flat navigation drawer if it is open (does nothing if JS disabled)
*
* @When /^I close flat navigation drawer$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function i_close_flat_navigation_drawer() {
if (!$this->running_javascript()) {
// Navigation drawer can not be closed without JS.
return;
}
$xpath = "//button[contains(@data-action,'toggle-drawer')]";
$node = $this->find('xpath', $xpath);
$expanded = $node->getAttribute('aria-expanded');
if ($expanded === 'true') {
$node->click();
$this->wait_for_pending_js();
}
}
/**
* Clicks link with specified id|title|alt|text in the flat navigation drawer.
*
* @When /^I select "(?P<link_string>(?:[^"]|\\")*)" from flat navigation drawer$/
* @throws ElementNotFoundException Thrown by behat_base::find
* @param string $link
*/
public function i_select_from_flat_navigation_drawer($link) {
$this->i_open_flat_navigation_drawer();
$this->execute('behat_general::i_click_on_in_the', [$link, 'link', '#nav-drawer', 'css_element']);
}
}

View File

@ -57,6 +57,8 @@ information provided here is intended especially for theme designer.
* CLI svgtool.php has moved from theme/base/cli to admin/cli and paths should be relative to the new location.
* mod_chat will now display the 'course theme' option for all themes (previously it was only displayed on
bootstrap2 based themes).
* Theme can choose how to display "Add a block" control in $THEME->addblockposition, default value is
BLOCK_ADDBLOCK_POSITION_DEFAULT that displays it as a fake block in editing mode.
=== 3.1 ===