See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Contains renderers for the course management pages. 19 * 20 * @package core_course 21 * @copyright 2013 Sam Hemelryk 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die; 26 27 require_once($CFG->dirroot.'/course/renderer.php'); 28 29 /** 30 * Main renderer for the course management pages. 31 * 32 * @package core_course 33 * @copyright 2013 Sam Hemelryk 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class core_course_management_renderer extends plugin_renderer_base { 37 38 /** 39 * Initialises the JS required to enhance the management interface. 40 * 41 * Thunderbirds are go, this function kicks into gear the JS that makes the 42 * course management pages that much cooler. 43 */ 44 public function enhance_management_interface() { 45 $this->page->requires->yui_module('moodle-course-management', 'M.course.management.init'); 46 $this->page->requires->strings_for_js( 47 array( 48 'show', 49 'showcategory', 50 'hide', 51 'expand', 52 'expandcategory', 53 'collapse', 54 'collapsecategory', 55 'confirmcoursemove', 56 'move', 57 'cancel', 58 'confirm' 59 ), 60 'moodle' 61 ); 62 } 63 64 /** 65 * Displays a heading for the management pages. 66 * 67 * @deprecated since Moodle 4.0. This is now handled/replaced with the tertiary navigation 68 * @todo Final deprecation MDL-73975 69 * @param string $heading The heading to display 70 * @param string|null $viewmode The current view mode if there are options. 71 * @param int|null $categoryid The currently selected category if there is one. 72 * @return string 73 */ 74 public function management_heading($heading, $viewmode = null, $categoryid = null) { 75 debugging('management_heading() is deprecated. Use the class manage_categories_action_bar instead.', DEBUG_DEVELOPER); 76 77 $html = html_writer::start_div('coursecat-management-header clearfix'); 78 if (!empty($heading)) { 79 $html .= $this->heading($heading); 80 } 81 if ($viewmode !== null) { 82 $html .= html_writer::start_div(); 83 $html .= $this->view_mode_selector(\core_course\management\helper::get_management_viewmodes(), $viewmode); 84 if ($viewmode === 'courses') { 85 $categories = core_course_category::make_categories_list(array('moodle/category:manage', 'moodle/course:create')); 86 $nothing = false; 87 if ($categoryid === null) { 88 $nothing = array('' => get_string('selectacategory')); 89 $categoryid = ''; 90 } 91 $select = new single_select($this->page->url, 'categoryid', $categories, $categoryid, $nothing); 92 $select->attributes['aria-label'] = get_string('selectacategory'); 93 $html .= $this->render($select); 94 } 95 $html .= html_writer::end_div(); 96 } 97 $html .= html_writer::end_div(); 98 return $html; 99 } 100 101 /** 102 * Prepares the form element for the course category listing bulk actions. 103 * 104 * @return string 105 */ 106 public function management_form_start() { 107 $form = array('action' => $this->page->url->out(), 'method' => 'POST', 'id' => 'coursecat-management'); 108 109 $html = html_writer::start_tag('form', $form); 110 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); 111 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'bulkaction')); 112 return $html; 113 } 114 115 /** 116 * Closes the course category bulk management form. 117 * 118 * @return string 119 */ 120 public function management_form_end() { 121 return html_writer::end_tag('form'); 122 } 123 124 /** 125 * Presents a course category listing. 126 * 127 * @param core_course_category $category The currently selected category. Also the category to highlight in the listing. 128 * @return string 129 */ 130 public function category_listing(core_course_category $category = null) { 131 132 if ($category === null) { 133 $selectedparents = array(); 134 $selectedcategory = null; 135 } else { 136 $selectedparents = $category->get_parents(); 137 $selectedparents[] = $category->id; 138 $selectedcategory = $category->id; 139 } 140 $catatlevel = \core_course\management\helper::get_expanded_categories(''); 141 $catatlevel[] = array_shift($selectedparents); 142 $catatlevel = array_unique($catatlevel); 143 144 $listing = core_course_category::top()->get_children(); 145 146 $attributes = array( 147 'class' => 'ml-1 list-unstyled', 148 'role' => 'tree', 149 'aria-labelledby' => 'category-listing-title' 150 ); 151 152 $html = html_writer::start_div('category-listing card w-100'); 153 $html .= html_writer::tag('h3', get_string('categories'), 154 array('class' => 'card-header', 'id' => 'category-listing-title')); 155 $html .= html_writer::start_div('card-body'); 156 $html .= $this->category_listing_actions($category); 157 $html .= html_writer::start_tag('ul', $attributes); 158 foreach ($listing as $listitem) { 159 // Render each category in the listing. 160 $subcategories = array(); 161 if (in_array($listitem->id, $catatlevel)) { 162 $subcategories = $listitem->get_children(); 163 } 164 $html .= $this->category_listitem( 165 $listitem, 166 $subcategories, 167 $listitem->get_children_count(), 168 $selectedcategory, 169 $selectedparents 170 ); 171 } 172 $html .= html_writer::end_tag('ul'); 173 $html .= $this->category_bulk_actions($category); 174 $html .= html_writer::end_div(); 175 $html .= html_writer::end_div(); 176 return $html; 177 } 178 179 /** 180 * Renders a category list item. 181 * 182 * This function gets called recursively to render sub categories. 183 * 184 * @param core_course_category $category The category to render as listitem. 185 * @param core_course_category[] $subcategories The subcategories belonging to the category being rented. 186 * @param int $totalsubcategories The total number of sub categories. 187 * @param int $selectedcategory The currently selected category 188 * @param int[] $selectedcategories The path to the selected category and its ID. 189 * @return string 190 */ 191 public function category_listitem(core_course_category $category, array $subcategories, $totalsubcategories, 192 $selectedcategory = null, $selectedcategories = array()) { 193 194 $isexpandable = ($totalsubcategories > 0); 195 $isexpanded = (!empty($subcategories)); 196 $activecategory = ($selectedcategory === $category->id); 197 $attributes = array( 198 'class' => 'listitem listitem-category list-group-item list-group-item-action', 199 'data-id' => $category->id, 200 'data-expandable' => $isexpandable ? '1' : '0', 201 'data-expanded' => $isexpanded ? '1' : '0', 202 'data-selected' => $activecategory ? '1' : '0', 203 'data-visible' => $category->visible ? '1' : '0', 204 'role' => 'treeitem', 205 'aria-expanded' => $isexpanded ? 'true' : 'false' 206 ); 207 $text = $category->get_formatted_name(); 208 if (($parent = $category->get_parent_coursecat()) && $parent->id) { 209 $a = new stdClass; 210 $a->category = $text; 211 $a->parentcategory = $parent->get_formatted_name(); 212 $textlabel = get_string('categorysubcategoryof', 'moodle', $a); 213 } 214 $courseicon = $this->output->pix_icon('i/course', get_string('courses')); 215 $bcatinput = array( 216 'id' => 'categorylistitem' . $category->id, 217 'type' => 'checkbox', 218 'name' => 'bcat[]', 219 'value' => $category->id, 220 'class' => 'bulk-action-checkbox custom-control-input', 221 'data-action' => 'select' 222 ); 223 224 $checkboxclass = ''; 225 if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) { 226 // Very very hardcoded here. 227 $checkboxclass = 'd-none'; 228 } 229 230 $viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id)); 231 if ($isexpanded) { 232 $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 233 'moodle', array('class' => 'tree-icon', 'title' => '')); 234 $icon = html_writer::link( 235 $viewcaturl, 236 $icon, 237 array( 238 'class' => 'float-left', 239 'data-action' => 'collapse', 240 'title' => get_string('collapsecategory', 'moodle', $text), 241 'aria-controls' => 'subcategoryof'.$category->id 242 ) 243 ); 244 } else if ($isexpandable) { 245 $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 246 'moodle', array('class' => 'tree-icon', 'title' => '')); 247 $icon = html_writer::link( 248 $viewcaturl, 249 $icon, 250 array( 251 'class' => 'float-left', 252 'data-action' => 'expand', 253 'title' => get_string('expandcategory', 'moodle', $text) 254 ) 255 ); 256 } else { 257 $icon = $this->output->pix_icon( 258 'i/empty', 259 '', 260 'moodle', 261 array('class' => 'tree-icon')); 262 $icon = html_writer::span($icon, 'float-left'); 263 } 264 $actions = \core_course\management\helper::get_category_listitem_actions($category); 265 $hasactions = !empty($actions) || $category->can_create_course(); 266 267 $html = html_writer::start_tag('li', $attributes); 268 $html .= html_writer::start_div('clearfix'); 269 $html .= html_writer::start_div('float-left ' . $checkboxclass); 270 $html .= html_writer::start_div('custom-control custom-checkbox mr-1 '); 271 $html .= html_writer::empty_tag('input', $bcatinput); 272 $labeltext = html_writer::span(get_string('bulkactionselect', 'moodle', $text), 'sr-only'); 273 $html .= html_writer::tag('label', $labeltext, array( 274 'class' => 'custom-control-label', 275 'for' => 'categorylistitem' . $category->id)); 276 $html .= html_writer::end_div(); 277 $html .= html_writer::end_div(); 278 $html .= $icon; 279 if ($hasactions) { 280 $textattributes = array('class' => 'float-left categoryname aalink'); 281 } else { 282 $textattributes = array('class' => 'float-left categoryname without-actions'); 283 } 284 if (isset($textlabel)) { 285 $textattributes['aria-label'] = $textlabel; 286 } 287 $html .= html_writer::link($viewcaturl, $text, $textattributes); 288 $html .= html_writer::start_div('float-right d-flex'); 289 if ($category->idnumber) { 290 $html .= html_writer::tag('span', s($category->idnumber), array('class' => 'text-muted idnumber')); 291 } 292 if ($hasactions) { 293 $html .= $this->category_listitem_actions($category, $actions); 294 } 295 $countid = 'course-count-'.$category->id; 296 $html .= html_writer::span( 297 html_writer::span($category->get_courses_count()) . 298 html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid)) . 299 $courseicon, 300 'course-count text-muted', 301 array('aria-labelledby' => $countid) 302 ); 303 $html .= html_writer::end_div(); 304 $html .= html_writer::end_div(); 305 if ($isexpanded) { 306 $html .= html_writer::start_tag('ul', 307 array('class' => 'ml', 'role' => 'group', 'id' => 'subcategoryof'.$category->id)); 308 $catatlevel = \core_course\management\helper::get_expanded_categories($category->path); 309 $catatlevel[] = array_shift($selectedcategories); 310 $catatlevel = array_unique($catatlevel); 311 foreach ($subcategories as $listitem) { 312 $childcategories = (in_array($listitem->id, $catatlevel)) ? $listitem->get_children() : array(); 313 $html .= $this->category_listitem( 314 $listitem, 315 $childcategories, 316 $listitem->get_children_count(), 317 $selectedcategory, 318 $selectedcategories 319 ); 320 } 321 $html .= html_writer::end_tag('ul'); 322 } 323 $html .= html_writer::end_tag('li'); 324 return $html; 325 } 326 327 /** 328 * Renderers the actions that are possible for the course category listing. 329 * 330 * These are not the actions associated with an individual category listing. 331 * That happens through category_listitem_actions. 332 * 333 * @param core_course_category $category 334 * @return string 335 */ 336 public function category_listing_actions(core_course_category $category = null) { 337 $actions = array(); 338 339 $cancreatecategory = $category && $category->can_create_subcategory(); 340 $cancreatecategory = $cancreatecategory || core_course_category::can_create_top_level_category(); 341 if ($category === null) { 342 $category = core_course_category::top(); 343 } 344 345 if ($cancreatecategory) { 346 $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id)); 347 $actions[] = html_writer::link($url, get_string('createnewcategory'), array('class' => 'btn btn-secondary')); 348 } 349 if (core_course_category::can_approve_course_requests()) { 350 $actions[] = html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending')); 351 } 352 if (count($actions) === 0) { 353 return ''; 354 } 355 return html_writer::div(join(' ', $actions), 'listing-actions category-listing-actions mb-3'); 356 } 357 358 /** 359 * Renderers the actions for individual category list items. 360 * 361 * @param core_course_category $category 362 * @param array $actions 363 * @return string 364 */ 365 public function category_listitem_actions(core_course_category $category, array $actions = null) { 366 if ($actions === null) { 367 $actions = \core_course\management\helper::get_category_listitem_actions($category); 368 } 369 $menu = new action_menu(); 370 $menu->attributes['class'] .= ' category-item-actions item-actions'; 371 $hasitems = false; 372 foreach ($actions as $key => $action) { 373 $hasitems = true; 374 $menu->add(new action_menu_link( 375 $action['url'], 376 $action['icon'], 377 $action['string'], 378 in_array($key, array('show', 'hide', 'moveup', 'movedown')), 379 array('data-action' => $key, 'class' => 'action-'.$key) 380 )); 381 } 382 if (!$hasitems) { 383 return ''; 384 } 385 386 // If the action menu has items, add the menubar role to the main element containing it. 387 $menu->attributes['role'] = 'menubar'; 388 389 return $this->render($menu); 390 } 391 392 public function render_action_menu($menu) { 393 return $this->output->render($menu); 394 } 395 396 /** 397 * Renders bulk actions for categories. 398 * 399 * @param core_course_category $category The currently selected category if there is one. 400 * @return string 401 */ 402 public function category_bulk_actions(core_course_category $category = null) { 403 // Resort courses. 404 // Change parent. 405 if (!core_course_category::can_resort_any() && !core_course_category::can_change_parent_any()) { 406 return ''; 407 } 408 $strgo = new lang_string('go'); 409 410 $html = html_writer::start_div('category-bulk-actions bulk-actions'); 411 $html .= html_writer::div(get_string('categorybulkaction'), 'accesshide', array('tabindex' => '0')); 412 if (core_course_category::can_resort_any()) { 413 $selectoptions = array( 414 'selectedcategories' => get_string('selectedcategories'), 415 'allcategories' => get_string('allcategories') 416 ); 417 $form = html_writer::start_div(); 418 if ($category) { 419 $selectoptions = array('thiscategory' => get_string('thiscategory')) + $selectoptions; 420 $form .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'currentcategoryid', 'value' => $category->id)); 421 } 422 $form .= html_writer::div( 423 html_writer::select( 424 $selectoptions, 425 'selectsortby', 426 'selectedcategories', 427 false, 428 array('aria-label' => get_string('selectcategorysort')) 429 ) 430 ); 431 $form .= html_writer::div( 432 html_writer::select( 433 array( 434 'name' => get_string('sortbyx', 'moodle', get_string('categoryname')), 435 'namedesc' => get_string('sortbyxreverse', 'moodle', get_string('categoryname')), 436 'idnumber' => get_string('sortbyx', 'moodle', get_string('idnumbercoursecategory')), 437 'idnumberdesc' => get_string('sortbyxreverse' , 'moodle' , get_string('idnumbercoursecategory')), 438 'none' => get_string('dontsortcategories') 439 ), 440 'resortcategoriesby', 441 'name', 442 false, 443 array('aria-label' => get_string('selectcategorysortby'), 'class' => 'mt-1') 444 ) 445 ); 446 $form .= html_writer::div( 447 html_writer::select( 448 array( 449 'fullname' => get_string('sortbyx', 'moodle', get_string('fullnamecourse')), 450 'fullnamedesc' => get_string('sortbyxreverse', 'moodle', get_string('fullnamecourse')), 451 'shortname' => get_string('sortbyx', 'moodle', get_string('shortnamecourse')), 452 'shortnamedesc' => get_string('sortbyxreverse', 'moodle', get_string('shortnamecourse')), 453 'idnumber' => get_string('sortbyx', 'moodle', get_string('idnumbercourse')), 454 'idnumberdesc' => get_string('sortbyxreverse', 'moodle', get_string('idnumbercourse')), 455 'timecreated' => get_string('sortbyx', 'moodle', get_string('timecreatedcourse')), 456 'timecreateddesc' => get_string('sortbyxreverse', 'moodle', get_string('timecreatedcourse')), 457 'none' => get_string('dontsortcourses') 458 ), 459 'resortcoursesby', 460 'fullname', 461 false, 462 array('aria-label' => get_string('selectcoursesortby'), 'class' => 'mt-1') 463 ) 464 ); 465 $form .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'bulksort', 466 'value' => get_string('sort'), 'class' => 'btn btn-secondary my-1')); 467 $form .= html_writer::end_div(); 468 469 $html .= html_writer::start_div('detail-pair row yui3-g my-1'); 470 $html .= html_writer::div(html_writer::span(get_string('sorting')), 'pair-key col-md-3 yui3-u-1-4'); 471 $html .= html_writer::div($form, 'pair-value col-md-9 yui3-u-3-4'); 472 $html .= html_writer::end_div(); 473 } 474 if (core_course_category::can_change_parent_any()) { 475 $options = array(); 476 if (core_course_category::top()->has_manage_capability()) { 477 $options[0] = core_course_category::top()->get_formatted_name(); 478 } 479 $options += core_course_category::make_categories_list('moodle/category:manage'); 480 $select = html_writer::select( 481 $options, 482 'movecategoriesto', 483 '', 484 array('' => 'choosedots'), 485 array('aria-labelledby' => 'moveselectedcategoriesto', 'class' => 'mr-1') 486 ); 487 $submit = array('type' => 'submit', 'name' => 'bulkmovecategories', 'value' => get_string('move'), 488 'class' => 'btn btn-secondary'); 489 $html .= $this->detail_pair( 490 html_writer::span(get_string('moveselectedcategoriesto'), '', array('id' => 'moveselectedcategoriesto')), 491 $select . html_writer::empty_tag('input', $submit) 492 ); 493 } 494 $html .= html_writer::end_div(); 495 return $html; 496 } 497 498 /** 499 * Renders a course listing. 500 * 501 * @param core_course_category $category The currently selected category. This is what the listing is focused on. 502 * @param core_course_list_element $course The currently selected course. 503 * @param int $page The page being displayed. 504 * @param int $perpage The number of courses to display per page. 505 * @param string|null $viewmode The view mode the page is in, one out of 'default', 'combined', 'courses' or 'categories'. 506 * @return string 507 */ 508 public function course_listing(core_course_category $category = null, core_course_list_element $course = null, 509 $page = 0, $perpage = 20, $viewmode = 'default') { 510 511 if ($category === null) { 512 $html = html_writer::start_div('select-a-category'); 513 $html .= html_writer::tag('h3', get_string('courses'), 514 array('id' => 'course-listing-title', 'tabindex' => '0')); 515 $html .= $this->output->notification(get_string('selectacategory'), 'notifymessage'); 516 $html .= html_writer::end_div(); 517 return $html; 518 } 519 520 $page = max($page, 0); 521 $perpage = max($perpage, 2); 522 $totalcourses = $category->coursecount; 523 $totalpages = ceil($totalcourses / $perpage); 524 if ($page > $totalpages - 1) { 525 $page = $totalpages - 1; 526 } 527 $options = array( 528 'offset' => $page * $perpage, 529 'limit' => $perpage 530 ); 531 $courseid = isset($course) ? $course->id : null; 532 $class = ''; 533 if ($page === 0) { 534 $class .= ' firstpage'; 535 } 536 if ($page + 1 === (int)$totalpages) { 537 $class .= ' lastpage'; 538 } 539 540 $html = html_writer::start_div('card course-listing w-100'.$class, array( 541 'data-category' => $category->id, 542 'data-page' => $page, 543 'data-totalpages' => $totalpages, 544 'data-totalcourses' => $totalcourses, 545 'data-canmoveoutof' => $category->can_move_courses_out_of() && $category->can_move_courses_into() 546 )); 547 $html .= html_writer::tag('h3', $category->get_formatted_name(), 548 array('id' => 'course-listing-title', 'tabindex' => '0', 'class' => 'card-header')); 549 $html .= html_writer::start_div('card-body'); 550 $html .= $this->course_listing_actions($category, $course, $perpage); 551 $html .= $this->listing_pagination($category, $page, $perpage, false, $viewmode); 552 $html .= html_writer::start_tag('ul', array('class' => 'ml course-list')); 553 foreach ($category->get_courses($options) as $listitem) { 554 $html .= $this->course_listitem($category, $listitem, $courseid); 555 } 556 $html .= html_writer::end_tag('ul'); 557 $html .= $this->listing_pagination($category, $page, $perpage, true, $viewmode); 558 $html .= $this->course_bulk_actions($category); 559 $html .= html_writer::end_div(); 560 $html .= html_writer::end_div(); 561 return $html; 562 } 563 564 /** 565 * Renders pagination for a course listing. 566 * 567 * @param core_course_category $category The category to produce pagination for. 568 * @param int $page The current page. 569 * @param int $perpage The number of courses to display per page. 570 * @param bool $showtotals Set to true to show the total number of courses and what is being displayed. 571 * @param string|null $viewmode The view mode the page is in, one out of 'default', 'combined', 'courses' or 'categories'. 572 * @return string 573 */ 574 protected function listing_pagination(core_course_category $category, $page, $perpage, $showtotals = false, 575 $viewmode = 'default') { 576 $html = ''; 577 $totalcourses = $category->get_courses_count(); 578 $totalpages = ceil($totalcourses / $perpage); 579 if ($showtotals) { 580 if ($totalpages == 0) { 581 $str = get_string('nocoursesyet'); 582 } else if ($totalpages == 1) { 583 $str = get_string('showingacourses', 'moodle', $totalcourses); 584 } else { 585 $a = new stdClass; 586 $a->start = ($page * $perpage) + 1; 587 $a->end = min((($page + 1) * $perpage), $totalcourses); 588 $a->total = $totalcourses; 589 $str = get_string('showingxofycourses', 'moodle', $a); 590 } 591 $html .= html_writer::div($str, 'listing-pagination-totals text-muted'); 592 } 593 594 if ($viewmode !== 'default') { 595 $baseurl = new moodle_url('/course/management.php', array('categoryid' => $category->id, 596 'view' => $viewmode)); 597 } else { 598 $baseurl = new moodle_url('/course/management.php', array('categoryid' => $category->id)); 599 } 600 601 $html .= $this->output->paging_bar($totalcourses, $page, $perpage, $baseurl); 602 return $html; 603 } 604 605 /** 606 * Renderers a course list item. 607 * 608 * This function will be called for every course being displayed by course_listing. 609 * 610 * @param core_course_category $category The currently selected category and the category the course belongs to. 611 * @param core_course_list_element $course The course to produce HTML for. 612 * @param int $selectedcourse The id of the currently selected course. 613 * @return string 614 */ 615 public function course_listitem(core_course_category $category, core_course_list_element $course, $selectedcourse) { 616 617 $text = $course->get_formatted_name(); 618 $attributes = array( 619 'class' => 'listitem listitem-course list-group-item list-group-item-action', 620 'data-id' => $course->id, 621 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0', 622 'data-visible' => $course->visible ? '1' : '0' 623 ); 624 625 $bulkcourseinput = array( 626 'id' => 'courselistitem' . $course->id, 627 'type' => 'checkbox', 628 'name' => 'bc[]', 629 'value' => $course->id, 630 'class' => 'bulk-action-checkbox custom-control-input', 631 'data-action' => 'select' 632 ); 633 634 $checkboxclass = ''; 635 if (!$category->has_manage_capability()) { 636 // Very very hardcoded here. 637 $checkboxclass = 'd-none'; 638 } 639 640 $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id)); 641 642 $html = html_writer::start_tag('li', $attributes); 643 $html .= html_writer::start_div('d-flex flex-wrap'); 644 645 if ($category->can_resort_courses()) { 646 // In order for dnd to be available the user must be able to resort the category children.. 647 $html .= html_writer::div($this->output->pix_icon('i/move_2d', get_string('dndcourse')), 'float-left drag-handle'); 648 } 649 650 $html .= html_writer::start_div('float-left ' . $checkboxclass); 651 $html .= html_writer::start_div('custom-control custom-checkbox mr-1 '); 652 $html .= html_writer::empty_tag('input', $bulkcourseinput); 653 $labeltext = html_writer::span(get_string('bulkactionselect', 'moodle', $text), 'sr-only'); 654 $html .= html_writer::tag('label', $labeltext, array( 655 'class' => 'custom-control-label', 656 'for' => 'courselistitem' . $course->id)); 657 $html .= html_writer::end_div(); 658 $html .= html_writer::end_div(); 659 $html .= html_writer::link( 660 $viewcourseurl, $text, array('class' => 'text-break col pl-0 mb-2 coursename aalink') 661 ); 662 $html .= html_writer::start_div('flex-shrink-0 ml-auto'); 663 if ($course->idnumber) { 664 $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'text-muted idnumber')); 665 } 666 $html .= $this->course_listitem_actions($category, $course); 667 $html .= html_writer::end_div(); 668 $html .= html_writer::end_div(); 669 $html .= html_writer::end_tag('li'); 670 return $html; 671 } 672 673 /** 674 * Renderers actions for the course listing. 675 * 676 * Not to be confused with course_listitem_actions which renderers the actions for individual courses. 677 * 678 * @param core_course_category $category 679 * @param core_course_list_element $course The currently selected course. 680 * @param int $perpage 681 * @return string 682 */ 683 public function course_listing_actions(core_course_category $category, core_course_list_element $course = null, $perpage = 20) { 684 $actions = array(); 685 if ($category->can_create_course()) { 686 $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'catmanage')); 687 $actions[] = html_writer::link($url, get_string('createnewcourse'), array('class' => 'btn btn-secondary')); 688 } 689 if ($category->can_request_course()) { 690 // Request a new course. 691 $url = new moodle_url('/course/request.php', array('category' => $category->id, 'return' => 'management')); 692 $actions[] = html_writer::link($url, get_string('requestcourse')); 693 } 694 if ($category->can_resort_courses()) { 695 $params = $this->page->url->params(); 696 $params['action'] = 'resortcourses'; 697 $params['sesskey'] = sesskey(); 698 $baseurl = new moodle_url('/course/management.php', $params); 699 $fullnameurl = new moodle_url($baseurl, array('resort' => 'fullname')); 700 $fullnameurldesc = new moodle_url($baseurl, array('resort' => 'fullnamedesc')); 701 $shortnameurl = new moodle_url($baseurl, array('resort' => 'shortname')); 702 $shortnameurldesc = new moodle_url($baseurl, array('resort' => 'shortnamedesc')); 703 $idnumberurl = new moodle_url($baseurl, array('resort' => 'idnumber')); 704 $idnumberdescurl = new moodle_url($baseurl, array('resort' => 'idnumberdesc')); 705 $timecreatedurl = new moodle_url($baseurl, array('resort' => 'timecreated')); 706 $timecreateddescurl = new moodle_url($baseurl, array('resort' => 'timecreateddesc')); 707 $menu = new action_menu(array( 708 new action_menu_link_secondary($fullnameurl, 709 null, 710 get_string('sortbyx', 'moodle', get_string('fullnamecourse'))), 711 new action_menu_link_secondary($fullnameurldesc, 712 null, 713 get_string('sortbyxreverse', 'moodle', get_string('fullnamecourse'))), 714 new action_menu_link_secondary($shortnameurl, 715 null, 716 get_string('sortbyx', 'moodle', get_string('shortnamecourse'))), 717 new action_menu_link_secondary($shortnameurldesc, 718 null, 719 get_string('sortbyxreverse', 'moodle', get_string('shortnamecourse'))), 720 new action_menu_link_secondary($idnumberurl, 721 null, 722 get_string('sortbyx', 'moodle', get_string('idnumbercourse'))), 723 new action_menu_link_secondary($idnumberdescurl, 724 null, 725 get_string('sortbyxreverse', 'moodle', get_string('idnumbercourse'))), 726 new action_menu_link_secondary($timecreatedurl, 727 null, 728 get_string('sortbyx', 'moodle', get_string('timecreatedcourse'))), 729 new action_menu_link_secondary($timecreateddescurl, 730 null, 731 get_string('sortbyxreverse', 'moodle', get_string('timecreatedcourse'))) 732 )); 733 $menu->set_menu_trigger(get_string('resortcourses')); 734 $actions[] = $this->render($menu); 735 } 736 $strall = get_string('all'); 737 $menu = new action_menu(array( 738 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 5)), null, 5), 739 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 10)), null, 10), 740 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 20)), null, 20), 741 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 50)), null, 50), 742 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 100)), null, 100), 743 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 999)), null, $strall), 744 )); 745 if ((int)$perpage === 999) { 746 $perpage = $strall; 747 } 748 $menu->attributes['class'] .= ' courses-per-page'; 749 $menu->set_menu_trigger(get_string('perpagea', 'moodle', $perpage)); 750 $actions[] = $this->render($menu); 751 return html_writer::div(join(' ', $actions), 'listing-actions course-listing-actions'); 752 } 753 754 /** 755 * Renderers actions for individual course actions. 756 * 757 * @param core_course_category $category The currently selected category. 758 * @param core_course_list_element $course The course to renderer actions for. 759 * @return string 760 */ 761 public function course_listitem_actions(core_course_category $category, core_course_list_element $course) { 762 $actions = \core_course\management\helper::get_course_listitem_actions($category, $course); 763 if (empty($actions)) { 764 return ''; 765 } 766 $actionshtml = array(); 767 foreach ($actions as $action) { 768 $action['attributes']['role'] = 'button'; 769 $actionshtml[] = $this->output->action_icon($action['url'], $action['icon'], null, $action['attributes']); 770 } 771 return html_writer::span(join('', $actionshtml), 'course-item-actions item-actions mr-0'); 772 } 773 774 /** 775 * Renderers bulk actions that can be performed on courses. 776 * 777 * @param core_course_category $category The currently selected category and the category in which courses that 778 * are selectable belong. 779 * @return string 780 */ 781 public function course_bulk_actions(core_course_category $category) { 782 $html = html_writer::start_div('course-bulk-actions bulk-actions'); 783 if ($category->can_move_courses_out_of()) { 784 $html .= html_writer::div(get_string('coursebulkaction'), 'accesshide', array('tabindex' => '0')); 785 $options = core_course_category::make_categories_list('moodle/category:manage'); 786 $select = html_writer::select( 787 $options, 788 'movecoursesto', 789 '', 790 array('' => 'choosedots'), 791 array('aria-labelledby' => 'moveselectedcoursesto', 'class' => 'mr-1') 792 ); 793 $submit = array('type' => 'submit', 'name' => 'bulkmovecourses', 'value' => get_string('move'), 794 'class' => 'btn btn-secondary'); 795 $html .= $this->detail_pair( 796 html_writer::span(get_string('moveselectedcoursesto'), '', array('id' => 'moveselectedcoursesto')), 797 $select . html_writer::empty_tag('input', $submit) 798 ); 799 } 800 $html .= html_writer::end_div(); 801 return $html; 802 } 803 804 /** 805 * Renderers bulk actions that can be performed on courses in search returns 806 * 807 * @return string 808 */ 809 public function course_search_bulk_actions() { 810 $html = html_writer::start_div('course-bulk-actions bulk-actions'); 811 $html .= html_writer::div(get_string('coursebulkaction'), 'accesshide', array('tabindex' => '0')); 812 $options = core_course_category::make_categories_list('moodle/category:manage'); 813 $select = html_writer::select( 814 $options, 815 'movecoursesto', 816 '', 817 array('' => 'choosedots'), 818 array('aria-labelledby' => 'moveselectedcoursesto') 819 ); 820 $submit = array('type' => 'submit', 'name' => 'bulkmovecourses', 'value' => get_string('move'), 821 'class' => 'btn btn-secondary'); 822 $html .= $this->detail_pair( 823 html_writer::span(get_string('moveselectedcoursesto'), '', array('id' => 'moveselectedcoursesto')), 824 $select . html_writer::empty_tag('input', $submit) 825 ); 826 $html .= html_writer::end_div(); 827 return $html; 828 } 829 830 /** 831 * Renderers detailed course information. 832 * 833 * @param core_course_list_element $course The course to display details for. 834 * @return string 835 */ 836 public function course_detail(core_course_list_element $course) { 837 $details = \core_course\management\helper::get_course_detail_array($course); 838 $fullname = $details['fullname']['value']; 839 840 $html = html_writer::start_div('course-detail card'); 841 $html .= html_writer::start_div('card-header'); 842 $html .= html_writer::tag('h3', $fullname, array('id' => 'course-detail-title', 843 'class' => 'card-title', 'tabindex' => '0')); 844 $html .= html_writer::end_div(); 845 $html .= html_writer::start_div('card-body'); 846 $html .= $this->course_detail_actions($course); 847 foreach ($details as $class => $data) { 848 $html .= $this->detail_pair($data['key'], $data['value'], $class); 849 } 850 $html .= html_writer::end_div(); 851 $html .= html_writer::end_div(); 852 return $html; 853 } 854 855 /** 856 * Renderers a key value pair of information for display. 857 * 858 * @param string $key 859 * @param string $value 860 * @param string $class 861 * @return string 862 */ 863 protected function detail_pair($key, $value, $class ='') { 864 $html = html_writer::start_div('detail-pair row yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class)); 865 $html .= html_writer::div(html_writer::span($key), 'pair-key col-md-3 yui3-u-1-4 font-weight-bold'); 866 $html .= html_writer::div(html_writer::span($value), 'pair-value col-md-8 yui3-u-3-4'); 867 $html .= html_writer::end_div(); 868 return $html; 869 } 870 871 /** 872 * A collection of actions for a course. 873 * 874 * @param core_course_list_element $course The course to display actions for. 875 * @return string 876 */ 877 public function course_detail_actions(core_course_list_element $course) { 878 $actions = \core_course\management\helper::get_course_detail_actions($course); 879 if (empty($actions)) { 880 return ''; 881 } 882 $options = array(); 883 foreach ($actions as $action) { 884 $options[] = $this->action_link($action['url'], $action['string'], null, 885 array('class' => 'btn btn-sm btn-secondary mr-1 mb-3')); 886 } 887 return html_writer::div(join('', $options), 'listing-actions course-detail-listing-actions'); 888 } 889 890 /** 891 * Creates an action button (styled link) 892 * 893 * @param moodle_url $url The URL to go to when clicked. 894 * @param string $text The text for the button. 895 * @param string $id An id to give the button. 896 * @param string $class A class to give the button. 897 * @param array $attributes Any additional attributes 898 * @return string 899 */ 900 protected function action_button(moodle_url $url, $text, $id = null, $class = null, $title = null, array $attributes = array()) { 901 if (isset($attributes['class'])) { 902 $attributes['class'] .= ' yui3-button'; 903 } else { 904 $attributes['class'] = 'yui3-button'; 905 } 906 if (!is_null($id)) { 907 $attributes['id'] = $id; 908 } 909 if (!is_null($class)) { 910 $attributes['class'] .= ' '.$class; 911 } 912 if (is_null($title)) { 913 $title = $text; 914 } 915 $attributes['title'] = $title; 916 if (!isset($attributes['role'])) { 917 $attributes['role'] = 'button'; 918 } 919 return html_writer::link($url, $text, $attributes); 920 } 921 922 /** 923 * Opens a grid. 924 * 925 * Call {@link core_course_management_renderer::grid_column_start()} to create columns. 926 * 927 * @param string $id An id to give this grid. 928 * @param string $class A class to give this grid. 929 * @return string 930 */ 931 public function grid_start($id = null, $class = null) { 932 $gridclass = 'grid-start grid-row-r d-flex flex-wrap row'; 933 if (is_null($class)) { 934 $class = $gridclass; 935 } else { 936 $class .= ' ' . $gridclass; 937 } 938 $attributes = array(); 939 if (!is_null($id)) { 940 $attributes['id'] = $id; 941 } 942 return html_writer::start_div($class, $attributes); 943 } 944 945 /** 946 * Closes the grid. 947 * 948 * @return string 949 */ 950 public function grid_end() { 951 return html_writer::end_div(); 952 } 953 954 /** 955 * Opens a grid column 956 * 957 * @param int $size The number of segments this column should span. 958 * @param string $id An id to give the column. 959 * @param string $class A class to give the column. 960 * @return string 961 */ 962 public function grid_column_start($size, $id = null, $class = null) { 963 964 if ($id == 'course-detail') { 965 $size = 12; 966 $bootstrapclass = 'col-md-'.$size; 967 } else { 968 $bootstrapclass = 'd-flex flex-wrap px-3 mb-3'; 969 } 970 971 $yuigridclass = "col-sm"; 972 if (in_array($size, [4, 5, 7])) { 973 $yuigridclass = "col-12 col-lg-6"; 974 } 975 976 if (is_null($class)) { 977 $class = $yuigridclass . ' ' . $bootstrapclass; 978 } else { 979 $class .= ' ' . $yuigridclass . ' ' . $bootstrapclass; 980 } 981 $attributes = array(); 982 if (!is_null($id)) { 983 $attributes['id'] = $id; 984 } 985 return html_writer::start_div($class . " grid_column_start", $attributes); 986 } 987 988 /** 989 * Closes a grid column. 990 * 991 * @return string 992 */ 993 public function grid_column_end() { 994 return html_writer::end_div(); 995 } 996 997 /** 998 * Renders an action_icon. 999 * 1000 * This function uses the {@link core_renderer::action_link()} method for the 1001 * most part. What it does different is prepare the icon as HTML and use it 1002 * as the link text. 1003 * 1004 * @param string|moodle_url $url A string URL or moodel_url 1005 * @param pix_icon $pixicon 1006 * @param component_action $action 1007 * @param array $attributes associative array of html link attributes + disabled 1008 * @param bool $linktext show title next to image in link 1009 * @return string HTML fragment 1010 */ 1011 public function action_icon($url, pix_icon $pixicon, component_action $action = null, 1012 array $attributes = null, $linktext = false) { 1013 if (!($url instanceof moodle_url)) { 1014 $url = new moodle_url($url); 1015 } 1016 $attributes = (array)$attributes; 1017 1018 if (empty($attributes['class'])) { 1019 // Let devs override the class via $attributes. 1020 $attributes['class'] = 'action-icon'; 1021 } 1022 1023 $icon = $this->render($pixicon); 1024 1025 if ($linktext) { 1026 $text = $pixicon->attributes['alt']; 1027 } else { 1028 $text = ''; 1029 } 1030 1031 return $this->action_link($url, $icon.$text, $action, $attributes); 1032 } 1033 1034 /** 1035 * Displays a view mode selector. 1036 * 1037 * @param array $modes An array of view modes. 1038 * @param string $currentmode The current view mode. 1039 * @param moodle_url $url The URL to use when changing actions. Defaults to the page URL. 1040 * @param string $param The param name. 1041 * @return string 1042 */ 1043 public function view_mode_selector(array $modes, $currentmode, moodle_url $url = null, $param = 'view') { 1044 if ($url === null) { 1045 $url = $this->page->url; 1046 } 1047 1048 $menu = new action_menu; 1049 $menu->attributes['class'] .= ' view-mode-selector vms ml-1'; 1050 1051 $selected = null; 1052 foreach ($modes as $mode => $modestr) { 1053 $attributes = array( 1054 'class' => 'vms-mode', 1055 'data-mode' => $mode 1056 ); 1057 if ($currentmode === $mode) { 1058 $attributes['class'] .= ' currentmode'; 1059 $selected = $modestr; 1060 } 1061 if ($selected === null) { 1062 $selected = $modestr; 1063 } 1064 $modeurl = new moodle_url($url, array($param => $mode)); 1065 if ($mode === 'default') { 1066 $modeurl->remove_params($param); 1067 } 1068 $menu->add(new action_menu_link_secondary($modeurl, null, $modestr, $attributes)); 1069 } 1070 1071 $menu->set_menu_trigger($selected); 1072 1073 $html = html_writer::start_div('view-mode-selector vms d-flex'); 1074 $html .= get_string('viewing').' '.$this->render($menu); 1075 $html .= html_writer::end_div(); 1076 1077 return $html; 1078 } 1079 1080 /** 1081 * Displays a search result listing. 1082 * 1083 * @param array $courses The courses to display. 1084 * @param int $totalcourses The total number of courses to display. 1085 * @param core_course_list_element $course The currently selected course if there is one. 1086 * @param int $page The current page, starting at 0. 1087 * @param int $perpage The number of courses to display per page. 1088 * @param string $search The string we are searching for. 1089 * @return string 1090 */ 1091 public function search_listing(array $courses, $totalcourses, core_course_list_element $course = null, $page = 0, $perpage = 20, 1092 $search = '') { 1093 $page = max($page, 0); 1094 $perpage = max($perpage, 2); 1095 $totalpages = ceil($totalcourses / $perpage); 1096 if ($page > $totalpages - 1) { 1097 $page = $totalpages - 1; 1098 } 1099 $courseid = isset($course) ? $course->id : null; 1100 $first = true; 1101 $last = false; 1102 $i = $page * $perpage; 1103 1104 $html = html_writer::start_div('course-listing w-100', array( 1105 'data-category' => 'search', 1106 'data-page' => $page, 1107 'data-totalpages' => $totalpages, 1108 'data-totalcourses' => $totalcourses 1109 )); 1110 $html .= html_writer::tag('h3', get_string('courses')); 1111 $html .= $this->search_pagination($totalcourses, $page, $perpage); 1112 $html .= html_writer::start_tag('ul', array('class' => 'ml')); 1113 foreach ($courses as $listitem) { 1114 $i++; 1115 if ($i == $totalcourses) { 1116 $last = true; 1117 } 1118 $html .= $this->search_listitem($listitem, $courseid, $first, $last); 1119 $first = false; 1120 } 1121 $html .= html_writer::end_tag('ul'); 1122 $html .= $this->search_pagination($totalcourses, $page, $perpage, true, $search); 1123 $html .= $this->course_search_bulk_actions(); 1124 $html .= html_writer::end_div(); 1125 return $html; 1126 } 1127 1128 /** 1129 * Displays pagination for search results. 1130 * 1131 * @param int $totalcourses The total number of courses to be displayed. 1132 * @param int $page The current page. 1133 * @param int $perpage The number of courses being displayed. 1134 * @param bool $showtotals Whether or not to print total information. 1135 * @param string $search The string we are searching for. 1136 * @return string 1137 */ 1138 protected function search_pagination($totalcourses, $page, $perpage, $showtotals = false, $search = '') { 1139 $html = ''; 1140 $totalpages = ceil($totalcourses / $perpage); 1141 if ($showtotals) { 1142 if ($totalpages == 0) { 1143 $str = get_string('nocoursesfound', 'moodle', s($search)); 1144 } else if ($totalpages == 1) { 1145 $str = get_string('showingacourses', 'moodle', $totalcourses); 1146 } else { 1147 $a = new stdClass; 1148 $a->start = ($page * $perpage) + 1; 1149 $a->end = min((($page + 1) * $perpage), $totalcourses); 1150 $a->total = $totalcourses; 1151 $str = get_string('showingxofycourses', 'moodle', $a); 1152 } 1153 $html .= html_writer::div($str, 'listing-pagination-totals text-muted'); 1154 } 1155 1156 if ($totalcourses < $perpage) { 1157 return $html; 1158 } 1159 $aside = 2; 1160 $span = $aside * 2 + 1; 1161 $start = max($page - $aside, 0); 1162 $end = min($page + $aside, $totalpages - 1); 1163 if (($end - $start) < $span) { 1164 if ($start == 0) { 1165 $end = min($totalpages - 1, $span - 1); 1166 } else if ($end == ($totalpages - 1)) { 1167 $start = max(0, $end - $span + 1); 1168 } 1169 } 1170 $items = array(); 1171 $baseurl = $this->page->url; 1172 if ($page > 0) { 1173 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => 0)), get_string('first')); 1174 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page - 1)), get_string('prev')); 1175 $items[] = '...'; 1176 } 1177 for ($i = $start; $i <= $end; $i++) { 1178 $class = ''; 1179 if ($page == $i) { 1180 $class = 'active-page'; 1181 } 1182 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $i)), $i + 1, null, $class); 1183 } 1184 if ($page < ($totalpages - 1)) { 1185 $items[] = '...'; 1186 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page + 1)), get_string('next')); 1187 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $totalpages - 1)), get_string('last')); 1188 } 1189 1190 $html .= html_writer::div(join('', $items), 'listing-pagination'); 1191 return $html; 1192 } 1193 1194 /** 1195 * Renderers a search result course list item. 1196 * 1197 * This function will be called for every course being displayed by course_listing. 1198 * 1199 * @param core_course_list_element $course The course to produce HTML for. 1200 * @param int $selectedcourse The id of the currently selected course. 1201 * @return string 1202 */ 1203 public function search_listitem(core_course_list_element $course, $selectedcourse) { 1204 1205 $text = $course->get_formatted_name(); 1206 $attributes = array( 1207 'class' => 'listitem listitem-course list-group-item list-group-item-action', 1208 'data-id' => $course->id, 1209 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0', 1210 'data-visible' => $course->visible ? '1' : '0' 1211 ); 1212 $bulkcourseinput = ''; 1213 if (core_course_category::get($course->category)->can_move_courses_out_of()) { 1214 $bulkcourseinput = array( 1215 'type' => 'checkbox', 1216 'id' => 'coursesearchlistitem' . $course->id, 1217 'name' => 'bc[]', 1218 'value' => $course->id, 1219 'class' => 'bulk-action-checkbox custom-control-input', 1220 'data-action' => 'select' 1221 ); 1222 } 1223 $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id)); 1224 $categoryname = core_course_category::get($course->category)->get_formatted_name(); 1225 1226 $html = html_writer::start_tag('li', $attributes); 1227 $html .= html_writer::start_div('clearfix'); 1228 $html .= html_writer::start_div('float-left'); 1229 if ($bulkcourseinput) { 1230 $html .= html_writer::start_div('custom-control custom-checkbox mr-1'); 1231 $html .= html_writer::empty_tag('input', $bulkcourseinput); 1232 $labeltext = html_writer::span(get_string('bulkactionselect', 'moodle', $text), 'sr-only'); 1233 $html .= html_writer::tag('label', $labeltext, array( 1234 'class' => 'custom-control-label', 1235 'for' => 'coursesearchlistitem' . $course->id)); 1236 $html .= html_writer::end_div(); 1237 } 1238 $html .= html_writer::end_div(); 1239 $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename aalink')); 1240 $html .= html_writer::tag('span', $categoryname, array('class' => 'float-left ml-3 text-muted')); 1241 $html .= html_writer::start_div('float-right'); 1242 $html .= $this->search_listitem_actions($course); 1243 $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'text-muted idnumber')); 1244 $html .= html_writer::end_div(); 1245 $html .= html_writer::end_div(); 1246 $html .= html_writer::end_tag('li'); 1247 return $html; 1248 } 1249 1250 /** 1251 * Renderers actions for individual course actions. 1252 * 1253 * @param core_course_list_element $course The course to renderer actions for. 1254 * @return string 1255 */ 1256 public function search_listitem_actions(core_course_list_element $course) { 1257 $baseurl = new moodle_url( 1258 '/course/managementsearch.php', 1259 array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => sesskey()) 1260 ); 1261 $actions = array(); 1262 // Edit. 1263 if ($course->can_access()) { 1264 if ($course->can_edit()) { 1265 $actions[] = $this->output->action_icon( 1266 new moodle_url('/course/edit.php', array('id' => $course->id)), 1267 new pix_icon('t/edit', get_string('edit')), 1268 null, 1269 array('class' => 'action-edit') 1270 ); 1271 } 1272 // Delete. 1273 if ($course->can_delete()) { 1274 $actions[] = $this->output->action_icon( 1275 new moodle_url('/course/delete.php', array('id' => $course->id)), 1276 new pix_icon('t/delete', get_string('delete')), 1277 null, 1278 array('class' => 'action-delete') 1279 ); 1280 } 1281 // Show/Hide. 1282 if ($course->can_change_visibility()) { 1283 $actions[] = $this->output->action_icon( 1284 new moodle_url($baseurl, array('action' => 'hidecourse')), 1285 new pix_icon('t/hide', get_string('hide')), 1286 null, 1287 array('data-action' => 'hide', 'class' => 'action-hide') 1288 ); 1289 $actions[] = $this->output->action_icon( 1290 new moodle_url($baseurl, array('action' => 'showcourse')), 1291 new pix_icon('t/show', get_string('show')), 1292 null, 1293 array('data-action' => 'show', 'class' => 'action-show') 1294 ); 1295 } 1296 } 1297 if (empty($actions)) { 1298 return ''; 1299 } 1300 return html_writer::span(join('', $actions), 'course-item-actions item-actions'); 1301 } 1302 1303 /** 1304 * Renders html to display a course search form 1305 * 1306 * @deprecated since Moodle 4.0. This is now handled within manage_categories_action_bar 1307 * @todo Final deprecation MDL-73975 1308 * @param string $value default value to populate the search field 1309 * @return string 1310 */ 1311 public function course_search_form($value = '') { 1312 debugging('course_search_form() is deprecated. Use the class manage_categories_action_bar instead.', DEBUG_DEVELOPER); 1313 $data = [ 1314 'action' => new moodle_url('/course/management.php'), 1315 'btnclass' => 'btn-primary', 1316 'extraclasses' => 'my-3 d-flex justify-content-center', 1317 'inputname' => 'search', 1318 'searchstring' => get_string('searchcourses'), 1319 'value' => $value 1320 ]; 1321 return $this->render_from_template('core/search_input', $data); 1322 } 1323 1324 /** 1325 * Creates access hidden skip to links for the displayed sections. 1326 * 1327 * @param bool $displaycategorylisting 1328 * @param bool $displaycourselisting 1329 * @param bool $displaycoursedetail 1330 * @return string 1331 */ 1332 public function accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail) { 1333 $html = html_writer::start_div('skiplinks accesshide'); 1334 $url = new moodle_url($this->page->url); 1335 if ($displaycategorylisting) { 1336 $url->set_anchor('category-listing'); 1337 $html .= html_writer::link($url, get_string('skiptocategorylisting'), array('class' => 'skip')); 1338 } 1339 if ($displaycourselisting) { 1340 $url->set_anchor('course-listing'); 1341 $html .= html_writer::link($url, get_string('skiptocourselisting'), array('class' => 'skip')); 1342 } 1343 if ($displaycoursedetail) { 1344 $url->set_anchor('course-detail'); 1345 $html .= html_writer::link($url, get_string('skiptocoursedetails'), array('class' => 'skip')); 1346 } 1347 $html .= html_writer::end_div(); 1348 return $html; 1349 } 1350 1351 /** 1352 * Render the tertiary nav for the manage categories page. 1353 * 1354 * @param \core_course\output\manage_categories_action_bar $actionbar 1355 * @return string The renderered template 1356 */ 1357 public function render_action_bar(\core_course\output\manage_categories_action_bar $actionbar): string { 1358 return $this->render_from_template('core_course/manage_category_actionbar', $actionbar->export_for_template($this)); 1359 } 1360 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body