MDL-62748 course: maxsections is a limit
[moodle.git] / course / format / renderer.php
CommitLineData
1804b7c1
DP
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Base renderer for outputting course formats.
19 *
20 * @package core
21 * @copyright 2012 Dan Poltawski
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 * @since Moodle 2.3
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28
29/**
a8f02a34 30 * This is a convenience renderer which can be used by section based formats
3df72f8c 31 * to reduce code duplication. It is not necessary for all course formats to
a8f02a34 32 * use this and its likely to change in future releases.
1804b7c1
DP
33 *
34 * @package core
35 * @copyright 2012 Dan Poltawski
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 * @since Moodle 2.3
38 */
a8f02a34 39abstract class format_section_renderer_base extends plugin_renderer_base {
1804b7c1 40
8341055e 41 /** @var core_course_renderer contains instance of core course renderer */
9a36be73
MG
42 protected $courserenderer;
43
44 /**
45 * Constructor method, calls the parent constructor
46 *
47 * @param moodle_page $page
48 * @param string $target one of rendering target constants
49 */
50 public function __construct(moodle_page $page, $target) {
51 parent::__construct($page, $target);
52 $this->courserenderer = $this->page->get_renderer('core', 'course');
53 }
54
1804b7c1
DP
55 /**
56 * Generate the starting container html for a list of sections
57 * @return string HTML to output.
58 */
cbf44997 59 abstract protected function start_section_list();
1804b7c1
DP
60
61 /**
62 * Generate the closing container html for a list of sections
63 * @return string HTML to output.
64 */
cbf44997 65 abstract protected function end_section_list();
1804b7c1
DP
66
67 /**
68 * Generate the title for this section page
69 * @return string the page title
70 */
cbf44997 71 abstract protected function page_title();
1804b7c1 72
9f3015ec 73 /**
f26481c2 74 * Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page
9f3015ec
RK
75 *
76 * @param stdClass $section The course_section entry from DB
77 * @param stdClass $course The course entry from DB
78 * @return string HTML to output.
79 */
80 public function section_title($section, $course) {
81 $title = get_section_name($course, $section);
923451c5
MG
82 $url = course_get_url($course, $section->section, array('navigation' => true));
83 if ($url) {
84 $title = html_writer::link($url, $title);
9f3015ec
RK
85 }
86 return $title;
87 }
88
f26481c2
MG
89 /**
90 * Generate the section title to be displayed on the section page, without a link
91 *
92 * @param stdClass $section The course_section entry from DB
93 * @param stdClass $course The course entry from DB
94 * @return string HTML to output.
95 */
96 public function section_title_without_link($section, $course) {
97 return get_section_name($course, $section);
98 }
99
60cf0742
S
100 /**
101 * Generate the edit control action menu
102 *
103 * @param array $controls The edit control items from section_edit_control_items
104 * @param stdClass $course The course entry from DB
105 * @param stdClass $section The course_section entry from DB
106 * @return string HTML to output.
107 */
108 protected function section_edit_control_menu($controls, $course, $section) {
109 $o = "";
110 if (!empty($controls)) {
111 $menu = new action_menu();
d76acdbc 112 $menu->set_menu_trigger(get_string('edit'));
60cf0742
S
113 $menu->attributes['class'] .= ' section-actions';
114 foreach ($controls as $value) {
115 $url = empty($value['url']) ? '' : $value['url'];
116 $icon = empty($value['icon']) ? '' : $value['icon'];
117 $name = empty($value['name']) ? '' : $value['name'];
c47b6f97 118 $attr = empty($value['attr']) ? array() : $value['attr'];
8857c715
DW
119 $class = empty($value['pixattr']['class']) ? '' : $value['pixattr']['class'];
120 $alt = empty($value['pixattr']['alt']) ? '' : $value['pixattr']['alt'];
60cf0742
S
121 $al = new action_menu_link_secondary(
122 new moodle_url($url),
8857c715 123 new pix_icon($icon, $alt, null, array('class' => "smallicon " . $class)),
60cf0742
S
124 $name,
125 $attr
126 );
127 $menu->add($al);
128 }
129
8341055e
MG
130 $o .= html_writer::div($this->render($menu), 'section_action_menu',
131 array('data-sectionid' => $section->id));
60cf0742
S
132 }
133
134 return $o;
135 }
136
a830d866
DP
137 /**
138 * Generate the content to displayed on the right part of a section
a830d866 139 * before course modules are included
96e12851 140 *
a830d866
DP
141 * @param stdClass $section The course_section entry from DB
142 * @param stdClass $course The course entry from DB
143 * @param bool $onsectionpage true if being printed on a section page
144 * @return string HTML to output.
145 */
cbf44997 146 protected function section_right_content($section, $course, $onsectionpage) {
a830d866
DP
147 $o = $this->output->spacer();
148
60cf0742
S
149 $controls = $this->section_edit_control_items($course, $section, $onsectionpage);
150 $o .= $this->section_edit_control_menu($controls, $course, $section);
a830d866
DP
151
152 return $o;
153 }
154
155 /**
156 * Generate the content to displayed on the left part of a section
a830d866 157 * before course modules are included
96e12851 158 *
a830d866
DP
159 * @param stdClass $section The course_section entry from DB
160 * @param stdClass $course The course entry from DB
161 * @param bool $onsectionpage true if being printed on a section page
162 * @return string HTML to output.
163 */
cbf44997 164 protected function section_left_content($section, $course, $onsectionpage) {
b9b409cf 165 $o = '';
a830d866
DP
166
167 if ($section->section != 0) {
168 // Only in the non-general sections.
081c8f7f 169 if (course_get_format($course)->is_section_current($section)) {
a830d866
DP
170 $o = get_accesshide(get_string('currentsection', 'format_'.$course->format));
171 }
172 }
173
174 return $o;
175 }
176
1804b7c1
DP
177 /**
178 * Generate the display of the header part of a section before
179 * course modules are included
180 *
181 * @param stdClass $section The course_section entry from DB
182 * @param stdClass $course The course entry from DB
9f3015ec 183 * @param bool $onsectionpage true if being printed on a single-section page
b8514b6a 184 * @param int $sectionreturn The section to return to after an action
1804b7c1
DP
185 * @return string HTML to output.
186 */
923451c5 187 protected function section_header($section, $course, $onsectionpage, $sectionreturn=null) {
1804b7c1
DP
188 global $PAGE;
189
190 $o = '';
191 $currenttext = '';
192 $sectionstyle = '';
1804b7c1 193
96e12851 194 if ($section->section != 0) {
1804b7c1
DP
195 // Only in the non-general sections.
196 if (!$section->visible) {
197 $sectionstyle = ' hidden';
8341055e
MG
198 }
199 if (course_get_format($course)->is_section_current($section)) {
1804b7c1 200 $sectionstyle = ' current';
1804b7c1 201 }
1804b7c1
DP
202 }
203
204 $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
9f66c7c8
JM
205 'class' => 'section main clearfix'.$sectionstyle, 'role'=>'region',
206 'aria-label'=> get_section_name($course, $section)));
a830d866 207
60cf0742 208 // Create a span that contains the section title to be used to create the keyboard section move menu.
f26481c2 209 $o .= html_writer::tag('span', get_section_name($course, $section), array('class' => 'hidden sectionname'));
60cf0742 210
a830d866 211 $leftcontent = $this->section_left_content($section, $course, $onsectionpage);
1804b7c1 212 $o.= html_writer::tag('div', $leftcontent, array('class' => 'left side'));
a830d866
DP
213
214 $rightcontent = $this->section_right_content($section, $course, $onsectionpage);
1804b7c1
DP
215 $o.= html_writer::tag('div', $rightcontent, array('class' => 'right side'));
216 $o.= html_writer::start_tag('div', array('class' => 'content'));
217
5946d376
FM
218 // When not on a section page, we display the section titles except the general section if null
219 $hasnamenotsecpg = (!$onsectionpage && ($section->section != 0 || !is_null($section->name)));
220
221 // When on a section page, we only display the general section title, if title is not the default one
222 $hasnamesecpg = ($onsectionpage && ($section->section == 0 && !is_null($section->name)));
223
bbb483b2 224 $classes = ' accesshide';
5946d376 225 if ($hasnamenotsecpg || $hasnamesecpg) {
bbb483b2 226 $classes = '';
1804b7c1 227 }
c73fd11e
S
228 $sectionname = html_writer::tag('span', $this->section_title($section, $course));
229 $o.= $this->output->heading($sectionname, 3, 'sectionname' . $classes);
1804b7c1 230
8341055e 231 $o .= $this->section_availability($section);
ce4dfd27 232
084c2ef1 233 $o .= html_writer::start_tag('div', array('class' => 'summary'));
3b29978c
MG
234 if ($section->uservisible || $section->visible) {
235 // Show summary if section is available or has availability restriction information.
236 // Do not show summary if section is hidden but we still display it because of course setting
237 // "Hidden sections are shown in collapsed form".
238 $o .= $this->format_summary_text($section);
239 }
084c2ef1
MG
240 $o .= html_writer::end_tag('div');
241
1804b7c1
DP
242 return $o;
243 }
244
245 /**
246 * Generate the display of the footer part of a section
247 *
248 * @return string HTML to output.
249 */
cbf44997 250 protected function section_footer() {
1804b7c1
DP
251 $o = html_writer::end_tag('div');
252 $o.= html_writer::end_tag('li');
253
254 return $o;
255 }
256
257 /**
258 * Generate the edit controls of a section
259 *
260 * @param stdClass $course The course entry from DB
261 * @param stdClass $section The course_section entry from DB
262 * @param bool $onsectionpage true if being printed on a section page
263 * @return array of links with edit controls
60cf0742
S
264 * @deprecated since Moodle 3.0 MDL-48947 - please do not use this function any more.
265 * @see format_section_renderer_base::section_edit_control_items()
1804b7c1 266 */
cbf44997 267 protected function section_edit_controls($course, $section, $onsectionpage = false) {
1804b7c1
DP
268 global $PAGE;
269
270 if (!$PAGE->user_is_editing()) {
271 return array();
272 }
273
60cf0742
S
274 $controls = array();
275 $items = $this->section_edit_control_items($course, $section, $onsectionpage);
276
277 foreach ($items as $key => $item) {
278 $url = empty($item['url']) ? '' : $item['url'];
279 $icon = empty($item['icon']) ? '' : $item['icon'];
280 $name = empty($item['name']) ? '' : $item['name'];
281 $attr = empty($item['attr']) ? '' : $item['attr'];
282 $class = empty($item['pixattr']['class']) ? '' : $item['pixattr']['class'];
283 $alt = empty($item['pixattr']['alt']) ? '' : $item['pixattr']['alt'];
284 $controls[$key] = html_writer::link(
663640f5 285 new moodle_url($url), $this->output->pix_icon($icon, $alt),
60cf0742
S
286 $attr);
287 }
288
289 debugging('section_edit_controls() is deprecated, please use section_edit_control_items() instead.', DEBUG_DEVELOPER);
290 return $controls;
291 }
292
293 /**
294 * Generate the edit control items of a section
295 *
296 * @param stdClass $course The course entry from DB
297 * @param stdClass $section The course_section entry from DB
298 * @param bool $onsectionpage true if being printed on a section page
299 * @return array of edit control items
300 */
301 protected function section_edit_control_items($course, $section, $onsectionpage = false) {
302 global $PAGE;
303
304 if (!$PAGE->user_is_editing()) {
305 return array();
306 }
307
8341055e
MG
308 $sectionreturn = $onsectionpage ? $section->section : null;
309
643b1de8 310 $coursecontext = context_course::instance($course->id);
89b909f6
MG
311 $numsections = course_get_format($course)->get_last_section_number();
312 $isstealth = $section->section > $numsections;
1804b7c1 313
8341055e 314 $baseurl = course_get_url($course, $sectionreturn);
1804b7c1
DP
315 $baseurl->param('sesskey', sesskey());
316
317 $controls = array();
318
60cf0742
S
319 if (!$isstealth && has_capability('moodle/course:update', $coursecontext)) {
320 if ($section->section > 0
321 && get_string_manager()->string_exists('editsection', 'format_'.$course->format)) {
322 $streditsection = get_string('editsection', 'format_'.$course->format);
643b1de8 323 } else {
60cf0742 324 $streditsection = get_string('editsection');
643b1de8 325 }
1804b7c1 326
60cf0742 327 $controls['edit'] = array(
6d1ee59a 328 'url' => new moodle_url('/course/editsection.php', array('id' => $section->id, 'sr' => $sectionreturn)),
60cf0742
S
329 'icon' => 'i/settings',
330 'name' => $streditsection,
331 'pixattr' => array('class' => '', 'alt' => $streditsection),
332 'attr' => array('class' => 'icon edit', 'title' => $streditsection));
ca9cae84
MG
333 }
334
60cf0742 335 if ($section->section) {
1804b7c1 336 $url = clone($baseurl);
60cf0742
S
337 if (!$isstealth) {
338 if (has_capability('moodle/course:sectionvisibility', $coursecontext)) {
339 if ($section->visible) { // Show the hide/show eye.
340 $strhidefromothers = get_string('hidefromothers', 'format_'.$course->format);
341 $url->param('hide', $section->section);
342 $controls['visiblity'] = array(
343 'url' => $url,
344 'icon' => 'i/hide',
345 'name' => $strhidefromothers,
346 'pixattr' => array('class' => '', 'alt' => $strhidefromothers),
8341055e
MG
347 'attr' => array('class' => 'icon editing_showhide', 'title' => $strhidefromothers,
348 'data-sectionreturn' => $sectionreturn, 'data-action' => 'hide'));
60cf0742
S
349 } else {
350 $strshowfromothers = get_string('showfromothers', 'format_'.$course->format);
351 $url->param('show', $section->section);
352 $controls['visiblity'] = array(
353 'url' => $url,
354 'icon' => 'i/show',
355 'name' => $strshowfromothers,
356 'pixattr' => array('class' => '', 'alt' => $strshowfromothers),
8341055e
MG
357 'attr' => array('class' => 'icon editing_showhide', 'title' => $strshowfromothers,
358 'data-sectionreturn' => $sectionreturn, 'data-action' => 'show'));
60cf0742
S
359 }
360 }
361
362 if (!$onsectionpage) {
363 if (has_capability('moodle/course:movesections', $coursecontext)) {
364 $url = clone($baseurl);
365 if ($section->section > 1) { // Add a arrow to move section up.
366 $url->param('section', $section->section);
367 $url->param('move', -1);
368 $strmoveup = get_string('moveup');
369 $controls['moveup'] = array(
370 'url' => $url,
371 'icon' => 'i/up',
372 'name' => $strmoveup,
373 'pixattr' => array('class' => '', 'alt' => $strmoveup),
374 'attr' => array('class' => 'icon moveup', 'title' => $strmoveup));
375 }
376
377 $url = clone($baseurl);
89b909f6 378 if ($section->section < $numsections) { // Add a arrow to move section down.
60cf0742
S
379 $url->param('section', $section->section);
380 $url->param('move', 1);
381 $strmovedown = get_string('movedown');
382 $controls['movedown'] = array(
383 'url' => $url,
384 'icon' => 'i/down',
385 'name' => $strmovedown,
386 'pixattr' => array('class' => '', 'alt' => $strmovedown),
387 'attr' => array('class' => 'icon movedown', 'title' => $strmovedown));
388 }
389 }
390 }
1804b7c1
DP
391 }
392
60cf0742
S
393 if (course_can_delete_section($course, $section)) {
394 if (get_string_manager()->string_exists('deletesection', 'format_'.$course->format)) {
395 $strdelete = get_string('deletesection', 'format_'.$course->format);
396 } else {
397 $strdelete = get_string('deletesection');
398 }
399 $url = new moodle_url('/course/editsection.php', array(
400 'id' => $section->id,
8341055e 401 'sr' => $sectionreturn,
88a7f775
MG
402 'delete' => 1,
403 'sesskey' => sesskey()));
60cf0742
S
404 $controls['delete'] = array(
405 'url' => $url,
406 'icon' => 'i/delete',
407 'name' => $strdelete,
408 'pixattr' => array('class' => '', 'alt' => $strdelete),
8341055e 409 'attr' => array('class' => 'icon editing_delete', 'title' => $strdelete));
1804b7c1
DP
410 }
411 }
412
413 return $controls;
414 }
415
416 /**
a91437a3 417 * Generate a summary of a section for display on the 'course index page'
1804b7c1
DP
418 *
419 * @param stdClass $section The course_section entry from DB
420 * @param stdClass $course The course entry from DB
eda43c7d 421 * @param array $mods (argument not used)
1804b7c1
DP
422 * @return string HTML to output.
423 */
d87debaa 424 protected function section_summary($section, $course, $mods) {
76055f5d
FM
425 $classattr = 'section main section-summary clearfix';
426 $linkclasses = '';
427
bcf7175f 428 // If section is hidden then display grey section link
76055f5d
FM
429 if (!$section->visible) {
430 $classattr .= ' hidden';
431 $linkclasses .= ' dimmed_text';
081c8f7f 432 } else if (course_get_format($course)->is_section_current($section)) {
76055f5d 433 $classattr .= ' current';
bcf7175f 434 }
1804b7c1 435
9f66c7c8 436 $title = get_section_name($course, $section);
1804b7c1 437 $o = '';
9f66c7c8
JM
438 $o .= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
439 'class' => $classattr, 'role'=>'region', 'aria-label'=> $title));
1804b7c1 440
76055f5d
FM
441 $o .= html_writer::tag('div', '', array('class' => 'left side'));
442 $o .= html_writer::tag('div', '', array('class' => 'right side'));
443 $o .= html_writer::start_tag('div', array('class' => 'content'));
444
bd43383d 445 if ($section->uservisible) {
e0735653 446 $title = html_writer::tag('a', $title,
bd43383d 447 array('href' => course_get_url($course, $section->section), 'class' => $linkclasses));
bd43383d 448 }
e0735653 449 $o .= $this->output->heading($title, 3, 'section-title');
1804b7c1 450
398ed469 451 $o .= $this->section_availability($section);
1804b7c1 452 $o.= html_writer::start_tag('div', array('class' => 'summarytext'));
398ed469 453
3b29978c
MG
454 if ($section->uservisible || $section->visible) {
455 // Show summary if section is available or has availability restriction information.
456 // Do not show summary if section is hidden but we still display it because of course setting
457 // "Hidden sections are shown in collapsed form".
458 $o .= $this->format_summary_text($section);
459 }
1804b7c1 460 $o.= html_writer::end_tag('div');
eda43c7d 461 $o.= $this->section_activity_summary($section, $course, null);
ce4dfd27 462
76055f5d
FM
463 $o .= html_writer::end_tag('div');
464 $o .= html_writer::end_tag('li');
1804b7c1
DP
465
466 return $o;
467 }
468
d87debaa
DP
469 /**
470 * Generate a summary of the activites in a section
471 *
472 * @param stdClass $section The course_section entry from DB
7c05cd0e 473 * @param stdClass $course the course record from DB
eda43c7d 474 * @param array $mods (argument not used)
d87debaa
DP
475 * @return string HTML to output.
476 */
3f435f89 477 protected function section_activity_summary($section, $course, $mods) {
eda43c7d
MG
478 $modinfo = get_fast_modinfo($course);
479 if (empty($modinfo->sections[$section->section])) {
d87debaa
DP
480 return '';
481 }
482
483 // Generate array with count of activities in this section:
484 $sectionmods = array();
7c05cd0e
AA
485 $total = 0;
486 $complete = 0;
0e5533b6
SH
487 $cancomplete = isloggedin() && !isguestuser();
488 $completioninfo = new completion_info($course);
eda43c7d
MG
489 foreach ($modinfo->sections[$section->section] as $cmid) {
490 $thismod = $modinfo->cms[$cmid];
d87debaa 491
00c41b40
DP
492 if ($thismod->modname == 'label') {
493 // Labels are special (not interesting for students)!
494 continue;
495 }
496
d87debaa
DP
497 if ($thismod->uservisible) {
498 if (isset($sectionmods[$thismod->modname])) {
eda43c7d 499 $sectionmods[$thismod->modname]['name'] = $thismod->modplural;
d87debaa
DP
500 $sectionmods[$thismod->modname]['count']++;
501 } else {
eda43c7d 502 $sectionmods[$thismod->modname]['name'] = $thismod->modfullname;
d87debaa
DP
503 $sectionmods[$thismod->modname]['count'] = 1;
504 }
0e5533b6 505 if ($cancomplete && $completioninfo->is_enabled($thismod) != COMPLETION_TRACKING_NONE) {
7c05cd0e 506 $total++;
0e5533b6 507 $completiondata = $completioninfo->get_data($thismod, true);
93c9a86d
MG
508 if ($completiondata->completionstate == COMPLETION_COMPLETE ||
509 $completiondata->completionstate == COMPLETION_COMPLETE_PASS) {
7c05cd0e
AA
510 $complete++;
511 }
512 }
d87debaa
DP
513 }
514 }
515
516 if (empty($sectionmods)) {
517 // No sections
518 return '';
519 }
520
521 // Output section activities summary:
522 $o = '';
523 $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
524 foreach ($sectionmods as $mod) {
525 $o.= html_writer::start_tag('span', array('class' => 'activity-count'));
526 $o.= $mod['name'].': '.$mod['count'];
527 $o.= html_writer::end_tag('span');
528 }
dce49c1c 529 $o.= html_writer::end_tag('div');
7c05cd0e
AA
530
531 // Output section completion data
0e5533b6
SH
532 if ($total > 0) {
533 $a = new stdClass;
534 $a->complete = $complete;
535 $a->total = $total;
536
dce49c1c 537 $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
0e5533b6 538 $o.= html_writer::tag('span', get_string('progresstotal', 'completion', $a), array('class' => 'activity-count'));
dce49c1c 539 $o.= html_writer::end_tag('div');
7c05cd0e 540 }
dce49c1c 541
d87debaa
DP
542 return $o;
543 }
544
ce4dfd27 545 /**
e81da154 546 * If section is not visible, display the message about that ('Not available
547 * until...', that sort of thing). Otherwise, returns blank.
548 *
549 * For users with the ability to view hidden sections, it shows the
550 * information even though you can view the section and also may include
551 * slightly fuller information (so that teachers can tell when sections
552 * are going to be unavailable etc). This logic is the same as for
553 * activities.
ce4dfd27 554 *
8341055e 555 * @param section_info $section The course_section entry from DB
e81da154 556 * @param bool $canviewhidden True if user can view hidden sections
ce4dfd27 557 * @return string HTML to output
558 */
e81da154 559 protected function section_availability_message($section, $canviewhidden) {
560 global $CFG;
ce4dfd27 561 $o = '';
8341055e
MG
562 if (!$section->visible) {
563 if ($canviewhidden) {
564 $o .= $this->courserenderer->availability_info(get_string('hiddenfromstudents'), 'ishidden');
3b29978c
MG
565 } else {
566 // We are here because of the setting "Hidden sections are shown in collapsed form".
567 // Student can not see the section contents but can see its name.
568 $o .= $this->courserenderer->availability_info(get_string('notavailable'), 'ishidden');
8341055e
MG
569 }
570 } else if (!$section->uservisible) {
571 if ($section->availableinfo) {
572 // Note: We only get to this function if availableinfo is non-empty,
573 // so there is definitely something to print.
574 $formattedinfo = \core_availability\info::format_info(
575 $section->availableinfo, $section->course);
7352805d 576 $o .= $this->courserenderer->availability_info($formattedinfo, 'isrestricted');
8341055e
MG
577 }
578 } else if ($canviewhidden && !empty($CFG->enableavailability)) {
579 // Check if there is an availability restriction.
00c832d7 580 $ci = new \core_availability\info_section($section);
e81da154 581 $fullinfo = $ci->get_full_information();
582 if ($fullinfo) {
00c832d7 583 $formattedinfo = \core_availability\info::format_info(
584 $fullinfo, $section->course);
7352805d 585 $o .= $this->courserenderer->availability_info($formattedinfo, 'isrestricted isfullinfo');
e81da154 586 }
ce4dfd27 587 }
588 return $o;
589 }
590
8341055e
MG
591 /**
592 * Displays availability information for the section (hidden, not available unles, etc.)
593 *
594 * @param section_info $section
595 * @return string
596 */
597 public function section_availability($section) {
598 $context = context_course::instance($section->course);
599 $canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
600 return html_writer::div($this->section_availability_message($section, $canviewhidden), 'section_availability');
601 }
602
1804b7c1
DP
603 /**
604 * Show if something is on on the course clipboard (moving around)
605 *
606 * @param stdClass $course The course entry from DB
a91437a3 607 * @param int $sectionno The section number in the course which is being displayed
1804b7c1
DP
608 * @return string HTML to output.
609 */
923451c5 610 protected function course_activity_clipboard($course, $sectionno = null) {
1804b7c1
DP
611 global $USER;
612
613 $o = '';
614 // If currently moving a file then show the current clipboard.
615 if (ismoving($course->id)) {
616 $url = new moodle_url('/course/mod.php',
617 array('sesskey' => sesskey(),
618 'cancelcopy' => true,
619 'sr' => $sectionno,
620 )
621 );
622
76055f5d 623 $o.= html_writer::start_tag('div', array('class' => 'clipboard'));
1804b7c1
DP
624 $o.= strip_tags(get_string('activityclipboard', '', $USER->activitycopyname));
625 $o.= ' ('.html_writer::link($url, get_string('cancel')).')';
76055f5d 626 $o.= html_writer::end_tag('div');
1804b7c1
DP
627 }
628
629 return $o;
630 }
631
632 /**
633 * Generate next/previous section links for naviation
634 *
635 * @param stdClass $course The course entry from DB
636 * @param array $sections The course_sections entries from the DB
a91437a3 637 * @param int $sectionno The section number in the course which is being displayed
36be7e14 638 * @return array associative array with previous and next section link
1804b7c1 639 */
cbf44997 640 protected function get_nav_links($course, $sections, $sectionno) {
1804b7c1 641 // FIXME: This is really evil and should by using the navigation API.
b5cf83f0 642 $course = course_get_format($course)->get_course();
1804b7c1
DP
643 $canviewhidden = has_capability('moodle/course:viewhiddensections', context_course::instance($course->id))
644 or !$course->hiddensections;
645
646 $links = array('previous' => '', 'next' => '');
647 $back = $sectionno - 1;
648 while ($back > 0 and empty($links['previous'])) {
569747fa 649 if ($canviewhidden || $sections[$back]->uservisible) {
bcf7175f
RT
650 $params = array();
651 if (!$sections[$back]->visible) {
652 $params = array('class' => 'dimmed_text');
653 }
36be7e14
RT
654 $previouslink = html_writer::tag('span', $this->output->larrow(), array('class' => 'larrow'));
655 $previouslink .= get_section_name($course, $sections[$back]);
bcf7175f 656 $links['previous'] = html_writer::link(course_get_url($course, $back), $previouslink, $params);
1804b7c1
DP
657 }
658 $back--;
659 }
660
661 $forward = $sectionno + 1;
89b909f6
MG
662 $numsections = course_get_format($course)->get_last_section_number();
663 while ($forward <= $numsections and empty($links['next'])) {
569747fa 664 if ($canviewhidden || $sections[$forward]->uservisible) {
bcf7175f
RT
665 $params = array();
666 if (!$sections[$forward]->visible) {
667 $params = array('class' => 'dimmed_text');
668 }
36be7e14
RT
669 $nextlink = get_section_name($course, $sections[$forward]);
670 $nextlink .= html_writer::tag('span', $this->output->rarrow(), array('class' => 'rarrow'));
bcf7175f 671 $links['next'] = html_writer::link(course_get_url($course, $forward), $nextlink, $params);
1804b7c1
DP
672 }
673 $forward++;
674 }
675
36be7e14 676 return $links;
1804b7c1
DP
677 }
678
679 /**
680 * Generate the header html of a stealth section
681 *
a91437a3 682 * @param int $sectionno The section number in the course which is being displayed
1804b7c1
DP
683 * @return string HTML to output.
684 */
cbf44997 685 protected function stealth_section_header($sectionno) {
1804b7c1
DP
686 $o = '';
687 $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix orphaned hidden'));
688 $o.= html_writer::tag('div', '', array('class' => 'left side'));
ca9cae84
MG
689 $course = course_get_format($this->page->course)->get_course();
690 $section = course_get_format($this->page->course)->get_section($sectionno);
691 $rightcontent = $this->section_right_content($section, $course, false);
692 $o.= html_writer::tag('div', $rightcontent, array('class' => 'right side'));
1804b7c1 693 $o.= html_writer::start_tag('div', array('class' => 'content'));
be99db5b 694 $o.= $this->output->heading(get_string('orphanedactivitiesinsectionno', '', $sectionno), 3, 'sectionname');
1804b7c1
DP
695 return $o;
696 }
697
698 /**
699 * Generate footer html of a stealth section
700 *
701 * @return string HTML to output.
702 */
cbf44997 703 protected function stealth_section_footer() {
1804b7c1
DP
704 $o = html_writer::end_tag('div');
705 $o.= html_writer::end_tag('li');
706 return $o;
707 }
708
709 /**
710 * Generate the html for a hidden section
711 *
a91437a3 712 * @param int $sectionno The section number in the course which is being displayed
0a293483 713 * @param int|stdClass $courseorid The course to get the section name for (object or just course id)
1804b7c1
DP
714 * @return string HTML to output.
715 */
0a293483
SH
716 protected function section_hidden($sectionno, $courseorid = null) {
717 if ($courseorid) {
718 $sectionname = get_section_name($courseorid, $sectionno);
719 $strnotavailable = get_string('notavailablecourse', '', $sectionname);
720 } else {
721 $strnotavailable = get_string('notavailable');
722 }
723
1804b7c1
DP
724 $o = '';
725 $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix hidden'));
726 $o.= html_writer::tag('div', '', array('class' => 'left side'));
727 $o.= html_writer::tag('div', '', array('class' => 'right side'));
728 $o.= html_writer::start_tag('div', array('class' => 'content'));
0a293483 729 $o.= html_writer::tag('div', $strnotavailable);
1804b7c1
DP
730 $o.= html_writer::end_tag('div');
731 $o.= html_writer::end_tag('li');
732 return $o;
733 }
734
d46e26f6
GB
735 /**
736 * Generate the html for the 'Jump to' menu on a single section page.
737 *
738 * @param stdClass $course The course entry from DB
739 * @param array $sections The course_sections entries from the DB
740 * @param $displaysection the current displayed section number.
741 *
742 * @return string HTML to output.
743 */
744 protected function section_nav_selection($course, $sections, $displaysection) {
745 global $CFG;
746 $o = '';
747 $sectionmenu = array();
748 $sectionmenu[course_get_url($course)->out(false)] = get_string('maincoursepage');
749 $modinfo = get_fast_modinfo($course);
750 $section = 1;
89b909f6
MG
751 $numsections = course_get_format($course)->get_last_section_number();
752 while ($section <= $numsections) {
d46e26f6 753 $thissection = $modinfo->get_section_info($section);
dc88f669 754 $showsection = $thissection->uservisible or !$course->hiddensections;
d46e26f6
GB
755 if (($showsection) && ($section != $displaysection) && ($url = course_get_url($course, $section))) {
756 $sectionmenu[$url->out(false)] = get_section_name($course, $section);
757 }
758 $section++;
759 }
760
761 $select = new url_select($sectionmenu, '', array('' => get_string('jumpto')));
762 $select->class = 'jumpmenu';
763 $select->formid = 'sectionmenu';
764 $o .= $this->output->render($select);
765
766 return $o;
767 }
768
1804b7c1
DP
769 /**
770 * Output the html for a single section page .
771 *
772 * @param stdClass $course The course entry from DB
eda43c7d
MG
773 * @param array $sections (argument not used)
774 * @param array $mods (argument not used)
775 * @param array $modnames (argument not used)
776 * @param array $modnamesused (argument not used)
1804b7c1
DP
777 * @param int $displaysection The section number in the course which is being displayed
778 */
779 public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) {
780 global $PAGE;
781
eda43c7d 782 $modinfo = get_fast_modinfo($course);
b5cf83f0 783 $course = course_get_format($course)->get_course();
1804b7c1 784
eda43c7d 785 // Can we view the section in question?
3b29978c
MG
786 if (!($sectioninfo = $modinfo->get_section_info($displaysection)) || !$sectioninfo->uservisible) {
787 // This section doesn't exist or is not available for the user.
788 // We actually already check this in course/view.php but just in case exit from this function as well.
789 print_error('unknowncoursesection', 'error', course_get_url($course),
790 format_string($course->fullname));
1804b7c1
DP
791 }
792
76055f5d
FM
793 // Copy activity clipboard..
794 echo $this->course_activity_clipboard($course, $displaysection);
eda43c7d
MG
795 $thissection = $modinfo->get_section_info(0);
796 if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
243b7003 797 echo $this->start_section_list();
b8514b6a 798 echo $this->section_header($thissection, $course, true, $displaysection);
9a36be73
MG
799 echo $this->courserenderer->course_section_cm_list($course, $thissection, $displaysection);
800 echo $this->courserenderer->course_section_add_cm_control($course, 0, $displaysection);
243b7003
DP
801 echo $this->section_footer();
802 echo $this->end_section_list();
803 }
804
c129f430
RT
805 // Start single-section div
806 echo html_writer::start_tag('div', array('class' => 'single-section'));
807
eda43c7d
MG
808 // The requested section page.
809 $thissection = $modinfo->get_section_info($displaysection);
810
36be7e14 811 // Title with section navigation links.
eda43c7d 812 $sectionnavlinks = $this->get_nav_links($course, $modinfo->get_section_info_all(), $displaysection);
36be7e14 813 $sectiontitle = '';
faf6010b 814 $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation navigationtitle'));
36be7e14
RT
815 $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
816 $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
bcf7175f 817 // Title attributes
2e2d0f75 818 $classes = 'sectionname';
eda43c7d 819 if (!$thissection->visible) {
2e2d0f75 820 $classes .= ' dimmed_text';
bcf7175f 821 }
f26481c2 822 $sectionname = html_writer::tag('span', $this->section_title_without_link($thissection, $course));
c73fd11e 823 $sectiontitle .= $this->output->heading($sectionname, 3, $classes);
2e2d0f75 824
36be7e14
RT
825 $sectiontitle .= html_writer::end_tag('div');
826 echo $sectiontitle;
827
1804b7c1
DP
828 // Now the list of sections..
829 echo $this->start_section_list();
830
b8514b6a 831 echo $this->section_header($thissection, $course, true, $displaysection);
c129f430
RT
832 // Show completion help icon.
833 $completioninfo = new completion_info($course);
834 echo $completioninfo->display_help_icon();
835
9a36be73
MG
836 echo $this->courserenderer->course_section_cm_list($course, $thissection, $displaysection);
837 echo $this->courserenderer->course_section_add_cm_control($course, $displaysection, $displaysection);
1804b7c1 838 echo $this->section_footer();
1804b7c1 839 echo $this->end_section_list();
36be7e14
RT
840
841 // Display section bottom navigation.
36be7e14
RT
842 $sectionbottomnav = '';
843 $sectionbottomnav .= html_writer::start_tag('div', array('class' => 'section-navigation mdl-bottom'));
844 $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
845 $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
d46e26f6
GB
846 $sectionbottomnav .= html_writer::tag('div', $this->section_nav_selection($course, $sections, $displaysection),
847 array('class' => 'mdl-align'));
36be7e14
RT
848 $sectionbottomnav .= html_writer::end_tag('div');
849 echo $sectionbottomnav;
c129f430 850
d46e26f6 851 // Close single-section div.
c129f430 852 echo html_writer::end_tag('div');
1804b7c1
DP
853 }
854
855 /**
856 * Output the html for a multiple section page
857 *
858 * @param stdClass $course The course entry from DB
eda43c7d
MG
859 * @param array $sections (argument not used)
860 * @param array $mods (argument not used)
861 * @param array $modnames (argument not used)
862 * @param array $modnamesused (argument not used)
1804b7c1
DP
863 */
864 public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) {
865 global $PAGE;
866
eda43c7d 867 $modinfo = get_fast_modinfo($course);
b5cf83f0 868 $course = course_get_format($course)->get_course();
eda43c7d 869
1804b7c1
DP
870 $context = context_course::instance($course->id);
871 // Title with completion help icon.
872 $completioninfo = new completion_info($course);
873 echo $completioninfo->display_help_icon();
803e36e1 874 echo $this->output->heading($this->page_title(), 2, 'accesshide');
1804b7c1
DP
875
876 // Copy activity clipboard..
923451c5 877 echo $this->course_activity_clipboard($course, 0);
1804b7c1
DP
878
879 // Now the list of sections..
880 echo $this->start_section_list();
89b909f6 881 $numsections = course_get_format($course)->get_last_section_number();
1804b7c1 882
eda43c7d
MG
883 foreach ($modinfo->get_section_info_all() as $section => $thissection) {
884 if ($section == 0) {
885 // 0-section is displayed a little different then the others
886 if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
887 echo $this->section_header($thissection, $course, false, 0);
31f47259
FM
888 echo $this->courserenderer->course_section_cm_list($course, $thissection, 0);
889 echo $this->courserenderer->course_section_add_cm_control($course, 0, 0);
eda43c7d
MG
890 echo $this->section_footer();
891 }
892 continue;
1804b7c1 893 }
89b909f6 894 if ($section > $numsections) {
eda43c7d
MG
895 // activities inside this section are 'orphaned', this section will be printed as 'stealth' below
896 continue;
1804b7c1 897 }
ce4dfd27 898 // Show the section if the user is permitted to access it, OR if it's not available
3b29978c
MG
899 // but there is some available info text which explains the reason & should display,
900 // OR it is hidden but the course has a setting to display hidden sections as unavilable.
ce4dfd27 901 $showsection = $thissection->uservisible ||
3b29978c
MG
902 ($thissection->visible && !$thissection->available && !empty($thissection->availableinfo)) ||
903 (!$thissection->visible && !$course->hiddensections);
ce4dfd27 904 if (!$showsection) {
1804b7c1
DP
905 continue;
906 }
907
908 if (!$PAGE->user_is_editing() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
909 // Display section summary only.
eda43c7d 910 echo $this->section_summary($thissection, $course, null);
1804b7c1 911 } else {
923451c5 912 echo $this->section_header($thissection, $course, false, 0);
ce4dfd27 913 if ($thissection->uservisible) {
31f47259
FM
914 echo $this->courserenderer->course_section_cm_list($course, $thissection, 0);
915 echo $this->courserenderer->course_section_add_cm_control($course, $section, 0);
1804b7c1
DP
916 }
917 echo $this->section_footer();
918 }
1804b7c1
DP
919 }
920
921 if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) {
922 // Print stealth sections if present.
eda43c7d 923 foreach ($modinfo->get_section_info_all() as $section => $thissection) {
89b909f6 924 if ($section <= $numsections or empty($modinfo->sections[$section])) {
eda43c7d 925 // this is not stealth section or it is empty
1804b7c1
DP
926 continue;
927 }
928 echo $this->stealth_section_header($section);
31f47259 929 echo $this->courserenderer->course_section_cm_list($course, $thissection, 0);
1804b7c1
DP
930 echo $this->stealth_section_footer();
931 }
1abf1253 932
575826c7
ARN
933 echo $this->end_section_list();
934
89b909f6
MG
935 echo $this->change_number_sections($course, 0);
936 } else {
937 echo $this->end_section_list();
938 }
939
940 }
941
942 /**
943 * Returns controls in the bottom of the page to increase/decrease number of sections
944 *
945 * @param stdClass $course
946 * @param int|null $sectionreturn
947 * @return string
948 */
949 protected function change_number_sections($course, $sectionreturn = null) {
950 $coursecontext = context_course::instance($course->id);
951 if (!has_capability('moodle/course:update', $coursecontext)) {
952 return '';
953 }
954
7c05d8a3
DW
955 $format = course_get_format($course);
956 $options = $format->get_format_options();
957 $maxsections = $format->get_max_sections();
958 $lastsection = $format->get_last_section_number();
89b909f6
MG
959 $supportsnumsections = array_key_exists('numsections', $options);
960
961 if ($supportsnumsections) {
962 // Current course format has 'numsections' option, which is very confusing and we suggest course format
963 // developers to get rid of it (see MDL-57769 on how to do it).
964 // Display "Increase section" / "Decrease section" links.
965
54495fe4 966 echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
3a35a9ff
DP
967
968 // Increase number of sections.
7c05d8a3
DW
969 if ($lastsection < $maxsections) {
970 $straddsection = get_string('increasesections', 'moodle');
971 $url = new moodle_url('/course/changenumsections.php',
972 array('courseid' => $course->id,
973 'increase' => true,
974 'sesskey' => sesskey()));
975 $icon = $this->output->pix_icon('t/switch_plus', $straddsection);
976 echo html_writer::link($url, $icon.get_accesshide($straddsection), array('class' => 'increase-sections'));
977 }
3a35a9ff
DP
978
979 if ($course->numsections > 0) {
980 // Reduce number of sections sections.
981 $strremovesection = get_string('reducesections', 'moodle');
982 $url = new moodle_url('/course/changenumsections.php',
983 array('courseid' => $course->id,
984 'increase' => false,
985 'sesskey' => sesskey()));
986 $icon = $this->output->pix_icon('t/switch_minus', $strremovesection);
987 echo html_writer::link($url, $icon.get_accesshide($strremovesection), array('class' => 'reduce-sections'));
988 }
989
1abf1253 990 echo html_writer::end_tag('div');
1804b7c1 991
89b909f6 992 } else if (course_get_format($course)->uses_sections()) {
7c05d8a3
DW
993 if ($lastsection >= $maxsections) {
994 // Don't allow more sections if we already hit the limit.
995 return;
996 }
89b909f6
MG
997 // Current course format does not have 'numsections' option but it has multiple sections suppport.
998 // Display the "Add section" link that will insert a section in the end.
999 // Note to course format developers: inserting sections in the other positions should check both
1000 // capabilities 'moodle/course:update' and 'moodle/course:movesections'.
89b909f6 1001 echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
f24e17c9
MG
1002 if (get_string_manager()->string_exists('addsections', 'format_'.$course->format)) {
1003 $straddsections = get_string('addsections', 'format_'.$course->format);
89b909f6 1004 } else {
f24e17c9 1005 $straddsections = get_string('addsections');
89b909f6
MG
1006 }
1007 $url = new moodle_url('/course/changenumsections.php',
1008 ['courseid' => $course->id, 'insertsection' => 0, 'sesskey' => sesskey()]);
1009 if ($sectionreturn !== null) {
1010 $url->param('sectionreturn', $sectionreturn);
1011 }
f24e17c9 1012 $icon = $this->output->pix_icon('t/add', $straddsections);
7c05d8a3 1013 $newsections = $maxsections - $lastsection;
f24e17c9 1014 echo html_writer::link($url, $icon . $straddsections,
7c05d8a3 1015 array('class' => 'add-sections', 'data-add-sections' => $straddsections, 'new-sections' => $newsections));
89b909f6
MG
1016 echo html_writer::end_tag('div');
1017 }
1804b7c1 1018 }
7cb8bfdb
DP
1019
1020 /**
1021 * Generate html for a section summary text
1022 *
1023 * @param stdClass $section The course_section entry from DB
1024 * @return string HTML to output.
1025 */
1026 protected function format_summary_text($section) {
1027 $context = context_course::instance($section->course);
1028 $summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php',
1029 $context->id, 'course', 'section', $section->id);
1030
1031 $options = new stdClass();
1032 $options->noclean = true;
1033 $options->overflowdiv = true;
1034 return format_text($summarytext, $section->summaryformat, $options);
1035 }
1804b7c1 1036}