See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 403]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * Renderer for use with the course section and all the goodness that falls 20 * within it. 21 * 22 * This renderer should contain methods useful to courses, and categories. 23 * 24 * @package moodlecore 25 * @copyright 2010 Sam Hemelryk 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 */ 28 29 /** 30 * The core course renderer 31 * 32 * Can be retrieved with the following: 33 * $renderer = $PAGE->get_renderer('core','course'); 34 */ 35 class core_course_renderer extends plugin_renderer_base { 36 const COURSECAT_SHOW_COURSES_NONE = 0; /* do not show courses at all */ 37 const COURSECAT_SHOW_COURSES_COUNT = 5; /* do not show courses but show number of courses next to category name */ 38 const COURSECAT_SHOW_COURSES_COLLAPSED = 10; 39 const COURSECAT_SHOW_COURSES_AUTO = 15; /* will choose between collapsed and expanded automatically */ 40 const COURSECAT_SHOW_COURSES_EXPANDED = 20; 41 const COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT = 30; 42 43 const COURSECAT_TYPE_CATEGORY = 0; 44 const COURSECAT_TYPE_COURSE = 1; 45 46 /** 47 * A cache of strings 48 * @var stdClass 49 */ 50 protected $strings; 51 52 /** 53 * Whether a category content is being initially rendered with children. This is mainly used by the 54 * core_course_renderer::corsecat_tree() to render the appropriate action for the Expand/Collapse all link on 55 * page load. 56 * @var bool 57 */ 58 protected $categoryexpandedonload = false; 59 60 /** 61 * Override the constructor so that we can initialise the string cache 62 * 63 * @param moodle_page $page 64 * @param string $target 65 */ 66 public function __construct(moodle_page $page, $target) { 67 $this->strings = new stdClass; 68 $courseid = $page->course->id; 69 parent::__construct($page, $target); 70 } 71 72 /** 73 * @deprecated since 3.2 74 */ 75 protected function add_modchoosertoggle() { 76 throw new coding_exception('core_course_renderer::add_modchoosertoggle() can not be used anymore.'); 77 } 78 79 /** 80 * Renders course info box. 81 * 82 * @param stdClass $course 83 * @return string 84 */ 85 public function course_info_box(stdClass $course) { 86 $content = ''; 87 $content .= $this->output->box_start('generalbox info'); 88 $chelper = new coursecat_helper(); 89 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED); 90 $content .= $this->coursecat_coursebox($chelper, $course); 91 $content .= $this->output->box_end(); 92 return $content; 93 } 94 95 /** 96 * Renderers a structured array of courses and categories into a nice XHTML tree structure. 97 * 98 * @deprecated since 2.5 99 * 100 * @param array $ignored argument ignored 101 * @return string 102 */ 103 public final function course_category_tree(array $ignored) { 104 debugging('Function core_course_renderer::course_category_tree() is deprecated, please use frontpage_combo_list()', DEBUG_DEVELOPER); 105 return $this->frontpage_combo_list(); 106 } 107 108 /** 109 * Renderers a category for use with course_category_tree 110 * 111 * @deprecated since 2.5 112 * 113 * @param array $category 114 * @param int $depth 115 * @return string 116 */ 117 protected final function course_category_tree_category(stdClass $category, $depth=1) { 118 debugging('Function core_course_renderer::course_category_tree_category() is deprecated', DEBUG_DEVELOPER); 119 return ''; 120 } 121 122 /** 123 * Render a modchooser. 124 * 125 * @param renderable $modchooser The chooser. 126 * @return string 127 */ 128 public function render_modchooser(renderable $modchooser) { 129 return $this->render_from_template('core_course/modchooser', $modchooser->export_for_template($this)); 130 } 131 132 /** 133 * @deprecated since 3.9 134 */ 135 public function course_modchooser() { 136 throw new coding_exception('course_modchooser() can not be used anymore, please use course_activitychooser() instead.'); 137 } 138 139 /** 140 * Build the HTML for the module chooser javascript popup. 141 * 142 * @param int $courseid The course id to fetch modules for. 143 * @return string 144 */ 145 public function course_activitychooser($courseid) { 146 147 if (!$this->page->requires->should_create_one_time_item_now('core_course_modchooser')) { 148 return ''; 149 } 150 151 // Build an object of config settings that we can then hook into in the Activity Chooser. 152 $chooserconfig = (object) [ 153 'tabmode' => get_config('core', 'activitychoosertabmode'), 154 ]; 155 $this->page->requires->js_call_amd('core_course/activitychooser', 'init', [$courseid, $chooserconfig]); 156 157 return ''; 158 } 159 160 /** 161 * Build the HTML for a specified set of modules 162 * 163 * @param array $modules A set of modules as used by the 164 * course_modchooser_module function 165 * @return string The composed HTML for the module 166 */ 167 protected function course_modchooser_module_types($modules) { 168 debugging('Method core_course_renderer::course_modchooser_module_types() is deprecated, ' . 169 'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER); 170 return ''; 171 } 172 173 /** 174 * Return the HTML for the specified module adding any required classes 175 * 176 * @param object $module An object containing the title, and link. An 177 * icon, and help text may optionally be specified. If the module 178 * contains subtypes in the types option, then these will also be 179 * displayed. 180 * @param array $classes Additional classes to add to the encompassing 181 * div element 182 * @return string The composed HTML for the module 183 */ 184 protected function course_modchooser_module($module, $classes = array('option')) { 185 debugging('Method core_course_renderer::course_modchooser_module() is deprecated, ' . 186 'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER); 187 return ''; 188 } 189 190 protected function course_modchooser_title($title, $identifier = null) { 191 debugging('Method core_course_renderer::course_modchooser_title() is deprecated, ' . 192 'see core_course_renderer::render_modchooser().', DEBUG_DEVELOPER); 193 return ''; 194 } 195 196 /** 197 * Renders HTML for displaying the sequence of course module editing buttons 198 * 199 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 200 * 201 * @see course_get_cm_edit_actions() 202 * 203 * @param action_link[] $actions Array of action_link objects 204 * @param cm_info $mod The module we are displaying actions for. 205 * @param array $displayoptions additional display options: 206 * ownerselector => A JS/CSS selector that can be used to find an cm node. 207 * If specified the owning node will be given the class 'action-menu-shown' when the action 208 * menu is being displayed. 209 * constraintselector => A JS/CSS selector that can be used to find the parent node for which to constrain 210 * the action menu to when it is being displayed. 211 * donotenhance => If set to true the action menu that gets displayed won't be enhanced by JS. 212 * @return string 213 */ 214 public function course_section_cm_edit_actions($actions, cm_info $mod = null, $displayoptions = array()) { 215 global $CFG; 216 217 debugging( 218 'course_section_cm_edit_actions is deprecated. Use core_courseformat\\output\\local\\content\\cm\\controlmenu instead.', 219 DEBUG_DEVELOPER 220 ); 221 222 if (empty($actions)) { 223 return ''; 224 } 225 226 if (isset($displayoptions['ownerselector'])) { 227 $ownerselector = $displayoptions['ownerselector']; 228 } else if ($mod) { 229 $ownerselector = '#module-'.$mod->id; 230 } else { 231 debugging('You should upgrade your call to '.__FUNCTION__.' and provide $mod', DEBUG_DEVELOPER); 232 $ownerselector = 'li.activity'; 233 } 234 235 if (isset($displayoptions['constraintselector'])) { 236 $constraint = $displayoptions['constraintselector']; 237 } else { 238 $constraint = '.course-content'; 239 } 240 241 $menu = new action_menu(); 242 $menu->set_owner_selector($ownerselector); 243 $menu->set_constraint($constraint); 244 $menu->set_menu_trigger(get_string('edit')); 245 246 foreach ($actions as $action) { 247 if ($action instanceof action_menu_link) { 248 $action->add_class('cm-edit-action'); 249 } 250 $menu->add($action); 251 } 252 $menu->attributes['class'] .= ' section-cm-edit-actions commands'; 253 254 // Prioritise the menu ahead of all other actions. 255 $menu->prioritise = true; 256 257 return $this->render($menu); 258 } 259 260 /** 261 * Renders HTML for the menus to add activities and resources to the current course 262 * 263 * Renders the ajax control (the link which when clicked produces the activity chooser modal). No noscript fallback. 264 * 265 * @param stdClass $course 266 * @param int $section relative section number (field course_sections.section) 267 * @param int $sectionreturn The section to link back to 268 * @param array $displayoptions additional display options, for example blocks add 269 * option 'inblock' => true, suggesting to display controls vertically 270 * @return string 271 */ 272 function course_section_add_cm_control($course, $section, $sectionreturn = null, $displayoptions = array()) { 273 // Check to see if user can add menus. 274 if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id)) 275 || !$this->page->user_is_editing()) { 276 return ''; 277 } 278 279 $data = [ 280 'sectionid' => $section, 281 'sectionreturn' => $sectionreturn 282 ]; 283 $ajaxcontrol = $this->render_from_template('course/activitychooserbutton', $data); 284 285 // Load the JS for the modal. 286 $this->course_activitychooser($course->id); 287 288 return $ajaxcontrol; 289 } 290 291 /** 292 * Render the deprecated nonajax activity chooser. 293 * 294 * @deprecated since Moodle 3.11 295 * 296 * @todo MDL-71331 deprecate this function 297 * @param stdClass $course the course object 298 * @param int $section relative section number (field course_sections.section) 299 * @param int $sectionreturn The section to link back to 300 * @param array $displayoptions additional display options, for example blocks add 301 * option 'inblock' => true, suggesting to display controls vertically 302 * @return string 303 */ 304 private function course_section_add_cm_control_nonajax($course, $section, $sectionreturn = null, 305 $displayoptions = array()): string { 306 global $USER; 307 308 $vertical = !empty($displayoptions['inblock']); 309 310 // Check to see if user can add menus. 311 if ( 312 !has_capability('moodle/course:manageactivities', context_course::instance($course->id)) 313 || !$this->page->user_is_editing() 314 ) { 315 return ''; 316 } 317 318 debugging('non-js dropdowns are deprecated.', DEBUG_DEVELOPER); 319 // Retrieve all modules with associated metadata. 320 $contentitemservice = \core_course\local\factory\content_item_service_factory::get_content_item_service(); 321 $urlparams = ['section' => $section]; 322 if (!is_null($sectionreturn)) { 323 $urlparams['sr'] = $sectionreturn; 324 } 325 $modules = $contentitemservice->get_content_items_for_user_in_course($USER, $course, $urlparams); 326 327 // Return if there are no content items to add. 328 if (empty($modules)) { 329 return ''; 330 } 331 332 // We'll sort resources and activities into two lists. 333 $activities = array(MOD_CLASS_ACTIVITY => array(), MOD_CLASS_RESOURCE => array()); 334 335 foreach ($modules as $module) { 336 $activityclass = MOD_CLASS_ACTIVITY; 337 if ($module->archetype == MOD_ARCHETYPE_RESOURCE) { 338 $activityclass = MOD_CLASS_RESOURCE; 339 } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) { 340 // System modules cannot be added by user, do not add to dropdown. 341 continue; 342 } 343 $link = $module->link; 344 $activities[$activityclass][$link] = $module->title; 345 } 346 347 $straddactivity = get_string('addactivity'); 348 $straddresource = get_string('addresource'); 349 $sectionname = get_section_name($course, $section); 350 $strresourcelabel = get_string('addresourcetosection', null, $sectionname); 351 $stractivitylabel = get_string('addactivitytosection', null, $sectionname); 352 353 $nonajaxcontrol = html_writer::start_tag('div', array('class' => 'section_add_menus', 'id' => 'add_menus-section-' 354 . $section)); 355 356 if (!$vertical) { 357 $nonajaxcontrol .= html_writer::start_tag('div', array('class' => 'horizontal')); 358 } 359 360 if (!empty($activities[MOD_CLASS_RESOURCE])) { 361 $select = new url_select($activities[MOD_CLASS_RESOURCE], '', array('' => $straddresource), "ressection$section"); 362 $select->set_help_icon('resources'); 363 $select->set_label($strresourcelabel, array('class' => 'accesshide')); 364 $nonajaxcontrol .= $this->output->render($select); 365 } 366 367 if (!empty($activities[MOD_CLASS_ACTIVITY])) { 368 $select = new url_select($activities[MOD_CLASS_ACTIVITY], '', array('' => $straddactivity), "section$section"); 369 $select->set_help_icon('activities'); 370 $select->set_label($stractivitylabel, array('class' => 'accesshide')); 371 $nonajaxcontrol .= $this->output->render($select); 372 } 373 374 if (!$vertical) { 375 $nonajaxcontrol .= html_writer::end_tag('div'); 376 } 377 378 $nonajaxcontrol .= html_writer::end_tag('div'); 379 380 return $nonajaxcontrol; 381 } 382 383 /** 384 * Renders html to display a course search form 385 * 386 * @param string $value default value to populate the search field 387 * @return string 388 */ 389 public function course_search_form($value = '') { 390 391 $data = [ 392 'action' => \core_search\manager::get_course_search_url(), 393 'btnclass' => 'btn-primary', 394 'inputname' => 'q', 395 'searchstring' => get_string('searchcourses'), 396 'hiddenfields' => (object) ['name' => 'areaids', 'value' => 'core_course-course'], 397 'query' => $value 398 ]; 399 return $this->render_from_template('core/search_input', $data); 400 } 401 402 /** 403 * Renders html for completion box on course page 404 * 405 * If completion is disabled, returns empty string 406 * If completion is automatic, returns an icon of the current completion state 407 * If completion is manual, returns a form (with an icon inside) that allows user to 408 * toggle completion 409 * 410 * @deprecated since Moodle 3.11 411 * @todo MDL-71183 Final deprecation in Moodle 4.3. 412 * @see \core_renderer::activity_information 413 * 414 * @param stdClass $course course object 415 * @param completion_info $completioninfo completion info for the course, it is recommended 416 * to fetch once for all modules in course/section for performance 417 * @param cm_info $mod module to show completion for 418 * @param array $displayoptions display options, not used in core 419 * @return string 420 */ 421 public function course_section_cm_completion($course, &$completioninfo, cm_info $mod, $displayoptions = array()) { 422 global $CFG, $DB, $USER; 423 424 debugging(__FUNCTION__ . ' is deprecated and is being replaced by the activity_information output component.', 425 DEBUG_DEVELOPER); 426 427 $output = ''; 428 429 $istrackeduser = $completioninfo->is_tracked_user($USER->id); 430 $isediting = $this->page->user_is_editing(); 431 432 if (!empty($displayoptions['hidecompletion']) || !isloggedin() || isguestuser() || !$mod->uservisible) { 433 return $output; 434 } 435 if ($completioninfo === null) { 436 $completioninfo = new completion_info($course); 437 } 438 $completion = $completioninfo->is_enabled($mod); 439 440 if ($completion == COMPLETION_TRACKING_NONE) { 441 if ($isediting) { 442 $output .= html_writer::span(' ', 'filler'); 443 } 444 return $output; 445 } 446 447 $completionicon = ''; 448 449 if ($isediting || !$istrackeduser) { 450 switch ($completion) { 451 case COMPLETION_TRACKING_MANUAL : 452 $completionicon = 'manual-enabled'; break; 453 case COMPLETION_TRACKING_AUTOMATIC : 454 $completionicon = 'auto-enabled'; break; 455 } 456 } else { 457 $completiondata = $completioninfo->get_data($mod, true); 458 if ($completion == COMPLETION_TRACKING_MANUAL) { 459 switch($completiondata->completionstate) { 460 case COMPLETION_INCOMPLETE: 461 $completionicon = 'manual-n' . ($completiondata->overrideby ? '-override' : ''); 462 break; 463 case COMPLETION_COMPLETE: 464 $completionicon = 'manual-y' . ($completiondata->overrideby ? '-override' : ''); 465 break; 466 } 467 } else { // Automatic 468 switch($completiondata->completionstate) { 469 case COMPLETION_INCOMPLETE: 470 $completionicon = 'auto-n' . ($completiondata->overrideby ? '-override' : ''); 471 break; 472 case COMPLETION_COMPLETE: 473 $completionicon = 'auto-y' . ($completiondata->overrideby ? '-override' : ''); 474 break; 475 case COMPLETION_COMPLETE_PASS: 476 $completionicon = 'auto-pass'; break; 477 case COMPLETION_COMPLETE_FAIL: 478 $completionicon = 'auto-fail'; break; 479 } 480 } 481 } 482 if ($completionicon) { 483 $formattedname = html_entity_decode($mod->get_formatted_name(), ENT_QUOTES, 'UTF-8'); 484 if (!$isediting && $istrackeduser && $completiondata->overrideby) { 485 $args = new stdClass(); 486 $args->modname = $formattedname; 487 $overridebyuser = \core_user::get_user($completiondata->overrideby, '*', MUST_EXIST); 488 $args->overrideuser = fullname($overridebyuser); 489 $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $args); 490 } else { 491 $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname); 492 } 493 494 if ($isediting || !$istrackeduser || !has_capability('moodle/course:togglecompletion', $mod->context)) { 495 // When editing, the icon is just an image. 496 $completionpixicon = new pix_icon('i/completion-'.$completionicon, $imgalt, '', 497 array('title' => $imgalt, 'class' => 'iconsmall')); 498 $output .= html_writer::tag('span', $this->output->render($completionpixicon), 499 array('class' => 'autocompletion')); 500 } else if ($completion == COMPLETION_TRACKING_MANUAL) { 501 $newstate = 502 $completiondata->completionstate == COMPLETION_COMPLETE 503 ? COMPLETION_INCOMPLETE 504 : COMPLETION_COMPLETE; 505 // In manual mode the icon is a toggle form... 506 507 // If this completion state is used by the 508 // conditional activities system, we need to turn 509 // off the JS. 510 $extraclass = ''; 511 if (!empty($CFG->enableavailability) && 512 core_availability\info::completion_value_used($course, $mod->id)) { 513 $extraclass = ' preventjs'; 514 } 515 $output .= html_writer::start_tag('form', array('method' => 'post', 516 'action' => new moodle_url('/course/togglecompletion.php'), 517 'class' => 'togglecompletion'. $extraclass)); 518 $output .= html_writer::start_tag('div'); 519 $output .= html_writer::empty_tag('input', array( 520 'type' => 'hidden', 'name' => 'id', 'value' => $mod->id)); 521 $output .= html_writer::empty_tag('input', array( 522 'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); 523 $output .= html_writer::empty_tag('input', array( 524 'type' => 'hidden', 'name' => 'modulename', 'value' => $formattedname)); 525 $output .= html_writer::empty_tag('input', array( 526 'type' => 'hidden', 'name' => 'completionstate', 'value' => $newstate)); 527 $output .= html_writer::tag('button', 528 $this->output->pix_icon('i/completion-' . $completionicon, $imgalt), 529 array('class' => 'btn btn-link', 'aria-live' => 'assertive')); 530 $output .= html_writer::end_tag('div'); 531 $output .= html_writer::end_tag('form'); 532 } else { 533 // In auto mode, the icon is just an image. 534 $completionpixicon = new pix_icon('i/completion-'.$completionicon, $imgalt, '', 535 array('title' => $imgalt)); 536 $output .= html_writer::tag('span', $this->output->render($completionpixicon), 537 array('class' => 'autocompletion')); 538 } 539 } 540 return $output; 541 } 542 543 /** 544 * Checks if course module has any conditions that may make it unavailable for 545 * all or some of the students 546 * 547 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 548 * 549 * @param cm_info $mod 550 * @return bool 551 */ 552 public function is_cm_conditionally_hidden(cm_info $mod) { 553 global $CFG; 554 555 debugging( 556 'is_cm_conditionally_hidden is deprecated. Use \core_availability\info_module::is_available_for_all instead', 557 DEBUG_DEVELOPER 558 ); 559 560 $conditionalhidden = false; 561 if (!empty($CFG->enableavailability)) { 562 $info = new \core_availability\info_module($mod); 563 $conditionalhidden = !$info->is_available_for_all(); 564 } 565 return $conditionalhidden; 566 } 567 568 /** 569 * Renders html to display a name with the link to the course module on a course page 570 * 571 * If module is unavailable for user but still needs to be displayed 572 * in the list, just the name is returned without a link 573 * 574 * Note, that for course modules that never have separate pages (i.e. labels) 575 * this function return an empty string 576 * 577 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 578 * 579 * @param cm_info $mod 580 * @param array $displayoptions 581 * @return string 582 */ 583 public function course_section_cm_name(cm_info $mod, $displayoptions = array()) { 584 debugging( 585 'course_section_cm_name is deprecated. Use core_courseformat\\output\\local\\content\\cm\\cmname class instead.', 586 DEBUG_DEVELOPER 587 ); 588 589 if (!$mod->is_visible_on_course_page() || !$mod->url) { 590 // Nothing to be displayed to the user. 591 return ''; 592 } 593 594 list($linkclasses, $textclasses) = $this->course_section_cm_classes($mod); 595 $groupinglabel = $mod->get_grouping_label($textclasses); 596 597 // Render element that allows to edit activity name inline. 598 $format = course_get_format($mod->course); 599 $cmnameclass = $format->get_output_classname('content\\cm\\cmname'); 600 // Mod inplace name editable. 601 $cmname = new $cmnameclass( 602 $format, 603 $mod->get_section_info(), 604 $mod, 605 null, 606 $displayoptions 607 ); 608 609 $renderer = $format->get_renderer($this->page); 610 return $renderer->render($cmname) . $groupinglabel; 611 } 612 613 /** 614 * Returns the CSS classes for the activity name/content 615 * 616 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 617 * 618 * For items which are hidden, unavailable or stealth but should be displayed 619 * to current user ($mod->is_visible_on_course_page()), we show those as dimmed. 620 * Students will also see as dimmed activities names that are not yet available 621 * but should still be displayed (without link) with availability info. 622 * 623 * @param cm_info $mod 624 * @return array array of two elements ($linkclasses, $textclasses) 625 */ 626 protected function course_section_cm_classes(cm_info $mod) { 627 628 debugging( 629 'course_section_cm_classes is deprecated. Now it is part of core_courseformat\\output\\local\\content\\cm ', 630 DEBUG_DEVELOPER 631 ); 632 633 $format = course_get_format($mod->course); 634 635 $cmclass = $format->get_output_classname('content\\cm'); 636 $cmoutput = new $cmclass( 637 $format, 638 $mod->get_section_info(), 639 $mod, 640 ); 641 return [ 642 $cmoutput->get_link_classes(), 643 $cmoutput->get_text_classes(), 644 ]; 645 } 646 647 /** 648 * Renders html to display a name with the link to the course module on a course page 649 * 650 * If module is unavailable for user but still needs to be displayed 651 * in the list, just the name is returned without a link 652 * 653 * Note, that for course modules that never have separate pages (i.e. labels) 654 * this function return an empty string 655 * 656 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 657 * 658 * @param cm_info $mod 659 * @param array $displayoptions 660 * @return string 661 */ 662 public function course_section_cm_name_title(cm_info $mod, $displayoptions = array()) { 663 664 debugging( 665 'course_section_cm_name_title is deprecated. Use core_courseformat\\output\\local\\cm\\title class instead.', 666 DEBUG_DEVELOPER 667 ); 668 669 $output = ''; 670 $url = $mod->url; 671 if (!$mod->is_visible_on_course_page() || !$url) { 672 // Nothing to be displayed to the user. 673 return $output; 674 } 675 676 //Accessibility: for files get description via icon, this is very ugly hack! 677 $instancename = $mod->get_formatted_name(); 678 $altname = $mod->modfullname; 679 // Avoid unnecessary duplication: if e.g. a forum name already 680 // includes the word forum (or Forum, etc) then it is unhelpful 681 // to include that in the accessible description that is added. 682 if (false !== strpos(core_text::strtolower($instancename), 683 core_text::strtolower($altname))) { 684 $altname = ''; 685 } 686 // File type after name, for alphabetic lists (screen reader). 687 if ($altname) { 688 $altname = get_accesshide(' '.$altname); 689 } 690 691 list($linkclasses, $textclasses) = $this->course_section_cm_classes($mod); 692 693 // Get on-click attribute value if specified and decode the onclick - it 694 // has already been encoded for display (puke). 695 $onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES); 696 697 // Display link itself. 698 $instancename = html_writer::tag('span', $instancename . $altname, ['class' => 'instancename ml-1']); 699 700 $imageicon = html_writer::empty_tag('img', ['src' => $mod->get_icon_url(), 701 'class' => 'activityicon', 'alt' => '', 'role' => 'presentation', 'aria-hidden' => 'true']); 702 $imageicon = html_writer::tag('span', $imageicon, ['class' => 'activityiconcontainer courseicon']); 703 $activitylink = $imageicon . $instancename; 704 705 if ($mod->uservisible) { 706 $output .= html_writer::link($url, $activitylink, array('class' => 'aalink' . $linkclasses, 'onclick' => $onclick)); 707 } else { 708 // We may be displaying this just in order to show information 709 // about visibility, without the actual link ($mod->is_visible_on_course_page()). 710 $output .= html_writer::tag('div', $activitylink, array('class' => $textclasses)); 711 } 712 return $output; 713 } 714 715 /** 716 * Renders html to display the module content on the course page (i.e. text of the labels) 717 * 718 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 719 * 720 * @param cm_info $mod 721 * @param array $displayoptions 722 * @return string 723 */ 724 public function course_section_cm_text(cm_info $mod, $displayoptions = array()) { 725 726 debugging( 727 'course_section_cm_text is deprecated. Now it is part of core_courseformat\\output\\local\\content\\cm ', 728 DEBUG_DEVELOPER 729 ); 730 731 $output = ''; 732 if (!$mod->is_visible_on_course_page()) { 733 // nothing to be displayed to the user 734 return $output; 735 } 736 $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); 737 list($linkclasses, $textclasses) = $this->course_section_cm_classes($mod); 738 if ($mod->url && $mod->uservisible) { 739 if ($content) { 740 // If specified, display extra content after link. 741 $output = html_writer::tag('div', $content, array('class' => 742 trim('contentafterlink ' . $textclasses))); 743 } 744 } else { 745 $groupinglabel = $mod->get_grouping_label($textclasses); 746 747 // No link, so display only content. 748 $output = html_writer::tag('div', $content . $groupinglabel, 749 array('class' => 'contentwithoutlink ' . $textclasses)); 750 } 751 return $output; 752 } 753 754 /** 755 * Displays availability info for a course section or course module 756 * 757 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 758 * @param string $text 759 * @param string $additionalclasses 760 * @return string 761 */ 762 public function availability_info($text, $additionalclasses = '') { 763 764 debugging( 765 'availability_info is deprecated. Use core_courseformat\\output\\local\\content\\section\\availability instead', 766 DEBUG_DEVELOPER 767 ); 768 769 $data = ['text' => $text, 'classes' => $additionalclasses]; 770 $additionalclasses = array_filter(explode(' ', $additionalclasses)); 771 772 if (in_array('ishidden', $additionalclasses)) { 773 $data['ishidden'] = 1; 774 775 } else if (in_array('isstealth', $additionalclasses)) { 776 $data['isstealth'] = 1; 777 778 } else if (in_array('isrestricted', $additionalclasses)) { 779 $data['isrestricted'] = 1; 780 781 if (in_array('isfullinfo', $additionalclasses)) { 782 $data['isfullinfo'] = 1; 783 } 784 } 785 786 return $this->render_from_template('core/availability_info', $data); 787 } 788 789 /** 790 * Renders HTML to show course module availability information (for someone who isn't allowed 791 * to see the activity itself, or for staff) 792 * 793 * @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more. 794 * @param cm_info $mod 795 * @param array $displayoptions 796 * @return string 797 */ 798 public function course_section_cm_availability(cm_info $mod, $displayoptions = array()) { 799 800 debugging( 801 'course_section_cm_availability is deprecated. Use core_courseformat\\output\\local\\content\\cm\\availability instead', 802 DEBUG_DEVELOPER 803 ); 804 805 $format = course_get_format($mod->course); 806 807 $availabilityclass = $format->get_output_classname('content\\cm\\availability'); 808 $availability = new $availabilityclass( 809 $format, 810 $mod->get_section_info(), 811 $mod, 812 ); 813 $renderer = $format->get_renderer($this->page); 814 return $renderer->render($availability); 815 } 816 817 /** 818 * Renders HTML to display one course module for display within a section. 819 * 820 * @deprecated since 4.0 - use core_course output components or course_format::course_section_updated_cm_item instead. 821 * 822 * This function calls: 823 * {@link core_course_renderer::course_section_cm()} 824 * 825 * @param stdClass $course 826 * @param completion_info $completioninfo 827 * @param cm_info $mod 828 * @param int|null $sectionreturn 829 * @param array $displayoptions 830 * @return String 831 */ 832 public function course_section_cm_list_item($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = []) { 833 834 debugging( 835 'course_section_cm_list_item is deprecated. Use renderer course_section_updated_cm_item instead', 836 DEBUG_DEVELOPER 837 ); 838 839 $output = ''; 840 if ($modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) { 841 $modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses; 842 $output .= html_writer::tag('li', $modulehtml, array('class' => $modclasses, 'id' => 'module-' . $mod->id)); 843 } 844 return $output; 845 } 846 847 /** 848 * Renders HTML to display one course module in a course section 849 * 850 * This includes link, content, availability, completion info and additional information 851 * that module type wants to display (i.e. number of unread forum posts) 852 * 853 * @deprecated since 4.0 MDL-72656 - use core_course output components instead. 854 * 855 * @param stdClass $course 856 * @param completion_info $completioninfo 857 * @param cm_info $mod 858 * @param int|null $sectionreturn 859 * @param array $displayoptions 860 * @return string 861 */ 862 public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = []) { 863 864 debugging( 865 'course_section_cm is deprecated. Use core_courseformat\\output\\content\\cm output class instead.', 866 DEBUG_DEVELOPER 867 ); 868 869 if (!$mod->is_visible_on_course_page()) { 870 return ''; 871 } 872 873 $format = course_get_format($course); 874 $modinfo = $format->get_modinfo(); 875 // Output renderers works only with real section_info objects. 876 if ($sectionreturn) { 877 $format->set_section_number($sectionreturn); 878 } 879 $section = $modinfo->get_section_info($format->get_section_number()); 880 881 $cmclass = $format->get_output_classname('content\\cm'); 882 $cm = new $cmclass($format, $section, $mod, $displayoptions); 883 // The course outputs works with format renderers, not with course renderers. 884 $renderer = $format->get_renderer($this->page); 885 $data = $cm->export_for_template($renderer); 886 return $this->output->render_from_template('core_courseformat/local/content/cm', $data); 887 } 888 889 /** 890 * Message displayed to the user when they try to access unavailable activity following URL 891 * 892 * This method is a very simplified version of {@link course_section_cm()} to be part of the error 893 * notification only. It also does not check if module is visible on course page or not. 894 * 895 * The message will be displayed inside notification! 896 * 897 * @param cm_info $cm 898 * @return string 899 */ 900 public function course_section_cm_unavailable_error_message(cm_info $cm) { 901 if ($cm->uservisible) { 902 return null; 903 } 904 if (!$cm->availableinfo) { 905 return get_string('activityiscurrentlyhidden'); 906 } 907 908 $altname = get_accesshide(' ' . $cm->modfullname); 909 $name = html_writer::empty_tag('img', array('src' => $cm->get_icon_url(), 910 'class' => 'iconlarge activityicon', 'alt' => ' ', 'role' => 'presentation')) . 911 html_writer::tag('span', ' '.$cm->get_formatted_name() . $altname, array('class' => 'instancename')); 912 $formattedinfo = \core_availability\info::format_info($cm->availableinfo, $cm->get_course()); 913 return html_writer::div($name, 'activityinstance-error') . 914 html_writer::div($formattedinfo, 'availabilityinfo-error'); 915 } 916 917 /** 918 * Renders HTML to display a list of course modules in a course section 919 * Also displays "move here" controls in Javascript-disabled mode. 920 * 921 * @deprecated since 4.0 MDL-72656 - use core_course output components instead. 922 * 923 * This function calls {@link core_course_renderer::course_section_cm()} 924 * 925 * @param stdClass $course course object 926 * @param int|stdClass|section_info $section relative section number or section object 927 * @param int $sectionreturn section number to return to 928 * @param int $displayoptions 929 * @return void 930 */ 931 public function course_section_cm_list($course, $section, $sectionreturn = null, $displayoptions = []) { 932 global $USER; 933 934 debugging('course_section_cm_list is deprecated. Use core_courseformat\\output\\local\\content\\section\\cmlist '. 935 'classes instead.', DEBUG_DEVELOPER); 936 937 $output = ''; 938 939 $format = course_get_format($course); 940 $modinfo = $format->get_modinfo(); 941 942 if (is_object($section)) { 943 $section = $modinfo->get_section_info($section->section); 944 } else { 945 $section = $modinfo->get_section_info($section); 946 } 947 $completioninfo = new completion_info($course); 948 949 // check if we are currently in the process of moving a module with JavaScript disabled 950 $ismoving = $format->show_editor() && ismoving($course->id); 951 952 if ($ismoving) { 953 $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'")); 954 } 955 956 // Get the list of modules visible to user (excluding the module being moved if there is one) 957 $moduleshtml = []; 958 if (!empty($modinfo->sections[$section->section])) { 959 foreach ($modinfo->sections[$section->section] as $modnumber) { 960 $mod = $modinfo->cms[$modnumber]; 961 962 if ($ismoving and $mod->id == $USER->activitycopy) { 963 // do not display moving mod 964 continue; 965 } 966 967 if ($modulehtml = $this->course_section_cm_list_item($course, 968 $completioninfo, $mod, $sectionreturn, $displayoptions)) { 969 $moduleshtml[$modnumber] = $modulehtml; 970 } 971 } 972 } 973 974 $sectionoutput = ''; 975 if (!empty($moduleshtml) || $ismoving) { 976 foreach ($moduleshtml as $modnumber => $modulehtml) { 977 if ($ismoving) { 978 $movingurl = new moodle_url('/course/mod.php', array('moveto' => $modnumber, 'sesskey' => sesskey())); 979 $sectionoutput .= html_writer::tag('li', 980 html_writer::link($movingurl, '', array('title' => $strmovefull, 'class' => 'movehere')), 981 array('class' => 'movehere')); 982 } 983 984 $sectionoutput .= $modulehtml; 985 } 986 987 if ($ismoving) { 988 $movingurl = new moodle_url('/course/mod.php', array('movetosection' => $section->id, 'sesskey' => sesskey())); 989 $sectionoutput .= html_writer::tag('li', 990 html_writer::link($movingurl, '', array('title' => $strmovefull, 'class' => 'movehere')), 991 array('class' => 'movehere')); 992 } 993 } 994 995 // Always output the section module list. 996 $output .= html_writer::tag('ul', $sectionoutput, array('class' => 'section img-text')); 997 998 return $output; 999 } 1000 1001 /** 1002 * Displays a custom list of courses with paging bar if necessary 1003 * 1004 * If $paginationurl is specified but $totalcount is not, the link 'View more' 1005 * appears under the list. 1006 * 1007 * If both $paginationurl and $totalcount are specified, and $totalcount is 1008 * bigger than count($courses), a paging bar is displayed above and under the 1009 * courses list. 1010 * 1011 * @param array $courses array of course records (or instances of core_course_list_element) to show on this page 1012 * @param bool $showcategoryname whether to add category name to the course description 1013 * @param string $additionalclasses additional CSS classes to add to the div.courses 1014 * @param moodle_url $paginationurl url to view more or url to form links to the other pages in paging bar 1015 * @param int $totalcount total number of courses on all pages, if omitted $paginationurl will be displayed as 'View more' link 1016 * @param int $page current page number (defaults to 0 referring to the first page) 1017 * @param int $perpage number of records per page (defaults to $CFG->coursesperpage) 1018 * @return string 1019 */ 1020 public function courses_list($courses, $showcategoryname = false, $additionalclasses = null, $paginationurl = null, $totalcount = null, $page = 0, $perpage = null) { 1021 global $CFG; 1022 // create instance of coursecat_helper to pass display options to function rendering courses list 1023 $chelper = new coursecat_helper(); 1024 if ($showcategoryname) { 1025 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT); 1026 } else { 1027 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED); 1028 } 1029 if ($totalcount !== null && $paginationurl !== null) { 1030 // add options to display pagination 1031 if ($perpage === null) { 1032 $perpage = $CFG->coursesperpage; 1033 } 1034 $chelper->set_courses_display_options(array( 1035 'limit' => $perpage, 1036 'offset' => ((int)$page) * $perpage, 1037 'paginationurl' => $paginationurl, 1038 )); 1039 } else if ($paginationurl !== null) { 1040 // add options to display 'View more' link 1041 $chelper->set_courses_display_options(array('viewmoreurl' => $paginationurl)); 1042 $totalcount = count($courses) + 1; // has to be bigger than count($courses) otherwise link will not be displayed 1043 } 1044 $chelper->set_attributes(array('class' => $additionalclasses)); 1045 $content = $this->coursecat_courses($chelper, $courses, $totalcount); 1046 return $content; 1047 } 1048 1049 /** 1050 * Returns HTML to display course name. 1051 * 1052 * @param coursecat_helper $chelper 1053 * @param core_course_list_element $course 1054 * @return string 1055 */ 1056 protected function course_name(coursecat_helper $chelper, core_course_list_element $course): string { 1057 $content = ''; 1058 if ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_EXPANDED) { 1059 $nametag = 'h3'; 1060 } else { 1061 $nametag = 'div'; 1062 } 1063 $coursename = $chelper->get_course_formatted_name($course); 1064 $coursenamelink = html_writer::link(new moodle_url('/course/view.php', ['id' => $course->id]), 1065 $coursename, ['class' => $course->visible ? 'aalink' : 'aalink dimmed']); 1066 $content .= html_writer::tag($nametag, $coursenamelink, ['class' => 'coursename']); 1067 // If we display course in collapsed form but the course has summary or course contacts, display the link to the info page. 1068 $content .= html_writer::start_tag('div', ['class' => 'moreinfo']); 1069 if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) { 1070 if ($course->has_summary() || $course->has_course_contacts() || $course->has_course_overviewfiles() 1071 || $course->has_custom_fields()) { 1072 $url = new moodle_url('/course/info.php', ['id' => $course->id]); 1073 $image = $this->output->pix_icon('i/info', $this->strings->summary); 1074 $content .= html_writer::link($url, $image, ['title' => $this->strings->summary]); 1075 // Make sure JS file to expand course content is included. 1076 $this->coursecat_include_js(); 1077 } 1078 } 1079 $content .= html_writer::end_tag('div'); 1080 return $content; 1081 } 1082 1083 /** 1084 * Returns HTML to display course enrolment icons. 1085 * 1086 * @param core_course_list_element $course 1087 * @return string 1088 */ 1089 protected function course_enrolment_icons(core_course_list_element $course): string { 1090 $content = ''; 1091 if ($icons = enrol_get_course_info_icons($course)) { 1092 $content .= html_writer::start_tag('div', ['class' => 'enrolmenticons']); 1093 foreach ($icons as $icon) { 1094 $content .= $this->render($icon); 1095 } 1096 $content .= html_writer::end_tag('div'); 1097 } 1098 return $content; 1099 } 1100 1101 /** 1102 * Displays one course in the list of courses. 1103 * 1104 * This is an internal function, to display an information about just one course 1105 * please use {@link core_course_renderer::course_info_box()} 1106 * 1107 * @param coursecat_helper $chelper various display options 1108 * @param core_course_list_element|stdClass $course 1109 * @param string $additionalclasses additional classes to add to the main <div> tag (usually 1110 * depend on the course position in list - first/last/even/odd) 1111 * @return string 1112 */ 1113 protected function coursecat_coursebox(coursecat_helper $chelper, $course, $additionalclasses = '') { 1114 if (!isset($this->strings->summary)) { 1115 $this->strings->summary = get_string('summary'); 1116 } 1117 if ($chelper->get_show_courses() <= self::COURSECAT_SHOW_COURSES_COUNT) { 1118 return ''; 1119 } 1120 if ($course instanceof stdClass) { 1121 $course = new core_course_list_element($course); 1122 } 1123 $content = ''; 1124 $classes = trim('coursebox clearfix '. $additionalclasses); 1125 if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) { 1126 $classes .= ' collapsed'; 1127 } 1128 1129 // .coursebox 1130 $content .= html_writer::start_tag('div', array( 1131 'class' => $classes, 1132 'data-courseid' => $course->id, 1133 'data-type' => self::COURSECAT_TYPE_COURSE, 1134 )); 1135 1136 $content .= html_writer::start_tag('div', array('class' => 'info')); 1137 $content .= $this->course_name($chelper, $course); 1138 $content .= $this->course_enrolment_icons($course); 1139 $content .= html_writer::end_tag('div'); 1140 1141 $content .= html_writer::start_tag('div', array('class' => 'content')); 1142 $content .= $this->coursecat_coursebox_content($chelper, $course); 1143 $content .= html_writer::end_tag('div'); 1144 1145 $content .= html_writer::end_tag('div'); // .coursebox 1146 return $content; 1147 } 1148 1149 /** 1150 * Returns HTML to display course summary. 1151 * 1152 * @param coursecat_helper $chelper 1153 * @param core_course_list_element $course 1154 * @return string 1155 */ 1156 protected function course_summary(coursecat_helper $chelper, core_course_list_element $course): string { 1157 $content = ''; 1158 if ($course->has_summary()) { 1159 $content .= html_writer::start_tag('div', ['class' => 'summary']); 1160 $content .= $chelper->get_course_formatted_summary($course, 1161 array('overflowdiv' => true, 'noclean' => true, 'para' => false)); 1162 $content .= html_writer::end_tag('div'); 1163 } 1164 return $content; 1165 } 1166 1167 /** 1168 * Returns HTML to display course contacts. 1169 * 1170 * @param core_course_list_element $course 1171 * @return string 1172 */ 1173 protected function course_contacts(core_course_list_element $course) { 1174 $content = ''; 1175 if ($course->has_course_contacts()) { 1176 $content .= html_writer::start_tag('ul', ['class' => 'teachers']); 1177 foreach ($course->get_course_contacts() as $coursecontact) { 1178 $rolenames = array_map(function ($role) { 1179 return $role->displayname; 1180 }, $coursecontact['roles']); 1181 $name = html_writer::tag('span', implode(", ", $rolenames).': ', ['class' => 'font-weight-bold']); 1182 $name .= html_writer::link(new moodle_url('/user/view.php', 1183 ['id' => $coursecontact['user']->id, 'course' => SITEID]), 1184 $coursecontact['username']); 1185 $content .= html_writer::tag('li', $name); 1186 } 1187 $content .= html_writer::end_tag('ul'); 1188 } 1189 return $content; 1190 } 1191 1192 /** 1193 * Returns HTML to display course overview files. 1194 * 1195 * @param core_course_list_element $course 1196 * @return string 1197 */ 1198 protected function course_overview_files(core_course_list_element $course): string { 1199 global $CFG; 1200 1201 $contentimages = $contentfiles = ''; 1202 foreach ($course->get_course_overviewfiles() as $file) { 1203 $isimage = $file->is_valid_image(); 1204 $url = moodle_url::make_file_url("$CFG->wwwroot/pluginfile.php", 1205 '/' . $file->get_contextid() . '/' . $file->get_component() . '/' . 1206 $file->get_filearea() . $file->get_filepath() . $file->get_filename(), !$isimage); 1207 if ($isimage) { 1208 $contentimages .= html_writer::tag('div', 1209 html_writer::empty_tag('img', ['src' => $url, 'alt' => '']), 1210 ['class' => 'courseimage']); 1211 } else { 1212 $image = $this->output->pix_icon(file_file_icon($file, 24), $file->get_filename(), 'moodle'); 1213 $filename = html_writer::tag('span', $image, ['class' => 'fp-icon']). 1214 html_writer::tag('span', $file->get_filename(), ['class' => 'fp-filename']); 1215 $contentfiles .= html_writer::tag('span', 1216 html_writer::link($url, $filename), 1217 ['class' => 'coursefile fp-filename-icon text-break']); 1218 } 1219 } 1220 return $contentimages . $contentfiles; 1221 } 1222 1223 /** 1224 * Returns HTML to display course category name. 1225 * 1226 * @param coursecat_helper $chelper 1227 * @param core_course_list_element $course 1228 * @return string 1229 */ 1230 protected function course_category_name(coursecat_helper $chelper, core_course_list_element $course): string { 1231 $content = ''; 1232 // Display course category if necessary (for example in search results). 1233 if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT) { 1234 if ($cat = core_course_category::get($course->category, IGNORE_MISSING)) { 1235 $content .= html_writer::start_tag('div', ['class' => 'coursecat']); 1236 $content .= html_writer::tag('span', get_string('category').': ', ['class' => 'font-weight-bold']); 1237 $content .= html_writer::link(new moodle_url('/course/index.php', ['categoryid' => $cat->id]), 1238 $cat->get_formatted_name(), ['class' => $cat->visible ? '' : 'dimmed']); 1239 $content .= html_writer::end_tag('div'); 1240 } 1241 } 1242 return $content; 1243 } 1244 1245 /** 1246 * Returns HTML to display course custom fields. 1247 * 1248 * @param core_course_list_element $course 1249 * @return string 1250 */ 1251 protected function course_custom_fields(core_course_list_element $course): string { 1252 $content = ''; 1253 if ($course->has_custom_fields()) { 1254 $handler = core_course\customfield\course_handler::create(); 1255 $customfields = $handler->display_custom_fields_data($course->get_custom_fields()); 1256 $content .= \html_writer::tag('div', $customfields, ['class' => 'customfields-container']); 1257 } 1258 return $content; 1259 } 1260 1261 /** 1262 * Returns HTML to display course content (summary, course contacts and optionally category name) 1263 * 1264 * This method is called from coursecat_coursebox() and may be re-used in AJAX 1265 * 1266 * @param coursecat_helper $chelper various display options 1267 * @param stdClass|core_course_list_element $course 1268 * @return string 1269 */ 1270 protected function coursecat_coursebox_content(coursecat_helper $chelper, $course) { 1271 if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) { 1272 return ''; 1273 } 1274 if ($course instanceof stdClass) { 1275 $course = new core_course_list_element($course); 1276 } 1277 $content = \html_writer::start_tag('div', ['class' => 'd-flex']); 1278 $content .= $this->course_overview_files($course); 1279 $content .= \html_writer::start_tag('div', ['class' => 'flex-grow-1']); 1280 $content .= $this->course_summary($chelper, $course); 1281 $content .= $this->course_contacts($course); 1282 $content .= $this->course_category_name($chelper, $course); 1283 $content .= $this->course_custom_fields($course); 1284 $content .= \html_writer::end_tag('div'); 1285 $content .= \html_writer::end_tag('div'); 1286 return $content; 1287 } 1288 1289 /** 1290 * Renders the list of courses 1291 * 1292 * This is internal function, please use {@link core_course_renderer::courses_list()} or another public 1293 * method from outside of the class 1294 * 1295 * If list of courses is specified in $courses; the argument $chelper is only used 1296 * to retrieve display options and attributes, only methods get_show_courses(), 1297 * get_courses_display_option() and get_and_erase_attributes() are called. 1298 * 1299 * @param coursecat_helper $chelper various display options 1300 * @param array $courses the list of courses to display 1301 * @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable), 1302 * defaulted to count($courses) 1303 * @return string 1304 */ 1305 protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) { 1306 global $CFG; 1307 if ($totalcount === null) { 1308 $totalcount = count($courses); 1309 } 1310 if (!$totalcount) { 1311 // Courses count is cached during courses retrieval. 1312 return ''; 1313 } 1314 1315 if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO) { 1316 // In 'auto' course display mode we analyse if number of courses is more or less than $CFG->courseswithsummarieslimit 1317 if ($totalcount <= $CFG->courseswithsummarieslimit) { 1318 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED); 1319 } else { 1320 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED); 1321 } 1322 } 1323 1324 // prepare content of paging bar if it is needed 1325 $paginationurl = $chelper->get_courses_display_option('paginationurl'); 1326 $paginationallowall = $chelper->get_courses_display_option('paginationallowall'); 1327 if ($totalcount > count($courses)) { 1328 // there are more results that can fit on one page 1329 if ($paginationurl) { 1330 // the option paginationurl was specified, display pagingbar 1331 $perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage); 1332 $page = $chelper->get_courses_display_option('offset') / $perpage; 1333 $pagingbar = $this->paging_bar($totalcount, $page, $perpage, 1334 $paginationurl->out(false, array('perpage' => $perpage))); 1335 if ($paginationallowall) { 1336 $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')), 1337 get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall')); 1338 } 1339 } else if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) { 1340 // the option for 'View more' link was specified, display more link 1341 $viewmoretext = $chelper->get_courses_display_option('viewmoretext', new lang_string('viewmore')); 1342 $morelink = html_writer::tag( 1343 'div', 1344 html_writer::link($viewmoreurl, $viewmoretext, ['class' => 'btn btn-secondary']), 1345 ['class' => 'paging paging-morelink'] 1346 ); 1347 } 1348 } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) { 1349 // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode 1350 $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)), 1351 get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage')); 1352 } 1353 1354 // display list of courses 1355 $attributes = $chelper->get_and_erase_attributes('courses'); 1356 $content = html_writer::start_tag('div', $attributes); 1357 1358 if (!empty($pagingbar)) { 1359 $content .= $pagingbar; 1360 } 1361 1362 $coursecount = 0; 1363 foreach ($courses as $course) { 1364 $coursecount ++; 1365 $classes = ($coursecount%2) ? 'odd' : 'even'; 1366 if ($coursecount == 1) { 1367 $classes .= ' first'; 1368 } 1369 if ($coursecount >= count($courses)) { 1370 $classes .= ' last'; 1371 } 1372 $content .= $this->coursecat_coursebox($chelper, $course, $classes); 1373 } 1374 1375 if (!empty($pagingbar)) { 1376 $content .= $pagingbar; 1377 } 1378 if (!empty($morelink)) { 1379 $content .= $morelink; 1380 } 1381 1382 $content .= html_writer::end_tag('div'); // .courses 1383 return $content; 1384 } 1385 1386 /** 1387 * Renders the list of subcategories in a category 1388 * 1389 * @param coursecat_helper $chelper various display options 1390 * @param core_course_category $coursecat 1391 * @param int $depth depth of the category in the current tree 1392 * @return string 1393 */ 1394 protected function coursecat_subcategories(coursecat_helper $chelper, $coursecat, $depth) { 1395 global $CFG; 1396 $subcategories = array(); 1397 if (!$chelper->get_categories_display_option('nodisplay')) { 1398 $subcategories = $coursecat->get_children($chelper->get_categories_display_options()); 1399 } 1400 $totalcount = $coursecat->get_children_count(); 1401 if (!$totalcount) { 1402 // Note that we call core_course_category::get_children_count() AFTER core_course_category::get_children() 1403 // to avoid extra DB requests. 1404 // Categories count is cached during children categories retrieval. 1405 return ''; 1406 } 1407 1408 // prepare content of paging bar or more link if it is needed 1409 $paginationurl = $chelper->get_categories_display_option('paginationurl'); 1410 $paginationallowall = $chelper->get_categories_display_option('paginationallowall'); 1411 if ($totalcount > count($subcategories)) { 1412 if ($paginationurl) { 1413 // the option 'paginationurl was specified, display pagingbar 1414 $perpage = $chelper->get_categories_display_option('limit', $CFG->coursesperpage); 1415 $page = $chelper->get_categories_display_option('offset') / $perpage; 1416 $pagingbar = $this->paging_bar($totalcount, $page, $perpage, 1417 $paginationurl->out(false, array('perpage' => $perpage))); 1418 if ($paginationallowall) { 1419 $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')), 1420 get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall')); 1421 } 1422 } else if ($viewmoreurl = $chelper->get_categories_display_option('viewmoreurl')) { 1423 // the option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id) 1424 if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) { 1425 $viewmoreurl->param('categoryid', $coursecat->id); 1426 } 1427 $viewmoretext = $chelper->get_categories_display_option('viewmoretext', new lang_string('viewmore')); 1428 $morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext), 1429 array('class' => 'paging paging-morelink')); 1430 } 1431 } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) { 1432 // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode 1433 $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)), 1434 get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage')); 1435 } 1436 1437 // display list of subcategories 1438 $content = html_writer::start_tag('div', array('class' => 'subcategories')); 1439 1440 if (!empty($pagingbar)) { 1441 $content .= $pagingbar; 1442 } 1443 1444 foreach ($subcategories as $subcategory) { 1445 $content .= $this->coursecat_category($chelper, $subcategory, $depth + 1); 1446 } 1447 1448 if (!empty($pagingbar)) { 1449 $content .= $pagingbar; 1450 } 1451 if (!empty($morelink)) { 1452 $content .= $morelink; 1453 } 1454 1455 $content .= html_writer::end_tag('div'); 1456 return $content; 1457 } 1458 1459 /** 1460 * Make sure that javascript file for AJAX expanding of courses and categories content is included 1461 */ 1462 protected function coursecat_include_js() { 1463 if (!$this->page->requires->should_create_one_time_item_now('core_course_categoryexpanderjsinit')) { 1464 return; 1465 } 1466 1467 // We must only load this module once. 1468 $this->page->requires->yui_module('moodle-course-categoryexpander', 1469 'Y.Moodle.course.categoryexpander.init'); 1470 } 1471 1472 /** 1473 * Returns HTML to display the subcategories and courses in the given category 1474 * 1475 * This method is re-used by AJAX to expand content of not loaded category 1476 * 1477 * @param coursecat_helper $chelper various display options 1478 * @param core_course_category $coursecat 1479 * @param int $depth depth of the category in the current tree 1480 * @return string 1481 */ 1482 protected function coursecat_category_content(coursecat_helper $chelper, $coursecat, $depth) { 1483 $content = ''; 1484 // Subcategories 1485 $content .= $this->coursecat_subcategories($chelper, $coursecat, $depth); 1486 1487 // AUTO show courses: Courses will be shown expanded if this is not nested category, 1488 // and number of courses no bigger than $CFG->courseswithsummarieslimit. 1489 $showcoursesauto = $chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO; 1490 if ($showcoursesauto && $depth) { 1491 // this is definitely collapsed mode 1492 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED); 1493 } 1494 1495 // Courses 1496 if ($chelper->get_show_courses() > core_course_renderer::COURSECAT_SHOW_COURSES_COUNT) { 1497 $courses = array(); 1498 if (!$chelper->get_courses_display_option('nodisplay')) { 1499 $courses = $coursecat->get_courses($chelper->get_courses_display_options()); 1500 } 1501 if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) { 1502 // the option for 'View more' link was specified, display more link (if it is link to category view page, add category id) 1503 if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) { 1504 $chelper->set_courses_display_option('viewmoreurl', new moodle_url($viewmoreurl, array('categoryid' => $coursecat->id))); 1505 } 1506 } 1507 $content .= $this->coursecat_courses($chelper, $courses, $coursecat->get_courses_count()); 1508 } 1509 1510 if ($showcoursesauto) { 1511 // restore the show_courses back to AUTO 1512 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO); 1513 } 1514 1515 return $content; 1516 } 1517 1518 /** 1519 * Returns HTML to display a course category as a part of a tree 1520 * 1521 * This is an internal function, to display a particular category and all its contents 1522 * use {@link core_course_renderer::course_category()} 1523 * 1524 * @param coursecat_helper $chelper various display options 1525 * @param core_course_category $coursecat 1526 * @param int $depth depth of this category in the current tree 1527 * @return string 1528 */ 1529 protected function coursecat_category(coursecat_helper $chelper, $coursecat, $depth) { 1530 // open category tag 1531 $classes = array('category'); 1532 if (empty($coursecat->visible)) { 1533 $classes[] = 'dimmed_category'; 1534 } 1535 if ($chelper->get_subcat_depth() > 0 && $depth >= $chelper->get_subcat_depth()) { 1536 // do not load content 1537 $categorycontent = ''; 1538 $classes[] = 'notloaded'; 1539 if ($coursecat->get_children_count() || 1540 ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_COLLAPSED && $coursecat->get_courses_count())) { 1541 $classes[] = 'with_children'; 1542 $classes[] = 'collapsed'; 1543 } 1544 } else { 1545 // load category content 1546 $categorycontent = $this->coursecat_category_content($chelper, $coursecat, $depth); 1547 $classes[] = 'loaded'; 1548 if (!empty($categorycontent)) { 1549 $classes[] = 'with_children'; 1550 // Category content loaded with children. 1551 $this->categoryexpandedonload = true; 1552 } 1553 } 1554 1555 // Make sure JS file to expand category content is included. 1556 $this->coursecat_include_js(); 1557 1558 $content = html_writer::start_tag('div', array( 1559 'class' => join(' ', $classes), 1560 'data-categoryid' => $coursecat->id, 1561 'data-depth' => $depth, 1562 'data-showcourses' => $chelper->get_show_courses(), 1563 'data-type' => self::COURSECAT_TYPE_CATEGORY, 1564 )); 1565 1566 // category name 1567 $categoryname = $coursecat->get_formatted_name(); 1568 $categoryname = html_writer::link(new moodle_url('/course/index.php', 1569 array('categoryid' => $coursecat->id)), 1570 $categoryname); 1571 if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_COUNT 1572 && ($coursescount = $coursecat->get_courses_count())) { 1573 $categoryname .= html_writer::tag('span', ' ('. $coursescount.')', 1574 array('title' => get_string('numberofcourses'), 'class' => 'numberofcourse')); 1575 } 1576 $content .= html_writer::start_tag('div', array('class' => 'info')); 1577 1578 $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'categoryname aabtn')); 1579 $content .= html_writer::end_tag('div'); // .info 1580 1581 // add category content to the output 1582 $content .= html_writer::tag('div', $categorycontent, array('class' => 'content')); 1583 1584 $content .= html_writer::end_tag('div'); // .category 1585 1586 // Return the course category tree HTML 1587 return $content; 1588 } 1589 1590 /** 1591 * Returns HTML to display a tree of subcategories and courses in the given category 1592 * 1593 * @param coursecat_helper $chelper various display options 1594 * @param core_course_category $coursecat top category (this category's name and description will NOT be added to the tree) 1595 * @return string 1596 */ 1597 protected function coursecat_tree(coursecat_helper $chelper, $coursecat) { 1598 // Reset the category expanded flag for this course category tree first. 1599 $this->categoryexpandedonload = false; 1600 $categorycontent = $this->coursecat_category_content($chelper, $coursecat, 0); 1601 if (empty($categorycontent)) { 1602 return ''; 1603 } 1604 1605 // Start content generation 1606 $content = ''; 1607 $attributes = $chelper->get_and_erase_attributes('course_category_tree clearfix'); 1608 $content .= html_writer::start_tag('div', $attributes); 1609 1610 if ($coursecat->get_children_count()) { 1611 $classes = array( 1612 'collapseexpand', 'aabtn' 1613 ); 1614 1615 // Check if the category content contains subcategories with children's content loaded. 1616 if ($this->categoryexpandedonload) { 1617 $classes[] = 'collapse-all'; 1618 $linkname = get_string('collapseall'); 1619 } else { 1620 $linkname = get_string('expandall'); 1621 } 1622 1623 // Only show the collapse/expand if there are children to expand. 1624 $content .= html_writer::start_tag('div', array('class' => 'collapsible-actions')); 1625 $content .= html_writer::link('#', $linkname, array('class' => implode(' ', $classes))); 1626 $content .= html_writer::end_tag('div'); 1627 $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle'); 1628 } 1629 1630 $content .= html_writer::tag('div', $categorycontent, array('class' => 'content')); 1631 1632 $content .= html_writer::end_tag('div'); // .course_category_tree 1633 1634 return $content; 1635 } 1636 1637 /** 1638 * Renders HTML to display particular course category - list of it's subcategories and courses 1639 * 1640 * Invoked from /course/index.php 1641 * 1642 * @param int|stdClass|core_course_category $category 1643 */ 1644 public function course_category($category) { 1645 global $CFG; 1646 $usertop = core_course_category::user_top(); 1647 if (empty($category)) { 1648 $coursecat = $usertop; 1649 } else if (is_object($category) && $category instanceof core_course_category) { 1650 $coursecat = $category; 1651 } else { 1652 $coursecat = core_course_category::get(is_object($category) ? $category->id : $category); 1653 } 1654 $site = get_site(); 1655 $actionbar = new \core_course\output\category_action_bar($this->page, $coursecat); 1656 $output = $this->render_from_template('core_course/category_actionbar', $actionbar->export_for_template($this)); 1657 1658 if (core_course_category::is_simple_site()) { 1659 // There is only one category in the system, do not display link to it. 1660 $strfulllistofcourses = get_string('fulllistofcourses'); 1661 $this->page->set_title($strfulllistofcourses); 1662 } else if (!$coursecat->id || !$coursecat->is_uservisible()) { 1663 $strcategories = get_string('categories'); 1664 $this->page->set_title($strcategories); 1665 } else { 1666 $strfulllistofcourses = get_string('fulllistofcourses'); 1667 $this->page->set_title($strfulllistofcourses); 1668 } 1669 1670 // Print current category description 1671 $chelper = new coursecat_helper(); 1672 if ($description = $chelper->get_category_formatted_description($coursecat)) { 1673 $output .= $this->box($description, array('class' => 'generalbox info')); 1674 } 1675 1676 // Prepare parameters for courses and categories lists in the tree 1677 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO) 1678 ->set_attributes(array('class' => 'category-browse category-browse-'.$coursecat->id)); 1679 1680 $coursedisplayoptions = array(); 1681 $catdisplayoptions = array(); 1682 $browse = optional_param('browse', null, PARAM_ALPHA); 1683 $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); 1684 $page = optional_param('page', 0, PARAM_INT); 1685 $baseurl = new moodle_url('/course/index.php'); 1686 if ($coursecat->id) { 1687 $baseurl->param('categoryid', $coursecat->id); 1688 } 1689 if ($perpage != $CFG->coursesperpage) { 1690 $baseurl->param('perpage', $perpage); 1691 } 1692 $coursedisplayoptions['limit'] = $perpage; 1693 $catdisplayoptions['limit'] = $perpage; 1694 if ($browse === 'courses' || !$coursecat->get_children_count()) { 1695 $coursedisplayoptions['offset'] = $page * $perpage; 1696 $coursedisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'courses')); 1697 $catdisplayoptions['nodisplay'] = true; 1698 $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories')); 1699 $catdisplayoptions['viewmoretext'] = new lang_string('viewallsubcategories'); 1700 } else if ($browse === 'categories' || !$coursecat->get_courses_count()) { 1701 $coursedisplayoptions['nodisplay'] = true; 1702 $catdisplayoptions['offset'] = $page * $perpage; 1703 $catdisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'categories')); 1704 $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses')); 1705 $coursedisplayoptions['viewmoretext'] = new lang_string('viewallcourses'); 1706 } else { 1707 // we have a category that has both subcategories and courses, display pagination separately 1708 $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1)); 1709 $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1)); 1710 } 1711 $chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions); 1712 1713 // Display course category tree. 1714 $output .= $this->coursecat_tree($chelper, $coursecat); 1715 1716 return $output; 1717 } 1718 1719 /** 1720 * Serves requests to /course/category.ajax.php 1721 * 1722 * In this renderer implementation it may expand the category content or 1723 * course content. 1724 * 1725 * @return string 1726 * @throws coding_exception 1727 */ 1728 public function coursecat_ajax() { 1729 global $DB, $CFG; 1730 1731 $type = required_param('type', PARAM_INT); 1732 1733 if ($type === self::COURSECAT_TYPE_CATEGORY) { 1734 // This is a request for a category list of some kind. 1735 $categoryid = required_param('categoryid', PARAM_INT); 1736 $showcourses = required_param('showcourses', PARAM_INT); 1737 $depth = required_param('depth', PARAM_INT); 1738 1739 $category = core_course_category::get($categoryid); 1740 1741 $chelper = new coursecat_helper(); 1742 $baseurl = new moodle_url('/course/index.php', array('categoryid' => $categoryid)); 1743 $coursedisplayoptions = array( 1744 'limit' => $CFG->coursesperpage, 1745 'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1)) 1746 ); 1747 $catdisplayoptions = array( 1748 'limit' => $CFG->coursesperpage, 1749 'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1)) 1750 ); 1751 $chelper->set_show_courses($showcourses)-> 1752 set_courses_display_options($coursedisplayoptions)-> 1753 set_categories_display_options($catdisplayoptions); 1754 1755 return $this->coursecat_category_content($chelper, $category, $depth); 1756 } else if ($type === self::COURSECAT_TYPE_COURSE) { 1757 // This is a request for the course information. 1758 $courseid = required_param('courseid', PARAM_INT); 1759 1760 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); 1761 1762 $chelper = new coursecat_helper(); 1763 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED); 1764 return $this->coursecat_coursebox_content($chelper, $course); 1765 } else { 1766 throw new coding_exception('Invalid request type'); 1767 } 1768 } 1769 1770 /** 1771 * Renders html to display search result page 1772 * 1773 * @param array $searchcriteria may contain elements: search, blocklist, modulelist, tagid 1774 * @return string 1775 */ 1776 public function search_courses($searchcriteria) { 1777 global $CFG; 1778 $content = ''; 1779 1780 $search = ''; 1781 if (!empty($searchcriteria['search'])) { 1782 $search = $searchcriteria['search']; 1783 } 1784 $content .= $this->course_search_form($search); 1785 1786 if (!empty($searchcriteria)) { 1787 // print search results 1788 1789 $displayoptions = array('sort' => array('displayname' => 1)); 1790 // take the current page and number of results per page from query 1791 $perpage = optional_param('perpage', 0, PARAM_RAW); 1792 if ($perpage !== 'all') { 1793 $displayoptions['limit'] = ((int)$perpage <= 0) ? $CFG->coursesperpage : (int)$perpage; 1794 $page = optional_param('page', 0, PARAM_INT); 1795 $displayoptions['offset'] = $displayoptions['limit'] * $page; 1796 } 1797 // options 'paginationurl' and 'paginationallowall' are only used in method coursecat_courses() 1798 $displayoptions['paginationurl'] = new moodle_url('/course/search.php', $searchcriteria); 1799 $displayoptions['paginationallowall'] = true; // allow adding link 'View all' 1800 1801 $class = 'course-search-result'; 1802 foreach ($searchcriteria as $key => $value) { 1803 if (!empty($value)) { 1804 $class .= ' course-search-result-'. $key; 1805 } 1806 } 1807 $chelper = new coursecat_helper(); 1808 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT)-> 1809 set_courses_display_options($displayoptions)-> 1810 set_search_criteria($searchcriteria)-> 1811 set_attributes(array('class' => $class)); 1812 1813 $courses = core_course_category::search_courses($searchcriteria, $chelper->get_courses_display_options()); 1814 $totalcount = core_course_category::search_courses_count($searchcriteria); 1815 $courseslist = $this->coursecat_courses($chelper, $courses, $totalcount); 1816 1817 if (!$totalcount) { 1818 if (!empty($searchcriteria['search'])) { 1819 $content .= $this->heading(get_string('nocoursesfound', '', $searchcriteria['search'])); 1820 } else { 1821 $content .= $this->heading(get_string('novalidcourses')); 1822 } 1823 } else { 1824 $content .= $this->heading(get_string('searchresults'). ": $totalcount"); 1825 $content .= $courseslist; 1826 } 1827 } 1828 return $content; 1829 } 1830 1831 /** 1832 * Renders html to print list of courses tagged with particular tag 1833 * 1834 * @param int $tagid id of the tag 1835 * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag 1836 * are displayed on the page and the per-page limit may be bigger 1837 * @param int $fromctx context id where the link was displayed, may be used by callbacks 1838 * to display items in the same context first 1839 * @param int $ctx context id where to search for records 1840 * @param bool $rec search in subcontexts as well 1841 * @param array $displayoptions 1842 * @return string empty string if no courses are marked with this tag or rendered list of courses 1843 */ 1844 public function tagged_courses($tagid, $exclusivemode = true, $ctx = 0, $rec = true, $displayoptions = null) { 1845 global $CFG; 1846 if (empty($displayoptions)) { 1847 $displayoptions = array(); 1848 } 1849 $showcategories = !core_course_category::is_simple_site(); 1850 $displayoptions += array('limit' => $CFG->coursesperpage, 'offset' => 0); 1851 $chelper = new coursecat_helper(); 1852 $searchcriteria = array('tagid' => $tagid, 'ctx' => $ctx, 'rec' => $rec); 1853 $chelper->set_show_courses($showcategories ? self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT : 1854 self::COURSECAT_SHOW_COURSES_EXPANDED)-> 1855 set_search_criteria($searchcriteria)-> 1856 set_courses_display_options($displayoptions)-> 1857 set_attributes(array('class' => 'course-search-result course-search-result-tagid')); 1858 // (we set the same css class as in search results by tagid) 1859 if ($totalcount = core_course_category::search_courses_count($searchcriteria)) { 1860 $courses = core_course_category::search_courses($searchcriteria, $chelper->get_courses_display_options()); 1861 if ($exclusivemode) { 1862 return $this->coursecat_courses($chelper, $courses, $totalcount); 1863 } else { 1864 $tagfeed = new core_tag\output\tagfeed(); 1865 $img = $this->output->pix_icon('i/course', ''); 1866 foreach ($courses as $course) { 1867 $url = course_get_url($course); 1868 $imgwithlink = html_writer::link($url, $img); 1869 $coursename = html_writer::link($url, $course->get_formatted_name()); 1870 $details = ''; 1871 if ($showcategories && ($cat = core_course_category::get($course->category, IGNORE_MISSING))) { 1872 $details = get_string('category').': '. 1873 html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)), 1874 $cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed')); 1875 } 1876 $tagfeed->add($imgwithlink, $coursename, $details); 1877 } 1878 return $this->output->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($this->output)); 1879 } 1880 } 1881 return ''; 1882 } 1883 1884 /** 1885 * Returns HTML to display one remote course 1886 * 1887 * @param stdClass $course remote course information, contains properties: 1888 id, remoteid, shortname, fullname, hostid, summary, summaryformat, cat_name, hostname 1889 * @return string 1890 */ 1891 protected function frontpage_remote_course(stdClass $course) { 1892 $url = new moodle_url('/auth/mnet/jump.php', array( 1893 'hostid' => $course->hostid, 1894 'wantsurl' => '/course/view.php?id='. $course->remoteid 1895 )); 1896 1897 $output = ''; 1898 $output .= html_writer::start_tag('div', array('class' => 'coursebox remotecoursebox clearfix')); 1899 $output .= html_writer::start_tag('div', array('class' => 'info')); 1900 $output .= html_writer::start_tag('h3', array('class' => 'coursename')); 1901 $output .= html_writer::link($url, format_string($course->fullname), array('title' => get_string('entercourse'))); 1902 $output .= html_writer::end_tag('h3'); // .name 1903 $output .= html_writer::tag('div', '', array('class' => 'moreinfo')); 1904 $output .= html_writer::end_tag('div'); // .info 1905 $output .= html_writer::start_tag('div', array('class' => 'content')); 1906 $output .= html_writer::start_tag('div', array('class' => 'summary')); 1907 $options = new stdClass(); 1908 $options->noclean = true; 1909 $options->para = false; 1910 $options->overflowdiv = true; 1911 $output .= format_text($course->summary, $course->summaryformat, $options); 1912 $output .= html_writer::end_tag('div'); // .summary 1913 $addinfo = format_string($course->hostname) . ' : ' 1914 . format_string($course->cat_name) . ' : ' 1915 . format_string($course->shortname); 1916 $output .= html_writer::tag('div', $addinfo, array('class' => 'remotecourseinfo')); 1917 $output .= html_writer::end_tag('div'); // .content 1918 $output .= html_writer::end_tag('div'); // .coursebox 1919 return $output; 1920 } 1921 1922 /** 1923 * Returns HTML to display one remote host 1924 * 1925 * @param array $host host information, contains properties: name, url, count 1926 * @return string 1927 */ 1928 protected function frontpage_remote_host($host) { 1929 $output = ''; 1930 $output .= html_writer::start_tag('div', array('class' => 'coursebox remotehost clearfix')); 1931 $output .= html_writer::start_tag('div', array('class' => 'info')); 1932 $output .= html_writer::start_tag('h3', array('class' => 'name')); 1933 $output .= html_writer::link($host['url'], s($host['name']), array('title' => s($host['name']))); 1934 $output .= html_writer::end_tag('h3'); // .name 1935 $output .= html_writer::tag('div', '', array('class' => 'moreinfo')); 1936 $output .= html_writer::end_tag('div'); // .info 1937 $output .= html_writer::start_tag('div', array('class' => 'content')); 1938 $output .= html_writer::start_tag('div', array('class' => 'summary')); 1939 $output .= $host['count'] . ' ' . get_string('courses'); 1940 $output .= html_writer::end_tag('div'); // .content 1941 $output .= html_writer::end_tag('div'); // .coursebox 1942 return $output; 1943 } 1944 1945 /** 1946 * Returns HTML to print list of courses user is enrolled to for the frontpage 1947 * 1948 * Also lists remote courses or remote hosts if MNET authorisation is used 1949 * 1950 * @return string 1951 */ 1952 public function frontpage_my_courses() { 1953 global $USER, $CFG, $DB; 1954 1955 if (!isloggedin() or isguestuser()) { 1956 return ''; 1957 } 1958 1959 $output = ''; 1960 $courses = enrol_get_my_courses('summary, summaryformat'); 1961 $rhosts = array(); 1962 $rcourses = array(); 1963 if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') { 1964 $rcourses = get_my_remotecourses($USER->id); 1965 $rhosts = get_my_remotehosts(); 1966 } 1967 1968 if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) { 1969 1970 $chelper = new coursecat_helper(); 1971 $totalcount = count($courses); 1972 if (count($courses) > $CFG->frontpagecourselimit) { 1973 // There are more enrolled courses than we can display, display link to 'My courses'. 1974 $courses = array_slice($courses, 0, $CFG->frontpagecourselimit, true); 1975 $chelper->set_courses_display_options(array( 1976 'viewmoreurl' => new moodle_url('/my/courses.php'), 1977 'viewmoretext' => new lang_string('mycourses') 1978 )); 1979 } else if (core_course_category::top()->is_uservisible()) { 1980 // All enrolled courses are displayed, display link to 'All courses' if there are more courses in system. 1981 $chelper->set_courses_display_options(array( 1982 'viewmoreurl' => new moodle_url('/course/index.php'), 1983 'viewmoretext' => new lang_string('fulllistofcourses') 1984 )); 1985 $totalcount = $DB->count_records('course') - 1; 1986 } 1987 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)-> 1988 set_attributes(array('class' => 'frontpage-course-list-enrolled')); 1989 $output .= $this->coursecat_courses($chelper, $courses, $totalcount); 1990 1991 // MNET 1992 if (!empty($rcourses)) { 1993 // at the IDP, we know of all the remote courses 1994 $output .= html_writer::start_tag('div', array('class' => 'courses')); 1995 foreach ($rcourses as $course) { 1996 $output .= $this->frontpage_remote_course($course); 1997 } 1998 $output .= html_writer::end_tag('div'); // .courses 1999 } elseif (!empty($rhosts)) { 2000 // non-IDP, we know of all the remote servers, but not courses 2001 $output .= html_writer::start_tag('div', array('class' => 'courses')); 2002 foreach ($rhosts as $host) { 2003 $output .= $this->frontpage_remote_host($host); 2004 } 2005 $output .= html_writer::end_tag('div'); // .courses 2006 } 2007 } 2008 return $output; 2009 } 2010 2011 /** 2012 * Returns HTML to print list of available courses for the frontpage 2013 * 2014 * @return string 2015 */ 2016 public function frontpage_available_courses() { 2017 global $CFG; 2018 2019 $chelper = new coursecat_helper(); 2020 $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)-> 2021 set_courses_display_options(array( 2022 'recursive' => true, 2023 'limit' => $CFG->frontpagecourselimit, 2024 'viewmoreurl' => new moodle_url('/course/index.php'), 2025 'viewmoretext' => new lang_string('fulllistofcourses'))); 2026 2027 $chelper->set_attributes(array('class' => 'frontpage-course-list-all')); 2028 $courses = core_course_category::top()->get_courses($chelper->get_courses_display_options()); 2029 $totalcount = core_course_category::top()->get_courses_count($chelper->get_courses_display_options()); 2030 if (!$totalcount && !$this->page->user_is_editing() && has_capability('moodle/course:create', context_system::instance())) { 2031 // Print link to create a new course, for the 1st available category. 2032 return $this->add_new_course_button(); 2033 } 2034 return $this->coursecat_courses($chelper, $courses, $totalcount); 2035 } 2036 2037 /** 2038 * Returns HTML to the "add new course" button for the page 2039 * 2040 * @return string 2041 */ 2042 public function add_new_course_button() { 2043 global $CFG; 2044 // Print link to create a new course, for the 1st available category. 2045 $output = $this->container_start('buttons'); 2046 $url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat')); 2047 $output .= $this->single_button($url, get_string('addnewcourse'), 'get'); 2048 $output .= $this->container_end('buttons'); 2049 return $output; 2050 } 2051 2052 /** 2053 * Returns HTML to print tree with course categories and courses for the frontpage 2054 * 2055 * @return string 2056 */ 2057 public function frontpage_combo_list() { 2058 global $CFG; 2059 // TODO MDL-10965 improve. 2060 $tree = core_course_category::top(); 2061 if (!$tree->get_children_count()) { 2062 return ''; 2063 } 2064 $chelper = new coursecat_helper(); 2065 $chelper->set_subcat_depth($CFG->maxcategorydepth)-> 2066 set_categories_display_options(array( 2067 'limit' => $CFG->coursesperpage, 2068 'viewmoreurl' => new moodle_url('/course/index.php', 2069 array('browse' => 'categories', 'page' => 1)) 2070 ))-> 2071 set_courses_display_options(array( 2072 'limit' => $CFG->coursesperpage, 2073 'viewmoreurl' => new moodle_url('/course/index.php', 2074 array('browse' => 'courses', 'page' => 1)) 2075 ))-> 2076 set_attributes(array('class' => 'frontpage-category-combo')); 2077 return $this->coursecat_tree($chelper, $tree); 2078 } 2079 2080 /** 2081 * Returns HTML to print tree of course categories (with number of courses) for the frontpage 2082 * 2083 * @return string 2084 */ 2085 public function frontpage_categories_list() { 2086 global $CFG; 2087 // TODO MDL-10965 improve. 2088 $tree = core_course_category::top(); 2089 if (!$tree->get_children_count()) { 2090 return ''; 2091 } 2092 $chelper = new coursecat_helper(); 2093 $chelper->set_subcat_depth($CFG->maxcategorydepth)-> 2094 set_show_courses(self::COURSECAT_SHOW_COURSES_COUNT)-> 2095 set_categories_display_options(array( 2096 'limit' => $CFG->coursesperpage, 2097 'viewmoreurl' => new moodle_url('/course/index.php', 2098 array('browse' => 'categories', 'page' => 1)) 2099 ))-> 2100 set_attributes(array('class' => 'frontpage-category-names')); 2101 return $this->coursecat_tree($chelper, $tree); 2102 } 2103 2104 /** 2105 * Renders the activity information. 2106 * 2107 * Defer to template. 2108 * 2109 * @param \core_course\output\activity_information $page 2110 * @return string html for the page 2111 */ 2112 public function render_activity_information(\core_course\output\activity_information $page) { 2113 $data = $page->export_for_template($this->output); 2114 return $this->output->render_from_template('core_course/activity_info', $data); 2115 } 2116 2117 /** 2118 * Renders the activity navigation. 2119 * 2120 * Defer to template. 2121 * 2122 * @param \core_course\output\activity_navigation $page 2123 * @return string html for the page 2124 */ 2125 public function render_activity_navigation(\core_course\output\activity_navigation $page) { 2126 $data = $page->export_for_template($this->output); 2127 return $this->output->render_from_template('core_course/activity_navigation', $data); 2128 } 2129 2130 /** 2131 * Display waiting information about backup size during uploading backup process 2132 * @param object $backupfile the backup stored_file 2133 * @return $html string 2134 */ 2135 public function sendingbackupinfo($backupfile) { 2136 $sizeinfo = new stdClass(); 2137 $sizeinfo->total = number_format($backupfile->get_filesize() / 1000000, 2); 2138 $html = html_writer::tag('div', get_string('sendingsize', 'hub', $sizeinfo), 2139 array('class' => 'courseuploadtextinfo')); 2140 return $html; 2141 } 2142 2143 /** 2144 * Hub information (logo - name - description - link) 2145 * @param object $hubinfo 2146 * @return string html code 2147 */ 2148 public function hubinfo($hubinfo) { 2149 $screenshothtml = html_writer::empty_tag('img', 2150 array('src' => $hubinfo['imgurl'], 'alt' => $hubinfo['name'])); 2151 $hubdescription = html_writer::tag('div', $screenshothtml, 2152 array('class' => 'hubscreenshot')); 2153 2154 $hubdescription .= html_writer::tag('a', $hubinfo['name'], 2155 array('class' => 'hublink', 'href' => $hubinfo['url'], 2156 'onclick' => 'this.target="_blank"')); 2157 2158 $hubdescription .= html_writer::tag('div', format_text($hubinfo['description'], FORMAT_PLAIN), 2159 array('class' => 'hubdescription')); 2160 $hubdescription = html_writer::tag('div', $hubdescription, array('class' => 'hubinfo clearfix')); 2161 2162 return $hubdescription; 2163 } 2164 2165 /** 2166 * Output frontpage summary text and frontpage modules (stored as section 1 in site course) 2167 * 2168 * This may be disabled in settings 2169 * 2170 * @return string 2171 */ 2172 public function frontpage_section1() { 2173 global $SITE, $USER; 2174 2175 $output = ''; 2176 $editing = $this->page->user_is_editing(); 2177 2178 if ($editing) { 2179 // Make sure section with number 1 exists. 2180 course_create_sections_if_missing($SITE, 1); 2181 } 2182 2183 $modinfo = get_fast_modinfo($SITE); 2184 $section = $modinfo->get_section_info(1); 2185 2186 2187 if (($section && (!empty($modinfo->sections[1]) or !empty($section->summary))) or $editing) { 2188 2189 $format = course_get_format($SITE); 2190 $frontpageclass = $format->get_output_classname('content\\frontpagesection'); 2191 $frontpagesection = new $frontpageclass($format, $section); 2192 2193 // The course outputs works with format renderers, not with course renderers. 2194 $renderer = $format->get_renderer($this->page); 2195 $output .= $renderer->render($frontpagesection); 2196 } 2197 2198 return $output; 2199 } 2200 2201 /** 2202 * Output news for the frontpage (extract from site-wide news forum) 2203 * 2204 * @param stdClass $forum record from db table 'forum' that represents the site news forum 2205 * @return string 2206 */ 2207 protected function frontpage_news($forum) { 2208 global $CFG, $SITE, $SESSION, $USER; 2209 require_once($CFG->dirroot .'/mod/forum/lib.php'); 2210 2211 $output = ''; 2212 2213 if (isloggedin()) { 2214 $SESSION->fromdiscussion = $CFG->wwwroot; 2215 $subtext = ''; 2216 if (\mod_forum\subscriptions::is_subscribed($USER->id, $forum)) { 2217 if (!\mod_forum\subscriptions::is_forcesubscribed($forum)) { 2218 $subtext = get_string('unsubscribe', 'forum'); 2219 } 2220 } else { 2221 $subtext = get_string('subscribe', 'forum'); 2222 } 2223 $suburl = new moodle_url('/mod/forum/subscribe.php', array('id' => $forum->id, 'sesskey' => sesskey())); 2224 $output .= html_writer::tag('div', html_writer::link($suburl, $subtext), array('class' => 'subscribelink')); 2225 } 2226 2227 $coursemodule = get_coursemodule_from_instance('forum', $forum->id); 2228 $context = context_module::instance($coursemodule->id); 2229 2230 $entityfactory = mod_forum\local\container::get_entity_factory(); 2231 $forumentity = $entityfactory->get_forum_from_stdclass($forum, $context, $coursemodule, $SITE); 2232 2233 $rendererfactory = mod_forum\local\container::get_renderer_factory(); 2234 $discussionsrenderer = $rendererfactory->get_frontpage_news_discussion_list_renderer($forumentity); 2235 $cm = \cm_info::create($coursemodule); 2236 return $output . $discussionsrenderer->render($USER, $cm, null, null, 0, $SITE->newsitems); 2237 } 2238 2239 /** 2240 * Renders part of frontpage with a skip link (i.e. "My courses", "Site news", etc.) 2241 * 2242 * @param string $skipdivid 2243 * @param string $contentsdivid 2244 * @param string $header Header of the part 2245 * @param string $contents Contents of the part 2246 * @return string 2247 */ 2248 protected function frontpage_part($skipdivid, $contentsdivid, $header, $contents) { 2249 if (strval($contents) === '') { 2250 return ''; 2251 } 2252 $output = html_writer::link('#' . $skipdivid, 2253 get_string('skipa', 'access', core_text::strtolower(strip_tags($header))), 2254 array('class' => 'skip-block skip aabtn')); 2255 2256 // Wrap frontpage part in div container. 2257 $output .= html_writer::start_tag('div', array('id' => $contentsdivid)); 2258 $output .= $this->heading($header); 2259 2260 $output .= $contents; 2261 2262 // End frontpage part div container. 2263 $output .= html_writer::end_tag('div'); 2264 2265 $output .= html_writer::tag('span', '', array('class' => 'skip-block-to', 'id' => $skipdivid)); 2266 return $output; 2267 } 2268 2269 /** 2270 * Outputs contents for frontpage as configured in $CFG->frontpage or $CFG->frontpageloggedin 2271 * 2272 * @return string 2273 */ 2274 public function frontpage() { 2275 global $CFG, $SITE; 2276 2277 $output = ''; 2278 2279 if (isloggedin() and !isguestuser() and isset($CFG->frontpageloggedin)) { 2280 $frontpagelayout = $CFG->frontpageloggedin; 2281 } else { 2282 $frontpagelayout = $CFG->frontpage; 2283 } 2284 2285 foreach (explode(',', $frontpagelayout) as $v) { 2286 switch ($v) { 2287 // Display the main part of the front page. 2288 case FRONTPAGENEWS: 2289 if ($SITE->newsitems) { 2290 // Print forums only when needed. 2291 require_once($CFG->dirroot .'/mod/forum/lib.php'); 2292 if (($newsforum = forum_get_course_forum($SITE->id, 'news')) && 2293 ($forumcontents = $this->frontpage_news($newsforum))) { 2294 $newsforumcm = get_fast_modinfo($SITE)->instances['forum'][$newsforum->id]; 2295 $output .= $this->frontpage_part('skipsitenews', 'site-news-forum', 2296 $newsforumcm->get_formatted_name(), $forumcontents); 2297 } 2298 } 2299 break; 2300 2301 case FRONTPAGEENROLLEDCOURSELIST: 2302 $mycourseshtml = $this->frontpage_my_courses(); 2303 if (!empty($mycourseshtml)) { 2304 $output .= $this->frontpage_part('skipmycourses', 'frontpage-course-list', 2305 get_string('mycourses'), $mycourseshtml); 2306 } 2307 break; 2308 2309 case FRONTPAGEALLCOURSELIST: 2310 $availablecourseshtml = $this->frontpage_available_courses(); 2311 $output .= $this->frontpage_part('skipavailablecourses', 'frontpage-available-course-list', 2312 get_string('availablecourses'), $availablecourseshtml); 2313 break; 2314 2315 case FRONTPAGECATEGORYNAMES: 2316 $output .= $this->frontpage_part('skipcategories', 'frontpage-category-names', 2317 get_string('categories'), $this->frontpage_categories_list()); 2318 break; 2319 2320 case FRONTPAGECATEGORYCOMBO: 2321 $output .= $this->frontpage_part('skipcourses', 'frontpage-category-combo', 2322 get_string('courses'), $this->frontpage_combo_list()); 2323 break; 2324 2325 case FRONTPAGECOURSESEARCH: 2326 $output .= $this->box($this->course_search_form(''), 'd-flex justify-content-center'); 2327 break; 2328 2329 } 2330 $output .= '<br />'; 2331 } 2332 2333 return $output; 2334 } 2335 } 2336 2337 /** 2338 * Class storing display options and functions to help display course category and/or courses lists 2339 * 2340 * This is a wrapper for core_course_category objects that also stores display options 2341 * and functions to retrieve sorted and paginated lists of categories/courses. 2342 * 2343 * If theme overrides methods in core_course_renderers that access this class 2344 * it may as well not use this class at all or extend it. 2345 * 2346 * @package core 2347 * @copyright 2013 Marina Glancy 2348 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2349 */ 2350 class coursecat_helper { 2351 /** @var string [none, collapsed, expanded] how (if) display courses list */ 2352 protected $showcourses = 10; /* core_course_renderer::COURSECAT_SHOW_COURSES_COLLAPSED */ 2353 /** @var int depth to expand subcategories in the tree (deeper subcategories will be loaded by AJAX or proceed to category page by clicking on category name) */ 2354 protected $subcatdepth = 1; 2355 /** @var array options to display courses list */ 2356 protected $coursesdisplayoptions = array(); 2357 /** @var array options to display subcategories list */ 2358 protected $categoriesdisplayoptions = array(); 2359 /** @var array additional HTML attributes */ 2360 protected $attributes = array(); 2361 /** @var array search criteria if the list is a search result */ 2362 protected $searchcriteria = null; 2363 2364 /** 2365 * Sets how (if) to show the courses - none, collapsed, expanded, etc. 2366 * 2367 * @param int $showcourses SHOW_COURSES_NONE, SHOW_COURSES_COLLAPSED, SHOW_COURSES_EXPANDED, etc. 2368 * @return coursecat_helper 2369 */ 2370 public function set_show_courses($showcourses) { 2371 $this->showcourses = $showcourses; 2372 // Automatically set the options to preload summary and coursecontacts for core_course_category::get_courses() 2373 // and core_course_category::search_courses(). 2374 $this->coursesdisplayoptions['summary'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_AUTO; 2375 $this->coursesdisplayoptions['coursecontacts'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_EXPANDED; 2376 $this->coursesdisplayoptions['customfields'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_COLLAPSED; 2377 return $this; 2378 } 2379 2380 /** 2381 * Returns how (if) to show the courses - none, collapsed, expanded, etc. 2382 * 2383 * @return int - COURSECAT_SHOW_COURSES_NONE, COURSECAT_SHOW_COURSES_COLLAPSED, COURSECAT_SHOW_COURSES_EXPANDED, etc. 2384 */ 2385 public function get_show_courses() { 2386 return $this->showcourses; 2387 } 2388 2389 /** 2390 * Sets the maximum depth to expand subcategories in the tree 2391 * 2392 * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name 2393 * 2394 * @param int $subcatdepth 2395 * @return coursecat_helper 2396 */ 2397 public function set_subcat_depth($subcatdepth) { 2398 $this->subcatdepth = $subcatdepth; 2399 return $this; 2400 } 2401 2402 /** 2403 * Returns the maximum depth to expand subcategories in the tree 2404 * 2405 * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name 2406 * 2407 * @return int 2408 */ 2409 public function get_subcat_depth() { 2410 return $this->subcatdepth; 2411 } 2412 2413 /** 2414 * Sets options to display list of courses 2415 * 2416 * Options are later submitted as argument to core_course_category::get_courses() and/or core_course_category::search_courses() 2417 * 2418 * Options that core_course_category::get_courses() accept: 2419 * - recursive - return courses from subcategories as well. Use with care, 2420 * this may be a huge list! 2421 * - summary - preloads fields 'summary' and 'summaryformat' 2422 * - coursecontacts - preloads course contacts 2423 * - customfields - preloads custom fields data 2424 * - isenrolled - preloads indication whether this user is enrolled in the course 2425 * - sort - list of fields to sort. Example 2426 * array('idnumber' => 1, 'shortname' => 1, 'id' => -1) 2427 * will sort by idnumber asc, shortname asc and id desc. 2428 * Default: array('sortorder' => 1) 2429 * Only cached fields may be used for sorting! 2430 * - offset 2431 * - limit - maximum number of children to return, 0 or null for no limit 2432 * 2433 * Options summary and coursecontacts are filled automatically in the set_show_courses() 2434 * 2435 * Also renderer can set here any additional options it wants to pass between renderer functions. 2436 * 2437 * @param array $options 2438 * @return coursecat_helper 2439 */ 2440 public function set_courses_display_options($options) { 2441 $this->coursesdisplayoptions = $options; 2442 $this->set_show_courses($this->showcourses); // this will calculate special display options 2443 return $this; 2444 } 2445 2446 /** 2447 * Sets one option to display list of courses 2448 * 2449 * @see coursecat_helper::set_courses_display_options() 2450 * 2451 * @param string $key 2452 * @param mixed $value 2453 * @return coursecat_helper 2454 */ 2455 public function set_courses_display_option($key, $value) { 2456 $this->coursesdisplayoptions[$key] = $value; 2457 return $this; 2458 } 2459 2460 /** 2461 * Return the specified option to display list of courses 2462 * 2463 * @param string $optionname option name 2464 * @param mixed $defaultvalue default value for option if it is not specified 2465 * @return mixed 2466 */ 2467 public function get_courses_display_option($optionname, $defaultvalue = null) { 2468 if (array_key_exists($optionname, $this->coursesdisplayoptions)) { 2469 return $this->coursesdisplayoptions[$optionname]; 2470 } else { 2471 return $defaultvalue; 2472 } 2473 } 2474 2475 /** 2476 * Returns all options to display the courses 2477 * 2478 * This array is usually passed to {@link core_course_category::get_courses()} or 2479 * {@link core_course_category::search_courses()} 2480 * 2481 * @return array 2482 */ 2483 public function get_courses_display_options() { 2484 return $this->coursesdisplayoptions; 2485 } 2486 2487 /** 2488 * Sets options to display list of subcategories 2489 * 2490 * Options 'sort', 'offset' and 'limit' are passed to core_course_category::get_children(). 2491 * Any other options may be used by renderer functions 2492 * 2493 * @param array $options 2494 * @return coursecat_helper 2495 */ 2496 public function set_categories_display_options($options) { 2497 $this->categoriesdisplayoptions = $options; 2498 return $this; 2499 } 2500 2501 /** 2502 * Return the specified option to display list of subcategories 2503 * 2504 * @param string $optionname option name 2505 * @param mixed $defaultvalue default value for option if it is not specified 2506 * @return mixed 2507 */ 2508 public function get_categories_display_option($optionname, $defaultvalue = null) { 2509 if (array_key_exists($optionname, $this->categoriesdisplayoptions)) { 2510 return $this->categoriesdisplayoptions[$optionname]; 2511 } else { 2512 return $defaultvalue; 2513 } 2514 } 2515 2516 /** 2517 * Returns all options to display list of subcategories 2518 * 2519 * This array is usually passed to {@link core_course_category::get_children()} 2520 * 2521 * @return array 2522 */ 2523 public function get_categories_display_options() { 2524 return $this->categoriesdisplayoptions; 2525 } 2526 2527 /** 2528 * Sets additional general options to pass between renderer functions, usually HTML attributes 2529 * 2530 * @param array $attributes 2531 * @return coursecat_helper 2532 */ 2533 public function set_attributes($attributes) { 2534 $this->attributes = $attributes; 2535 return $this; 2536 } 2537 2538 /** 2539 * Return all attributes and erases them so they are not applied again 2540 * 2541 * @param string $classname adds additional class name to the beginning of $attributes['class'] 2542 * @return array 2543 */ 2544 public function get_and_erase_attributes($classname) { 2545 $attributes = $this->attributes; 2546 $this->attributes = array(); 2547 if (empty($attributes['class'])) { 2548 $attributes['class'] = ''; 2549 } 2550 $attributes['class'] = $classname . ' '. $attributes['class']; 2551 return $attributes; 2552 } 2553 2554 /** 2555 * Sets the search criteria if the course is a search result 2556 * 2557 * Search string will be used to highlight terms in course name and description 2558 * 2559 * @param array $searchcriteria 2560 * @return coursecat_helper 2561 */ 2562 public function set_search_criteria($searchcriteria) { 2563 $this->searchcriteria = $searchcriteria; 2564 return $this; 2565 } 2566 2567 /** 2568 * Returns formatted and filtered description of the given category 2569 * 2570 * @param core_course_category $coursecat category 2571 * @param stdClass|array $options format options, by default [noclean,overflowdiv], 2572 * if context is not specified it will be added automatically 2573 * @return string|null 2574 */ 2575 public function get_category_formatted_description($coursecat, $options = null) { 2576 if ($coursecat->id && $coursecat->is_uservisible() && !empty($coursecat->description)) { 2577 if (!isset($coursecat->descriptionformat)) { 2578 $descriptionformat = FORMAT_MOODLE; 2579 } else { 2580 $descriptionformat = $coursecat->descriptionformat; 2581 } 2582 if ($options === null) { 2583 $options = array('noclean' => true, 'overflowdiv' => true); 2584 } else { 2585 $options = (array)$options; 2586 } 2587 $context = context_coursecat::instance($coursecat->id); 2588 if (!isset($options['context'])) { 2589 $options['context'] = $context; 2590 } 2591 $text = file_rewrite_pluginfile_urls($coursecat->description, 2592 'pluginfile.php', $context->id, 'coursecat', 'description', null); 2593 return format_text($text, $descriptionformat, $options); 2594 } 2595 return null; 2596 } 2597 2598 /** 2599 * Returns given course's summary with proper embedded files urls and formatted 2600 * 2601 * @param core_course_list_element $course 2602 * @param array|stdClass $options additional formatting options 2603 * @return string 2604 */ 2605 public function get_course_formatted_summary($course, $options = array()) { 2606 global $CFG; 2607 require_once($CFG->libdir. '/filelib.php'); 2608 if (!$course->has_summary()) { 2609 return ''; 2610 } 2611 $options = (array)$options; 2612 $context = context_course::instance($course->id); 2613 if (!isset($options['context'])) { 2614 // TODO see MDL-38521 2615 // option 1 (current), page context - no code required 2616 // option 2, system context 2617 // $options['context'] = context_system::instance(); 2618 // option 3, course context: 2619 // $options['context'] = $context; 2620 // option 4, course category context: 2621 // $options['context'] = $context->get_parent_context(); 2622 } 2623 $summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', null); 2624 $summary = format_text($summary, $course->summaryformat, $options, $course->id); 2625 if (!empty($this->searchcriteria['search'])) { 2626 $summary = highlight($this->searchcriteria['search'], $summary); 2627 } 2628 return $summary; 2629 } 2630 2631 /** 2632 * Returns course name as it is configured to appear in courses lists formatted to course context 2633 * 2634 * @param core_course_list_element $course 2635 * @param array|stdClass $options additional formatting options 2636 * @return string 2637 */ 2638 public function get_course_formatted_name($course, $options = array()) { 2639 $options = (array)$options; 2640 if (!isset($options['context'])) { 2641 $options['context'] = context_course::instance($course->id); 2642 } 2643 $name = format_string(get_course_display_name_for_list($course), true, $options); 2644 if (!empty($this->searchcriteria['search'])) { 2645 $name = highlight($this->searchcriteria['search'], $name); 2646 } 2647 return $name; 2648 } 2649 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body