. /** * Base renderer for outputting course formats. * * @package core * @copyright 2012 Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.3 */ defined('MOODLE_INTERNAL') || die(); /** * This is a convenience renderer which can be used by section based formats * to reduce code duplication. It is not necessary for all course formats to * use this and its likely to change in future releases. * * @package core * @copyright 2012 Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.3 */ abstract class format_section_renderer_base extends plugin_renderer_base { /** * Generate the starting container html for a list of sections * @return string HTML to output. */ abstract protected function start_section_list(); /** * Generate the closing container html for a list of sections * @return string HTML to output. */ abstract protected function end_section_list(); /** * Generate the title for this section page * @return string the page title */ abstract protected function page_title(); /** * Generate the content to displayed on the right part of a section * before course modules are included * * @param stdClass $section The course_section entry from DB * @param stdClass $course The course entry from DB * @param bool $onsectionpage true if being printed on a section page * @return string HTML to output. */ protected function section_right_content($section, $course, $onsectionpage) { $o = $this->output->spacer(); if ($section->section != 0) { $controls = $this->section_edit_controls($course, $section, $onsectionpage); if (!empty($controls)) { $o = implode('
', $controls); } } return $o; } /** * Generate the content to displayed on the left part of a section * before course modules are included * * @param stdClass $section The course_section entry from DB * @param stdClass $course The course entry from DB * @param bool $onsectionpage true if being printed on a section page * @return string HTML to output. */ protected function section_left_content($section, $course, $onsectionpage) { $o = $this->output->spacer(); if ($section->section != 0) { // Only in the non-general sections. if ($this->is_section_current($section, $course)) { $o = get_accesshide(get_string('currentsection', 'format_'.$course->format)); } } return $o; } /** * Generate the display of the header part of a section before * course modules are included * * @param stdClass $section The course_section entry from DB * @param stdClass $course The course entry from DB * @param bool $onsectionpage true if being printed on a section page * @return string HTML to output. */ protected function section_header($section, $course, $onsectionpage) { global $PAGE; $o = ''; $currenttext = ''; $sectionstyle = ''; $linktitle = false; if ($section->section != 0) { // Only in the non-general sections. if (!$section->visible) { $sectionstyle = ' hidden'; } else if ($this->is_section_current($section, $course)) { $sectionstyle = ' current'; } $linktitle = ($course->coursedisplay == COURSE_DISPLAY_MULTIPAGE); } $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section, 'class' => 'section main clearfix'.$sectionstyle)); $leftcontent = $this->section_left_content($section, $course, $onsectionpage); $o.= html_writer::tag('div', $leftcontent, array('class' => 'left side')); $rightcontent = $this->section_right_content($section, $course, $onsectionpage); $o.= html_writer::tag('div', $rightcontent, array('class' => 'right side')); $o.= html_writer::start_tag('div', array('class' => 'content')); if (!$onsectionpage) { $title = get_section_name($course, $section); if ($linktitle) { $title = html_writer::link(course_get_url($course, $section->section), $title); } $o.= $this->output->heading($title, 3, 'sectionname'); } $o.= html_writer::start_tag('div', array('class' => 'summary')); $o.= $this->format_summary_text($section); $context = context_course::instance($course->id); if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $context)) { $url = new moodle_url('/course/editsection.php', array('id'=>$section->id)); if ($onsectionpage) { $url->param('sectionreturn', 1); } $o.= html_writer::link($url, html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/edit'), 'class' => 'iconsmall edit')), array('title' => get_string('editsummary'))); } $o.= html_writer::end_tag('div'); $o .= $this->section_availability_message($section); return $o; } /** * Generate the display of the footer part of a section * * @return string HTML to output. */ protected function section_footer() { $o = html_writer::end_tag('div'); $o.= html_writer::end_tag('li'); return $o; } /** * Generate the edit controls of a section * * @param stdClass $course The course entry from DB * @param stdClass $section The course_section entry from DB * @param bool $onsectionpage true if being printed on a section page * @return array of links with edit controls */ protected function section_edit_controls($course, $section, $onsectionpage = false) { global $PAGE; if (!$PAGE->user_is_editing()) { return array(); } if (!has_capability('moodle/course:update', context_course::instance($course->id))) { return array(); } if ($onsectionpage) { $baseurl = course_get_url($course, $section->section); } else { $baseurl = course_get_url($course); } $baseurl->param('sesskey', sesskey()); $controls = array(); $url = clone($baseurl); if ($section->visible) { // Show the hide/show eye. $strhidefromothers = get_string('hidefromothers', 'format_'.$course->format); $url->param('hide', $section->section); $controls[] = html_writer::link($url, html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/hide'), 'class' => 'icon hide', 'alt' => $strhidefromothers)), array('title' => $strhidefromothers, 'class' => 'editing_showhide')); } else { $strshowfromothers = get_string('showfromothers', 'format_'.$course->format); $url->param('show', $section->section); $controls[] = html_writer::link($url, html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/show'), 'class' => 'icon hide', 'alt' => $strshowfromothers)), array('title' => $strshowfromothers, 'class' => 'editing_showhide')); } if (!$onsectionpage) { $url = clone($baseurl); if ($section->section > 1) { // Add a arrow to move section up. $url->param('section', $section->section); $url->param('move', -1); $strmoveup = get_string('moveup'); $controls[] = html_writer::link($url, html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/up'), 'class' => 'icon up', 'alt' => $strmoveup)), array('title' => $strmoveup, 'class' => 'moveup')); } $url = clone($baseurl); if ($section->section < $course->numsections) { // Add a arrow to move section down. $url->param('section', $section->section); $url->param('move', 1); $strmovedown = get_string('movedown'); $controls[] = html_writer::link($url, html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/down'), 'class' => 'icon down', 'alt' => $strmovedown)), array('title' => $strmovedown, 'class' => 'movedown')); } } return $controls; } /** * Generate a summary of a section for display on the 'coruse index page' * * @param stdClass $section The course_section entry from DB * @param stdClass $course The course entry from DB * @return string HTML to output. */ protected function section_summary($section, $course) { // If section is hidden then display grey section link $classattr = 'section-summary clearfix'; If (!$section->visible) { $classattr .= ' dimmed_text'; } $o = ''; $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section, 'class' => $classattr)); $title = get_section_name($course, $section); $o.= html_writer::start_tag('a', array('href' => course_get_url($course, $section->section))); $o.= $this->output->heading($title, 3, 'header section-title'); $o.= html_writer::end_tag('a'); $o.= html_writer::start_tag('div', array('class' => 'summarytext')); $o.= $this->format_summary_text($section); $o.= html_writer::end_tag('div'); $o .= $this->section_availability_message($section); $o.= html_writer::end_tag('li'); return $o; } /** * If section is not visible to current user, display the message about that * ('Not available until...', that sort of thing). Otherwise, returns blank. * * @param stdClass $section The course_section entry from DB * @return string HTML to output */ protected function section_availability_message($section) { $o = ''; if (!$section->uservisible || $section->availableinfo) { $o .= html_writer::start_tag('div', array('class' => 'availabilityinfo')); if (!empty($section->availableinfo)) { $o .= $section->availableinfo; } else { $o .= get_string('notavailable'); } $o .= html_writer::end_tag('div'); } return $o; } /** * Show if something is on on the course clipboard (moving around) * * @param stdClass $course The course entry from DB * @param int $sectionno The section number in the coruse which is being dsiplayed * @return string HTML to output. */ protected function course_activity_clipboard($course, $sectionno = 0) { global $USER; $o = ''; // If currently moving a file then show the current clipboard. if (ismoving($course->id)) { $url = new moodle_url('/course/mod.php', array('sesskey' => sesskey(), 'cancelcopy' => true, 'sr' => $sectionno, ) ); $strcancel= get_string('cancel'); $o.= html_writer::start_tag('li', array('class' => 'clipboard')); $o.= strip_tags(get_string('activityclipboard', '', $USER->activitycopyname)); $o.= ' ('.html_writer::link($url, get_string('cancel')).')'; $o.= html_writer::end_tag('li'); } return $o; } /** * Generate next/previous section links for naviation * * @param stdClass $course The course entry from DB * @param array $sections The course_sections entries from the DB * @param int $sectionno The section number in the coruse which is being dsiplayed * @return array associative array with previous and next section link */ protected function get_nav_links($course, $sections, $sectionno) { // FIXME: This is really evil and should by using the navigation API. $canviewhidden = has_capability('moodle/course:viewhiddensections', context_course::instance($course->id)) or !$course->hiddensections; $links = array('previous' => '', 'next' => ''); $back = $sectionno - 1; while ($back > 0 and empty($links['previous'])) { if ($canviewhidden || $sections[$back]->visible) { $params = array(); if (!$sections[$back]->visible) { $params = array('class' => 'dimmed_text'); } $previouslink = html_writer::tag('span', $this->output->larrow(), array('class' => 'larrow')); $previouslink .= get_section_name($course, $sections[$back]); $links['previous'] = html_writer::link(course_get_url($course, $back), $previouslink, $params); } $back--; } $forward = $sectionno + 1; while ($forward <= $course->numsections and empty($links['next'])) { if ($canviewhidden || $sections[$forward]->visible) { $params = array(); if (!$sections[$forward]->visible) { $params = array('class' => 'dimmed_text'); } $nextlink = get_section_name($course, $sections[$forward]); $nextlink .= html_writer::tag('span', $this->output->rarrow(), array('class' => 'rarrow')); $links['next'] = html_writer::link(course_get_url($course, $forward), $nextlink, $params); } $forward++; } return $links; } /** * Generate the header html of a stealth section * * @param int $sectionno The section number in the coruse which is being dsiplayed * @return string HTML to output. */ protected function stealth_section_header($sectionno) { $o = ''; $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix orphaned hidden')); $o.= html_writer::tag('div', '', array('class' => 'left side')); $o.= html_writer::tag('div', '', array('class' => 'right side')); $o.= html_writer::start_tag('div', array('class' => 'content')); $o.= $this->output->heading(get_string('orphanedactivities'), 3, 'sectionname'); return $o; } /** * Generate footer html of a stealth section * * @return string HTML to output. */ protected function stealth_section_footer() { $o = html_writer::end_tag('div'); $o.= html_writer::end_tag('li'); return $o; } /** * Generate the html for a hidden section * * @param int $sectionno The section number in the coruse which is being dsiplayed * @return string HTML to output. */ protected function section_hidden($sectionno) { $o = ''; $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix hidden')); $o.= html_writer::tag('div', '', array('class' => 'left side')); $o.= html_writer::tag('div', '', array('class' => 'right side')); $o.= html_writer::start_tag('div', array('class' => 'content')); $o.= get_string('notavailable'); $o.= html_writer::end_tag('div'); $o.= html_writer::end_tag('li'); return $o; } /** * Output the html for a single section page . * * @param stdClass $course The course entry from DB * @param array $sections The course_sections entries from the DB * @param array $mods used for print_section() * @param array $modnames used for print_section() * @param array $modnamesused used for print_section() * @param int $displaysection The section number in the course which is being displayed */ public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) { global $PAGE; // Can we view the section in question? $context = context_course::instance($course->id); $canviewhidden = has_capability('moodle/course:viewhiddensections', $context); if (!isset($sections[$displaysection])) { // This section doesn't exist print_error('unknowncoursesection', 'error', null, $course->fullname); return; } if (!$sections[$displaysection]->visible && !$canviewhidden) { if (!$course->hiddensections) { echo $this->start_section_list(); echo $this->section_hidden($displaysection); echo $this->end_section_list(); } // Can't view this section. return; } // General section if non-empty. $thissection = $sections[0]; if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) { echo $this->start_section_list(); echo $this->section_header($thissection, $course, true); print_section($course, $thissection, $mods, $modnamesused, true); if ($PAGE->user_is_editing()) { print_section_add_menus($course, 0, $modnames); } echo $this->section_footer(); echo $this->end_section_list(); } // Start single-section div echo html_writer::start_tag('div', array('class' => 'single-section')); // Title with section navigation links. $sectionnavlinks = $this->get_nav_links($course, $sections, $displaysection); $sectiontitle = ''; $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation header headingblock')); $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left')); $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right')); // Title attributes $titleattr = 'mdl-align title'; if (!$sections[$displaysection]->visible) { $titleattr .= ' dimmed_text'; } $sectiontitle .= html_writer::tag('div', get_section_name($course, $sections[$displaysection]), array('class' => $titleattr)); $sectiontitle .= html_writer::end_tag('div'); echo $sectiontitle; // Copy activity clipboard.. echo $this->course_activity_clipboard($course, $displaysection); // Now the list of sections.. echo $this->start_section_list(); // The requested section page. $thissection = $sections[$displaysection]; echo $this->section_header($thissection, $course, true); // Show completion help icon. $completioninfo = new completion_info($course); echo $completioninfo->display_help_icon(); print_section($course, $thissection, $mods, $modnamesused, true); if ($PAGE->user_is_editing()) { print_section_add_menus($course, $displaysection, $modnames); } echo $this->section_footer(); echo $this->end_section_list(); // Display section bottom navigation. $courselink = html_writer::link(course_get_url($course), get_string('returntomaincoursepage')); $sectionbottomnav = ''; $sectionbottomnav .= html_writer::start_tag('div', array('class' => 'section-navigation mdl-bottom')); $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left')); $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right')); $sectionbottomnav .= html_writer::tag('div', $courselink, array('class' => 'mdl-align')); $sectionbottomnav .= html_writer::end_tag('div'); echo $sectionbottomnav; // close single-section div. echo html_writer::end_tag('div'); } /** * Output the html for a multiple section page * * @param stdClass $course The course entry from DB * @param array $sections The course_sections entries from the DB * @param array $mods used for print_section() * @param array $modnames used for print_section() * @param array $modnamesused used for print_section() */ public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) { global $PAGE; $context = context_course::instance($course->id); // Title with completion help icon. $completioninfo = new completion_info($course); echo $completioninfo->display_help_icon(); echo $this->output->heading($this->page_title(), 2, 'accesshide'); // Copy activity clipboard.. echo $this->course_activity_clipboard($course); // Now the list of sections.. echo $this->start_section_list(); // General section if non-empty. $thissection = $sections[0]; unset($sections[0]); if ($thissection->summary or $thissection->sequence or $PAGE->user_is_editing()) { echo $this->section_header($thissection, $course, true); print_section($course, $thissection, $mods, $modnamesused, true); if ($PAGE->user_is_editing()) { print_section_add_menus($course, 0, $modnames); } echo $this->section_footer(); } $canviewhidden = has_capability('moodle/course:viewhiddensections', $context); for ($section = 1; $section <= $course->numsections; $section++) { if (!empty($sections[$section])) { $thissection = $sections[$section]; } else { // This will create a course section if it doesn't exist.. $thissection = get_course_section($section, $course->id); // The returned section is only a bare database object rather than // a section_info object - we will need at least the uservisible // field in it. $thissection->uservisible = true; $thissection->availableinfo = null; $thissection->showavailability = 0; } // Show the section if the user is permitted to access it, OR if it's not available // but showavailability is turned on $showsection = $thissection->uservisible || ($thissection->visible && !$thissection->available && $thissection->showavailability); if (!$showsection) { // Hidden section message is overridden by 'unavailable' control // (showavailability option). if (!$course->hiddensections && $thissection->available) { echo $this->section_hidden($section); } unset($sections[$section]); continue; } if (!$PAGE->user_is_editing() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) { // Display section summary only. echo $this->section_summary($thissection, $course); } else { echo $this->section_header($thissection, $course, false); if ($thissection->uservisible) { print_section($course, $thissection, $mods, $modnamesused); if ($PAGE->user_is_editing()) { print_section_add_menus($course, $section, $modnames); } } echo $this->section_footer(); } unset($sections[$section]); } if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) { // Print stealth sections if present. $modinfo = get_fast_modinfo($course); foreach ($sections as $section => $thissection) { if (empty($modinfo->sections[$section])) { continue; } echo $this->stealth_section_header($section); print_section($course, $thissection, $mods, $modnamesused); echo $this->stealth_section_footer(); } echo $this->end_section_list(); echo html_writer::start_tag('div', array('class' => 'mdl-right')); // Increase number of sections. $straddsection = get_string('increasesections', 'moodle'); $url = new moodle_url('/course/changenumsections.php', array('courseid' => $course->id, 'increase' => true, 'sesskey' => sesskey())); $icon = $this->output->pix_icon('t/switch_plus', $straddsection); echo html_writer::link($url, $icon.get_accesshide($straddsection), array('class' => 'increase-sections')); if ($course->numsections > 0) { // Reduce number of sections sections. $strremovesection = get_string('reducesections', 'moodle'); $url = new moodle_url('/course/changenumsections.php', array('courseid' => $course->id, 'increase' => false, 'sesskey' => sesskey())); $icon = $this->output->pix_icon('t/switch_minus', $strremovesection); echo html_writer::link($url, $icon.get_accesshide($strremovesection), array('class' => 'reduce-sections')); } echo html_writer::end_tag('div'); } else { echo $this->end_section_list(); } } /** * Generate html for a section summary text * * @param stdClass $section The course_section entry from DB * @return string HTML to output. */ protected function format_summary_text($section) { $context = context_course::instance($section->course); $summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php', $context->id, 'course', 'section', $section->id); $options = new stdClass(); $options->noclean = true; $options->overflowdiv = true; return format_text($summarytext, $section->summaryformat, $options); } /** * Is the section passed in the current section? (Note this isn't strictly * a renderering method, but neater here). * * @param stdClass $course The course entry from DB * @param stdClass $section The course_section entry from the DB * @return bool true if the section is current */ protected function is_section_current($section, $course) { return ($course->marker == $section->section); } }