MDL-32508 course/format: Display general section above title
[moodle.git] / course / format / renderer.php
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/>.
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  */
26 defined('MOODLE_INTERNAL') || die();
29 /**
30  * This renderer is used by section based formats
31  *
32  * @package core
33  * @copyright 2012 Dan Poltawski
34  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  * @since Moodle 2.3
36  */
37 abstract class format_renderer_base extends plugin_renderer_base {
39     /**
40      * Generate the starting container html for a list of sections
41      * @return string HTML to output.
42      */
43     abstract public function start_section_list();
45     /**
46      * Generate the closing container html for a list of sections
47      * @return string HTML to output.
48      */
49     abstract public function end_section_list();
51     /**
52      * Generate the title for this section page
53      * @return string the page title
54      */
55     abstract public function page_title();
57     /**
58      * Generate the content to displayed on the right part of a section
59      *
60      * before course modules are included
61      * @param stdClass $section The course_section entry from DB
62      * @param stdClass $course The course entry from DB
63      * @param bool $onsectionpage true if being printed on a section page
64      * @return string HTML to output.
65      */
66     public function section_right_content($section, $course, $onsectionpage) {
67         $o = $this->output->spacer();
69         if ($section->section != 0) {
70             $controls = $this->section_edit_controls($course, $section, $onsectionpage);
71             if (!empty($controls)) {
72                 $o = implode('<br />', $controls);
73             }
74         }
76         return $o;
77     }
79     /**
80      * Generate the content to displayed on the left part of a section
81      *
82      * before course modules are included
83      * @param stdClass $section The course_section entry from DB
84      * @param stdClass $course The course entry from DB
85      * @param bool $onsectionpage true if being printed on a section page
86      * @return string HTML to output.
87      */
88     public function section_left_content($section, $course, $onsectionpage) {
89         $o = $this->output->spacer();
91         if ($section->section != 0) {
92             // Only in the non-general sections.
93             if ($course->marker == $section->section) {
94                 $o = get_accesshide(get_string('currentsection', 'format_'.$course->format));
95             }
96         }
98         return $o;
99     }
101     /**
102      * Generate the display of the header part of a section before
103      * course modules are included
104      *
105      * @param stdClass $section The course_section entry from DB
106      * @param stdClass $course The course entry from DB
107      * @param bool $onsectionpage true if being printed on a section page
108      * @return string HTML to output.
109      */
110     public function section_header($section, $course, $onsectionpage) {
111         global $PAGE;
113         $o = '';
114         $currenttext = '';
115         $sectionstyle = '';
116         $linktitle = false;
118         if ($section->section != 0 ) {
119             // Only in the non-general sections.
120             if (!$section->visible) {
121                 $sectionstyle = ' hidden';
122             } else if ($course->marker == $section->section) {
123                 $sectionstyle = ' current';
124             }
125             $linktitle = ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE);
126         }
128         $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
129             'class' => 'section main clearfix'.$sectionstyle));
131         $leftcontent = $this->section_left_content($section, $course, $onsectionpage);
132         $o.= html_writer::tag('div', $leftcontent, array('class' => 'left side'));
134         $rightcontent = $this->section_right_content($section, $course, $onsectionpage);
135         $o.= html_writer::tag('div', $rightcontent, array('class' => 'right side'));
136         $o.= html_writer::start_tag('div', array('class' => 'content'));
138         if (!$onsectionpage) {
139             $title = get_section_name($course, $section);
140             if ($linktitle) {
141                 $title = html_writer::link(course_get_url($course, $section->section), $title);
142             }
143             $o.= $this->output->heading($title, 3, 'sectionname');
144         }
146         $o.= html_writer::start_tag('div', array('class' => 'summary'));
148         $context = context_course::instance($section->course);
149         $summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php',
150             $context->id, 'course', 'section', $section->id);
151         $summaryformatoptions = new stdClass();
152         $summaryformatoptions->noclean = true;
153         $summaryformatoptions->overflowdiv = true;
155         $o.= format_text($summarytext, $section->summaryformat, $summaryformatoptions);
157         if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $context)) {
158             $url = new moodle_url('/course/editsection.php', array('id'=>$section->id));
160             if ($onsectionpage) {
161                 $url->param('sectionreturn', 1);
162             }
164             $o.= html_writer::link($url,
165                 html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/edit'), 'class' => 'iconsmall edit')),
166                 array('title' => get_string('editsummary')));
167         }
168         $o.= html_writer::end_tag('div');
170         return $o;
171     }
173     /**
174      * Generate the display of the footer part of a section
175      *
176      * @return string HTML to output.
177      */
178     public function section_footer() {
179         $o = html_writer::end_tag('div');
180         $o.= html_writer::end_tag('li');
182         return $o;
183     }
185     /**
186      * Generate the edit controls of a section
187      *
188      * @param stdClass $course The course entry from DB
189      * @param stdClass $section The course_section entry from DB
190      * @param bool $onsectionpage true if being printed on a section page
191      * @return array of links with edit controls
192      */
193     public function section_edit_controls($course, $section, $onsectionpage = false) {
194         global $PAGE;
196         if (!$PAGE->user_is_editing()) {
197             return array();
198         }
200         if (!has_capability('moodle/course:update', context_course::instance($course->id))) {
201             return array();
202         }
204         if ($onsectionpage) {
205             $baseurl = course_get_url($course, $section->section);
206         } else {
207             $baseurl = course_get_url($course);
208         }
209         $baseurl->param('sesskey', sesskey());
211         $controls = array();
213         $url = clone($baseurl);
214         if ($section->visible) { // Show the hide/show eye.
215             $strhidefromothers = get_string('hidefromothers', 'format_'.$course->format);
216             $url->param('hide', $section->section);
217             $controls[] = html_writer::link($url,
218                 html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/hide'),
219                 'class' => 'icon hide', 'alt' => $strhidefromothers)),
220                 array('title' => $strhidefromothers, 'class' => 'editing_showhide'));
221         } else {
222             $strshowfromothers = get_string('showfromothers', 'format_'.$course->format);
223             $url->param('show',  $section->section);
224             $controls[] = html_writer::link($url,
225                 html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/show'),
226                 'class' => 'icon hide', 'alt' => $strshowfromothers)),
227                 array('title' => $strshowfromothers, 'class' => 'editing_showhide'));
228         }
230         if (!$onsectionpage) {
231             $url = clone($baseurl);
232             if ($section->section > 1) { // Add a arrow to move section up.
233                 $url->param('section', $section->section);
234                 $url->param('move', -1);
235                 $strmoveup = get_string('moveup');
237                 $controls[] = html_writer::link($url,
238                     html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/up'),
239                     'class' => 'icon up', 'alt' => $strmoveup)),
240                     array('title' => $strmoveup, 'class' => 'moveup'));
241             }
243             $url = clone($baseurl);
244             if ($section->section < $course->numsections) { // Add a arrow to move section down.
245                 $url->param('section', $section->section);
246                 $url->param('move', 1);
247                 $strmovedown =  get_string('movedown');
249                 $controls[] = html_writer::link($url,
250                     html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/down'),
251                     'class' => 'icon down', 'alt' => $strmovedown)),
252                     array('title' => $strmovedown, 'class' => 'movedown'));
253             }
254         }
256         return $controls;
257     }
259     /**
260      * Generate a summary of a section for display on the 'coruse index page'
261      *
262      * @param stdClass $section The course_section entry from DB
263      * @param stdClass $course The course entry from DB
264      * @return string HTML to output.
265      */
266     public function section_summary($section, $course) {
268         $o = '';
269         $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section));
271         $title = get_section_name($course, $section);
272         $o.= html_writer::start_tag('div', array('class' => 'section-summary'));
273         $o.= html_writer::start_tag('a', array('href' => course_get_url($course, $section->section)));
274         $o.= $this->output->heading($title, 3, 'header section-title');
275         $o.= html_writer::end_tag('a');
277         $o.= html_writer::start_tag('div', array('class' => 'summarytext'));
278         $o.= format_text($section->summary, $section->summaryformat);
279         $o.= html_writer::end_tag('div');
280         $o.= html_writer::end_tag('div');
281         $o.= html_writer::end_tag('li');
283         return $o;
284     }
286     /**
287      * Show if something is on on the course clipboard (moving around)
288      *
289      * @param stdClass $course The course entry from DB
290      * @param int $sectionno The section number in the coruse which is being dsiplayed
291      * @return string HTML to output.
292      */
293     public function course_activity_clipboard($course, $sectionno = 0) {
294         global $USER;
296         $o = '';
297         // If currently moving a file then show the current clipboard.
298         if (ismoving($course->id)) {
299             $url = new moodle_url('/course/mod.php',
300                 array('sesskey' => sesskey(),
301                       'cancelcopy' => true,
302                       'sr' => $sectionno,
303                 )
304             );
306             $strcancel= get_string('cancel');
308             $o.= html_writer::start_tag('li', array('class' => 'clipboard'));
309             $o.= strip_tags(get_string('activityclipboard', '', $USER->activitycopyname));
310             $o.= ' ('.html_writer::link($url, get_string('cancel')).')';
311             $o.= html_writer::end_tag('li');
312         }
314         return $o;
315     }
317     /**
318      * Generate next/previous section links for naviation
319      *
320      * @param stdClass $course The course entry from DB
321      * @param array $sections The course_sections entries from the DB
322      * @param int $sectionno The section number in the coruse which is being dsiplayed
323      * @return string HTML to output.
324      */
325     public function get_nav_links($course, $sections, $sectionno) {
326         // FIXME: This is really evil and should by using the navigation API.
327         $canviewhidden = has_capability('moodle/course:viewhiddensections', context_course::instance($course->id))
328             or !$course->hiddensections;
330         $links = array('previous' => '', 'next' => '');
331         $back = $sectionno - 1;
332         while ($back > 0 and empty($links['previous'])) {
333             if ($canviewhidden || $sections[$back]->visible) {
334                 $links['previous'] = html_writer::link(course_get_url($course, $back),
335                     $this->output->larrow().$this->output->spacer().get_section_name($course, $sections[$back]));
336             }
337             $back--;
338         }
340         $forward = $sectionno + 1;
341         while ($forward <= $course->numsections and empty($links['next'])) {
342             if ($canviewhidden || $sections[$forward]->visible) {
343                 $links['next'] = html_writer::link(course_get_url($course, $forward),
344                     get_section_name($course, $sections[$forward]).$this->output->spacer().$this->output->rarrow());
345             }
346             $forward++;
347         }
349         $o = '';
350         $o.= html_writer::start_tag('div', array('class' => 'section-navigation yui3-g'));
351         $o.= html_writer::tag('div', $links['previous'], array('class' => 'yui3-u'));
352         $o.= html_writer::tag('div', $links['next'], array('class' => 'right yui3-u'));
353         $o.= html_writer::end_tag('div');
355         return $o;
356     }
358     /**
359      * Generate the header html of a stealth section
360      *
361      * @param int $sectionno The section number in the coruse which is being dsiplayed
362      * @return string HTML to output.
363      */
364     public function stealth_section_header($sectionno) {
365         $o = '';
366         $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix orphaned hidden'));
367         $o.= html_writer::tag('div', '', array('class' => 'left side'));
368         $o.= html_writer::tag('div', '', array('class' => 'right side'));
369         $o.= html_writer::start_tag('div', array('class' => 'content'));
370         $o.= $this->output->heading(get_string('orphanedactivities'), 3, 'sectionname');
371         return $o;
372     }
374     /**
375      * Generate footer html of a stealth section
376      *
377      * @return string HTML to output.
378      */
379     public function stealth_section_footer() {
380         $o = html_writer::end_tag('div');
381         $o.= html_writer::end_tag('li');
382         return $o;
383     }
385     /**
386      * Generate the html for a hidden section
387      *
388      * @param int $sectionno The section number in the coruse which is being dsiplayed
389      * @return string HTML to output.
390      */
391     public function section_hidden($sectionno) {
392         $o = '';
393         $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix hidden'));
394         $o.= html_writer::tag('div', '', array('class' => 'left side'));
395         $o.= html_writer::tag('div', '', array('class' => 'right side'));
396         $o.= html_writer::start_tag('div', array('class' => 'content'));
397         $o.= get_string('notavailable');
398         $o.= html_writer::end_tag('div');
399         $o.= html_writer::end_tag('li');
400         return $o;
401     }
403     /**
404      * Output the html for a single section page .
405      *
406      * @param stdClass $course The course entry from DB
407      * @param array $sections The course_sections entries from the DB
408      * @param array $mods used for print_section()
409      * @param array $modnames used for print_section()
410      * @param array $modnamesused used for print_section()
411      * @param int $displaysection The section number in the course which is being displayed
412      */
413     public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) {
414         global $PAGE;
416         // Can we view the section in question?
417         $context = context_course::instance($course->id);
418         $canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
420         if (!$sections[$displaysection]->visible && !$canviewhidden) {
421             if (!$course->hiddensections) {
422                 echo $this->start_section_list();
423                 echo $this->section_hidden($displaysection);
424                 echo $this->end_section_list();
425                 echo $sectionnavlinks;
426             }
427             // Can't view this section.
428             return;
429         }
431         // General section if non-empty.
432         $thissection = $sections[0];
433         if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) {
434             echo $this->start_section_list();
435             echo $this->section_header($thissection, $course, true);
436             print_section($course, $thissection, $mods, $modnamesused, true);
437             if ($PAGE->user_is_editing()) {
438                 print_section_add_menus($course, 0, $modnames);
439             }
440             echo $this->section_footer();
441             echo $this->end_section_list();
442         }
444         // Section next/previous links.
445         $sectionnavlinks = $this->get_nav_links($course, $sections, $displaysection);
446         echo $sectionnavlinks;
449         // Title with completion help icon.
450         $completioninfo = new completion_info($course);
451         echo $completioninfo->display_help_icon();
452         $title = get_section_name($course, $sections[$displaysection]);
453         echo $this->output->heading($title, 2, 'headingblock header outline');
455         // Copy activity clipboard..
456         echo $this->course_activity_clipboard($course, $displaysection);
458         // Now the list of sections..
459         echo $this->start_section_list();
461         // The requested section page.
462         $thissection = $sections[$displaysection];
463         echo $this->section_header($thissection, $course, true);
464         print_section($course, $thissection, $mods, $modnamesused, true);
465         if ($PAGE->user_is_editing()) {
466             print_section_add_menus($course, $displaysection, $modnames);
467         }
468         echo $this->section_footer();
469         echo $sectionnavlinks;
470         echo $this->end_section_list();
471     }
473     /**
474      * Output the html for a multiple section page
475      *
476      * @param stdClass $course The course entry from DB
477      * @param array $sections The course_sections entries from the DB
478      * @param array $mods used for print_section()
479      * @param array $modnames used for print_section()
480      * @param array $modnamesused used for print_section()
481      */
482     public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) {
483         global $PAGE;
485         $context = context_course::instance($course->id);
486         // Title with completion help icon.
487         $completioninfo = new completion_info($course);
488         echo $completioninfo->display_help_icon();
489         echo $this->output->heading($this->page_title(), 2, 'accesshide');
491         // Copy activity clipboard..
492         echo $this->course_activity_clipboard($course);
494         // Now the list of sections..
495         echo $this->start_section_list();
497         // General section if non-empty.
498         $thissection = $sections[0];
499         unset($sections[0]);
500         if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) {
501             echo $this->section_header($thissection, $course, true);
502             print_section($course, $thissection, $mods, $modnamesused, true);
503             if ($PAGE->user_is_editing()) {
504                 print_section_add_menus($course, 0, $modnames);
505             }
506             echo $this->section_footer();
507         }
509         $canviewhidden = has_capability('moodle/course:viewhiddensections', $context);
510         for ($section = 1; $section <= $course->numsections; $section++) {
511             if (!empty($sections[$section])) {
512                 $thissection = $sections[$section];
513             } else {
514                 // This will create a course section if it doesn't exist..
515                 $thissection = get_course_section($section, $course->id);
516             }
517             $showsection = ($canviewhidden or $thissection->visible or !$course->hiddensections);
518             if (!$thissection->visible && !$canviewhidden) {
519                 if (!$course->hiddensections) {
520                     echo $this->section_hidden($section);
521                 }
523                 unset($sections[$section]);
524                 continue;
525             }
527             if (!$PAGE->user_is_editing() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
528                 // Display section summary only.
529                 echo $this->section_summary($thissection, $course);
530             } else {
531                 echo $this->section_header($thissection, $course, false);
532                 print_section($course, $thissection, $mods, $modnamesused);
533                 if ($PAGE->user_is_editing()) {
534                     print_section_add_menus($course, $section, $modnames);
535                 }
536                 echo $this->section_footer();
537             }
539             unset($sections[$section]);
540         }
542         if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) {
543             // Print stealth sections if present.
544             $modinfo = get_fast_modinfo($course);
545             foreach ($sections as $section => $thissection) {
546                 if (empty($modinfo->sections[$section])) {
547                     continue;
548                 }
549                 echo $this->stealth_section_header($section);
550                 print_section($course, $thissection, $mods, $modnamesused);
551                 echo $this->stealth_section_footer();
552             }
554             $straddsection = get_string('addanadditionalsection', 'moodle');
555             echo html_writer::start_tag('div', array('class' => 'mdl-align'));
556             echo $this->output->action_link(
557                 new moodle_url('/course/addsection.php',
558                     array('courseid' => $course->id, 'sesskey' => sesskey())
559                 ), $this->output->pix_icon('t/add', $straddsection).$straddsection, null,
560                     array('class' => 'addsectionlink')
561             );
562             echo html_writer::end_tag('div');
563         }
565         echo $this->end_section_list();
567     }