MDL-58428 renderer: Move renderer override from files
[moodle.git] / theme / bootstrapbase / classes / output / core_course / management / renderer.php
1 <?php
2 // This file is part of The Bootstrap Moodle theme
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/>.
17 /**
18  * Renderers to align Moodle's HTML with that expected by Bootstrap
19  *
20  * @package    theme_bootstrapbase
21  * @copyright   2018 Bas Brands
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace theme_bootstrapbase\output\core_course\management;
26 defined('MOODLE_INTERNAL') || die();
28 require_once($CFG->dirroot . "/course/classes/management_renderer.php");
30 use html_writer;
31 use core_course_category;
32 use moodle_url;
33 use core_course_list_element;
34 use lang_string;
35 use context_system;
36 use stdClass;
37 use action_menu;
38 use action_menu_link_secondary;
40 /**
41  * Main renderer for the course management pages.
42  *
43  * @package theme_bootstrapbase
44  * @copyright 2013 Sam Hemelryk
45  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46  */
47 class renderer extends \core_course_management_renderer {
49     /**
50      * Opens a grid.
51      *
52      * Call {@link core_course_management_renderer::grid_column_start()} to create columns.
53      *
54      * @param string $id An id to give this grid.
55      * @param string $class A class to give this grid.
56      * @return string
57      */
58     public function grid_start($id = null, $class = null) {
59         $gridclass = 'grid-row-r row-fluid';
60         if (is_null($class)) {
61             $class = $gridclass;
62         } else {
63             $class .= ' ' . $gridclass;
64         }
65         $attributes = array();
66         if (!is_null($id)) {
67             $attributes['id'] = $id;
68         }
69         return html_writer::start_div($class, $attributes);
70     }
72     /**
73      * Opens a grid column
74      *
75      * @param int $size The number of segments this column should span.
76      * @param string $id An id to give the column.
77      * @param string $class A class to give the column.
78      * @return string
79      */
80     public function grid_column_start($size, $id = null, $class = null) {
82         // Calculate Bootstrap grid sizing.
83         $bootstrapclass = 'span'.$size.' col-md-'.$size;
85         // Calculate YUI grid sizing.
86         if ($size === 12) {
87             $maxsize = 1;
88             $size = 1;
89         } else {
90             $maxsize = 12;
91             $divisors = array(8, 6, 5, 4, 3, 2);
92             foreach ($divisors as $divisor) {
93                 if (($maxsize % $divisor === 0) && ($size % $divisor === 0)) {
94                     $maxsize = $maxsize / $divisor;
95                     $size = $size / $divisor;
96                     break;
97                 }
98             }
99         }
100         if ($maxsize > 1) {
101             $yuigridclass =  "grid-col-{$size}-{$maxsize} grid-col";
102         } else {
103             $yuigridclass =  "grid-col-1 grid-col";
104         }
106         if (is_null($class)) {
107             $class = $yuigridclass . ' ' . $bootstrapclass;
108         } else {
109             $class .= ' ' . $yuigridclass . ' ' . $bootstrapclass;
110         }
111         $attributes = array();
112         if (!is_null($id)) {
113             $attributes['id'] = $id;
114         }
115         return html_writer::start_div($class, $attributes);
116     }
118     /**
119      * Renderers detailed course information.
120      *
121      * @param core_course_list_element  $course The course to display details for.
122      * @return string
123      */
124     public function course_detail(core_course_list_element $course) {
125         $details = \core_course\management\helper::get_course_detail_array($course);
126         $fullname = $details['fullname']['value'];
128         $html  = html_writer::start_div('course-detail');
129         $html .= html_writer::tag('h3', $fullname, array('id' => 'course-detail-title', 'tabindex' => '0'));
130         $html .= $this->course_detail_actions($course);
131         foreach ($details as $class => $data) {
132             $html .= $this->detail_pair($data['key'], $data['value'], $class);
133         }
134         $html .= html_writer::end_div();
135         return $html;
136     }
138     /**
139      * Renders html to display a course search form
140      *
141      * @param string $value default value to populate the search field
142      * @param string $format display format - 'plain' (default), 'short' or 'navbar'
143      * @return string
144      */
145     public function course_search_form($value = '', $format = 'plain') {
146         static $count = 0;
147         $formid = 'coursesearch';
148         if ((++$count) > 1) {
149             $formid .= $count;
150         }
152         switch ($format) {
153             case 'navbar' :
154                 $formid = 'coursesearchnavbar';
155                 $inputid = 'navsearchbox';
156                 $inputsize = 20;
157                 break;
158             case 'short' :
159                 $inputid = 'shortsearchbox';
160                 $inputsize = 12;
161                 break;
162             default :
163                 $inputid = 'coursesearchbox';
164                 $inputsize = 30;
165         }
167         $strsearchcourses = get_string("searchcourses");
168         $searchurl = new moodle_url('/course/management.php');
170         $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get',
171                 'class' => 'form-inline'));
172         $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset m-y-1'));
173         $output .= html_writer::tag('label', $strsearchcourses, array('for' => $inputid));
174         $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid, 'size' => $inputsize,
175                 'name' => 'search', 'value' => s($value), 'class' => 'form-control m-x-1'));
176         $output .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go'),
177                 'class' => 'btn btn-secondary'));
178         $output .= html_writer::end_tag('fieldset');
179         $output .= html_writer::end_tag('form');
181         return $output;
182     }
184     /**
185      * Presents a course category listing.
186      *
187      * @param core_course_category $category The currently selected category. Also the category to highlight in the listing.
188      * @return string
189      */
190     public function category_listing(core_course_category $category = null) {
192         if ($category === null) {
193             $selectedparents = array();
194             $selectedcategory = null;
195         } else {
196             $selectedparents = $category->get_parents();
197             $selectedparents[] = $category->id;
198             $selectedcategory = $category->id;
199         }
200         $catatlevel = \core_course\management\helper::get_expanded_categories('');
201         $catatlevel[] = array_shift($selectedparents);
202         $catatlevel = array_unique($catatlevel);
204         $listing = core_course_category::get(0)->get_children();
206         $attributes = array(
207                 'class' => 'ml',
208                 'role' => 'tree',
209                 'aria-labelledby' => 'category-listing-title'
210         );
212         $html  = html_writer::start_div('category-listing');
213         $html .= html_writer::tag('h3', get_string('categories'), array('id' => 'category-listing-title'));
214         $html .= $this->category_listing_actions($category);
215         $html .= html_writer::start_tag('ul', $attributes);
216         foreach ($listing as $listitem) {
217             // Render each category in the listing.
218             $subcategories = array();
219             if (in_array($listitem->id, $catatlevel)) {
220                 $subcategories = $listitem->get_children();
221             }
222             $html .= $this->category_listitem(
223                     $listitem,
224                     $subcategories,
225                     $listitem->get_children_count(),
226                     $selectedcategory,
227                     $selectedparents
228             );
229         }
230         $html .= html_writer::end_tag('ul');
231         $html .= $this->category_bulk_actions($category);
232         $html .= html_writer::end_div();
233         return $html;
234     }
236     /**
237      * Renders a category list item.
238      *
239      * This function gets called recursively to render sub categories.
240      *
241      * @param core_course_category $category The category to render as listitem.
242      * @param core_course_category[] $subcategories The subcategories belonging to the category being rented.
243      * @param int $totalsubcategories The total number of sub categories.
244      * @param int $selectedcategory The currently selected category
245      * @param int[] $selectedcategories The path to the selected category and its ID.
246      * @return string
247      */
248     public function category_listitem(core_course_category $category, array $subcategories, $totalsubcategories,
249             $selectedcategory = null, $selectedcategories = array()) {
251         $isexpandable = ($totalsubcategories > 0);
252         $isexpanded = (!empty($subcategories));
253         $activecategory = ($selectedcategory === $category->id);
254         $attributes = array(
255                 'class' => 'listitem listitem-category',
256                 'data-id' => $category->id,
257                 'data-expandable' => $isexpandable ? '1' : '0',
258                 'data-expanded' => $isexpanded ? '1' : '0',
259                 'data-selected' => $activecategory ? '1' : '0',
260                 'data-visible' => $category->visible ? '1' : '0',
261                 'role' => 'treeitem',
262                 'aria-expanded' => $isexpanded ? 'true' : 'false'
263         );
264         $text = $category->get_formatted_name();
265         if ($category->parent) {
266             $a = new stdClass;
267             $a->category = $text;
268             $a->parentcategory = $category->get_parent_coursecat()->get_formatted_name();
269             $textlabel = get_string('categorysubcategoryof', 'moodle', $a);
270         }
271         $courseicon = $this->output->pix_icon('i/course', get_string('courses'));
272         $bcatinput = array(
273                 'type' => 'checkbox',
274                 'name' => 'bcat[]',
275                 'value' => $category->id,
276                 'class' => 'bulk-action-checkbox',
277                 'aria-label' => get_string('bulkactionselect', 'moodle', $text),
278                 'data-action' => 'select'
279         );
281         if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) {
282             // Very very hardcoded here.
283             $bcatinput['style'] = 'visibility:hidden';
284         }
286         $viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
287         if ($isexpanded) {
288             $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
289             $icon = html_writer::link(
290                     $viewcaturl,
291                     $icon,
292                     array(
293                             'class' => 'float-left',
294                             'data-action' => 'collapse',
295                             'title' => get_string('collapsecategory', 'moodle', $text),
296                             'aria-controls' => 'subcategoryof'.$category->id
297                     )
298             );
299         } else if ($isexpandable) {
300             $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
301             $icon = html_writer::link(
302                     $viewcaturl,
303                     $icon,
304                     array(
305                             'class' => 'float-left',
306                             'data-action' => 'expand',
307                             'title' => get_string('expandcategory', 'moodle', $text)
308                     )
309             );
310         } else {
311             $icon = $this->output->pix_icon(
312                     'i/empty',
313                     '',
314                     'moodle',
315                     array('class' => 'tree-icon'));
316             $icon = html_writer::span($icon, 'float-left');
317         }
318         $actions = \core_course\management\helper::get_category_listitem_actions($category);
319         $hasactions = !empty($actions) || $category->can_create_course();
321         $html = html_writer::start_tag('li', $attributes);
322         $html .= html_writer::start_div('clearfix');
323         $html .= html_writer::start_div('float-left ba-checkbox');
324         $html .= html_writer::empty_tag('input', $bcatinput).'&nbsp;';
325         $html .= html_writer::end_div();
326         $html .= $icon;
327         if ($hasactions) {
328             $textattributes = array('class' => 'float-left categoryname');
329         } else {
330             $textattributes = array('class' => 'float-left categoryname without-actions');
331         }
332         if (isset($textlabel)) {
333             $textattributes['aria-label'] = $textlabel;
334         }
335         $html .= html_writer::link($viewcaturl, $text, $textattributes);
336         $html .= html_writer::start_div('float-right');
337         if ($category->idnumber) {
338             $html .= html_writer::tag('span', s($category->idnumber), array('class' => 'dimmed idnumber'));
339         }
340         if ($hasactions) {
341             $html .= $this->category_listitem_actions($category, $actions);
342         }
343         $countid = 'course-count-'.$category->id;
344         $html .= html_writer::span(
345                 html_writer::span($category->get_courses_count()) .
346                 html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid)) .
347                 $courseicon,
348                 'course-count dimmed',
349                 array('aria-labelledby' => $countid)
350         );
351         $html .= html_writer::end_div();
352         $html .= html_writer::end_div();
353         if ($isexpanded) {
354             $html .= html_writer::start_tag('ul',
355                     array('class' => 'ml', 'role' => 'group', 'id' => 'subcategoryof'.$category->id));
356             $catatlevel = \core_course\management\helper::get_expanded_categories($category->path);
357             $catatlevel[] = array_shift($selectedcategories);
358             $catatlevel = array_unique($catatlevel);
359             foreach ($subcategories as $listitem) {
360                 $childcategories = (in_array($listitem->id, $catatlevel)) ? $listitem->get_children() : array();
361                 $html .= $this->category_listitem(
362                         $listitem,
363                         $childcategories,
364                         $listitem->get_children_count(),
365                         $selectedcategory,
366                         $selectedcategories
367                 );
368             }
369             $html .= html_writer::end_tag('ul');
370         }
371         $html .= html_writer::end_tag('li');
372         return $html;
373     }
375     /**
376      * Renderers the actions that are possible for the course category listing.
377      *
378      * These are not the actions associated with an individual category listing.
379      * That happens through category_listitem_actions.
380      *
381      * @param core_course_category $category
382      * @return string
383      */
384     public function category_listing_actions(core_course_category $category = null) {
385         $actions = array();
387         $cancreatecategory = $category && $category->can_create_subcategory();
388         $cancreatecategory = $cancreatecategory || core_course_category::can_create_top_level_category();
389         if ($category === null) {
390             $category = core_course_category::get(0);
391         }
393         if ($cancreatecategory) {
394             $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id));
395             $actions[] = html_writer::link($url, get_string('createnewcategory'));
396         }
397         if (core_course_category::can_approve_course_requests()) {
398             $actions[] = html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending'));
399         }
400         if (count($actions) === 0) {
401             return '';
402         }
403         return html_writer::div(join(' | ', $actions), 'listing-actions category-listing-actions');
404     }
406     /**
407      * Renders a course listing.
408      *
409      * @param core_course_category $category The currently selected category. This is what the listing is focused on.
410      * @param core_course_list_element  $course The currently selected course.
411      * @param int $page The page being displayed.
412      * @param int $perpage The number of courses to display per page.
413      * @param string|null $viewmode The view mode the page is in, one out of 'default', 'combined', 'courses' or 'categories'.
414      * @return string
415      */
416     public function course_listing(core_course_category $category = null, core_course_list_element $course = null,
417             $page = 0, $perpage = 20,
418             $viewmode = 'default') {
420         if ($category === null) {
421             $html = html_writer::start_div('select-a-category');
422             $html .= html_writer::tag('h3', get_string('courses'),
423                     array('id' => 'course-listing-title', 'tabindex' => '0'));
424             $html .= $this->output->notification(get_string('selectacategory'), 'notifymessage');
425             $html .= html_writer::end_div();
426             return $html;
427         }
429         $page = max($page, 0);
430         $perpage = max($perpage, 2);
431         $totalcourses = $category->coursecount;
432         $totalpages = ceil($totalcourses / $perpage);
433         if ($page > $totalpages - 1) {
434             $page = $totalpages - 1;
435         }
436         $options = array(
437                 'offset' => $page * $perpage,
438                 'limit' => $perpage
439         );
440         $courseid = isset($course) ? $course->id : null;
441         $class = '';
442         if ($page === 0) {
443             $class .= ' firstpage';
444         }
445         if ($page + 1 === (int)$totalpages) {
446             $class .= ' lastpage';
447         }
449         $html  = html_writer::start_div('course-listing'.$class, array(
450                 'data-category' => $category->id,
451                 'data-page' => $page,
452                 'data-totalpages' => $totalpages,
453                 'data-totalcourses' => $totalcourses,
454                 'data-canmoveoutof' => $category->can_move_courses_out_of() && $category->can_move_courses_into()
455         ));
456         $html .= html_writer::tag('h3', $category->get_formatted_name(),
457                 array('id' => 'course-listing-title', 'tabindex' => '0'));
458         $html .= $this->course_listing_actions($category, $course, $perpage);
459         $html .= $this->listing_pagination($category, $page, $perpage, false, $viewmode);
460         $html .= html_writer::start_tag('ul', array('class' => 'ml course-list', 'role' => 'group'));
461         foreach ($category->get_courses($options) as $listitem) {
462             $html .= $this->course_listitem($category, $listitem, $courseid);
463         }
464         $html .= html_writer::end_tag('ul');
465         $html .= $this->listing_pagination($category, $page, $perpage, true, $viewmode);
466         $html .= $this->course_bulk_actions($category);
467         $html .= html_writer::end_div();
468         return $html;
469     }
471     /**
472      * Renderers a course list item.
473      *
474      * This function will be called for every course being displayed by course_listing.
475      *
476      * @param core_course_category $category The currently selected category and the category the course belongs to.
477      * @param core_course_list_element  $course The course to produce HTML for.
478      * @param int $selectedcourse The id of the currently selected course.
479      * @return string
480      */
481     public function course_listitem(core_course_category $category, core_course_list_element $course, $selectedcourse) {
483         $text = $course->get_formatted_name();
484         $attributes = array(
485                 'class' => 'listitem listitem-course',
486                 'data-id' => $course->id,
487                 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
488                 'data-visible' => $course->visible ? '1' : '0'
489         );
491         $bulkcourseinput = array(
492                 'type' => 'checkbox',
493                 'name' => 'bc[]',
494                 'value' => $course->id,
495                 'class' => 'bulk-action-checkbox',
496                 'aria-label' => get_string('bulkactionselect', 'moodle', $text),
497                 'data-action' => 'select'
498         );
499         if (!$category->has_manage_capability()) {
500             // Very very hardcoded here.
501             $bulkcourseinput['style'] = 'visibility:hidden';
502         }
504         $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
506         $html  = html_writer::start_tag('li', $attributes);
507         $html .= html_writer::start_div('clearfix');
509         if ($category->can_resort_courses()) {
510             // In order for dnd to be available the user must be able to resort the category children..
511             $html .= html_writer::div($this->output->pix_icon('i/move_2d', get_string('dndcourse')), 'float-left drag-handle');
512         }
514         $html .= html_writer::start_div('ba-checkbox float-left');
515         $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp;';
516         $html .= html_writer::end_div();
517         $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
518         $html .= html_writer::start_div('float-right');
519         if ($course->idnumber) {
520             $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
521         }
522         $html .= $this->course_listitem_actions($category, $course);
523         $html .= html_writer::end_div();
524         $html .= html_writer::end_div();
525         $html .= html_writer::end_tag('li');
526         return $html;
527     }
529     /**
530      * Renderers actions for the course listing.
531      *
532      * Not to be confused with course_listitem_actions which renderers the actions for individual courses.
533      *
534      * @param core_course_category $category
535      * @param core_course_list_element $course The currently selected course.
536      * @param int $perpage
537      * @return string
538      */
539     public function course_listing_actions(core_course_category $category, core_course_list_element $course = null, $perpage = 20) {
540         $actions = array();
541         if ($category->can_create_course()) {
542             $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'catmanage'));
543             $actions[] = html_writer::link($url, get_string('createnewcourse'));
544         }
545         if ($category->can_request_course()) {
546             // Request a new course.
547             $url = new moodle_url('/course/request.php', array('return' => 'management'));
548             $actions[] = html_writer::link($url, get_string('requestcourse'));
549         }
550         if ($category->can_resort_courses()) {
551             $params = $this->page->url->params();
552             $params['action'] = 'resortcourses';
553             $params['sesskey'] = sesskey();
554             $baseurl = new moodle_url('/course/management.php', $params);
555             $fullnameurl = new moodle_url($baseurl, array('resort' => 'fullname'));
556             $fullnameurldesc = new moodle_url($baseurl, array('resort' => 'fullnamedesc'));
557             $shortnameurl = new moodle_url($baseurl, array('resort' => 'shortname'));
558             $shortnameurldesc = new moodle_url($baseurl, array('resort' => 'shortnamedesc'));
559             $idnumberurl = new moodle_url($baseurl, array('resort' => 'idnumber'));
560             $idnumberdescurl = new moodle_url($baseurl, array('resort' => 'idnumberdesc'));
561             $timecreatedurl = new moodle_url($baseurl, array('resort' => 'timecreated'));
562             $timecreateddescurl = new moodle_url($baseurl, array('resort' => 'timecreateddesc'));
563             $menu = new action_menu(array(
564                     new action_menu_link_secondary($fullnameurl,
565                             null,
566                             get_string('sortbyx', 'moodle', get_string('fullnamecourse'))),
567                     new action_menu_link_secondary($fullnameurldesc,
568                             null,
569                             get_string('sortbyxreverse', 'moodle', get_string('fullnamecourse'))),
570                     new action_menu_link_secondary($shortnameurl,
571                             null,
572                             get_string('sortbyx', 'moodle', get_string('shortnamecourse'))),
573                     new action_menu_link_secondary($shortnameurldesc,
574                             null,
575                             get_string('sortbyxreverse', 'moodle', get_string('shortnamecourse'))),
576                     new action_menu_link_secondary($idnumberurl,
577                             null,
578                             get_string('sortbyx', 'moodle', get_string('idnumbercourse'))),
579                     new action_menu_link_secondary($idnumberdescurl,
580                             null,
581                             get_string('sortbyxreverse', 'moodle', get_string('idnumbercourse'))),
582                     new action_menu_link_secondary($timecreatedurl,
583                             null,
584                             get_string('sortbyx', 'moodle', get_string('timecreatedcourse'))),
585                     new action_menu_link_secondary($timecreateddescurl,
586                             null,
587                             get_string('sortbyxreverse', 'moodle', get_string('timecreatedcourse')))
588             ));
589             $menu->set_menu_trigger(get_string('resortcourses'));
590             $actions[] = $this->render($menu);
591         }
592         $strall = get_string('all');
593         $menu = new action_menu(array(
594                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 5)), null, 5),
595                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 10)), null, 10),
596                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 20)), null, 20),
597                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 50)), null, 50),
598                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 100)), null, 100),
599                 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 999)), null, $strall),
600         ));
601         if ((int)$perpage === 999) {
602             $perpage = $strall;
603         }
604         $menu->attributes['class'] .= ' courses-per-page';
605         $menu->set_menu_trigger(get_string('perpagea', 'moodle', $perpage));
606         $actions[] = $this->render($menu);
607         return html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions');
608     }
610     /**
611      * Displays a search result listing.
612      *
613      * @param array $courses The courses to display.
614      * @param int $totalcourses The total number of courses to display.
615      * @param core_course_list_element  $course The currently selected course if there is one.
616      * @param int $page The current page, starting at 0.
617      * @param int $perpage The number of courses to display per page.
618      * @param string $search The string we are searching for.
619      * @return string
620      */
621     public function search_listing(array $courses, $totalcourses, core_course_list_element $course = null, $page = 0, $perpage = 20,
622             $search = '') {
623         $page = max($page, 0);
624         $perpage = max($perpage, 2);
625         $totalpages = ceil($totalcourses / $perpage);
626         if ($page > $totalpages - 1) {
627             $page = $totalpages - 1;
628         }
629         $courseid = isset($course) ? $course->id : null;
630         $first = true;
631         $last = false;
632         $i = $page * $perpage;
634         $html  = html_writer::start_div('course-listing', array(
635                 'data-category' => 'search',
636                 'data-page' => $page,
637                 'data-totalpages' => $totalpages,
638                 'data-totalcourses' => $totalcourses
639         ));
640         $html .= html_writer::tag('h3', get_string('courses'));
641         $html .= $this->search_pagination($totalcourses, $page, $perpage);
642         $html .= html_writer::start_tag('ul', array('class' => 'ml'));
643         foreach ($courses as $listitem) {
644             $i++;
645             if ($i == $totalcourses) {
646                 $last = true;
647             }
648             $html .= $this->search_listitem($listitem, $courseid, $first, $last);
649             $first = false;
650         }
651         $html .= html_writer::end_tag('ul');
652         $html .= $this->search_pagination($totalcourses, $page, $perpage, true, $search);
653         $html .= $this->course_search_bulk_actions();
654         $html .= html_writer::end_div();
655         return $html;
656     }
658     /**
659      * Renderers a search result course list item.
660      *
661      * This function will be called for every course being displayed by course_listing.
662      *
663      * @param core_course_list_element  $course The course to produce HTML for.
664      * @param int $selectedcourse The id of the currently selected course.
665      * @return string
666      */
667     public function search_listitem(core_course_list_element $course, $selectedcourse) {
669         $text = $course->get_formatted_name();
670         $attributes = array(
671                 'class' => 'listitem listitem-course',
672                 'data-id' => $course->id,
673                 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
674                 'data-visible' => $course->visible ? '1' : '0'
675         );
676         $bulkcourseinput = '';
677         if (core_course_category::get($course->category)->can_move_courses_out_of()) {
678             $bulkcourseinput = array(
679                     'type' => 'checkbox',
680                     'name' => 'bc[]',
681                     'value' => $course->id,
682                     'class' => 'bulk-action-checkbox',
683                     'aria-label' => get_string('bulkactionselect', 'moodle', $text),
684                     'data-action' => 'select'
685             );
686         }
687         $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
688         $categoryname = core_course_category::get($course->category)->get_formatted_name();
690         $html  = html_writer::start_tag('li', $attributes);
691         $html .= html_writer::start_div('clearfix');
692         $html .= html_writer::start_div('float-left');
693         if ($bulkcourseinput) {
694             $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp;';
695         }
696         $html .= html_writer::end_div();
697         $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
698         $html .= html_writer::tag('span', $categoryname, array('class' => 'float-left categoryname'));
699         $html .= html_writer::start_div('float-right');
700         $html .= $this->search_listitem_actions($course);
701         $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
702         $html .= html_writer::end_div();
703         $html .= html_writer::end_div();
704         $html .= html_writer::end_tag('li');
705         return $html;
706     }
708     /**
709      * Renderers a key value pair of information for display.
710      *
711      * @param string $key
712      * @param string $value
713      * @param string $class
714      * @return string
715      */
716     protected function detail_pair($key, $value, $class ='') {
717         $html = html_writer::start_div('detail-pair row yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class));
718         $html .= html_writer::div(html_writer::span($key), 'pair-key span3 col-md-3 yui3-u-1-4');
719         $html .= html_writer::div(html_writer::span($value), 'pair-value span9 col-md-9 m-b-1 yui3-u-3-4 form-inline');
720         $html .= html_writer::end_div();
721         return $html;
722     }
724     /**
725      * A collection of actions for a course.
726      *
727      * @param core_course_list_element  $course The course to display actions for.
728      * @return string
729      */
730     public function course_detail_actions(core_course_list_element $course) {
731         $actions = \core_course\management\helper::get_course_detail_actions($course);
732         if (empty($actions)) {
733             return '';
734         }
735         $options = array();
736         foreach ($actions as $action) {
737             $options[] = $this->action_link($action['url'], $action['string']);
738         }
739         return html_writer::div(join(' | ', $options), 'listing-actions course-detail-listing-actions');
740     }