Merge branch 'MDL-82767-main-v03' of https://github.com/ferranrecio/moodle

This commit is contained in:
Huong Nguyen 2025-01-23 11:18:50 +07:00
commit bb72db2074
No known key found for this signature in database
GPG Key ID: 40D88AB693A3E72A
31 changed files with 830 additions and 163 deletions

View File

@ -0,0 +1,11 @@
issueNumber: MDL-82767
notes:
core_courseformat:
- message: >-
A new course/format/update.php url is added as a non-ajax alternative to
the core_courseformat_course_update webservice
type: improved
- message: >-
The core_courseformat\base::get_non_ajax_cm_action_url is now
deprecated. Use get_update_url instead.
type: deprecated

View File

@ -0,0 +1,9 @@
issueNumber: MDL-82767
notes:
format_topics:
- message: >-
In format topics, the section controlmenu class deprecates the
get_course_url method. This may affect formats extending the topics
format and adding extra items to the section menu. Use
$this->format->get_update_url instead.
type: deprecated

View File

@ -0,0 +1,11 @@
issueNumber: MDL-82767
notes:
core_courseformat:
- message: >-
Many get actions from course/view.php and course/mod.php are now
deprecated. Use the new course/format/update.php instead to replace all
direct edit urls in your code. The affected actions are: indent,
duplicate, hide, show, stealth, delete, groupmode and marker (highlight).
The course/format/updates.php uses the same parameters as the
core_courseformat_course_update webservice
type: deprecated

View File

@ -0,0 +1,10 @@
issueNumber: MDL-82767
notes:
core_courseformat:
- message: >-
From now on, deleting an activity without Ajax will be consistent with
deleting an activity using Ajax. This ensures that all activity deletions
will use the recycle bin and avoid code duplication. If your format uses
the old non-Ajax method to bypass the recycle bin it won't work anymore
as the non-Ajax deletions are now handled in course/format/update.php.
type: changed

View File

@ -49,6 +49,7 @@ Feature: Basic recycle bin functionality
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I delete "Test assign 1" activity
And I run all adhoc tasks
When I navigate to "Recycle bin" in current page administration
Then I should see "Test assign 1"
And I should see "Contents will be permanently deleted after 7 days"

View File

