. /** * This is the squared theme. * * The squared theme makes uses a custom version of squared blocks * * @package theme_squared * @copyright 2018 onwards Onlinecampus Virtuelle PH * www.virtuelle-ph.at, David Bogner www.edulabs.org * @author G J Barnard - {@link http://moodle.org/user/profile.php?id=442195} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die; class theme_squared_core_course_renderer extends core_course_renderer { private $activitylayout = false; private static $contentcontrolinit = false; private $coursecat_toolbox = null; private $currentcategoryid = 0; private $categorysearchsort = 1; private $userisediting = false; private const moddivider = 1; private const hidemodsummary = 2; private const showmodsummary = 3; public function __construct(moodle_page $page, $target) { parent::__construct($page, $target); if (!$this->page->user_is_editing()) { if (!self::$contentcontrolinit) { $this->page->requires->js_call_amd('theme_squared/content_control', 'init'); self::$contentcontrolinit = true; } } $this->coursecat_toolbox = \theme_squared\coursecat_toolbox::get_instance(); $this->activitylayout = (!empty($this->page->theme->settings->activitylayout)) ? $this->page->theme->settings->activitylayout : false; $this->userisediting = $this->page->user_is_editing(); } /** * Renders html to display a name with the link to the course module on a course page * * If module is unavailable for user but still needs to be displayed * in the list, just the name is returned without a link * * Note, that for course modules that never have separate pages (i.e. labels) * this function return an empty string * * @param cm_info $mod * @param array $displayoptions * @return string */ public function course_section_cm_name_title(cm_info $mod, $displayoptions = array()) { if (($this->userisediting == true) || ($this->activitylayout == false)) { return parent::course_section_cm_name_title($mod, $displayoptions); } $output = ''; $url = $mod->url; if (!$mod->is_visible_on_course_page() || !$url) { // Nothing to be displayed to the user. return $output; } // Accessibility: for files get description via icon, this is very ugly hack! $instancename = $mod->get_formatted_name(); $altname = $mod->modfullname; /* Avoid unnecessary duplication: if e.g. a forum name already includes the word forum (or Forum, etc) then it is unhelpful to include that in the accessible description that is added. */ if (false !== strpos(core_text::strtolower($instancename), core_text::strtolower($altname))) { $altname = ''; } // File type after name, for alphabetic lists (screen reader). if ($altname) { $altname = get_accesshide(' ' . $altname); } list($linkclasses, $textclasses) = $this->course_section_cm_classes($mod); /* Get on-click attribute value if specified and decode the onclick - it has already been encoded for display. */ $onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES); // Display link itself. $activitylink = html_writer::tag('span', $instancename . $altname, array('class' => 'instancename')); if ($mod->uservisible) { $output .= html_writer::link($url, $activitylink, array('class' => $linkclasses, 'onclick' => $onclick)); } else { /* We may be displaying this just in order to show information about visibility, without the actual link ($mod->is_visible_on_course_page()). */ $output .= html_writer::tag('div', $activitylink, array('class' => $textclasses)); } return $output; } /** * Renders html to display the card activity header when used on a course page. * * @param cm_info $mod * @return array(boolean toexpand, string outout). */ protected function squared_activity_header(cm_info $mod) { $output = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(), 'class' => 'sqactivityicon iconlarge activityicon', 'alt' => get_string('activityicon', 'theme_squared'), 'role' => 'presentation')); $instancename = $mod->get_formatted_name(); $altname = $mod->modfullname; /* Avoid unnecessary duplication: if e.g. a forum name already includes the word forum (or Forum, etc) then it is unhelpful to include that in the accessible description that is added. */ if (false !== strpos(core_text::strtolower($instancename), core_text::strtolower($altname))) { $altname = ''; } // File type after name, for alphabetic lists (screen reader). if ($altname) { $altname = get_accesshide(' ' . $altname); } $output .= html_writer::tag('span', $instancename.$altname, array('class' => 'instancename modname')); return array('toexpand' => (core_text::strlen($instancename) > 48), 'output'=> $output); } /** * Renders HTML to display one course module in a course section * * This includes link, content, availability, completion info and additional information * that module type wants to display (i.e. number of unread forum posts) * * This function calls: * {@link core_course_renderer::course_section_cm_name()} * {@link core_course_renderer::course_section_cm_text()} * {@link core_course_renderer::course_section_cm_availability()} * {@link core_course_renderer::course_section_cm_completion()} * {@link course_get_cm_edit_actions()} * {@link core_course_renderer::course_section_cm_edit_actions()} * * @param stdClass $course * @param completion_info $completioninfo * @param cm_info $mod * @param int|null $sectionreturn * @param array $displayoptions * @return string */ public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) { if ((empty($mod->url)) || ($this->userisediting == true) || ($this->activitylayout == false)) { return parent::course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions); } $output = ''; /* We return empty string (because course module will not be displayed at all) if: 1) The activity is not visible to users and 2) The 'availableinfo' is empty, i.e. the activity was hidden in a way that leaves no info, such as using the eye icon. */ if (!$mod->is_visible_on_course_page()) { return $output; } $modicons = $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions); if (!empty($modicons)) { $output .= html_writer::span($modicons, 'actions sqactions'); } // Display the link to the module (or do nothing if module has no url) $cmname = $this->course_section_cm_name($mod, $displayoptions); if (!empty($cmname)) { // Start the div for the activity title, excluding the edit icons. $output .= html_writer::start_tag('div', array('class' => 'activityinstance')); $output .= $cmname; // Module can put text after the link (e.g. forum unread) $output .= $mod->afterlink; // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this. $output .= html_writer::end_tag('div'); // .activityinstance } // Show availability info (if module is not available). $output .= $this->course_section_cm_availability($mod, $displayoptions); return $output; } /** * Renders HTML to display one course module for display within a section. * * This function calls: * {@link core_course_renderer::course_section_cm()} * * @param stdClass $course * @param completion_info $completioninfo * @param cm_info $mod * @param int|null $sectionreturn * @param array $displayoptions * @return String */ public function course_section_cm_list_item($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) { if (($this->userisediting == true) || ($this->activitylayout == false)) { return parent::course_section_cm_list_item($course, $completioninfo, $mod, $sectionreturn, $displayoptions); } $output = ''; $modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses; if (empty($mod->url)) { $modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions); $output .= html_writer::tag('div', $modulehtml, array('class' => $modclasses, 'id' => 'module-' . $mod->id)); $url = $mod->url; if (!empty($url)) { $output .= $this->course_section_cm_text($mod, $displayoptions); } } else { $url = $mod->url; if (!$mod->is_visible_on_course_page() || !$url) { // Nothing to be displayed to the user. return $output; } $modclasses = trim($modclasses).' card'; list($linkclasses, $textclasses) = $this->course_section_cm_classes($mod); /* Get on-click attribute value if specified and decode the onclick - it has already been encoded for display. */ $onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES); $activitydata = $this->squared_activity_header($mod); $activity = $activitydata['output']; if ($mod->uservisible) { $cardcontent = html_writer::link($url, $activity, array('class' => $linkclasses.' card-header', 'onclick' => $onclick)); } else { /* We may be displaying this just in order to show information about visibility, without the actual link ($mod->is_visible_on_course_page()). */ $cardcontent = html_writer::tag('div', $activity, array('class' => $textclasses.' card-header')); } $formattedcontent = $mod->get_formatted_content(array('overflowdiv' => false, 'noclean' => true)); $contentlen = core_text::strlen(strip_tags($mod->content)); if ((!empty($formattedcontent)) || ($displayoptions['sqshowdescription'] == self::showmodsummary)) { if ($contentlen > 45) { $textclasses .= ' sqexpandedcontent'; } if ($contentlen > 2748) { $textclasses .= ' sqmorecontent'; } $bodycontent = html_writer::tag('div', $formattedcontent, array('class' => trim('contentafterlink sqcontent '.$textclasses))); $cardcontent .= html_writer::start_tag('div', array('class' => 'card-body')); $cardcontent .= $bodycontent; $cardcontent .= html_writer::end_tag('div'); } // Show availability info (if module is not available). $footercontent = $this->course_section_cm_availability($mod, $displayoptions); $modicons = $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions); if (!empty($modicons)) { $footercontent .= html_writer::span($modicons, 'actions sqactions'); } if ((($formattedcontent) && ($contentlen > 45)) || ($activitydata['toexpand'])) { if (\theme_squared\toolbox::get_config_setting('fav')) { $icon = 'fas fa-chevron-circle-down'; } else { $icon = 'fa fa-chevron-circle-down'; } $footercontent .= html_writer::tag('div', html_writer::tag('i', null, array('class' => $icon, 'aria-hidden' => 'true', 'role' => 'button')). html_writer::tag('span', get_string('expand'), array('class' => 'sqcc sqccopen', 'aria-hidden' => 'false')). html_writer::tag('span', get_string('closebuttontitle'), array('class' => 'sqcc sqccclose hidden', 'aria-hidden' => 'true')), array('class' => 'sqcontentcontrol')); } $cardcontent .= html_writer::start_tag('div', array('class' => 'card-footer')); $cardcontent .= $footercontent; $cardcontent .= html_writer::end_tag('div'); $output .= html_writer::tag('div', $cardcontent, array('class' => $modclasses, 'id' => 'module-' . $mod->id)); } return $output; } /** * Renders HTML to display a list of course modules in a course section * Also displays "move here" controls in Javascript-disabled mode * * This function calls {@link core_course_renderer::course_section_cm()} * * @param stdClass $course course object * @param int|stdClass|section_info $section relative section number or section object * @param int $sectionreturn section number to return to * @param array $displayoptions * @return void */ public function course_section_cm_list($course, $section, $sectionreturn = null, $displayoptions = array()) { if (($this->userisediting == true) || ($this->activitylayout == false)) { return parent::course_section_cm_list($course, $section, $sectionreturn, $displayoptions); } $output = ''; $modinfo = get_fast_modinfo($course); if (is_object($section)) { $section = $modinfo->get_section_info($section->section); } else { $section = $modinfo->get_section_info($section); } $completioninfo = new completion_info($course); // Get the list of modules visible to user. $sectionoutput = ''; if (!empty($modinfo->sections[$section->section])) { $showdescriptions = $this->calculate_show_descriptions($modinfo->sections[$section->section], $modinfo); foreach ($modinfo->sections[$section->section] as $modnumber) { $mod = $modinfo->cms[$modnumber]; if ($showdescriptions[$modnumber] != self::moddivider) { $displayoptions['sqshowdescription'] = $showdescriptions[$modnumber]; } if ($modulehtml = $this->course_section_cm_list_item($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) { if (!(empty($mod->url))) { switch ($this->activitylayout) { case 1: // 1,3,3. $modclasses = 'col-sm-12 col-md-4 col-lg-4'; break; case 2: // 1,2,4. $modclasses = 'col-sm-12 col-md-6 col-lg-3'; break; case 3: // 1,2,3. $modclasses = 'col-sm-12 col-md-6 col-lg-4'; break; default: // 1,3,3. $modclasses = 'col-sm-12 col-md-4 col-lg-4'; } $modclasses .= ' sqcol'; } else { $modclasses = 'col-sm-12'; } $sectionoutput .= html_writer::tag('div', $modulehtml, array('class' => $modclasses)); } } } // Always output the section module list. $output .= html_writer::tag('div', $sectionoutput, array('class' => 'section img-text row')); return $output; } /** * Calculates if a module should show its description. * * This function calls {@link core_course_renderer::course_section_cm()} * * @param array $sectionmodules int module numbers * @param course_modinfo $modinfo module info for the course. * * @return array boolean indicating if the description should be shown or self::moddivider if a label, indexed by module number. */ private function calculate_show_descriptions($sectionmodules, $modinfo) { $showdescriptions = array(); $current = false; foreach ($sectionmodules as $modnumber) { $mod = $modinfo->cms[$modnumber]; if (!(empty($mod->url))) { if ($current == false) { $formattedcontent = $mod->get_formatted_content(array('overflowdiv' => false, 'noclean' => true)); if (empty($formattedcontent)) { $showdescriptions[$modnumber] = self::hidemodsummary; } else { $current = true; $showdescriptions[$modnumber] = self::showmodsummary; // Go back to the last label / start of section and show the rest. $reversed = array_reverse($showdescriptions, true); foreach($reversed as $modno => $modsd) { if ($modsd == self::moddivider) { // Label, so break. break; } $showdescriptions[$modno] = self::showmodsummary; } } } else { $showdescriptions[$modnumber] = self::showmodsummary; } } else { $showdescriptions[$modnumber] = self::moddivider; // A label. $current = false; // A label is a divider. } } return $showdescriptions; } /** * Serves requests to /theme/squared/inspector.ajax.php * * @param string $term search term. * @return array of results. * @throws coding_exception */ public function inspector_ajax($term) { global $USER; $data = array(); $courses = enrol_get_my_courses(); $site = get_site(); if (array_key_exists($site->id, $courses)) { unset($courses[$site->id]); } foreach ($courses as $c) { if (isset($USER->lastcourseaccess[$c->id])) { $courses[$c->id]->lastaccess = $USER->lastcourseaccess[$c->id]; } else { $courses[$c->id]->lastaccess = 0; } } // Get remote courses. $remotecourses = array(); if (is_enabled_auth('mnet')) { $remotecourses = get_my_remotecourses(); } // Remote courses will have -ve remoteid as key, so it can be differentiated from normal courses. foreach ($remotecourses as $id => $val) { $remoteid = $val->remoteid * -1; $val->id = $remoteid; $courses[$remoteid] = $val; } foreach ($courses as $course) { $modinfo = get_fast_modinfo($course); $courseformat = course_get_format($course->id); $course = $courseformat->get_course(); $courseformatsettings = $courseformat->get_format_options(); $coursenumsections = $courseformat->get_last_section_number(); $sesskey = sesskey(); // Course name. $label = $course->fullname; if (stristr($label, $term)) { $courseurl = new moodle_url('/course/view.php'); $courseurl->param('id', $course->id); $courseurl->param('sesskey', $sesskey); $coursehref = $courseurl->out(false); $data[] = array('id' => $coursehref, 'label' => $label, 'value' => $label); } foreach ($modinfo->get_section_info_all() as $section => $thissection) { if (!$thissection->uservisible) { continue; } if (is_object($thissection)) { $thissection = $modinfo->get_section_info($thissection->section); } else { $thissection = $modinfo->get_section_info($thissection); } if ((string) $thissection->name !== '') { $sectionname = format_string($thissection->name, true, array('context' => context_course::instance($course->id))); } else { $sectionname = $courseformat->get_section_name($thissection->section); } if ($thissection->section <= $coursenumsections) { // Do not link 'orphaned' sections. $label = $course->fullname . ' - ' . $sectionname; if (stristr($label, $term)) { $courseurl = new moodle_url('/course/view.php'); $courseurl->param('id', $course->id); $courseurl->param('sesskey', $sesskey); if ((!empty($courseformatsettings['coursedisplay'])) && ($courseformatsettings['coursedisplay'] == COURSE_DISPLAY_MULTIPAGE)) { $courseurl->param('section', $thissection->section); $coursehref = $courseurl->out(false); } else { $coursehref = $courseurl->out(false) . '#section-' . $thissection->section; } $data[] = array('id' => $coursehref, 'label' => $label, 'value' => $label); } } if (!empty($modinfo->sections[$thissection->section])) { foreach ($modinfo->sections[$thissection->section] as $modnumber) { $mod = $modinfo->cms[$modnumber]; if (!empty($mod->url)) { $instancename = $mod->get_formatted_name(); $label = $course->fullname . ' - ' . $sectionname . ' - ' . $instancename; if (stristr($label, $term)) { $data[] = array('id' => $mod->url->out(false), 'label' => $label, 'value' => $label); } } } } } } return $data; } // Course listing #1009. /** * Renders HTML to display particular course category - list of it's subcategories and courses * * Invoked from /course/index.php * * @param int|stdClass|coursecat $category */ public function course_category($category) { global $CFG; $coursecat = core_course_category::get(is_object($category) ? $category->id : $category); $site = get_site(); $output = ''; if (can_edit_in_category($coursecat->id)) { // Add 'Manage' button if user has permissions to edit this category. $managebutton = $this->single_button(new moodle_url('/course/management.php', array('categoryid' => $coursecat->id)), get_string('managecourses'), 'get'); $this->page->set_button($managebutton); } $catcount = core_course_category::is_simple_site(); if (!$coursecat->id) { if ($catcount == 1) { // There exists only one category in the system, do not display link to it $coursecat = core_course_category::get_default(); $strfulllistofcourses = get_string('fulllistofcourses'); $this->page->set_title("$site->shortname: $strfulllistofcourses"); } else { $strcategories = get_string('categories'); $this->page->set_title("$site->shortname: $strcategories"); } } else { $title = $site->shortname; if ($catcount > 1) { $title .= ": " . $coursecat->get_formatted_name(); } $this->page->set_title($title); } // Print current category description $chelper = new coursecat_helper(); if ($description = $chelper->get_category_formatted_description($coursecat)) { $output .= $this->box($description, array('class' => 'generalbox info')); } $coursedisplayoptions = array(); // Used in 'coursecat_tree()' and needed in 'coursecat_courses()'. $coursedisplayoptions['sqcategorysearch'] = optional_param('sqcategorysearch', '', PARAM_TEXT); $coursedisplayoptions['sqcardlayout'] = true; $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); $page = optional_param('page', 0, PARAM_INT); $baseurl = new moodle_url('/course/index.php'); if ($coursecat->id) { $baseurl->param('categoryid', $coursecat->id); } else { $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT); // State the category when all categories. } if ($perpage != $CFG->coursesperpage) { $baseurl->param('perpage', $perpage); } if ($perpage == 'all') { // Does the search on all and established in coursecat_courses_content(). $coursedisplayoptions['limit'] = null; $coursedisplayoptions['offset'] = 0; } else { $coursedisplayoptions['limit'] = $perpage; $coursedisplayoptions['offset'] = $page * $perpage; } $coursedisplayoptions['paginationurl'] = new moodle_url($baseurl); $this->set_squared_search($coursedisplayoptions['paginationurl']); $chelper->set_courses_display_options($coursedisplayoptions); // Display course category tree. $output .= $this->coursecat_tree($chelper, $coursecat); // Add action buttons $output .= $this->container_start('buttons'); $context = get_category_or_system_context($coursecat->id); if (has_capability('moodle/course:create', $context)) { // Print link to create a new course, for the 1st available category. if ($coursecat->id) { $url = new moodle_url('/course/edit.php', array('category' => $coursecat->id, 'returnto' => 'category')); } else { $url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat')); } $output .= $this->single_button($url, get_string('addnewcourse'), 'get'); } ob_start(); if ($catcount == 1) { print_course_request_buttons(context_system::instance()); } else { print_course_request_buttons($context); } $output .= ob_get_contents(); ob_end_clean(); $output .= $this->container_end(); return $output; } /** * Returns HTML to display a tree of subcategories and courses in the given category * * @param coursecat_helper $chelper various display options * @param coursecat $coursecat top category (this category's name and description will NOT be added to the tree) * @return string */ protected function coursecat_tree(coursecat_helper $chelper, $coursecat) { if (!$this->is_card_layout($chelper)) { return parent::coursecat_tree($chelper, $coursecat); } $this->currentcategoryid = $coursecat->id; $this->categorysearchsort = optional_param('searchsort', 1, PARAM_INT); $courses = $this->coursecat_toolbox->search_courses( $chelper->get_courses_display_option('sqcategorysearch'), array( 'categoryid' => $coursecat->id, 'limit' => $chelper->get_courses_display_option('limit'), 'offset' => $chelper->get_courses_display_option('offset'), 'sort' => array('sortorder' => $this->categorysearchsort) ) ); $categorycontent = $this->coursecat_courses($chelper, $courses['courses'], $courses['totalcount']); if (empty($categorycontent)) { return ''; } // Start content generation $content = ''; $attributes = $chelper->get_and_erase_attributes('course_category_tree clearfix'); $content .= html_writer::start_tag('div', $attributes); $content .= html_writer::tag('div', $categorycontent, array('class' => 'content')); $content .= html_writer::end_tag('div'); // .course_category_tree return $content; } /** * Renders the list of courses * * This is internal function, please use {@link core_course_renderer::courses_list()} or another public * method from outside of the class * * If list of courses is specified in $courses; the argument $chelper is only used * to retrieve display options and attributes, only methods get_show_courses(), * get_courses_display_option() and get_and_erase_attributes() are called. * * @param coursecat_helper $chelper various display options * @param array $courses the list of courses to display * @param init $totalcount total count of the courses * @return string */ protected function coursecat_courses_content(coursecat_helper $chelper, $courses, $totalcount) { global $CFG; // Prepare content of paging bar if it is needed. $pagingbar = null; $paginationurl = $chelper->get_courses_display_option('paginationurl'); if ($paginationurl) { if ($totalcount > count($courses)) { // There are more results that can fit on one page. $perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage); $page = $chelper->get_courses_display_option('offset') / $perpage; $pagingbar = $this->paging_bar($totalcount, $page, $perpage, $paginationurl->out(false, array('perpage' => $perpage))); $pagingallbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')), get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall mdl-align')); } else if ($totalcount > $CFG->coursesperpage) { // There is more than one page of results and we are in 'show all' mode, suggest to go back to paginated view mode. $pagingallbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)), get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage mdl-align')); } } // Display list of courses. $content = ''; if (!empty($pagingbar)) { $content .= $pagingbar; } $content .= html_writer::start_tag('div', array('class' => 'sqcoursecards card-deck')); $coursecount = 0; foreach ($courses as $course) { $coursecount++; $classes = ($coursecount % 2) ? 'odd' : 'even'; if ($coursecount == 1) { $classes .= ' first'; } if ($coursecount >= count($courses)) { $classes .= ' last'; } $content .= $this->coursecat_coursebox($chelper, $course, $classes); } $content .= html_writer::end_tag('div'); // .card-deck if (!empty($pagingbar)) { $content .= $pagingbar; } if (!empty($pagingallbar)) { $content .= $pagingallbar; } return $content; } /** * Renders the list of courses * * This is internal function, please use {@link core_course_renderer::courses_list()} or another public * method from outside of the class * * If list of courses is specified in $courses; the argument $chelper is only used * to retrieve display options and attributes, only methods get_show_courses(), * get_courses_display_option() and get_and_erase_attributes() are called. * * @param coursecat_helper $chelper various display options * @param array $courses the list of courses to display * @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable), * defaulted to count($courses) * @return string */ protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) { if (!$this->is_card_layout($chelper)) { return parent::coursecat_courses($chelper, $courses, $totalcount); } if ($totalcount === null) { $totalcount = count($courses); } if (!$totalcount) { // Courses count is cached during courses retrieval. return ''; } $content = html_writer::start_tag('div', array('id' => 'sqccf', 'class' => 'row justify-content-between')); // Start search row. $content .= html_writer::start_tag('div', array('class' => 'col-md-6 col-lg-4')); // Start search category. $content .= $this->squared_category_select_search(); $content .= html_writer::end_tag('div'); // End search category. $content .= html_writer::start_tag('div', array('class' => 'col-md-6 col-lg-4')); // Start search form. $content .= $this->squared_category_course_search($chelper->get_courses_display_option('sqcategorysearch')); $content .= html_writer::end_tag('div'); // End search form. $content .= html_writer::start_tag('div', array('class' => 'col-md-12 col-lg-4')); // Start search sort. $content .= $this->squared_search_sort(); $content .= html_writer::end_tag('div'); // End search sort. $content .= html_writer::end_tag('div'); // End search row. // Display list of courses. $attributes = $chelper->get_and_erase_attributes('courses'); $attributes['id'] = 'sqccs'; $content .= html_writer::start_tag('div', $attributes); $content .= $this->coursecat_courses_content($chelper, $courses, $totalcount); $content .= html_writer::end_tag('div'); // .courses return $content; } /** * Generate the category select markup. * * @return string Markup. */ protected function squared_category_select_search() { $cats = $this->coursecat_toolbox::make_categories_list(); $content = html_writer::start_tag('div', array('class' => 'sqscat')); $content .= html_writer::start_tag('form', array('class' => 'mdl-align')); $content .= html_writer::tag('label', get_string('coursecategory') . ': ', array('for' => 'sq-category-select', 'class' => 'd-inline')); $content .= html_writer::start_tag('select', array( 'class' => 'sqselect sqformelement', 'disabled' => 'disabled', 'id' => 'sq-category-select', 'name' => 'squaredcategoryselect') ); $attrs = array('value' => '0'); if ($this->currentcategoryid == 0) { $attrs['selected'] = 'selected'; } $content .= html_writer::tag('option', get_string('all'), $attrs); foreach ($cats as $catkey => $catdata) { $coursecat = core_course_category::get($catkey); $attrs = array('value' => $catkey); if ($catkey == $this->currentcategoryid) { $attrs['selected'] = 'selected'; } if ((!empty($coursecat->theme)) && ($coursecat->theme != 'squared')) { $attrs['data-differenttheme'] = 'true'; } $catdatalen = mb_strlen($catdata); if ($catdatalen >= 50) { $catdata = '...'.mb_substr($catdata, ($catdatalen - 47)); } $content .= html_writer::tag('option', $catdata, $attrs); } $content .= html_writer::end_tag('select'); $content .= html_writer::end_tag('form'); $content .= html_writer::end_tag('div'); // AJAX initialised in squared_category_course_search(). return $content; } /** * Returns the Squared category course search form and initialises the associated jQuery AMD module. * * @param string $sqcategorysearch Current search if any. * @return string Markup. */ protected function squared_category_course_search($sqcategorysearch) { $content = html_writer::start_tag('form', array('class' => 'mdl-align')); $content .= html_writer::tag('label', get_string('searchcourses') . ': ', array('for' => 'sq-category-search', 'class' => 'd-inline')); $content .= html_writer::empty_tag('input', array( 'class' => 'sqinput sqformelement', 'disabled' => 'disabled', 'id' => 'sq-category-search', 'name' => 'sqcategorysearch', 'size' => '28', 'type' => 'text', 'value' => $sqcategorysearch // Deal with populating the field if the user presses enter. ) ); $content .= html_writer::end_tag('form'); $siteurl = new \moodle_url('/course/index.php'); // Needs to be this as can read category id. $ajaxurl = clone $siteurl; $this->set_squared_search($ajaxurl); $categorycoursesearchdata = array('data' => array( 'siteurl' => $siteurl->out(false), 'ajaxurl' => $ajaxurl->out(false), 'categorystr' => get_string('category'), 'catid' => $this->currentcategoryid, 'sort' => $this->categorysearchsort)); $this->page->requires->js_call_amd('theme_squared/category_course_search', 'init', $categorycoursesearchdata); return $content; } /** * Returns the Squared category course sort search form. * * @return string Markup. */ protected function squared_search_sort() { $content = html_writer::start_tag('div', array('class' => 'sqssort')); $content .= html_writer::start_tag('form', array('class' => 'mdl-align')); $content .= html_writer::tag('label', get_string('sort') . ': ', array('for' => 'sq-category-sort', 'class' => 'd-inline')); $content .= html_writer::start_tag('select', array( 'class' => 'sqselect sqformelement', 'disabled' => 'disabled', 'id' => 'sq-category-sort', 'name' => 'squaredcategorysort') ); $attrs = array('value' => '1'); if ($this->categorysearchsort == 1) { $attrs['selected'] = 'selected'; } $content .= html_writer::tag('option', get_string('asc'), $attrs); $attrs = array('value' => '-1'); if ($this->categorysearchsort == -1) { $attrs['selected'] = 'selected'; } $content .= html_writer::tag('option', get_string('desc'), $attrs); $content .= html_writer::end_tag('select'); $content .= html_writer::end_tag('form'); $content .= html_writer::end_tag('div'); // AJAX initialised in squared_category_course_search(). return $content; } /** * Displays one course in the list of courses. * * This is an internal function, to display an information about just one course * please use {@link core_course_renderer::course_info_box()} * * @param coursecat_helper $chelper various display options * @param course_in_list|stdClass $course * @param string $additionalclasses additional classes to add to the main