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