@ -33,8 +33,8 @@ use html_writer;
use section_info;
use context_course;
use editsection_form;
use moodle_exception;
use coding_exception;
use core\exception\moodle_exception;
use core\exception\coding_exception;
use moodle_url;
use lang_string;
use core_external\external_api;
@ -387,6 +387,37 @@ abstract class base {
return $this->modinfo;
}
/**
* Return a format state updates instance.
*/
public function get_stateupdates_instance(): \core_courseformat\stateupdates {
$defaultupdatesclass = 'core_courseformat\\stateupdates';
$updatesclass = 'format_' . $this->format . '\\courseformat\\stateupdates';
if (!class_exists($updatesclass)) {
$updatesclass = $defaultupdatesclass;
}
$updates = new $updatesclass($this);
if (!is_a($updates, $defaultupdatesclass)) {
throw new coding_exception("The \"$updatesclass\" class must extend \"$defaultupdatesclass\"");
}
return $updates;
}
/**
* Return a format state actions instance.
* @return \core_courseformat\stateactions
*/
public function get_stateactions_instance(): \core_courseformat\stateactions {
// Get the actions class from the course format.
$actionsclass = 'format_'. $this->format.'\\courseformat\\stateactions';
if (!class_exists($actionsclass)) {
$actionsclass = 'core_courseformat\\stateactions';
}
return new $actionsclass();
}
/**
* Method used in the rendered and during backup instead of legacy 'numsections'
*
@ -886,7 +917,7 @@ abstract class base {
* of the view script, it is not enough to change just this function. Do not forget
* to add proper redirection.
*
* @param int|stdClass|section_info $section Section object from database or just field course_sections.section
* @param int|stdClass|section_info|null $section Section object from database or just field course_sections.section
* if null the course view page is returned
* @param array $options options for view URL. At the moment core uses:
* 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page.
@ -925,6 +956,51 @@ abstract class base {
return $url;
}
/**
* The URL to update the course format.
*
* If no section is specified, the update will redirect to the general course page.
*
* @param string $action action name the reactive action
* @param array $ids list of ids to update
* @param int|null $targetsectionid optional target section id
* @param int|null $targetcmid optional target cm id
* @param moodle_url|null $returnurl optional custom return url
* @return moodle_url
*/
public function get_update_url(
string $action,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
?moodle_url $returnurl = null
): moodle_url {
$params = [
'courseid' => $this->get_courseid(),
'sesskey' => sesskey(),
'action' => $action,
];
if (count($ids) === 1) {
$params['id'] = reset($ids);
} else {
foreach ($ids as $key => $id) {
$params["ids[]"] = $id;
}
}
if ($targetsectionid) {
$params['sectionid'] = $targetsectionid;
}
if ($targetcmid) {
$params['cmid'] = $targetcmid;
}
if ($returnurl) {
$params['returnurl'] = $returnurl->out_as_local_url();
}
return new moodle_url('/course/format/update.php', $params);
}
/**
* Return the old non-ajax activity action url.
*
@ -932,30 +1008,36 @@ abstract class base {
* so we must translate to an old non-ajax url while non-ajax
* course editing is still supported.
*
* @deprecated since Moodle 5.0
* @todo Remove this method in Moodle 6.0 (MDL-83530).
*
* @param string $action action name the reactive action
* @param cm_info $cm course module
* @return moodle_url
*/
#[\core\attribute\deprecated(
replacement: 'core_courseformat\base::get_update_url',
since: '5.0',
mdl: 'MDL-82767',
)]
public function get_non_ajax_cm_action_url(string $action, cm_info $cm): moodle_url {
$nonajaxactions = [
'cmDelete' => 'delete',
'cmDuplicate' => 'duplicate',
'cmHide' => 'hide',
'cmShow' => 'show',
'cmStealth' => 'stealth',
'cmDelete' => 'cm_delete',
'cmDuplicate' => 'cm_duplicate',
'cmHide' => 'cm_hide',
'cmShow' => 'cm_show',
'cmStealth' => 'cm_stealth',
];
if (!isset($nonajaxactions[$action])) {
throw new coding_exception('Unknown activity action: ' . $action);
}
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
$nonajaxaction = $nonajaxactions[$action];
$nonajaxurl = new moodle_url(
'/course/mod.php',
['sesskey' => sesskey(), $nonajaxaction => $cm->id]
return $this->get_update_url(
action: $nonajaxaction,
ids: [$cm->id],
returnurl: $this->get_view_url($this->get_sectionnum(), ['navigation' => true]),
);
if (!is_null($this->get_sectionid())) {
$nonajaxurl->param('sr', $this->get_sectionnum());
}
return $nonajaxurl;
}
/**

View File

@ -16,12 +16,11 @@
namespace core_courseformat\external;
use core\exception\moodle_exception;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_value;
use moodle_exception;
use coding_exception;
use context_course;
use core_courseformat\base as course_format;
@ -106,32 +105,16 @@ class update_course extends external_api {
self::validate_context(context_course::instance($courseid));
$courseformat = course_get_format($courseid);
$format = course_get_format($courseid);
// Create a course changes tracker object.
$defaultupdatesclass = 'core_courseformat\\stateupdates';
$updatesclass = 'format_' . $courseformat->get_format() . '\\courseformat\\stateupdates';
if (!class_exists($updatesclass)) {
$updatesclass = $defaultupdatesclass;
}
$updates = new $updatesclass($courseformat);
if (!is_a($updates, $defaultupdatesclass)) {
throw new coding_exception("The \"$updatesclass\" class must extend \"$defaultupdatesclass\"");
}
// Get the actions class from the course format.
$actionsclass = 'format_'. $courseformat->get_format().'\\courseformat\\stateactions';
if (!class_exists($actionsclass)) {
$actionsclass = 'core_courseformat\\stateactions';
}
$actions = new $actionsclass();
$updates = $format->get_stateupdates_instance();
$actions = $format->get_stateactions_instance();
if (!is_callable([$actions, $action])) {
throw new moodle_exception("Invalid course state action $action in ".get_class($actions));
}
$course = $courseformat->get_course();
$course = $format->get_course();
// Execute the action.
$actions->$action($updates, $course, $ids, $targetsectionid, $targetcmid);

View File

@ -83,6 +83,15 @@ abstract class basecontrolmenu implements named_templatable, renderable {
$this->baseurl = $format->get_view_url($format->get_sectionnum(), ['navigation' => true]);
}
/**
* Change the default base URL to return after each action.
*
* @param url $baseurl
*/
public function set_baseurl(url $baseurl) {
$this->baseurl = $baseurl;
}
/**
* Export this data so it can be used as the context for a mustache template.
*

View File

@ -241,7 +241,11 @@ class controlmenu extends basecontrolmenu {
return null;
}
$url = new url($this->basemodurl, ['id' => $this->mod->id, 'indent' => 1]);
$url = $this->format->get_update_url(
action: 'cm_moveright',
ids: [$this->mod->id],
returnurl: $this->baseurl,
);
$icon = (right_to_left()) ? 't/left' : 't/right';
@ -269,7 +273,11 @@ class controlmenu extends basecontrolmenu {
return null;
}
$url = new url($this->basemodurl, ['id' => $this->mod->id, 'indent' => -1]);
$url = $this->format->get_update_url(
action: 'cm_moveleft',
ids: [$this->mod->id],
returnurl: $this->baseurl,
);
$icon = (right_to_left()) ? 't/right' : 't/left';
@ -319,7 +327,11 @@ class controlmenu extends basecontrolmenu {
return null;
}
$url = new url($this->basemodurl, ['duplicate' => $this->mod->id]);
$url = $this->format->get_update_url(
action: 'cm_duplicate',
ids: [$this->mod->id],
returnurl: $this->baseurl,
);
return new link_secondary(
url: $url,
@ -392,7 +404,11 @@ class controlmenu extends basecontrolmenu {
return null;
}
$url = new url($this->basemodurl, ['delete' => $this->mod->id]);
$url = $this->format->get_update_url(
action: 'cm_delete',
ids: [$this->mod->id],
returnurl: $this->baseurl,
);
return new link_secondary(
url: $url,

View File

@ -198,13 +198,12 @@ class delegatedcontrolmenu extends basecontrolmenu {
}
$sectionreturn = $this->format->get_sectionnum();
$url = clone ($this->baseurl);
$strhide = get_string('hide');
$strshow = get_string('show');
if ($this->section->visible) {
$url->param('hide', $this->section->sectionnum);
$action = 'section_hide';
$icon = 'i/show';
$name = $strhide;
$attributes = [
@ -217,7 +216,7 @@ class delegatedcontrolmenu extends basecontrolmenu {
'data-swapicon' => 'i/hide',
];
} else {
$url->param('show', $this->section->sectionnum);
$action = 'section_show';
$icon = 'i/hide';
$name = $strshow;
$attributes = [
@ -231,6 +230,12 @@ class delegatedcontrolmenu extends basecontrolmenu {
];
}
$url = $this->format->get_update_url(
action: $action,
ids: [$this->section->id],
returnurl: $this->baseurl,
);
return new link_secondary(
url: $url,
icon: new pix_icon($icon, ''),
@ -282,14 +287,10 @@ class delegatedcontrolmenu extends basecontrolmenu {
return null;
}
// Removing a delegated section without ajax returns to the parent section.
$url = new url(
'/course/mod.php',
[
'sesskey' => sesskey(),
'delete' => $this->mod->id,
'sr' => $this->mod->sectionnum,
],
$url = $this->format->get_update_url(
action: 'cm_delete',
ids: [$this->mod->id],
returnurl: $this->baseurl,
);
return new link_secondary(

View File

@ -158,17 +158,17 @@ class groupmode implements named_templatable, renderable {
$choice->add_option(
NOGROUPS,
get_string('groupsnone', 'group'),
$this->get_option_data(null, 'cmNoGroups', $this->mod->id)
$this->get_option_data(null, 'cmNoGroups', 'cm_nogroups')
);
$choice->add_option(
SEPARATEGROUPS,
get_string('groupsseparate', 'group'),
$this->get_option_data('groupsseparate', 'cmSeparateGroups', $this->mod->id)
$this->get_option_data('groupsseparate', 'cmSeparateGroups', 'cm_separategroups')
);
$choice->add_option(
VISIBLEGROUPS,
get_string('groupsvisible', 'group'),
$this->get_option_data('groupsvisible', 'cmVisibleGroups', $this->mod->id)
$this->get_option_data('groupsvisible', 'cmVisibleGroups', 'cm_visiblegroups')
);
$choice->set_selected_value($this->mod->effectivegroupmode);
return $choice;
@ -177,18 +177,26 @@ class groupmode implements named_templatable, renderable {
/**
* Get the data for the option.
* @param string|null $name the name of the option
* @param string $action the state action of the option
* @param int $id the id of the module
* @param string $mutation the mutation name
* @param string $stateaction the state action name
* @return array
*/
private function get_option_data(?string $name, string $action, int $id): array {
private function get_option_data(?string $name, string $mutation, string $stateaction): array {
$format = $this->format;
$nonajaxurl = $format->get_update_url(
action: $stateaction,
ids: [$this->mod->id],
returnurl: $format->get_view_url($format->get_sectionnum(), ['navigation' => true]),
);
return [
'description' => ($name) ? get_string("groupmode_{$name}_help", 'group') : null,
// The dropdown icons are decorative, so we don't need to provide alt text.
'icon' => $this->get_action_icon($action),
'icon' => $this->get_action_icon($mutation),
'url' => $nonajaxurl,
'extras' => [
'data-id' => $id,
'data-action' => $action,
'data-id' => $this->mod->id,
'data-action' => $mutation,
]
];
}

View File

@ -236,20 +236,20 @@ class visibility implements named_templatable, renderable {
$choice->add_option(
'show',
get_string("availability_{$label}", 'core_courseformat'),
$this->get_option_data($label, 'cmShow')
$this->get_option_data($label, 'cmShow', 'cm_show')
);
}
$choice->add_option(
'hide',
get_string('availability_hide', 'core_courseformat'),
$this->get_option_data('hide', 'cmHide')
$this->get_option_data('hide', 'cmHide', 'cm_hide')
);
if ($CFG->allowstealth && $this->format->allow_stealth_module_visibility($this->mod, $this->section)) {
$choice->add_option(
'stealth',
get_string('availability_stealth', 'core_courseformat'),
$this->get_option_data('stealth', 'cmStealth')
$this->get_option_data('stealth', 'cmStealth', 'cm_stealth')
);
}
return $choice;
@ -258,19 +258,25 @@ class visibility implements named_templatable, renderable {
/**
* Get the data for the option.
* @param string $name the name of the option
* @param string $action the state action of the option
* @param string $mutation the mutation name
* @param string $stateaction the state action name
* @return array
*/
private function get_option_data(string $name, string $action): array {
private function get_option_data(string $name, string $mutation, string $stateaction): array {
$format = $this->format;
$nonajaxurl = $format->get_update_url(
action: $stateaction,
ids: [$this->mod->id],
returnurl: $format->get_view_url($format->get_sectionnum(), ['navigation' => true]),
);
return [
'description' => get_string("availability_{$name}_help", 'core_courseformat'),
'icon' => $this->get_icon($name),
// Non-ajax behat is not smart enough to discrimante hidden links
// so we need to keep providing the non-ajax links.
'url' => $this->format->get_non_ajax_cm_action_url($action, $this->mod),
'url' => $nonajaxurl,
'extras' => [
'data-id' => $this->mod->id,
'data-action' => $action,
'data-action' => $mutation,
]
];
}

View File

@ -198,13 +198,11 @@ class controlmenu extends basecontrolmenu {
}
$sectionreturn = $this->format->get_sectionnum();
$url = new url($this->baseurl, ['sesskey' => sesskey()]);
$strhide = get_string('hide');
$strshow = get_string('show');
if ($this->section->visible) {
$url->param('hide', $this->section->sectionnum);
$stateaction = 'section_hide';
$icon = 'i/show';
$name = $strhide;
$attributes = [
@ -217,7 +215,7 @@ class controlmenu extends basecontrolmenu {
'data-swapicon' => 'i/hide',
];
} else {
$url->param('show', $this->section->sectionnum);
$stateaction = 'section_show';
$icon = 'i/hide';
$name = $strshow;
$attributes = [
@ -231,6 +229,12 @@ class controlmenu extends basecontrolmenu {
];
}
$url = $this->format->get_update_url(
action: $stateaction,
ids: [$this->section->id],
returnurl: $this->baseurl,
);
return new link_secondary(
url: $url,
icon: new pix_icon($icon, ''),
@ -400,16 +404,10 @@ class controlmenu extends basecontrolmenu {
return null;
}
$params = [
'id' => $this->section->id,
'delete' => 1,
'sesskey' => sesskey(),
];
$params['sr'] ??= $this->format->get_sectionnum();
$url = new url(
'/course/editsection.php',
$params,
$url = $this->format->get_update_url(
action: 'section_delete',
ids: [$this->section->id],
returnurl: $this->baseurl,
);
return new link_secondary(
url: $url,

View File

@ -119,12 +119,12 @@ class visibility implements named_templatable, renderable {
$choice->add_option(
'show',
get_string('availability_show', 'core_courseformat'),
$this->get_option_data('show', 'sectionShow')
$this->get_option_data('show', 'sectionShow', 'section_show')
);
$choice->add_option(
'hide',
get_string('availability_hide', 'core_courseformat'),
$this->get_option_data('hide', 'sectionHide')
$this->get_option_data('hide', 'sectionHide', 'section_hide')
);
$choice->set_selected_value('hide');
@ -140,23 +140,25 @@ class visibility implements named_templatable, renderable {
* Get the data for the option.
*
* @param string $name the name of the option
* @param string $action the state action of the option
* @param string $mutation the mutation name
* @param string $stateaction the state action name
* @return array
*/
private function get_option_data(string $name, string $action): array {
$baseurl = course_get_url($this->section->course, $this->section);
$baseurl->param('sesskey', sesskey());
$baseurl->param($action, $this->section->section);
private function get_option_data(string $name, string $mutation, string $stateaction): array {
$format = $this->format;
$nonajaxurl = $format->get_update_url(
action: $stateaction,
ids: [$this->section->id],
returnurl: $format->get_view_url($format->get_sectionnum(), ['navigation' => true]),
);
return [
'description' => get_string("availability_{$name}_help", 'core_courseformat'),
'icon' => $this->get_icon($name),
// Non-ajax behat is not smart enough to discrimante hidden links
// so we need to keep providing the non-ajax links.
'url' => $baseurl,
'url' => $nonajaxurl,
'extras' => [
'data-id' => $this->section->id,
'data-action' => $action,
'data-action' => $mutation,
],
];
}

View File

@ -0,0 +1,206 @@
<?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/>.
namespace core_courseformat\output\local;
use core_courseformat\base as course_format;
use core\output\renderer_base;
use core\output\single_button;
use core\url;
use stdClass;
/**
* Support UIs for non-ajax course updates alternatives.
*
* This class is used from course/format/update.php to provide confirmation
* dialogs for specific actions that require user confirmation.
*
* All protected methods has the same parameters as the core_courseformat\stateactions
* even if they are not used for a specific action.
*
* @package core_courseformat
* @copyright 2024 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class courseupdate {
use courseformat_named_templatable;
/**
* Constructor.
*
* @param course_format $format the course format class.
* @param url $actionurl the current action url.
* @param url $returnurl the return url if the user cancel the action.
*/
public function __construct(
/** @var course_format the course format class */
protected course_format $format,
/** @var url the current action url */
protected url $actionurl,
/** @var url the return url if the user cancel the action */
protected url $returnurl,
) {
}
/**
* Check if a specific action requires confirmation.
*
* Format plugins can override this method to provide confirmation
* dialogs for specific actions.
*
* @param string $action the action name
* @return bool
*/
public function is_confirmation_required(
string $action,
): bool {
$methodname = $action . '_confirmation_dialog';
return method_exists($this, $methodname);
}
/**
* Get the confirmation dialog for a specific action.
*
* Format plugins can override this method to provide confirmation
* dialogs for specific actions.
*
* @param renderer_base $output the course renderer
* @param stdClass $course
* @param string $action the state action name to execute
* @param array $ids the section or cm ids.
* @param int|null $targetsectionid the optional target section id
* @param int|null $targetcmid the optional target cm id
* @return string the HTML output
*/
public function get_confirmation_dialog(
renderer_base $output,
stdClass $course,
string $action,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
): string {
$methodname = $action . '_confirmation_dialog';
if (method_exists($this, $methodname)) {
return $this->$methodname(
output: $output,
course: $course,
ids: $ids,
targetsectionid: $targetsectionid,
targetcmid: $targetcmid,
);
}
return '';
}
/**
* Render the section delete confirmation dialog.
*
* @param renderer_base $output the course renderer
* @param stdClass $course
* @param array $ids the action ids.
* @param int|null $targetsectionid the target section id (not used)
* @param int|null $targetcmid the target cm id (not used)
* @return string the HTML output
*/
protected function section_delete_confirmation_dialog(
renderer_base $output,
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
): string {
if (count($ids) == 1) {
$modinfo = $this->format->get_modinfo();
$section = $modinfo->get_section_info_by_id($ids[0]);
$title = get_string('sectiondelete_title', 'core_courseformat');
$message = get_string(
'sectiondelete_info',
'core_courseformat',
['name' => $this->format->get_section_name($section)]
);
} else {
$title = get_string('sectionsdelete_title', 'core_courseformat');
$message = get_string('sectionsdelete_info', 'core_courseformat', ['count' => count($ids)]);
}
return $output->confirm(
message: $message,
cancel: $this->returnurl,
continue: new url($this->actionurl, ['confirm' => 1]),
displayoptions: [
'confirmtitle' => $title,
'type' => single_button::BUTTON_DANGER,
'continuestr' => get_string('delete'),
]
);
}
/**
* Render the cm delete confirmation dialog.
*
* @param renderer_base $output the course renderer
* @param stdClass $course
* @param array $ids the action ids.
* @param int|null $targetsectionid the target section id (not used)
* @param int|null $targetcmid the target cm id (not used)
* @return string the HTML output
*/
protected function cm_delete_confirmation_dialog(
renderer_base $output,
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
): string {
if (count($ids) == 1) {
$modinfo = $this->format->get_modinfo();
$cm = $modinfo->get_cm($ids[0]);
if ($cm->get_delegated_section_info()) {
$title = get_string('cmdelete_subsectiontitle', 'core_courseformat');
$meesagestr = 'sectiondelete_info';
} else {
$title = get_string('cmdelete_title', 'core_courseformat');
$meesagestr = 'cmdelete_info';
}
$message = get_string(
$meesagestr,
'core_courseformat',
(object) [
'type' => get_string('pluginname', 'mod_' . $cm->modname),
'name' => $cm->name,
],
);
} else {
$title = get_string('cmsdelete_title', 'core_courseformat');
$message = get_string('cmsdelete_info', 'core_courseformat', ['count' => count($ids)]);
}
return $output->confirm(
message: $message,
cancel: $this->returnurl,
continue: new url($this->actionurl, ['confirm' => 1]),
displayoptions: [
'confirmtitle' => $title,
'type' => single_button::BUTTON_DANGER,
'continuestr' => get_string('delete'),
]
);
}
}

View File

@ -790,7 +790,10 @@ final class base_test extends advanced_testcase {
$this->expectException(\coding_exception::class);
}
$result = $format->get_non_ajax_cm_action_url($action, $cminfo);
$this->assertEquals($assign0->cmid, $result->param($expectedparam));
if (!$exception) {
$this->assertDebuggingCalled();
}
$this->assertEquals($assign0->cmid, $result->param('id'));
}
/**

View File

@ -0,0 +1,108 @@
@core @core_courseformat
Feature: Validate some activity editing has a non-ajax alternative
In order to edit the course activities faster
As a teacher
I need to be able use some edit tools without ajax.
Background:
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| category | 0 |
| numsections | 3 |
| initsections | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section |
| assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
Scenario: Activity settings can be accessed without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I click on "Edit settings" "link" in the "Activity sample 1" "activity"
Then I should see "Assignment name"
And I set the field "Assignment name" to "New name"
And I press "Save and return to course"
And I should see "New name"
Scenario: Indent an activity can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I should not see "Move left"
When I click on "Move right" "link" in the "Activity sample 1" "activity"
Then I should see "Move left"
And I should not see "Move right"
And I click on "Move left" "link" in the "Activity sample 1" "activity"
And I should not see "Move left"
And I should see "Move right"
Scenario: Hide and show an activity can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I should not see "Show" in the ".cm_action_menu" "css_element"
When I click on "Hide" "link" in the "Activity sample 1" "activity"
Then I should see "Show" in the ".cm_action_menu" "css_element"
And I should not see "Hide" in the ".cm_action_menu" "css_element"
And I click on "Show" "link" in the "Activity sample 1" "activity"
And I should not see "Show" in the ".cm_action_menu" "css_element"
And I should see "Hide" in the ".cm_action_menu" "css_element"
Scenario: Activity visibility with stealth option can be changed without ajax
Given the following config values are set as admin:
| allowstealth | 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I should see "Show on course page"
And I should see "Hide on course page"
And I should see "Make available but don't show on course page"
And ".activity-badges" "css_element" should not exist
When I click on "Hide on course page" "link" in the "Activity sample 1" "activity"
Then I should see "Hidden from students" in the "Activity sample 1" "core_courseformat > Activity visibility"
And I should not see "Available but not shown on course page" in the "Activity sample 1" "core_courseformat > Activity visibility"
And I click on "Make available but don't show on course page" "link" in the "Activity sample 1" "activity"
And I should not see "Hidden from students" in the "Activity sample 1" "core_courseformat > Activity visibility"
And I should see "Available but not shown on course page" in the "Activity sample 1" "core_courseformat > Activity visibility"
And I click on "Show on course page" "link" in the "Activity sample 1" "activity"
And ".activity-badges" "css_element" should not exist
Scenario: Duplicate activity can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I click on "Duplicate" "link" in the "Activity sample 1" "activity"
Then I should see "Activity sample 1 (copy)"
Scenario: Delete activity can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I click on "Delete" "link" in the "Activity sample 1" "activity"
And I should see "Delete activity?"
And I should see "This will delete Activity sample 1 and any user data it contains"
And I click on "Delete" "button"
Then I should not see "Activity sample 1"
Scenario: The activity groupmode can be changed without ajax
Given the following "groups" exist:
| name | course | idnumber |
| G1 | C1 | GI1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And "No groups" "icon" should exist in the "Activity sample 1" "activity"
And "Visible groups" "icon" should not exist in the "Activity sample 1" "activity"
And "Separate groups" "icon" should not exist in the "Activity sample 1" "activity"
When I click on "Separate groups" "link" in the "Activity sample 1" "activity"
And "No groups" "icon" should not exist in the "Activity sample 1" "activity"
And "Visible groups" "icon" should not exist in the "Activity sample 1" "activity"
And "Separate groups" "icon" should exist in the "Activity sample 1" "activity"
And I click on "Visible groups" "link" in the "Activity sample 1" "activity"
And "No groups" "icon" should not exist in the "Activity sample 1" "activity"
And "Visible groups" "icon" should exist in the "Activity sample 1" "activity"
And "Separate groups" "icon" should not exist in the "Activity sample 1" "activity"
And I click on "No groups" "link" in the "Activity sample 1" "activity"
And "No groups" "icon" should exist in the "Activity sample 1" "activity"
And "Visible groups" "icon" should not exist in the "Activity sample 1" "activity"
And "Separate groups" "icon" should not exist in the "Activity sample 1" "activity"

View File

@ -0,0 +1,51 @@
@core @core_courseformat
Feature: Validate some section editing has a non-ajax alternative
In order to edit the course sections faster
As a teacher
I need to be able use some edit tools without ajax.
Background:
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| category | 0 |
| numsections | 3 |
| initsections | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section |
| assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
Scenario: Section settings can be accessed without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I click on "Edit settings" "link" in the "Section 1" "core_courseformat > Section actions menu"
Then I should see "Section name"
And I set the field "Section name" to "New name"
And I press "Save changes"
And I should see "New name"
Scenario: Hide and show a section can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I should not see "Show" in the "Section 1" "core_courseformat > Section actions menu"
When I click on "Hide" "link" in the "Section 1" "core_courseformat > Section actions menu"
Then I should see "Show" in the "Section 1" "core_courseformat > Section actions menu"
And I should not see "Hide" in the "Section 1" "core_courseformat > Section actions menu"
And I click on "Show" "link" in the "Section 1" "core_courseformat > Section actions menu"
And I should not see "Show" in the "Section 1" "core_courseformat > Section actions menu"
And I should see "Hide" in the "Section 1" "core_courseformat > Section actions menu"
Scenario: Delete a section can be done without ajax
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
When I click on "Delete" "link" in the "Section 1" "core_courseformat > Section actions menu"
Then I should see "Delete section?"
And I should see "This will delete Section 1 and all the activities it contains."
And I click on "Delete" "button"
And I should not see "Section 1"

View File

@ -72,7 +72,13 @@ class controlmenu extends controlmenu_base {
*
* @return url
*/
#[\core\attribute\deprecated(
since: '5.0',
mdl: 'MDL-82767',
reason: 'Not used anymore, use $this->format->get_update_url instead',
)]
protected function get_course_url(): url {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
$format = $this->format;
$section = $this->section;
$course = $format->get_course();
@ -97,10 +103,6 @@ class controlmenu extends controlmenu_base {
$section = $this->section;
$course = $format->get_course();
$sectionreturn = $format->get_sectionnum();
$url = $this->get_course_url();
if (!is_null($sectionreturn)) {
$url->param('sectionid', $format->get_sectionid());
}
$highlightoff = get_string('highlightoff');
$highlightofficon = 'i/marked';
@ -109,7 +111,7 @@ class controlmenu extends controlmenu_base {
$highlightonicon = 'i/marker';
if ($course->marker == $section->sectionnum) { // Show the "light globe" on/off.
$url->param('marker', 0);
$action = 'section_unhighlight';
$icon = $highlightofficon;
$name = $highlightoff;
$attributes = [
@ -122,7 +124,7 @@ class controlmenu extends controlmenu_base {
'data-swapicon' => $highlightonicon,
];
} else {
$url->param('marker', $section->section);
$action = 'section_highlight';
$icon = $highlightonicon;
$name = $highlighton;
$attributes = [
@ -135,6 +137,13 @@ class controlmenu extends controlmenu_base {
'data-swapicon' => $highlightofficon,
];
}
$url = $this->format->get_update_url(
action: $action,
ids: [$section->id],
returnurl: $this->baseurl,
);
return new action_menu_link_secondary(
url: $url,
icon: new pix_icon($icon, ''),

View File

@ -70,7 +70,7 @@ Feature: Sections can be edited and deleted in custom sections format
Scenario: Deleting the last section in custom sections format
Given I am on "Course 1" course homepage with editing mode on
When I delete section "5"
Then I should see "Are you absolutely sure you want to completely delete \"Section 5\" and all the activities it contains?"
Then I should see "This will delete Section 5 and all the activities it contains."
And I press "Delete"
And I should not see "Section 5"
And I should see "Section 4"

View File

@ -51,3 +51,11 @@ Feature: Sections can be highlighted
When I open section "3" edit menu
And I click on "Unhighlight" "link" in the "Section 3" "section"
Then I should not see "Highlighted" in the "Section 3" "section"
Scenario: Highlight and unhighlight a section can be done without ajax
# Without javascript hidden elements cannot be detected with a simple I should see step.
Given ".section.current" "css_element" should not exist
When I click on "Highlight" "link" in the "Section 2" "core_courseformat > Section actions menu"
Then ".section.current[data-number='2']" "css_element" should exist
And I click on "Unhighlight" "link" in the "Section 2" "core_courseformat > Section actions menu"
And ".section.current" "css_element" should not exist

127
course/format/update.php Normal file
View File

@ -0,0 +1,127 @@
<?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/>.
/**
* Execute an update action on a course format and structure.
*
* @package core_courseformat
* @copyright 2024 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../../config.php');
require_once($CFG->dirroot . '/course/lib.php');
use core\url;
use core\exception\moodle_exception;
use core_courseformat\base as course_format;
$action = required_param('action', PARAM_ALPHANUMEXT);
$courseid = required_param('courseid', PARAM_INT);
$targetsectionid = optional_param('targetsectionid', null, PARAM_INT);
$targetcmid = optional_param('targetcmid', null, PARAM_INT);
$confirm = optional_param('confirm', false, PARAM_BOOL);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
// All state updates are designed to be batch compatible. However, we also
// accept single id values for simplicity.
$ids = optional_param_array('ids', [], PARAM_INT);
if (empty($ids)) {
$ids = [required_param('id', PARAM_INT)];
}
if (empty($ids)) {
throw new moodle_exception('missingparam', '', '', 'ids');
}
$format = course_get_format($courseid);
$course = $format->get_course();
if ($returnurl === null) {
$returnurl = new url('/course/view.php', ['id' => $course->id]);
}
// Normalize the return URL.
$returnurl = new moodle_url($returnurl);
$currenturl = new moodle_url(
'/course/format/update.php',
[
'action' => $action,
'courseid' => $courseid,
'targetsectionid' => $targetsectionid,
'targetcmid' => $targetcmid,
'returnurl' => $returnurl,
'sesskey' => sesskey(),
]
);
foreach ($ids as $key => $id) {
$currenturl->param("ids[]", $id);
}
require_sesskey();
$PAGE->set_url($currenturl);
$PAGE->set_context($format->get_context());
$PAGE->set_pagelayout('course');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_heading($course->fullname);
require_login($course);
require_all_capabilities(
['moodle/course:update', 'moodle/course:sectionvisibility', 'moodle/course:activityvisibility'],
$format->get_context(),
);
// Some actions may require a confirmation dialog.
$actionuiclass = $format->get_output_classname('courseupdate');
/** @var core_courseformat\output\local\courseupdate $actionui */
$actionui = new $actionuiclass($format, $currenturl, $returnurl);
if (
!$confirm
&& $actionui->is_confirmation_required($action)
) {
/** @var \core_course_renderer $renderer */
$renderer = $format->get_renderer($PAGE);
echo $renderer->header();
echo $actionui->get_confirmation_dialog(
output: $renderer,
course: $course,
action: $action,
ids: $ids,
targetsectionid: $targetsectionid,
targetcmid: $targetcmid,
);
echo $renderer->footer();
die;
}
$updates = $format->get_stateupdates_instance();
$actions = $format->get_stateactions_instance();
if (!is_callable([$actions, $action])) {
throw new moodle_exception("Invalid course state action $action in ".get_class($actions));
}
// Execute the action.
$actions->$action($updates, $course, $ids, $targetsectionid, $targetcmid);
// Any state action mark the state cache as dirty.
course_format::session_cache_reset($course);
redirect($returnurl);

View File

@ -63,7 +63,7 @@ Feature: Sections can be edited and deleted in weekly sections format
Scenario: Deleting the last section in weeks format
Given I should see "29 May - 4 June" in the "29 May - 4 June" "section"
When I delete section "5"
Then I should see "Are you absolutely sure you want to completely delete \"29 May - 4 June\" and all the activities it contains?"
Then I should see "This will delete 29 May - 4 June and all the activities it contains."
And I press "Delete"
And I should not see "29 May - 4 June"
And I should see "22 May - 28 May"

View File

@ -29,20 +29,20 @@ require_once("lib.php");
$sectionreturn = optional_param('sr', null, PARAM_INT);
$add = optional_param('add', '', PARAM_ALPHANUM);
$type = optional_param('type', '', PARAM_ALPHA);
$indent = optional_param('indent', 0, PARAM_INT);
$indent = optional_param('indent', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$update = optional_param('update', 0, PARAM_INT);
$duplicate = optional_param('duplicate', 0, PARAM_INT);
$hide = optional_param('hide', 0, PARAM_INT);
$stealth = optional_param('stealth', 0, PARAM_INT);
$show = optional_param('show', 0, PARAM_INT);
$duplicate = optional_param('duplicate', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$hide = optional_param('hide', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$stealth = optional_param('stealth', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$show = optional_param('show', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$copy = optional_param('copy', 0, PARAM_INT);
$moveto = optional_param('moveto', 0, PARAM_INT);
$movetosection = optional_param('movetosection', 0, PARAM_INT);
$delete = optional_param('delete', 0, PARAM_INT);
$delete = optional_param('delete', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$course = optional_param('course', 0, PARAM_INT);
$groupmode = optional_param('groupmode', -1, PARAM_INT);
$groupmode = optional_param('groupmode', -1, PARAM_INT); // TODO remove this param as part of MDL-83530.
$cancelcopy = optional_param('cancelcopy', 0, PARAM_BOOL);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$confirm = optional_param('confirm', 0, PARAM_BOOL); // TODO remove this param as part of MDL-83530.
// This page should always redirect
$url = new moodle_url('/course/mod.php');
@ -118,6 +118,11 @@ if (!empty($add)) {
)
);
} else if (!empty($duplicate) and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The duplicate param is deprecated. Please use action cm_duplicate in course/format/update.php instead.',
DEBUG_DEVELOPER
);
$cm = get_coursemodule_from_id('', $duplicate, 0, true, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
@ -130,6 +135,11 @@ if (!empty($add)) {
redirect(course_get_url($course, $cm->sectionnum, $urloptions));
} else if (!empty($delete)) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The delete param is deprecated. Please use action cm_delete in course/format/update.php instead.',
DEBUG_DEVELOPER
);
$cm = get_coursemodule_from_id('', $delete, 0, true, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
@ -220,6 +230,11 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
redirect(course_get_url($course, $section->section, $urloptions));
} else if (!empty($indent) and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The indent param deprecated. Please use action cm_moveleft and cm_moveright in course/format/update.php instead.',
DEBUG_DEVELOPER
);
$id = required_param('id', PARAM_INT);
$cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);
@ -245,6 +260,11 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
redirect(course_get_url($course, $cm->sectionnum, $urloptions));
} else if (!empty($hide) and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The hide param deprecated. Please use action cm_hide in course/format/update.php instead.',
DEBUG_DEVELOPER
);
$cm = get_coursemodule_from_id('', $hide, 0, true, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
@ -259,6 +279,11 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
redirect(course_get_url($course, $cm->sectionnum, $urloptions));
} else if (!empty($stealth) and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The stealth param deprecated. Please use action cm_stealth in course/format/update.php instead.',
DEBUG_DEVELOPER
);
list($course, $cm) = get_course_and_cm_from_cmid($stealth);
require_login($course, false, $cm);
require_capability('moodle/course:activityvisibility', $cm->context);
@ -269,6 +294,11 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
} else if (!empty($show) and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The show param deprecated. Please use action cm_show in course/format/update.php instead.',
DEBUG_DEVELOPER
);
list($course, $cm) = get_course_and_cm_from_cmid($show);
require_login($course, false, $cm);
require_capability('moodle/course:activityvisibility', $cm->context);
@ -280,6 +310,11 @@ if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
redirect(course_get_url($course, $section->section, $urloptions));
} else if ($groupmode > -1 and confirm_sesskey()) {
// TODO remove this else if as part of MDL-83530.
debugging(
'The groupmode param deprecated. Please use the group mode actions in course/format/update.php instead.',
DEBUG_DEVELOPER
);
$id = required_param('id', PARAM_INT);
$cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);

View File

@ -1,45 +0,0 @@
<?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/>.
/**
* Duplicates a given course module
*
* The script backups and restores a single activity as if it was imported
* from the same course, using the default import settings. The newly created
* copy of the activity is then moved right below the original one.
*
* @package core
* @subpackage course
* @deprecated Moodle 2.8 MDL-46428 - Now redirects to mod.php.
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../config.php');
$cmid = required_param('cmid', PARAM_INT);
$courseid = required_param('course', PARAM_INT);
$sectionreturn = optional_param('sr', null, PARAM_INT);
require_sesskey();
debugging('Please use moodle_url(\'/course/mod.php\', array(\'duplicate\' => $cmid
, \'id\' => $courseid, \'sesskey\' => sesskey(), \'sr\' => $sectionreturn)))
instead of new moodle_url(\'/course/modduplicate.php\', array(\'cmid\' => $cmid
, \'course\' => $courseid, \'sr\' => $sectionreturn))', DEBUG_DEVELOPER);
redirect(new moodle_url('/course/mod.php', array('duplicate' => $cmid, 'id' => $courseid,
'sesskey' => sesskey(), 'sr' => $sectionreturn)));

View File

@ -1161,7 +1161,7 @@ class behat_course extends behat_base {
]
);
} else {
$this->execute("behat_forms::press_button", get_string('yes'));
$this->execute("behat_forms::press_button", get_string('delete'));
}
return $steps;

View File

@ -31,15 +31,15 @@ redirect_if_major_upgrade_required();
$id = optional_param('id', 0, PARAM_INT);
$name = optional_param('name', '', PARAM_TEXT);
$edit = optional_param('edit', -1, PARAM_BOOL);
$hide = optional_param('hide', 0, PARAM_INT);
$show = optional_param('show', 0, PARAM_INT);
$hide = optional_param('hide', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$show = optional_param('show', 0, PARAM_INT); // TODO remove this param as part of MDL-83530.
$duplicatesection = optional_param('duplicatesection', 0, PARAM_INT);
$idnumber = optional_param('idnumber', '', PARAM_RAW);
$sectionid = optional_param('sectionid', 0, PARAM_INT);
$section = optional_param('section', null, PARAM_INT);
$expandsection = optional_param('expandsection', -1, PARAM_INT);
$move = optional_param('move', 0, PARAM_INT);
$marker = optional_param('marker', -1 , PARAM_INT);
$marker = optional_param('marker', -1 , PARAM_INT); // TODO remove this param as part of MDL-83530.
$switchrole = optional_param('switchrole', -1, PARAM_INT); // Deprecated, use course/switchrole.php instead.
$return = optional_param('return', 0, PARAM_LOCALURL);
@ -193,8 +193,13 @@ if ($PAGE->user_allowed_editing()) {
}
}
// TODO remove this if as part of MDL-83530.
if (has_capability('moodle/course:sectionvisibility', $context)) {
if ($hide && confirm_sesskey()) {
debugging(
'The hide param in course view is deprecated. Please use course/format/update.php instead.',
DEBUG_DEVELOPER
);
set_section_visible($course->id, $hide, '0');
if ($sectionid) {
redirect(course_get_url($course, $section, ['navigation' => true]));
@ -204,6 +209,10 @@ if ($PAGE->user_allowed_editing()) {
}
if ($show && confirm_sesskey()) {
debugging(
'The show param in course view is deprecated. Please use course/format/update.php instead.',
DEBUG_DEVELOPER
);
set_section_visible($course->id, $show, '1');
if ($sectionid) {
redirect(course_get_url($course, $section, ['navigation' => true]));
@ -213,7 +222,12 @@ if ($PAGE->user_allowed_editing()) {
}
}
// TODO remove this if as part of MDL-83530.
if ($marker >= 0 && confirm_sesskey()) {
debugging(
'The marker param in course view is deprecated. Please use course/format/update.php instead.',
DEBUG_DEVELOPER
);
course_set_marker($course->id, $marker);
if ($sectionid) {
redirect(course_get_url($course, $section, ['navigation' => true]));

View File

@ -228,7 +228,7 @@ Feature: Mapping courses in a feedback
And I add the "Feedback" block
And I add the "Main menu" block
And I click on "Delete" "link" in the "Course feedback" activity
And I press "Yes"
And I press "Delete"
And I turn editing mode off
And I am on site homepage
Then "Feedback" "block" should not exist

View File

@ -19,6 +19,7 @@ namespace core_question\output;
use action_link;
use renderer_base;
use core_courseformat\output\local\content\cm\controlmenu;
use core_question\local\bank\question_bank_helper;
/**
* Create a list of question bank type links to manage their respective instances.
@ -52,8 +53,10 @@ class question_bank_list implements \renderable, \templatable {
$banks = [];
foreach ($this->bankinstances as $instance) {
if (plugin_supports('mod', $instance->cminfo->modname, FEATURE_PUBLISHES_QUESTIONS)) {
$returnurl = question_bank_helper::get_url_for_qbank_list($instance->cminfo->course);
$format = course_get_format($instance->cminfo->course);
$controlmenu = new controlmenu($format, $instance->cminfo->get_section_info(), $instance->cminfo);
$controlmenu->set_baseurl($returnurl);
$actions = $controlmenu->get_cm_control_items();
$actionmenu = new \action_menu();
$actionmenu->set_kebab_trigger(get_string('edit'));

View File

@ -53,6 +53,6 @@ Feature: Manage question banks
When I navigate to "Question banks" in current page administration
And I open the action menu in "bank1" "list_item"
And I choose "Delete" in the open action menu
And I click on "Yes" "button" in the "Confirm" "dialogue"
And I click on "Delete" "button"
Then I should not see "bank1"
But I should see "bank2"

View File

@ -26,6 +26,7 @@ Feature: In a report, admin can filter log data by action
# Delete Action.
And I delete "Test assignment 1" activity
And I log out
And I trigger cron
Scenario: View only create actions.
Given I log in as "admin"