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