MDL-79986 course: Create new course/section.php page
The page course/section.php has been created, to display the content of a
single section. This page will only accept sectionid (to get rid of the
legacy sectionnumber).
Some features of this page:
- The General section won't be displayed anymore above the selected
section.
- In the main course page content, regardless of the Course layout ("All
sections on one page" vs "One section per page"), all the section names
will link to the new page (except when editing mode is on).
This commit is contained in:
@@ -57,7 +57,11 @@ class block_section_links_renderer extends plugin_renderer_base {
|
||||
if ($section->highlight) {
|
||||
$sectiontext = html_writer::tag('strong', $sectiontext);
|
||||
}
|
||||
$html .= html_writer::link(course_get_url($course, $section->section), $sectiontext, $attributes);
|
||||
$html .= html_writer::link(
|
||||
course_get_url($course, $section->section, ['navigation' => true]),
|
||||
$sectiontext,
|
||||
$attributes
|
||||
);
|
||||
$html .= html_writer::end_tag('li').' ';
|
||||
}
|
||||
$html .= html_writer::end_tag('ol');
|
||||
@@ -78,4 +82,4 @@ class block_section_links_renderer extends plugin_renderer_base {
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,15 +711,14 @@ abstract class base {
|
||||
* @param int|stdClass $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 has no separate page, the function returns null
|
||||
* 'sr' (int) used by multipage formats to specify to which section to return
|
||||
* 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page.
|
||||
* 'sr' (int) used by course formats to specify to which section to return
|
||||
* 'expanded' (bool) if true the section will be shown expanded, true by default
|
||||
* @return null|moodle_url
|
||||
*/
|
||||
public function get_view_url($section, $options = array()) {
|
||||
global $CFG;
|
||||
$course = $this->get_course();
|
||||
$url = new moodle_url('/course/view.php', array('id' => $course->id));
|
||||
$url = new moodle_url('/course/view.php', ['id' => $course->id]);
|
||||
|
||||
if (array_key_exists('sr', $options)) {
|
||||
$sectionno = $options['sr'];
|
||||
@@ -728,9 +727,10 @@ abstract class base {
|
||||
} else {
|
||||
$sectionno = $section;
|
||||
}
|
||||
if (empty($CFG->linkcoursesections) && !empty($options['navigation']) && $sectionno !== null) {
|
||||
// By default assume that sections are never displayed on separate pages.
|
||||
return null;
|
||||
if ((!empty($options['navigation']) || array_key_exists('sr', $options)) && $sectionno !== null) {
|
||||
// Display section on separate page.
|
||||
$sectioninfo = $this->get_section($sectionno);
|
||||
return new moodle_url('/course/section.php', ['id' => $sectioninfo->id]);
|
||||
}
|
||||
if ($this->uses_sections() && $sectionno !== null) {
|
||||
// The url includes the parameter to expand the section by default.
|
||||
@@ -743,6 +743,7 @@ abstract class base {
|
||||
}
|
||||
$url->set_anchor('section-'.$sectionno);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
@@ -1915,4 +1916,13 @@ abstract class base {
|
||||
|
||||
return get_fast_modinfo($course)->get_section_info_by_id($newsection->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required javascript files for the course format.
|
||||
*
|
||||
* @return array The list of javascript files required by the course format.
|
||||
*/
|
||||
public function get_required_jsfiles(): array {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,8 @@ class content implements named_templatable, renderable {
|
||||
global $PAGE;
|
||||
$format = $this->format;
|
||||
|
||||
// Most formats uses section 0 as a separate section so we remove from the list.
|
||||
$sections = $this->export_sections($output);
|
||||
$initialsection = '';
|
||||
if (!empty($sections)) {
|
||||
$initialsection = array_shift($sections);
|
||||
}
|
||||
|
||||
$data = (object)[
|
||||
'title' => $format->page_title(), // This method should be in the course_format class.
|
||||
@@ -188,7 +184,6 @@ class content implements named_templatable, renderable {
|
||||
$singlesection = $this->format->get_section_number();
|
||||
if ($singlesection) {
|
||||
return [
|
||||
$modinfo->get_section_info(0),
|
||||
$modinfo->get_section_info($singlesection),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ class section implements named_templatable, renderable {
|
||||
'summary' => $summary->export_for_template($output),
|
||||
'highlightedlabel' => $format->get_section_highlighted_name(),
|
||||
'sitehome' => $course->id == SITEID,
|
||||
'editing' => $PAGE->user_is_editing()
|
||||
'editing' => $PAGE->user_is_editing(),
|
||||
'displayonesection' => ($course->id != SITEID && $format->get_section_number() !== 0),
|
||||
];
|
||||
|
||||
$haspartials = [];
|
||||
|
||||
@@ -297,9 +297,8 @@ class controlmenu implements named_templatable, renderable {
|
||||
], $coursecontext)
|
||||
) {
|
||||
$sectionlink = new moodle_url(
|
||||
'/course/view.php',
|
||||
['id' => $course->id],
|
||||
"sectionid-{$section->id}-title"
|
||||
'/course/section.php',
|
||||
['id' => $section->id]
|
||||
);
|
||||
$controls['permalink'] = [
|
||||
'url' => $sectionlink,
|
||||
|
||||
@@ -76,14 +76,28 @@ class header implements named_templatable, renderable {
|
||||
'id' => $section->id,
|
||||
];
|
||||
|
||||
$data->title = $output->section_title_without_link($section, $course);
|
||||
$data->editing = $format->show_editor();
|
||||
|
||||
if ($course->id == SITEID) {
|
||||
$data->title = $output->section_title_without_link($section, $course);
|
||||
$data->sitehome = true;
|
||||
} else {
|
||||
if ($format->get_section_number() === 0) {
|
||||
// All sections are displayed.
|
||||
if (!$data->editing) {
|
||||
$data->title = $output->section_title($section, $course);
|
||||
} else {
|
||||
$data->title = $output->section_title_without_link($section, $course);
|
||||
}
|
||||
} else {
|
||||
// Only one section is displayed.
|
||||
$data->displayonesection = true;
|
||||
$data->title = $output->section_title_without_link($section, $course);
|
||||
}
|
||||
}
|
||||
|
||||
$coursedisplay = $format->get_course_display();
|
||||
$data->headerdisplaymultipage = false;
|
||||
if ($coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
|
||||
$data->headerdisplaymultipage = true;
|
||||
$data->title = $output->section_title($section, $course);
|
||||
}
|
||||
$data->headerdisplaymultipage = ($coursedisplay == COURSE_DISPLAY_MULTIPAGE);
|
||||
|
||||
if ($section->section > $format->get_last_section_number()) {
|
||||
// Stealth sections (orphaned) has special title.
|
||||
@@ -94,16 +108,8 @@ class header implements named_templatable, renderable {
|
||||
$data->ishidden = true;
|
||||
}
|
||||
|
||||
if ($course->id == SITEID) {
|
||||
$data->sitehome = true;
|
||||
}
|
||||
|
||||
$data->editing = $format->show_editor();
|
||||
|
||||
if (!$format->show_editor() && $coursedisplay == COURSE_DISPLAY_MULTIPAGE && empty($data->issinglesection)) {
|
||||
if ($section->uservisible) {
|
||||
$data->url = course_get_url($course, $section->section);
|
||||
}
|
||||
if (!$data->editing && $section->uservisible) {
|
||||
$data->url = course_get_url($course, $section->section, ['navigation' => true]);
|
||||
}
|
||||
$data->name = get_section_name($course, $section);
|
||||
$data->selecttext = $format->get_format_string('selectsection', $data->name);
|
||||
|
||||
@@ -100,7 +100,7 @@ class sectionnavigation implements named_templatable, renderable {
|
||||
$data->previoushidden = true;
|
||||
}
|
||||
$data->previousname = get_section_name($course, $sections[$back]);
|
||||
$data->previousurl = course_get_url($course, $back);
|
||||
$data->previousurl = course_get_url($course, $back, ['navigation' => true]);
|
||||
$data->hasprevious = true;
|
||||
}
|
||||
$back--;
|
||||
@@ -114,7 +114,7 @@ class sectionnavigation implements named_templatable, renderable {
|
||||
$data->nexthidden = true;
|
||||
}
|
||||
$data->nextname = get_section_name($course, $sections[$forward]);
|
||||
$data->nexturl = course_get_url($course, $forward);
|
||||
$data->nexturl = course_get_url($course, $forward, ['navigation' => true]);
|
||||
$data->hasnext = true;
|
||||
}
|
||||
$forward++;
|
||||
|
||||
@@ -83,7 +83,7 @@ class section implements renderable {
|
||||
'rawtitle' => $section->name,
|
||||
'cmlist' => [],
|
||||
'visible' => !empty($section->visible),
|
||||
'sectionurl' => course_get_url($course, $section->section)->out(),
|
||||
'sectionurl' => course_get_url($course, $section->section, ['navigation' => true])->out(),
|
||||
'current' => $format->is_section_current($section),
|
||||
'indexcollapsed' => $indexcollapsed,
|
||||
'contentcollapsed' => $contentcollapsed,
|
||||
|
||||
@@ -45,22 +45,12 @@ class format_singleactivity extends core_courseformat\base {
|
||||
* @param int|stdClass $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 has no separate page, the function returns null
|
||||
* 'sr' (int) used by multipage formats to specify to which section to return
|
||||
* 'navigation' (bool) ignored by this format
|
||||
* 'sr' (int) ignored by this format
|
||||
* @return null|moodle_url
|
||||
*/
|
||||
public function get_view_url($section, $options = array()) {
|
||||
$sectionnum = $section;
|
||||
if (is_object($sectionnum)) {
|
||||
$sectionnum = $section->section;
|
||||
}
|
||||
if ($sectionnum == 1) {
|
||||
return new moodle_url('/course/view.php', array('id' => $this->courseid, 'section' => 1));
|
||||
}
|
||||
if (!empty($options['navigation']) && $section !== null) {
|
||||
return null;
|
||||
}
|
||||
return new moodle_url('/course/view.php', array('id' => $this->courseid));
|
||||
return new moodle_url('/course/view.php', ['id' => $this->courseid]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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 format_singleactivity;
|
||||
|
||||
/**
|
||||
* Single activity course format related unit tests.
|
||||
*
|
||||
* @package format_singleactivity
|
||||
* @copyright 2023 Sara Arjona <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \format_singleactivity
|
||||
*/
|
||||
class format_singleactivity_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test for get_view_url().
|
||||
*
|
||||
* @covers ::get_view_url
|
||||
*/
|
||||
public function test_get_view_url(): void {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Generate a course with two sections (0 and 1) and two modules.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course1 = $generator->create_course(['format' => 'singleactivity']);
|
||||
course_create_sections_if_missing($course1, [0, 1]);
|
||||
|
||||
$data = (object)['id' => $course1->id];
|
||||
$format = course_get_format($course1);
|
||||
$format->update_course_format_options($data);
|
||||
|
||||
// In page.
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
|
||||
// Navigation.
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['sr' => 0]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['sr' => 0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_required_jsfiles().
|
||||
*
|
||||
* @covers ::get_required_jsfiles
|
||||
*/
|
||||
public function test_get_required_jsfiles(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(['format' => 'singleactivity']);
|
||||
$format = course_get_format($course);
|
||||
$this->assertEmpty($format->get_required_jsfiles());
|
||||
}
|
||||
}
|
||||
@@ -41,15 +41,12 @@ class format_social extends core_courseformat\base {
|
||||
* @param int|stdClass $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 has no separate page, the function returns null
|
||||
* 'sr' (int) used by multipage formats to specify to which section to return
|
||||
* 'navigation' (bool) ignored by this format
|
||||
* 'sr' (int) ignored by this format
|
||||
* @return null|moodle_url
|
||||
*/
|
||||
public function get_view_url($section, $options = array()) {
|
||||
if (!empty($options['navigation']) && $section !== null) {
|
||||
return null;
|
||||
}
|
||||
return new moodle_url('/course/view.php', array('id' => $this->courseid));
|
||||
return new moodle_url('/course/view.php', ['id' => $this->courseid]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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 format_social;
|
||||
|
||||
/**
|
||||
* Social course format related unit tests.
|
||||
*
|
||||
* @package format_social
|
||||
* @copyright 2023 Sara Arjona <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \format_social
|
||||
*/
|
||||
class format_social_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test for get_view_url().
|
||||
*
|
||||
* @covers ::get_view_url
|
||||
*/
|
||||
public function test_get_view_url(): void {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Generate a course with two sections (0 and 1) and two modules.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course1 = $generator->create_course(['format' => 'social']);
|
||||
course_create_sections_if_missing($course1, [0, 1]);
|
||||
|
||||
$data = (object)['id' => $course1->id];
|
||||
$format = course_get_format($course1);
|
||||
$format->update_course_format_options($data);
|
||||
|
||||
// In page.
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
|
||||
// Navigation.
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0, ['sr' => 0]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1, ['sr' => 0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_required_jsfiles().
|
||||
*
|
||||
* @covers ::get_required_jsfiles
|
||||
*/
|
||||
public function test_get_required_jsfiles(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(['format' => 'social']);
|
||||
$format = course_get_format($course);
|
||||
$this->assertEmpty($format->get_required_jsfiles());
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@
|
||||
{{/header}}
|
||||
</div>
|
||||
<div id="coursecontentcollapse{{num}}"
|
||||
class="content {{^iscoursedisplaymultipage}}{{^sitehome}}course-content-item-content collapse {{^contentcollapsed}}show{{/contentcollapsed}}{{/sitehome}}{{/iscoursedisplaymultipage}}">
|
||||
class="content {{^iscoursedisplaymultipage}}{{^sitehome}}{{^displayonesection}}course-content-item-content collapse {{^contentcollapsed}}show{{/contentcollapsed}}{{/displayonesection}}{{/sitehome}}{{/iscoursedisplaymultipage}}">
|
||||
<div class="{{#hasavailability}}description{{/hasavailability}} my-3" data-for="sectioninfo">
|
||||
{{#summary}}
|
||||
{{$ core_courseformat/local/content/section/summary }}
|
||||
|
||||
@@ -47,30 +47,36 @@
|
||||
</h2>
|
||||
{{/sitehome}}
|
||||
{{^sitehome}}
|
||||
<div class="d-flex align-items-start position-relative">
|
||||
<a role="button"
|
||||
data-toggle="collapse"
|
||||
data-for="sectiontoggler"
|
||||
href="#coursecontentcollapse{{num}}"
|
||||
id="collapssesection{{num}}"
|
||||
aria-expanded="{{^contentcollapsed}}true{{/contentcollapsed}}{{#contentcollapsed}}false{{/contentcollapsed}}"
|
||||
aria-controls="coursecontentcollapse{{num}}"
|
||||
class="btn btn-icon mr-1 icons-collapse-expand justify-content-center
|
||||
{{^editing}} stretched-link {{/editing}}
|
||||
{{#contentcollapsed}} collapsed {{/contentcollapsed}}"
|
||||
aria-label="{{name}}">
|
||||
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, core {{/str}}">
|
||||
{{#pix}} t/expandedchevron, core {{/pix}}
|
||||
</span>
|
||||
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, core {{/str}}">
|
||||
<span class="dir-rtl-hide">{{#pix}} t/collapsedchevron, core {{/pix}}</span>
|
||||
<span class="dir-ltr-hide">{{#pix}} t/collapsedchevron_rtl, core {{/pix}}</span>
|
||||
</span>
|
||||
</a>
|
||||
<h3 class="sectionname course-content-item d-flex align-self-stretch align-items-center mb-0"
|
||||
id="sectionid-{{id}}-title" data-for="section_title" data-id="{{id}}" data-number="{{num}}">
|
||||
{{#displayonesection}}
|
||||
<h3 id="sectionid-{{id}}-title" class="sectionname">
|
||||
{{{title}}}
|
||||
</h3>
|
||||
</div>
|
||||
{{/displayonesection}}
|
||||
{{^displayonesection}}
|
||||
<div class="d-flex align-items-start position-relative">
|
||||
<a role="button"
|
||||
data-toggle="collapse"
|
||||
data-for="sectiontoggler"
|
||||
href="#coursecontentcollapse{{num}}"
|
||||
id="collapssesection{{num}}"
|
||||
aria-expanded="{{^contentcollapsed}}true{{/contentcollapsed}}{{#contentcollapsed}}false{{/contentcollapsed}}"
|
||||
aria-controls="coursecontentcollapse{{num}}"
|
||||
class="btn btn-icon mr-1 icons-collapse-expand justify-content-center
|
||||
{{#contentcollapsed}} collapsed {{/contentcollapsed}}"
|
||||
aria-label="{{name}}">
|
||||
<span class="expanded-icon icon-no-margin p-2" title="{{#str}} collapse, core {{/str}}">
|
||||
{{#pix}} t/expandedchevron, core {{/pix}}
|
||||
</span>
|
||||
<span class="collapsed-icon icon-no-margin p-2" title="{{#str}} expand, core {{/str}}">
|
||||
<span class="dir-rtl-hide">{{#pix}} t/collapsedchevron, core {{/pix}}</span>
|
||||
<span class="dir-ltr-hide">{{#pix}} t/collapsedchevron_rtl, core {{/pix}}</span>
|
||||
</span>
|
||||
</a>
|
||||
<h3 class="sectionname course-content-item d-flex align-self-stretch align-items-center mb-0"
|
||||
id="sectionid-{{id}}-title" data-for="section_title" data-id="{{id}}" data-number="{{num}}">
|
||||
{{{title}}}
|
||||
</h3>
|
||||
</div>
|
||||
{{/displayonesection}}
|
||||
{{/sitehome}}
|
||||
{{/headerdisplaymultipage}}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
* @package core_course
|
||||
* @copyright 2014 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \core_courseformat\base
|
||||
* @coversDefaultClass \core_courseformat\base
|
||||
*/
|
||||
class base_test extends advanced_testcase {
|
||||
@@ -199,14 +200,14 @@ class base_test extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_view_url() to ensure that the url is only given for the correct cases
|
||||
* Test for get_view_url().
|
||||
*
|
||||
* @covers ::get_view_url
|
||||
*/
|
||||
public function test_get_view_url() {
|
||||
public function test_get_view_url(): void {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$linkcoursesections = $CFG->linkcoursesections;
|
||||
|
||||
// Generate a course with two sections (0 and 1) and two modules. Course format is set to 'testformat'.
|
||||
// This will allow us to test the default implementation of get_view_url.
|
||||
$generator = $this->getDataGenerator();
|
||||
@@ -218,22 +219,20 @@ class base_test extends advanced_testcase {
|
||||
$format->update_course_format_options($data);
|
||||
|
||||
// In page.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
|
||||
// Navigation.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNull($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNull($format->get_view_url(0, ['navigation' => 1]));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['navigation' => 1]));
|
||||
// When sr parameter is defined, the section.php page should be returned.
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 0]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 0]));
|
||||
|
||||
// Expand section.
|
||||
// The current course format $format uses the format 'testformat' which does not use sections.
|
||||
@@ -756,6 +755,21 @@ class base_test extends advanced_testcase {
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_required_jsfiles().
|
||||
*
|
||||
* @covers ::get_required_jsfiles
|
||||
*/
|
||||
public function test_get_required_jsfiles(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(['format' => 'testformat']);
|
||||
$format = course_get_format($course);
|
||||
$this->assertEmpty($format->get_required_jsfiles());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@core @core_courseformat @core_course @show_editor @javascript
|
||||
Feature: Bulk course section actions one section per page.
|
||||
Feature: Bulk course section actions one section per page
|
||||
In order to edit the course section in one section per page setting
|
||||
As a teacher
|
||||
I need to be able to edit sections in bulk in both display modes.
|
||||
@@ -33,8 +33,10 @@ Feature: Bulk course section actions one section per page.
|
||||
Scenario: Bulk section edit is only available when multiple sections are displayed
|
||||
Given I click on "Select topic Topic 1" "checkbox"
|
||||
And I should see "1 selected" in the "sticky-footer" "region"
|
||||
And I click on "Close bulk actions" "button" in the "sticky-footer" "region"
|
||||
# Move to single topic page.
|
||||
When I click on "Topic 1" "link" in the "region-main" "region"
|
||||
And I open section "1" edit menu
|
||||
When I click on "View" "link" in the "Topic 1" "section"
|
||||
And I click on "Bulk actions" "button"
|
||||
Then "Select topic Topic 1" "checkbox" should not exist
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@core @core_courseformat @show_editor
|
||||
Feature: Verify edit utils availability.
|
||||
Feature: Verify edit utils availability
|
||||
In order to edit the course activities
|
||||
As a student with capability 'moodle/course:manageactivities'
|
||||
I need to be able to use the edit utils.
|
||||
@@ -49,16 +49,17 @@ Feature: Verify edit utils availability.
|
||||
Then I should not see "Edit mode"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit tools should be available to students with the capability 'moodle/course:manageactivities',
|
||||
but should not be allowed to add and edit sections without having 'moodle/course:update'
|
||||
Scenario: Edit tools should be available to students with manageactivities capability but not allowed to add sections without course:update
|
||||
Given I log in as "author1"
|
||||
When I am on "Course 1" course homepage
|
||||
And I turn editing mode on
|
||||
Then I should see "Add an activity or resource"
|
||||
And I should not see "Add topic"
|
||||
But I should not see "Add topic"
|
||||
And I open "Activity sample 1" actions menu
|
||||
And I should see "Edit settings"
|
||||
And ".section_action_menu" "css_element" should not exist in the "Topic 1" "section"
|
||||
And I open section "1" edit menu
|
||||
And I should not see "Edit settings"
|
||||
And I should see "View"
|
||||
|
||||
@javascript
|
||||
Scenario: Section adding should be available to students if they also have the capability 'moodle/course:update'.
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
@core @core_courseformat
|
||||
Feature: Single section course page
|
||||
In order to improve the course page
|
||||
As a user
|
||||
I need to be able to see a section in a single page
|
||||
|
||||
Background:
|
||||
Given the following "course" exists:
|
||||
| fullname | Course 1 |
|
||||
| shortname | C1 |
|
||||
| category | 0 |
|
||||
| numsections | 3 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | idnumber | section |
|
||||
| assign | Activity sample 0.1 | C1 | sample1 | 0 |
|
||||
| assign | Activity sample 1.1 | C1 | sample1 | 1 |
|
||||
| assign | Activity sample 1.2 | C1 | sample2 | 1 |
|
||||
| assign | Activity sample 1.3 | C1 | sample3 | 1 |
|
||||
| assign | Activity sample 2.1 | C1 | sample3 | 2 |
|
||||
| assign | Activity sample 2.2 | C1 | sample3 | 2 |
|
||||
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 |
|
||||
Given I am on the "C1" "Course" page logged in as "teacher1"
|
||||
|
||||
@javascript
|
||||
Scenario: Collapsed sections are always expanded in the single section page
|
||||
Given I press "Collapse all"
|
||||
And I should not see "Activity sample 1.1" in the "region-main" "region"
|
||||
When I click on "Topic 1" "link" in the "region-main" "region"
|
||||
Then I should see "Activity sample 1.1"
|
||||
And I should see "Activity sample 1.2"
|
||||
And I should see "Activity sample 1.3"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
|
||||
Scenario: General section is not displayed in the single section page
|
||||
When I click on "Topic 1" "link" in the "region-main" "region"
|
||||
Then I should not see "General" in the "region-main" "region"
|
||||
And I should not see "Activity sample 0.1" in the "region-main" "region"
|
||||
And I should see "Activity sample 1.1"
|
||||
And I should see "Activity sample 1.2"
|
||||
And I should see "Activity sample 1.3"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: The view action for sections displays the single section page
|
||||
Given I turn editing mode on
|
||||
And I open section "1" edit menu
|
||||
When I click on "View" "link" in the "Topic 1" "section"
|
||||
Then I should not see "General" in the "region-main" "region"
|
||||
And I should not see "Activity sample 0.1" in the "region-main" "region"
|
||||
And I should see "Activity sample 1.1"
|
||||
And I should see "Activity sample 1.2"
|
||||
And I should see "Activity sample 1.3"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I should not see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I am on "Course 1" course homepage
|
||||
And I open section "2" edit menu
|
||||
And I click on "View" "link" in the "Topic 2" "section"
|
||||
And I should not see "General" in the "region-main" "region"
|
||||
And I should not see "Activity sample 0.1" in the "region-main" "region"
|
||||
And I should not see "Activity sample 1.1"
|
||||
And I should not see "Activity sample 1.2"
|
||||
And I should not see "Activity sample 1.3"
|
||||
And I should see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I should see "Activity sample 2.1" in the "region-main" "region"
|
||||
# The following steps will need to be changed in MDL-80248, when the General section will be displayed in isolation.
|
||||
But I am on "Course 1" course homepage
|
||||
And I open section "0" edit menu
|
||||
And I click on "View" "link" in the "General" "section"
|
||||
And I should see "General" in the "region-main" "region"
|
||||
And I should see "Activity sample 0.1" in the "region-main" "region"
|
||||
And I should see "Activity sample 1.1"
|
||||
And I should see "Activity sample 1.2"
|
||||
And I should see "Activity sample 1.3"
|
||||
And I should see "Activity sample 2.1" in the "region-main" "region"
|
||||
And I should see "Activity sample 2.1" in the "region-main" "region"
|
||||
@@ -108,45 +108,26 @@ class format_topics extends core_courseformat\base {
|
||||
* @param int|stdClass $section Section object from database or just field course_sections.section
|
||||
* if omitted the course view page is returned
|
||||
* @param array $options options for view URL. At the moment core uses:
|
||||
* 'navigation' (bool) if true and section has no separate page, the function returns null
|
||||
* 'sr' (int) used by multipage formats to specify to which section to return
|
||||
* 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page.
|
||||
* 'sr' (int) used by course formats to specify to which section to return
|
||||
* @return null|moodle_url
|
||||
*/
|
||||
public function get_view_url($section, $options = []) {
|
||||
global $CFG;
|
||||
$course = $this->get_course();
|
||||
$url = new moodle_url('/course/view.php', ['id' => $course->id]);
|
||||
|
||||
$sr = null;
|
||||
if (array_key_exists('sr', $options)) {
|
||||
$sr = $options['sr'];
|
||||
}
|
||||
if (is_object($section)) {
|
||||
$sectionno = $options['sr'];
|
||||
} else if (is_object($section)) {
|
||||
$sectionno = $section->section;
|
||||
} else {
|
||||
$sectionno = $section;
|
||||
}
|
||||
if ($sectionno !== null) {
|
||||
if ($sr !== null) {
|
||||
if ($sr) {
|
||||
$usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
|
||||
$sectionno = $sr;
|
||||
} else {
|
||||
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
|
||||
}
|
||||
} else {
|
||||
$usercoursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
|
||||
}
|
||||
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
|
||||
$url->param('section', $sectionno);
|
||||
} else {
|
||||
if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) {
|
||||
return null;
|
||||
}
|
||||
$url->set_anchor('section-'.$sectionno);
|
||||
}
|
||||
if ((!empty($options['navigation']) || array_key_exists('sr', $options)) && $sectionno !== null) {
|
||||
// Display section on separate page.
|
||||
$sectioninfo = $this->get_section($sectionno);
|
||||
return new moodle_url('/course/section.php', ['id' => $sectioninfo->id]);
|
||||
}
|
||||
return $url;
|
||||
|
||||
return new moodle_url('/course/view.php', ['id' => $course->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -457,6 +438,15 @@ class format_topics extends core_courseformat\base {
|
||||
$formatoptions['indentation'] = get_config('format_topics', 'indentation');
|
||||
return $formatoptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required javascript files for the course format.
|
||||
*
|
||||
* @return array The list of javascript files required by the course format.
|
||||
*/
|
||||
public function get_required_jsfiles(): array {
|
||||
return ['/course/format/topics/format.js'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ require_once($CFG->dirroot . '/course/lib.php');
|
||||
* @package format_topics
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \format_topics
|
||||
*/
|
||||
class format_topics_test extends \advanced_testcase {
|
||||
|
||||
@@ -232,16 +233,14 @@ class format_topics_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_view_url() to ensure that the url is only given for the correct cases.
|
||||
* Test for get_view_url().
|
||||
*
|
||||
* @return void
|
||||
* @covers ::get_view_url
|
||||
*/
|
||||
public function test_get_view_url() {
|
||||
public function test_get_view_url(): void {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$linkcoursesections = $CFG->linkcoursesections;
|
||||
|
||||
// Generate a course with two sections (0 and 1) and two modules.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course1 = $generator->create_course(['format' => 'topics']);
|
||||
@@ -252,21 +251,34 @@ class format_topics_test extends \advanced_testcase {
|
||||
$format->update_course_format_options($data);
|
||||
|
||||
// In page.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
|
||||
// Navigation.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNull($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNull($format->get_view_url(0, ['navigation' => 1]));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['navigation' => 1]));
|
||||
// When sr parameter is defined, the section.php page should be returned.
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 0]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_required_jsfiles().
|
||||
*
|
||||
* @covers ::get_required_jsfiles
|
||||
*/
|
||||
public function test_get_required_jsfiles(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(['format' => 'topics']);
|
||||
$format = course_get_format($course);
|
||||
$this->assertNotEmpty($format->get_required_jsfiles());
|
||||
}
|
||||
}
|
||||
|
||||
+19
-29
@@ -118,45 +118,26 @@ class format_weeks extends core_courseformat\base {
|
||||
* @param int|stdClass $section Section object from database or just field course_sections.section
|
||||
* if omitted the course view page is returned
|
||||
* @param array $options options for view URL. At the moment core uses:
|
||||
* 'navigation' (bool) if true and section has no separate page, the function returns null
|
||||
* 'sr' (int) used by multipage formats to specify to which section to return
|
||||
* 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page.
|
||||
* 'sr' (int) used by course formats to specify to which section to return
|
||||
* @return null|moodle_url
|
||||
*/
|
||||
public function get_view_url($section, $options = array()) {
|
||||
global $CFG;
|
||||
$course = $this->get_course();
|
||||
$url = new moodle_url('/course/view.php', array('id' => $course->id));
|
||||
|
||||
$sr = null;
|
||||
if (array_key_exists('sr', $options)) {
|
||||
$sr = $options['sr'];
|
||||
}
|
||||
if (is_object($section)) {
|
||||
$sectionno = $options['sr'];
|
||||
} else if (is_object($section)) {
|
||||
$sectionno = $section->section;
|
||||
} else {
|
||||
$sectionno = $section;
|
||||
}
|
||||
if ($sectionno !== null) {
|
||||
if ($sr !== null) {
|
||||
if ($sr) {
|
||||
$usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
|
||||
$sectionno = $sr;
|
||||
} else {
|
||||
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
|
||||
}
|
||||
} else {
|
||||
$usercoursedisplay = $course->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE;
|
||||
}
|
||||
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
|
||||
$url->param('section', $sectionno);
|
||||
} else {
|
||||
if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) {
|
||||
return null;
|
||||
}
|
||||
$url->set_anchor('section-'.$sectionno);
|
||||
}
|
||||
if ((!empty($options['navigation']) || array_key_exists('sr', $options)) && $sectionno !== null) {
|
||||
// Display section on separate page.
|
||||
$sectioninfo = $this->get_section($sectionno);
|
||||
return new moodle_url('/course/section.php', ['id' => $sectioninfo->id]);
|
||||
}
|
||||
return $url;
|
||||
|
||||
return new moodle_url('/course/view.php', ['id' => $course->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,6 +612,15 @@ class format_weeks extends core_courseformat\base {
|
||||
$formatoptions['indentation'] = get_config('format_weeks', 'indentation');
|
||||
return $formatoptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required javascript files for the course format.
|
||||
*
|
||||
* @return array The list of javascript files required by the course format.
|
||||
*/
|
||||
public function get_required_jsfiles(): array {
|
||||
return ['/course/format/weeks/format.js'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ require_once($CFG->dirroot . '/course/lib.php');
|
||||
* @package format_weeks
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \format_weeks
|
||||
*/
|
||||
class format_weeks_test extends \advanced_testcase {
|
||||
|
||||
@@ -229,14 +230,14 @@ class format_weeks_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for get_view_url() to ensure that the url is only given for the correct cases
|
||||
* Test for get_view_url().
|
||||
*
|
||||
* @covers ::get_view_url
|
||||
*/
|
||||
public function test_get_view_url() {
|
||||
public function test_get_view_url(): void {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$linkcoursesections = $CFG->linkcoursesections;
|
||||
|
||||
// Generate a course with two sections (0 and 1) and two modules.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course1 = $generator->create_course(array('format' => 'weeks'));
|
||||
@@ -247,22 +248,34 @@ class format_weeks_test extends \advanced_testcase {
|
||||
$format->update_course_format_options($data);
|
||||
|
||||
// In page.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(null));
|
||||
$this->assertNotEmpty($format->get_view_url(0));
|
||||
$this->assertNotEmpty($format->get_view_url(1));
|
||||
|
||||
// Navigation.
|
||||
$CFG->linkcoursesections = 0;
|
||||
$this->assertNull($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNull($format->get_view_url(0, ['navigation' => 1]));
|
||||
$CFG->linkcoursesections = 1;
|
||||
$this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1]));
|
||||
$this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(0));
|
||||
$this->assertStringContainsString('course/view.php', $format->get_view_url(1));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['navigation' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['navigation' => 1]));
|
||||
// When sr parameter is defined, the section.php page should be returned.
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 1]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 0]));
|
||||
$this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_required_jsfiles().
|
||||
*
|
||||
* @covers ::get_required_jsfiles
|
||||
*/
|
||||
public function test_get_required_jsfiles(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
$course = $generator->create_course(['format' => 'weeks']);
|
||||
$format = course_get_format($course);
|
||||
$this->assertNotEmpty($format->get_required_jsfiles());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Display a course section.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2023 Sara Arjona <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once('lib.php');
|
||||
require_once($CFG->libdir.'/completionlib.php');
|
||||
|
||||
redirect_if_major_upgrade_required();
|
||||
|
||||
$sectionid = required_param('id', PARAM_INT);
|
||||
// This parameter is used by the classic theme to force editing on.
|
||||
$edit = optional_param('edit', -1, PARAM_BOOL);
|
||||
|
||||
$section = $DB->get_record('course_sections', ['id' => $sectionid], '*', MUST_EXIST);
|
||||
|
||||
// Defined here to avoid notices on errors.
|
||||
$PAGE->set_url('/course/section.php', ['id' => $sectionid]);
|
||||
|
||||
if ($section->course == SITEID) {
|
||||
// The home page is not a real course.
|
||||
redirect($CFG->wwwroot .'/?redirect=0');
|
||||
}
|
||||
|
||||
$course = get_course($section->course);
|
||||
// Fix course format if it is no longer installed.
|
||||
$format = course_get_format($course);
|
||||
$course->format = $format->get_format();
|
||||
|
||||
// When the course format doesn't support sections, redirect to course page.
|
||||
if (!course_format_uses_sections($course->format)) {
|
||||
redirect(new moodle_url('/course/view.php', ['id' => $course->id]));
|
||||
}
|
||||
|
||||
// Prevent caching of this page to stop confusion when changing page after making AJAX changes.
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
context_helper::preload_course($course->id);
|
||||
$context = context_course::instance($course->id, MUST_EXIST);
|
||||
|
||||
require_login($course);
|
||||
|
||||
// Must set layout before getting section info. See MDL-47555.
|
||||
$PAGE->set_pagelayout('course');
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
// Get section details and check it exists.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$coursesections = $modinfo->get_section_info($section->section, MUST_EXIST);
|
||||
|
||||
// Check user is allowed to see it.
|
||||
if (!$coursesections->uservisible) {
|
||||
// Check if coursesection has conditions affecting availability and if
|
||||
// so, output availability info.
|
||||
if ($coursesections->visible && $coursesections->availableinfo) {
|
||||
$sectionname = get_section_name($course, $coursesections);
|
||||
$message = get_string('notavailablecourse', '', $sectionname);
|
||||
redirect(course_get_url($course), $message, null, \core\output\notification::NOTIFY_ERROR);
|
||||
} else {
|
||||
// Note: We actually already know they don't have this capability
|
||||
// or uservisible would have been true; this is just to get the
|
||||
// correct error message shown.
|
||||
require_capability('moodle/course:viewhiddensections', $context);
|
||||
}
|
||||
}
|
||||
|
||||
$PAGE->set_pagetype('course-view-' . $course->format);
|
||||
$PAGE->set_other_editing_capability('moodle/course:update');
|
||||
$PAGE->set_other_editing_capability('moodle/course:manageactivities');
|
||||
$PAGE->set_other_editing_capability('moodle/course:activityvisibility');
|
||||
$PAGE->set_other_editing_capability('moodle/course:sectionvisibility');
|
||||
$PAGE->set_other_editing_capability('moodle/course:movesections');
|
||||
|
||||
$renderer = $PAGE->get_renderer('format_' . $course->format);
|
||||
|
||||
// This is used by the Classic theme to change the editing mode based on the 'edit' parameter value.
|
||||
if (!isset($USER->editing)) {
|
||||
$USER->editing = 0;
|
||||
}
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
if (($edit == 1) && confirm_sesskey()) {
|
||||
$USER->editing = 1;
|
||||
$url = new moodle_url($PAGE->url, ['notifyeditingon' => 1]);
|
||||
redirect($url);
|
||||
} else if (($edit == 0) && confirm_sesskey()) {
|
||||
$USER->editing = 0;
|
||||
if (!empty($USER->activitycopy) && $USER->activitycopycourse == $course->id) {
|
||||
$USER->activitycopy = false;
|
||||
$USER->activitycopycourse = null;
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
}
|
||||
|
||||
// This is used by the Classic theme, to display the Turn editing on/off button.
|
||||
// We are currently keeping the button here from 1.x to help new teachers figure out what to do, even though the link also appears
|
||||
// in the course admin block. It also means you can back out of a situation where you removed the admin block.
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
$buttons = $OUTPUT->edit_button($PAGE->url);
|
||||
$PAGE->set_button($buttons);
|
||||
}
|
||||
|
||||
// Make the title more specific when editing, for accessibility reasons.
|
||||
$editingtitle = '';
|
||||
if ($PAGE->user_is_editing()) {
|
||||
$editingtitle = 'editing';
|
||||
}
|
||||
$sectionname = get_string('sectionname', "format_$course->format");
|
||||
$sectiontitle = get_section_name($course, $section);
|
||||
$PAGE->set_title(
|
||||
get_string(
|
||||
'coursesectiontitle' . $editingtitle,
|
||||
'moodle',
|
||||
['course' => $course->fullname, 'sectiontitle' => $sectiontitle, 'sectionname' => $sectionname]
|
||||
)
|
||||
);
|
||||
|
||||
// Add bulk editing control.
|
||||
$bulkbutton = $renderer->bulk_editing_button($format);
|
||||
if (!empty($bulkbutton)) {
|
||||
$PAGE->add_header_action($bulkbutton);
|
||||
}
|
||||
|
||||
$PAGE->set_heading($course->fullname);
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// Show communication room status notification.
|
||||
if (core_communication\api::is_available() && has_capability('moodle/course:update', $context)) {
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
$context,
|
||||
'core_course',
|
||||
'coursecommunication',
|
||||
$course->id
|
||||
);
|
||||
$communication->show_communication_room_status_notification();
|
||||
}
|
||||
|
||||
// Display a warning if asynchronous backups are pending for this course.
|
||||
if ($PAGE->user_is_editing()) {
|
||||
require_once($CFG->dirroot . '/backup/util/helper/async_helper.class.php');
|
||||
if (async_helper::is_async_pending($course->id, 'course', 'backup')) {
|
||||
echo $OUTPUT->notification(get_string('pendingasyncedit', 'backup'), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
echo $renderer->container_start('course-content');
|
||||
|
||||
// Include course AJAX.
|
||||
include_course_ajax($course, $modinfo->get_used_module_names());
|
||||
|
||||
$format->set_section_number($section->section);
|
||||
$outputclass = $format->get_output_classname('content');
|
||||
$widget = new $outputclass($format);
|
||||
echo $renderer->render($widget);
|
||||
|
||||
// Include course format javascript files.
|
||||
$jsfiles = $format->get_required_jsfiles();
|
||||
foreach ($jsfiles as $jsfile) {
|
||||
$PAGE->requires->js($jsfile);
|
||||
}
|
||||
|
||||
echo $renderer->container_end();
|
||||
|
||||
// Trigger course viewed event.
|
||||
course_view($context, $section->section);
|
||||
|
||||
// Load the view JS module if completion tracking is enabled for this course.
|
||||
$completion = new completion_info($course);
|
||||
if ($completion->is_enabled()) {
|
||||
$PAGE->requires->js_call_amd('core_course/view', 'init');
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -31,7 +31,7 @@ Feature: Delete activity and resource works correctly
|
||||
And I open "Glossary 1" actions menu
|
||||
And I click on "Delete" "link" in the "Glossary 1" activity
|
||||
And I click on "Delete" "button" in the "Delete activity?" "dialogue"
|
||||
# Confirm that glossary is successfully deleted
|
||||
# Confirm that glossary is successfully deleted.
|
||||
And I should not see "Glossary 1"
|
||||
# Reload the page and confirm that both the label and glossary are really deleted
|
||||
And I reload the page
|
||||
|
||||
@@ -16,7 +16,7 @@ Feature: Course activity controls works as expected
|
||||
# * Course controls with paged mode in a section's page
|
||||
|
||||
@javascript @_cross_browser
|
||||
Scenario Outline: General activities course controls using topics and weeks formats, and paged mode and not paged mode works as expected
|
||||
Scenario Outline: Check activities using topics and weeks formats, and paged mode and not paged mode
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
@@ -30,15 +30,14 @@ Feature: Course activity controls works as expected
|
||||
| activity | course | section | name |
|
||||
| forum | C1 | 1 | Test forum name 1 |
|
||||
| forum | C1 | 1 | Test forum name 2 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I am on the "Course 1" course page logged in as "teacher1"
|
||||
When I click on <targetpage> "link" in the "region-main" "region"
|
||||
And I turn editing mode on
|
||||
And I add the "Recent activity" block
|
||||
And I open the action menu in "Recent activity" "block"
|
||||
And I click on "Delete Recent activity block" "link"
|
||||
And I click on "Delete" "button" in the "Delete block?" "dialogue"
|
||||
And <belowpage> "section" <should_see_other_sections> exist
|
||||
And <belowpage> "section" <should_see_other_sections> exist
|
||||
And I open "Test forum name 1" actions menu
|
||||
And I click on "Edit settings" "link" in the "Test forum name 1" activity
|
||||
And I should see "Updating Forum"
|
||||
@@ -76,14 +75,16 @@ Feature: Course activity controls works as expected
|
||||
|
||||
Examples:
|
||||
| courseformat | coursedisplay | targetpage | should_see_other_sections | should_see_other_sections_following_block_sections_links | belowpage |
|
||||
| topics | 0 | "General" | should | should | "Topic 2" |
|
||||
| topics | 1 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| topics | 0 | "General" | should | should not | "Topic 2" |
|
||||
| topics | 1 | "General" | should | should not | "Topic 2" |
|
||||
| weeks | 0 | "General" | should | should | "8 January - 14 January" |
|
||||
| weeks | 1 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
| topics | 0 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| topics | 1 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| weeks | 0 | "General" | should | should not | "8 January - 14 January" |
|
||||
| weeks | 1 | "General" | should | should not | "8 January - 14 January" |
|
||||
| weeks | 0 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
| weeks | 1 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
|
||||
Scenario Outline: General activities course controls using topics and weeks formats, and paged mode and not paged mode works as expected without javascript
|
||||
Scenario Outline: Check, without javascript, activities using topics and weeks formats, and paged mode and not paged mode
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
@@ -97,9 +98,9 @@ Feature: Course activity controls works as expected
|
||||
| activity | name | course | idnumber | section |
|
||||
| forum | Test forum name 1 | C1 | 0001 | 1 |
|
||||
| forum | Test forum name 2 | C1 | 0002 | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I am on the "Course 1" course page logged in as "teacher1"
|
||||
When I click on <targetpage> "link" in the "region-main" "region"
|
||||
And I turn editing mode on
|
||||
And I add the "Recent activity" block
|
||||
And I open the action menu in "Recent activity" "block"
|
||||
And I click on "Delete Recent activity block" "link"
|
||||
@@ -121,28 +122,27 @@ Feature: Course activity controls works as expected
|
||||
And I should see "Test forum name 2"
|
||||
And I should see "Edited test forum name 2"
|
||||
And I hide section "1"
|
||||
And <belowpage> "section" <should_see_other_sections> exist
|
||||
And section "1" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And I show section "1"
|
||||
And <belowpage> "section" <should_see_other_sections> exist
|
||||
And section "1" should be visible
|
||||
And the following config values are set as admin:
|
||||
| unaddableblocks | | theme_boost|
|
||||
And I add the "Section links" block
|
||||
And <belowpage> "section" <should_see_other_sections> exist
|
||||
And I should see "1 2 3 4 5" in the "Section links" "block"
|
||||
And I click on "2" "link" in the "Section links" "block"
|
||||
And I <should_see_other_sections_following_block_sections_links> see "Test forum name 2"
|
||||
|
||||
Examples:
|
||||
| courseformat | coursedisplay | targetpage | should_see_other_sections | should_see_other_sections_following_block_sections_links | belowpage |
|
||||
| topics | 0 | "General" | should | should | "Topic 2" |
|
||||
| topics | 1 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| topics | 0 | "General" | should | should not | "Topic 2" |
|
||||
| topics | 1 | "General" | should | should not | "Topic 2" |
|
||||
| weeks | 0 | "General" | should | should | "8 January - 14 January" |
|
||||
| weeks | 1 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
| topics | 0 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| topics | 1 | "Topic 1" | should not | should not | "Topic 2" |
|
||||
| weeks | 0 | "General" | should | should not | "8 January - 14 January" |
|
||||
| weeks | 1 | "General" | should | should not | "8 January - 14 January" |
|
||||
| weeks | 0 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
| weeks | 1 | "1 January - 7 January" | should not | should not | "8 January - 14 January" |
|
||||
|
||||
@javascript
|
||||
Scenario Outline: Indentation should allow one level only
|
||||
|
||||
@@ -17,17 +17,19 @@ Feature: Sections can be moved
|
||||
And the following "activities" exist:
|
||||
| activity | name | course | idnumber | section |
|
||||
| forum | Test forum name | C1 | forum1 | 1 |
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
|
||||
Scenario: Move up and down a section with Javascript disabled in a single page course
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I move down section "1"
|
||||
Then I should see "Test forum name" in the "Topic 2" "section"
|
||||
And I move up section "2"
|
||||
And I should see "Test forum name" in the "Topic 1" "section"
|
||||
|
||||
Scenario: Move up and down a section with Javascript disabled in the course home of a course using paged mode
|
||||
Given I navigate to "Settings" in current page administration
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Course layout | Show one section per page |
|
||||
And I press "Save and display"
|
||||
@@ -37,11 +39,13 @@ Feature: Sections can be moved
|
||||
And I should see "Test forum name" in the "Topic 1" "section"
|
||||
|
||||
Scenario: Sections can not be moved with Javascript disabled in a section page of a course using paged mode
|
||||
Given I navigate to "Settings" in current page administration
|
||||
Given I am on the "Course 1" course page logged in as "teacher1"
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Course layout | Show one section per page |
|
||||
And I press "Save and display"
|
||||
When I follow "Topic 2"
|
||||
When I click on "Topic 2" "link" in the "region-main" "region"
|
||||
And I turn editing mode on
|
||||
Then "Topic 1" "section" should not exist
|
||||
And "Topic 3" "section" should not exist
|
||||
And "Move down" "link" should not exist
|
||||
@@ -49,6 +53,8 @@ Feature: Sections can be moved
|
||||
|
||||
@javascript
|
||||
Scenario: Move section with javascript
|
||||
Given I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
When I open section "1" edit menu
|
||||
And I click on "Move" "link" in the "Topic 1" "section"
|
||||
And I click on "Topic 3" "link" in the ".modal-body" "css_element"
|
||||
|
||||
@@ -48,7 +48,8 @@ Feature: Course paged mode
|
||||
| chat | C1 | Chat room |
|
||||
When I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I click on <section1> "link" in the <section1> "section"
|
||||
And I open section <sectionnumber1> edit menu
|
||||
And I click on "View" "link" in the <section1> "section"
|
||||
And I should see <section1> in the "div.single-section" "css_element"
|
||||
And I should see <section2> in the ".single-section div.nextsection" "css_element"
|
||||
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
|
||||
@@ -57,9 +58,9 @@ Feature: Course paged mode
|
||||
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
|
||||
|
||||
Examples:
|
||||
| courseformat | section1 | section2 | prevunexistingsection |
|
||||
| topics | "Topic 1" | "Topic 2" | "Topic 0" |
|
||||
| weeks | "1 January - 7 January" | "8 January - 14 January" | "25 December - 31 December" |
|
||||
| courseformat | section1 | sectionnumber1 | section2 | prevunexistingsection |
|
||||
| topics | "Topic 1" | "1" | "Topic 2" | "Topic 0" |
|
||||
| weeks | "1 January - 7 January" | "1" | "8 January - 14 January" | "25 December - 31 December" |
|
||||
|
||||
Scenario Outline: Weekly and topics course formats with Javascript disabled
|
||||
Given the following "courses" exist:
|
||||
|
||||
Reference in New Issue
Block a user