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