MDL-31830 course: improvements to JS coding style
[moodle.git] / course / classes / management_renderer.php
CommitLineData
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
25defined('MOODLE_INTERNAL') || die;
26
27require_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 */
36class core_course_management_renderer extends plugin_renderer_base {
37
38 /**
39 * Initialises the JS required to enhance the management interface.
40 *
41 * Thunderbirds are go, this function kicks into gear the JS that makes the
42 * course management pages that much cooler.
43 */
44 public function enhance_management_interface() {
45 $this->page->requires->yui_module('moodle-course-management', 'M.course.init_management');
46 $this->page->requires->strings_for_js(
47 array('show', 'hide', 'expand', 'collapse', 'confirmcoursemove', 'yes', 'no', 'confirm'),
48 'moodle'
49 );
50 }
51
52 /**
53 * Displays a heading for the management pages.
54 *
55 * @param string $heading The heading to display
56 * @param string|null $viewmode The current view mode if there are options.
57 * @param int|null $categoryid The currently selected category if there is one.
58 * @return string
59 */
60 public function management_heading($heading, $viewmode = null, $categoryid = null) {
61 $html = html_writer::start_div('coursecat-management-header clearfix');
62 if (!empty($heading)) {
63 $html .= $this->heading($heading);
64 }
65 if ($viewmode !== null) {
66 if ($viewmode === 'courses') {
67 $categories = coursecat::make_categories_list();
68 $nothing = false;
69 if ($categoryid === null) {
70 $nothing = array('' => get_string('selectacategory'));
71 $categoryid = '';
72 }
73 $select = new single_select($this->page->url, 'categoryid', $categories, $categoryid, $nothing);
74 $html .= $this->render($select);
75 }
67e1f268 76 $html .= $this->view_mode_selector(\core_course\management\helper::get_management_viewmodes(), $viewmode);
5dc361e1
SH
77 }
78 $html .= html_writer::end_div();
79 return $html;
80 }
81
82 /**
83 * Prepares the form element for the course category listing bulk actions.
84 *
85 * @return string
86 */
87 public function management_form_start() {
88 $form = array('action' => $this->page->url->out(), 'method' => 'POST', 'id' => 'coursecat-management');
89
90 $html = html_writer::start_tag('form', $form);
91 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
92 $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'bulkaction'));
93 return $html;
94 }
95
96 /**
97 * Closes the course category bulk management form.
98 *
99 * @return string
100 */
101 public function management_form_end() {
102 return html_writer::end_tag('form');
103 }
104
105 /**
106 * Presents a course category listing.
107 *
108 * @param coursecat $category The currently selected category. Also the category to highlight in the listing.
109 * @return string
110 */
111 public function category_listing(coursecat $category = null) {
112
113 if ($category === null) {
5dc361e1 114 $selectedparents = array();
f454e324 115 $selectedcategory = null;
5dc361e1
SH
116 } else {
117 $selectedparents = $category->get_parents();
118 $selectedparents[] = $category->id;
f454e324 119 $selectedcategory = $category->id;
5dc361e1
SH
120 }
121 $catatlevel = array_shift($selectedparents);
122
67e1f268 123 $listing = coursecat::get(0)->get_children();
5dc361e1
SH
124
125 $html = html_writer::start_div('category-listing');
126 $html .= html_writer::tag('h3', get_string('categories'));
b488058f 127 $html .= $this->category_listing_actions($category);
5dc361e1
SH
128 $html .= html_writer::start_tag('ul', array('class' => 'ml'));
129 foreach ($listing as $listitem) {
130 // Render each category in the listing.
131 $subcategories = array();
132 if ($listitem->id == $catatlevel) {
133 $subcategories = $listitem->get_children();
134 }
135 $html .= $this->category_listitem(
136 $listitem,
137 $subcategories,
138 $listitem->get_children_count(),
f454e324 139 $selectedcategory,
5dc361e1
SH
140 $selectedparents
141 );
142 }
143 $html .= html_writer::end_tag('ul');
144 $html .= $this->category_bulk_actions();
145 $html .= html_writer::end_div();
146 return $html;
147 }
148
149 /**
150 * Renders a category list item.
151 *
152 * This function gets called recursively to render sub categories.
153 *
154 * @param coursecat $category The category to render as listitem.
155 * @param coursecat[] $subcategories The subcategories belonging to the category being rented.
156 * @param int $totalsubcategories The total number of sub categories.
157 * @param int $selectedcategory The currently selected category
158 * @param int[] $selectedcategories The path to the selected category and its ID.
159 * @return string
160 */
161 public function category_listitem(coursecat $category, array $subcategories, $totalsubcategories,
162 $selectedcategory = null, $selectedcategories = array()) {
163 $isexpandable = ($totalsubcategories > 0);
164 $isexpanded = (!empty($subcategories));
f454e324 165 $activecategory = ($selectedcategory === $category->id);
5dc361e1
SH
166 $attributes = array(
167 'class' => 'listitem listitem-category',
168 'data-id' => $category->id,
169 'data-expandable' => $isexpandable ? '1' : '0',
170 'data-expanded' => $isexpanded ? '1' : '0',
171 'data-selected' => $activecategory ? '1' : '0',
172 'data-visible' => $category->visible ? '1' : '0'
173 );
174 $text = $category->get_formatted_name();
175 $courseicon = $this->output->pix_icon('i/course', get_string('courses'));
176 $bcatinput = array('type' => 'checkbox', 'name' => 'bcat[]', 'value' => $category->id, 'class' => 'bulk-action-checkbox');
b488058f
SH
177
178 if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) {
179 // Very very hardcoded here.
180 $bcatinput['style'] = 'visibility:hidden';
181 }
182
5dc361e1
SH
183 $viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
184 if ($isexpanded) {
185 $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon'));
186 $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left', 'data-action' => 'collapse'));
187 } else if ($isexpandable) {
188 $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon'));
189 $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left', 'data-action' => 'expand'));
190 } else {
191 $icon = $this->output->pix_icon('i/navigationitem', '', 'moodle', array('class' => 'tree-icon'));
192 $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left'));
193 }
b488058f
SH
194 $actions = \core_course\management\helper::get_category_listitem_actions($category);
195 $hasactions = !empty($actions);
5dc361e1
SH
196
197 $html = html_writer::start_tag('li', $attributes);
198 $html .= html_writer::start_div('clearfix');
199 $html .= html_writer::start_div('float-left');
200 $html .= html_writer::empty_tag('input', $bcatinput).'&nbsp;';
201 $html .= html_writer::end_div();
202 $html .= $icon;
b488058f
SH
203 if ($hasactions) {
204 $html .= html_writer::link($viewcaturl, $text, array('class' => 'float-left categoryname'));
205 } else {
206 $html .= html_writer::link($viewcaturl, $text, array('class' => 'float-left categoryname without-actions'));
207 }
5dc361e1 208 $html .= html_writer::start_div('float-right');
b488058f
SH
209 if ($hasactions) {
210 $html .= $this->category_listitem_actions($category, $actions);
211 }
5dc361e1
SH
212 $html .= html_writer::span($category->coursecount.$courseicon, 'course-count dimmed');
213 $html .= html_writer::end_div();
214 $html .= html_writer::end_div();
215 if ($isexpanded) {
216 $html .= html_writer::start_tag('ul', array('class' => 'ml'));
217 $catatlevel = array_shift($selectedcategories);
218 foreach ($subcategories as $listitem) {
219 $childcategories = ($listitem->id == $catatlevel) ? $listitem->get_children() : array();
220 $html .= $this->category_listitem(
221 $listitem,
222 $childcategories,
223 $listitem->get_children_count(),
224 $selectedcategory,
225 $selectedcategories
226 );
227 }
228 $html .= html_writer::end_tag('ul');
229 }
230 $html .= html_writer::end_tag('li');
231 return $html;
232 }
233
234 /**
235 * Renderers the actions that are possible for the course category listing.
236 *
237 * These are not the actions associated with an individual category listing.
238 * That happens through category_listitem_actions.
239 *
240 * @param coursecat $category
241 * @return string
242 */
b488058f 243 public function category_listing_actions(coursecat $category = null) {
5dc361e1
SH
244 $actions = array();
245 $createtoplevel = coursecat::can_create_top_level_category();
b488058f
SH
246 $createsubcategory = $category && $category->can_create_subcategory();
247 if ($category === null) {
248 $category = coursecat::get(0);
249 }
5dc361e1
SH
250
251 $hasitems = false;
252 if ($createtoplevel || $createsubcategory) {
253 $hasitems = true;
254 $menu = new action_menu;
255 if ($createtoplevel) {
256 $url = new moodle_url('/course/editcategory.php', array('parent' => 0));
257 $menu->add(new action_menu_link_secondary(
258 $url,
259 null,
260 get_string('toplevelcategory')
261 ));
262 }
263 if ($createsubcategory) {
264 $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id));
265 $attributes = array(
266 'title' => get_string('createsubcategoryof', 'moodle', $category->get_formatted_name())
267 );
268 $menu->add(new action_menu_link_secondary(
269 $url,
270 null,
271 get_string('subcategory'),
272 $attributes
273 ));
274 }
275 $menu->actiontext = get_string('createnew');
276 $menu->actionicon = new pix_icon('t/add', ' ', 'moodle', array('class' => 'iconsmall'));
277 $actions[] = $this->render($menu);
278 }
67e1f268 279 if ($category->can_resort_subcategories()) {
5dc361e1
SH
280 $hasitems = true;
281 $params = $this->page->url->params();
282 $params['action'] = 'resortcategories';
283 $params['sesskey'] = sesskey();
284 $baseurl = new moodle_url('/course/management.php', $params);
285 $menu = new action_menu(array(
286 new action_menu_link_secondary(
287 new moodle_url($baseurl, array('resort' => 'name')),
288 null,
289 get_string('resortbyname')
290 ),
291 new action_menu_link_secondary(
292 new moodle_url($baseurl, array('resort' => 'idnumber')),
293 null,
294 get_string('resortbyidnumber')
295 )
296 ));
b488058f
SH
297 if ($category->id === 0) {
298 $menu->actiontext = get_string('resortcategories');
299 } else {
300 $menu->actiontext = get_string('resortsubcategories');
301 }
5dc361e1
SH
302 $menu->actionicon = new pix_icon('t/sort', ' ', 'moodle', array('class' => 'iconsmall'));
303 $actions[] = $this->render($menu);
304 }
305 if (!$hasitems) {
306 return '';
307 }
308 return html_writer::div(join(' | ', $actions), 'listing-actions category-listing-actions');
309 }
310
311 /**
312 * Renderers the actions for individual category list items.
313 *
314 * @param coursecat $category
315 * @return string
316 */
b488058f
SH
317 public function category_listitem_actions(coursecat $category, array $actions = null) {
318 if ($actions === null) {
319 $actions = \core_course\management\helper::get_category_listitem_actions($category);
320 }
5dc361e1
SH
321 $menu = new action_menu();
322 $menu->attributes['class'] .= ' category-item-actions item-actions';
323 $hasitems = false;
b488058f 324 foreach ($actions as $key => $action) {
5dc361e1
SH
325 $hasitems = true;
326 $menu->add(new action_menu_link(
327 $action['url'],
328 $action['icon'],
329 $action['string'],
330 in_array($key, array('show', 'hide', 'moveup', 'movedown')),
331 array('data-action' => $key, 'class' => 'action-'.$key)
332 ));
333 }
334 if (!$hasitems) {
335 return '';
336 }
337 return $this->render($menu);
338 }
339
340 /**
341 * Renders bulk actions for categories.
342 *
343 * @return string
344 */
345 public function category_bulk_actions() {
346 // Resort courses.
347 // Change parent.
348 $strgo = new lang_string('go');
349
350 $html = html_writer::start_div('category-bulk-actions bulk-actions');
351 if (coursecat::can_resort_any()) {
352 $options = array(
353 'name' => get_string('resortbyname'),
354 'idnumber' => get_string('resortbyidnumber'),
355 );
356 $select = html_writer::select($options, 'resortcategoriesby');
357 $submit = array('type' => 'submit', 'name' => 'bulkresortcategories', 'value' => $strgo);
358 $html .= $this->detail_pair(
359 get_string('resortselectedcategoriesby'),
360 $select . html_writer::empty_tag('input', $submit)
361 );
362 }
363 if (coursecat::can_change_parent_any()) {
364 $options = coursecat::make_categories_list('moodle/category:manage');
365 $select = html_writer::select($options, 'movecategoriesto');
366 $submit = array('type' => 'submit', 'name' => 'bulkmovecategories', 'value' => $strgo);
367 $html .= $this->detail_pair(
368 get_string('moveselectedcategoriesto'),
369 $select . html_writer::empty_tag('input', $submit)
370 );
371 }
372 $html .= html_writer::end_div();
373 return $html;
374 }
375
376 /**
377 * Renders a course listing.
378 *
379 * @param coursecat $category The currently selected category. This is what the listing is focused on.
380 * @param course_in_list $course The currently selected course.
381 * @param int $page The page being displayed.
382 * @param int $perpage The number of courses to display per page.
383 * @return string
384 */
385 public function course_listing(coursecat $category = null, course_in_list $course = null, $page = 0, $perpage = 20) {
386
387 if ($category === null) {
388 $html = html_writer::start_div('select-a-category');
389 $html .= html_writer::tag('h3', get_string('courses'));
390 $html .= $this->output->notification(get_string('selectacategory'), 'notifymessage');
391 $html .= html_writer::end_div();
392 return $html;
393 }
394
395 $page = max($page, 0);
396 $perpage = max($perpage, 2);
397 $totalcourses = $category->coursecount;
398 $totalpages = ceil($totalcourses / $perpage);
399 if ($page > $totalpages - 1) {
400 $page = $totalpages - 1;
401 }
402 $options = array(
403 'offset' => $page * $perpage,
404 'limit' => $perpage
405 );
406 $courseid = isset($course) ? $course->id : null;
5dc361e1
SH
407
408 $html = html_writer::start_div('course-listing', array(
409 'data-category' => $category->id,
410 'data-page' => $page,
411 'data-totalpages' => $totalpages,
412 'data-totalcourses' => $totalcourses,
413 'data-canmoveoutof' => $category->can_move_courses_out_of() && $category->can_move_courses_into()
414 ));
415 $html .= html_writer::tag('h3', $category->get_formatted_name());
416 $html .= $this->course_listing_actions($category, $course, $perpage);
417 $html .= $this->listing_pagination($category, $page, $perpage);
418 $html .= html_writer::start_tag('ul', array('class' => 'ml'));
419 foreach ($category->get_courses($options) as $listitem) {
f454e324 420 $html .= $this->course_listitem($category, $listitem, $courseid);
5dc361e1
SH
421 }
422 $html .= html_writer::end_tag('ul');
423 $html .= $this->listing_pagination($category, $page, $perpage, true);
424 $html .= $this->course_bulk_actions($category);
425 $html .= html_writer::end_div();
426 return $html;
427 }
428
429 /**
430 * Renders pagination for a course listing.
431 *
432 * @param coursecat $category The category to produce pagination for.
433 * @param int $page The current page.
434 * @param int $perpage The number of courses to display per page.
435 * @param bool $showtotals Set to true to show the total number of courses and what is being displayed.
436 * @return string
437 */
438 protected function listing_pagination(coursecat $category, $page, $perpage, $showtotals = false) {
439 $html = '';
440 $totalcourses = $category->coursecount;
441 $totalpages = ceil($totalcourses / $perpage);
442 if ($showtotals) {
443 if ($totalpages == 0) {
444 $str = get_string('nocoursesyet');
445 } else if ($totalpages == 1) {
446 $str = get_string('showingacourses', 'moodle', $totalcourses);
447 } else {
448 $a = new stdClass;
449 $a->start = ($page * $perpage) + 1;
450 $a->end = min((($page + 1) * $perpage), $totalcourses);
451 $a->total = $totalcourses;
452 $str = get_string('showingxofycourses', 'moodle', $a);
453 }
454 $html .= html_writer::div($str, 'listing-pagination-totals dimmed');
455 }
456
457 if ($totalcourses < $perpage) {
458 return $html;
459 }
460 $aside = 2;
461 $span = $aside * 2 + 1;
462 $start = max($page - $aside, 0);
463 $end = min($page + $aside, $totalpages - 1);
464 if (($end - $start) < $span) {
465 if ($start == 0) {
466 $end = min($totalpages - 1, $span - 1);
467 } else if ($end == ($totalpages - 1)) {
468 $start = max(0, $end - $span + 1);
469 }
470 }
471 $items = array();
472 $baseurl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
473 if ($page > 0) {
474 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => 0)), get_string('first'));
475 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page - 1)), get_string('prev'));
476 $items[] = '...';
477 }
478 for ($i = $start; $i <= $end; $i++) {
479 $class = '';
480 if ($page == $i) {
481 $class = 'active-page';
482 }
483 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $i)), $i + 1, null, $class);
484 }
485 if ($page < ($totalpages - 1)) {
486 $items[] = '...';
487 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page + 1)), get_string('next'));
488 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $totalpages - 1)), get_string('last'));
489 }
490
491 $html .= html_writer::div(join('', $items), 'listing-pagination');
492 return $html;
493 }
494
495 /**
496 * Renderers a course list item.
497 *
498 * This function will be called for every course being displayed by course_listing.
499 *
500 * @param coursecat $category The currently selected category and the category the course belongs to.
501 * @param course_in_list $course The course to produce HTML for.
502 * @param int $selectedcourse The id of the currently selected course.
5dc361e1
SH
503 * @return string
504 */
f454e324 505 public function course_listitem(coursecat $category, course_in_list $course, $selectedcourse) {
5dc361e1
SH
506
507 $text = $course->get_formatted_name();
508 $attributes = array(
509 'class' => 'listitem listitem-course',
510 'data-id' => $course->id,
511 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
512 'data-visible' => $course->visible ? '1' : '0'
513 );
514
515 $bulkcourseinput = array('type' => 'checkbox', 'name' => 'bc[]', 'value' => $course->id, 'class' => 'bulk-action-checkbox');
516 $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
517
518 $html = html_writer::start_tag('li', $attributes);
519 $html .= html_writer::start_div('clearfix');
520
67e1f268 521 if ($category->can_resort_courses()) {
5dc361e1
SH
522 // In order for dnd to be available the user must be able to resort the category children..
523 $html .= html_writer::div($this->output->pix_icon('i/dragdrop', get_string('dndcourse')), 'float-left drag-handle');
524 }
525
526 $html .= html_writer::start_div('float-left');
527 $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp';
528 $html .= html_writer::end_div();
529 $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
530 $html .= html_writer::start_div('float-right');
5dc361e1 531 $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
f454e324 532 $html .= $this->course_listitem_actions($category, $course);
5dc361e1
SH
533 $html .= html_writer::end_div();
534 $html .= html_writer::end_div();
535 $html .= html_writer::end_tag('li');
536 return $html;
537 }
538
539 /**
540 * Renderers actions for the course listing.
541 *
542 * Not to be confused with course_listitem_actions which renderers the actions for individual courses.
543 *
544 * @param coursecat $category
545 * @param course_in_list $course The currently selected course.
546 * @param int $perpage
547 * @return string
548 */
549 public function course_listing_actions(coursecat $category, course_in_list $course = null, $perpage = 20) {
550 $actions = array();
551 if ($category->can_create_course()) {
552 $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'catmanage'));
553 $actions[] = html_writer::link($url, get_string('newcourse'));
554 }
67e1f268 555 if ($category->can_resort_courses()) {
5dc361e1
SH
556 $params = $this->page->url->params();
557 $params['action'] = 'resortcourses';
558 $params['sesskey'] = sesskey();
559 $baseurl = new moodle_url('/course/management.php', $params);
560 $fullnameurl = new moodle_url($baseurl, array('resort' => 'fullname'));
561 $shortnameurl = new moodle_url($baseurl, array('resort' => 'shortname'));
562 $idnumberurl = new moodle_url($baseurl, array('resort' => 'idnumber'));
563 $menu = new action_menu(array(
564 new action_menu_link_secondary($fullnameurl, null, get_string('resortbyfullname')),
565 new action_menu_link_secondary($shortnameurl, null, get_string('resortbyshortname')),
566 new action_menu_link_secondary($idnumberurl, null, get_string('resortbyidnumber'))
567 ));
568 $menu->actiontext = get_string('resortcourses');
569 $menu->actionicon = new pix_icon('t/sort', ' ', 'moodle', array('class' => 'iconsmall'));
570 $actions[] = $this->render($menu);
571 }
572 $strall = get_string('all');
573 $menu = new action_menu(array(
574 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 5)), null, 5),
575 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 10)), null, 10),
576 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 20)), null, 20),
577 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 50)), null, 50),
578 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 100)), null, 100),
579 new action_menu_link_secondary(new moodle_url($this->page->url, array('perpage' => 999)), null, $strall),
580 ));
581 if ($perpage === 999) {
582 $perpage = $strall;
583 }
584 $menu->attributes['class'] .= ' courses-per-page';
585 $menu->actiontext = get_string('perpagea', 'moodle', $perpage);
586 $actions[] = $this->render($menu);
587 return html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions');
588 }
589
590 /**
591 * Renderers actions for individual course actions.
592 *
593 * @param coursecat $category The currently selected category.
594 * @param course_in_list $course The course to renderer actions for.
5dc361e1
SH
595 * @return string
596 */
f454e324 597 public function course_listitem_actions(coursecat $category, course_in_list $course) {
5dc361e1
SH
598 $baseurl = new moodle_url(
599 '/course/management.php',
600 array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => sesskey())
601 );
602 $actions = array();
603 // Edit.
604 if ($course->can_edit()) {
605 $actions[] = $this->output->action_icon(
606 new moodle_url('/course/edit.php', array('id' => $course->id)),
607 new pix_icon('t/edit', get_string('edit')),
608 null,
609 array('class' => 'action-edit')
610 );
611 }
612 // Show/Hide.
613 if ($course->can_change_visibility()) {
614 $actions[] = $this->output->action_icon(
615 new moodle_url($baseurl, array('action' => 'hidecourse')),
616 new pix_icon('t/hide', get_string('hide')),
617 null,
618 array('data-action' => 'hide', 'class' => 'action-hide')
619 );
620 $actions[] = $this->output->action_icon(
621 new moodle_url($baseurl, array('action' => 'showcourse')),
622 new pix_icon('t/show', get_string('show')),
623 null,
624 array('data-action' => 'show', 'class' => 'action-show')
625 );
626 }
627 // Move up/down.
67e1f268 628 if ($category->can_resort_courses()) {
f454e324
SH
629 $actions[] = $this->action_icon(
630 new moodle_url($baseurl, array('action' => 'movecourseup')),
631 new pix_icon('t/up', get_string('up')),
632 null,
633 array('data-action' => 'moveup', 'class' => 'action-moveup')
634 );
635 $actions[] = $this->action_icon(
636 new moodle_url($baseurl, array('action' => 'movecoursedown')),
637 new pix_icon('t/down', get_string('down')),
638 null,
639 array('data-action' => 'movedown', 'class' => 'action-movedown')
640 );
5dc361e1
SH
641 }
642 if (empty($actions)) {
643 return '';
644 }
645 return html_writer::span(join('', $actions), 'course-item-actions item-actions');
646 }
647
648 /**
649 * Renderers bulk actions that can be performed on courses.
650 *
651 * @param coursecat $category The currently selected category and the category in which courses that
652 * are selectable belong.
653 * @return string
654 */
655 public function course_bulk_actions(coursecat $category) {
656 $html = html_writer::start_div('course-bulk-actions bulk-actions');
657 if ($category->can_move_courses_out_of()) {
658 $options = coursecat::make_categories_list('moodle/category:manage');
659 $select = html_writer::select($options, 'movecoursesto');
660 $submit = array('type' => 'submit', 'name' => 'bulkmovecourses', 'value' => get_string('go'));
661 $html .= $this->detail_pair(
662 get_string('moveselectedcoursesto'),
663 $select . html_writer::empty_tag('input', $submit)
664 );
665 }
666 $html .= html_writer::end_div();
667 return $html;
668 }
669
670 /**
671 * Renderers detailed course information.
672 *
673 * @param course_in_list $course The course to display details for.
674 * @return string
675 */
676 public function course_detail(course_in_list $course) {
677 $details = \core_course\management\helper::get_course_detail_array($course);
678 $fullname = $details['fullname']['value'];
679
680 $html = html_writer::start_div('course-detail');
681 $html .= html_writer::tag('h3', $fullname);
682 $html .= $this->course_detail_actions($course);
683 foreach ($details as $class => $data) {
684 $html .= $this->detail_pair($data['key'], $data['value'], $class);
685 }
686 $html .= html_writer::end_div();
687 return $html;
688 }
689
690 /**
691 * Renderers a key value pair of information for display.
692 *
693 * @param string $key
694 * @param string $value
695 * @param string $class
696 * @return string
697 */
698 protected function detail_pair($key, $value, $class ='') {
699 $html = html_writer::start_div('detail-pair yui3-g '.preg_replace('#[^a-zA-Z0-9_\-]#', '-', $class));
700 $html .= html_writer::div(html_writer::span($key), 'pair-key yui3-u-1-4');
701 $html .= html_writer::div(html_writer::span($value), 'pair-value yui3-u-3-4');
702 $html .= html_writer::end_div();
703 return $html;
704 }
705
706 /**
707 * A collection of actions for a course.
708 *
709 * @param course_in_list $course The course to display actions for.
710 * @return string
711 */
712 public function course_detail_actions(course_in_list $course) {
713 $actions = \core_course\management\helper::get_course_detail_actions($course);
714 if (empty($actions)) {
715 return '';
716 }
717 $options = array();
718 foreach ($actions as $action) {
719 $options[] = $this->action_button($action['url'], $action['string']);
720 }
721 return html_writer::div(join('', $options), 'listing-actions course-detail-listing-actions');
722 }
723
724 /**
725 * Creates an action button (styled link)
726 *
727 * @param moodle_url $url The URL to go to when clicked.
728 * @param string $text The text for the button.
729 * @param string $id An id to give the button.
730 * @param string $class A class to give the button.
731 * @return string
732 */
733 protected function action_button(moodle_url $url, $text, $id = null, $class = null) {
734 $attributes = array(
735 'class' => 'yui3-button',
736 );
737 if (!is_null($id)) {
738 $attributes['id'] = $id;
739 }
740 if (!is_null($class)) {
741 $attributes['class'] .= ' '.$class;
742 }
743 return html_writer::link($url, $text, $attributes);
744 }
745
746 /**
747 * Opens a grid.
748 *
749 * Call {@link core_course_management_renderer::grid_column_start()} to create columns.
750 *
751 * @param string $id An id to give this grid.
752 * @param string $class A class to give this grid.
753 * @return string
754 */
755 public function grid_start($id = null, $class = null) {
756 $gridclass = 'grid-row-r row-fluid';
757 if (is_null($class)) {
758 $class = $gridclass;
759 } else {
760 $class .= ' ' . $gridclass;
761 }
762 $attributes = array();
763 if (!is_null($id)) {
764 $attributes['id'] = $id;
765 }
766 return html_writer::start_div($class, $attributes);
767 }
768
769 /**
770 * Closes the grid.
771 *
772 * @return string
773 */
774 public function grid_end() {
775 return html_writer::end_div();
776 }
777
778 /**
779 * Opens a grid column
780 *
781 * @param int $size The number of segments this column should span.
782 * @param string $id An id to give the column.
783 * @param string $class A class to give the column.
784 * @return string
785 */
786 public function grid_column_start($size, $id = null, $class = null) {
787
788 // Calculate Bootstrap grid sizing.
789 $bootstrapclass = 'span'.$size;
790
791 // Calculate YUI grid sizing.
792 if ($size === 12) {
793 $maxsize = 1;
794 $size = 1;
795 } else {
796 $maxsize = 12;
797 $divisors = array(8, 6, 5, 4, 3, 2);
798 foreach ($divisors as $divisor) {
799 if (($maxsize % $divisor === 0) && ($size % $divisor === 0)) {
800 $maxsize = $maxsize / $divisor;
801 $size = $size / $divisor;
802 break;
803 }
804 }
805 }
806 if ($maxsize > 1) {
807 $yuigridclass = "grid-col-{$size}-{$maxsize} grid-col";
808 } else {
809 $yuigridclass = "grid-col-1 grid-col";
810 }
811
812 if (is_null($class)) {
813 $class = $yuigridclass . ' ' . $bootstrapclass;
814 } else {
815 $class .= ' ' . $yuigridclass . ' ' . $bootstrapclass;
816 }
817 $attributes = array();
818 if (!is_null($id)) {
819 $attributes['id'] = $id;
820 }
821 return html_writer::start_div($class, $attributes);
822 }
823
824 /**
825 * Closes a grid column.
826 *
827 * @return string
828 */
829 public function grid_column_end() {
830 return html_writer::end_div();
831 }
832
833 /**
834 * Renders an action_icon.
835 *
836 * This function uses the {@link core_renderer::action_link()} method for the
837 * most part. What it does different is prepare the icon as HTML and use it
838 * as the link text.
839 *
840 * @param string|moodle_url $url A string URL or moodel_url
841 * @param pix_icon $pixicon
842 * @param component_action $action
843 * @param array $attributes associative array of html link attributes + disabled
844 * @param bool $linktext show title next to image in link
845 * @return string HTML fragment
846 */
847 public function action_icon($url, pix_icon $pixicon, component_action $action = null,
848 array $attributes = null, $linktext = false) {
849 if (!($url instanceof moodle_url)) {
850 $url = new moodle_url($url);
851 }
852 $attributes = (array)$attributes;
853
854 if (empty($attributes['class'])) {
855 // Let devs override the class via $attributes.
856 $attributes['class'] = 'action-icon';
857 }
858
859 $icon = $this->render($pixicon);
860
861 if ($linktext) {
862 $text = $pixicon->attributes['alt'];
863 } else {
864 $text = '';
865 }
866
867 return $this->action_link($url, $icon.$text, $action, $attributes);
868 }
869
870 /**
871 * Displays a view mode selector.
872 *
873 * @param array $modes An array of view modes.
874 * @param string $currentmode The current view mode.
875 * @param moodle_url $url The URL to use when changing actions. Defaults to the page URL.
876 * @param string $param The param name.
877 * @return string
878 */
879 public function view_mode_selector(array $modes, $currentmode, moodle_url $url = null, $param = 'view') {
880 if ($url === null) {
881 $url = $this->page->url;
882 }
883
884 $menu = new action_menu;
885 $menu->attributes['class'] .= ' view-mode-selector vms';
886
887 $selected = null;
888 foreach ($modes as $mode => $modestr) {
889 $attributes = array(
890 'class' => 'vms-mode',
891 'data-mode' => $mode
892 );
893 if ($currentmode === $mode) {
894 $attributes['class'] .= ' currentmode';
895 $selected = $modestr;
896 }
897 if ($selected === null) {
898 $selected = $modestr;
899 }
900 $modeurl = new moodle_url($url, array($param => $mode));
901 if ($mode === 'default') {
902 $modeurl->remove_params($param);
903 }
904 $menu->add(new action_menu_link_secondary($modeurl, null, $modestr, $attributes));
905 }
906
907 $menu->actiontext = get_string('viewing', 'moodle', $selected);
908
909 $html = html_writer::start_div('view-mode-selector vms');
910 $html .= $this->render($menu);
911 $html .= html_writer::end_div();
912
913 return $html;
914 }
915
916 /**
917 * Displays a search result listing.
918 *
919 * @param array $courses The courses to display.
920 * @param int $totalcourses The total number of courses to display.
921 * @param course_in_list $course The currently selected course if there is one.
922 * @param int $page The current page, starting at 0.
923 * @param int $perpage The number of courses to display per page.
924 * @return string
925 */
926 public function search_listing(array $courses, $totalcourses, course_in_list $course = null, $page = 0, $perpage = 20) {
927 $page = max($page, 0);
928 $perpage = max($perpage, 2);
929 $totalpages = ceil($totalcourses / $perpage);
930 if ($page > $totalpages - 1) {
931 $page = $totalpages - 1;
932 }
933 $courseid = isset($course) ? $course->id : null;
934 $first = true;
935 $last = false;
936 $i = $page * $perpage;
937
938 $html = html_writer::start_div('course-listing', array(
939 'data-category' => 'search',
940 'data-page' => $page,
941 'data-totalpages' => $totalpages,
942 'data-totalcourses' => $totalcourses
943 ));
944 $html .= html_writer::tag('h3', get_string('courses'));
945 $html .= $this->search_pagination($totalcourses, $page, $perpage);
946 $html .= html_writer::start_tag('ul', array('class' => 'ml'));
947 foreach ($courses as $listitem) {
948 $i++;
949 if ($i == $totalcourses) {
950 $last = true;
951 }
952 $html .= $this->search_listitem($listitem, $courseid, $first, $last);
953 $first = false;
954 }
955 $html .= html_writer::end_tag('ul');
956 $html .= $this->search_pagination($totalcourses, $page, $perpage, true);
957 $html .= html_writer::end_div();
958 return $html;
959 }
960
961 /**
962 * Displays pagination for search results.
963 *
964 * @param int $totalcourses The total number of courses to be displayed.
965 * @param int $page The current page.
966 * @param int $perpage The number of courses being displayed.
967 * @param bool $showtotals Whether or not to print total information.
968 * @return string
969 */
970 protected function search_pagination($totalcourses, $page, $perpage, $showtotals = false) {
971 $html = '';
972 $totalpages = ceil($totalcourses / $perpage);
973 if ($showtotals) {
974 if ($totalpages == 1) {
975 $str = get_string('showingacourses', 'moodle', $totalcourses);
976 } else {
977 $a = new stdClass;
978 $a->start = ($page * $perpage) + 1;
979 $a->end = min((($page + 1) * $perpage), $totalcourses);
980 $a->total = $totalcourses;
981 $str = get_string('showingxofycourses', 'moodle', $a);
982 }
983 $html .= html_writer::div($str, 'listing-pagination-totals dimmed');
984 }
985
986 if ($totalcourses < $perpage) {
987 return $html;
988 }
989 $aside = 2;
990 $span = $aside * 2 + 1;
991 $start = max($page - $aside, 0);
992 $end = min($page + $aside, $totalpages - 1);
993 if (($end - $start) < $span) {
994 if ($start == 0) {
995 $end = min($totalpages - 1, $span - 1);
996 } else if ($end == ($totalpages - 1)) {
997 $start = max(0, $end - $span + 1);
998 }
999 }
1000 $items = array();
1001 $baseurl = $this->page->url;
1002 if ($page > 0) {
1003 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => 0)), get_string('first'));
1004 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page - 1)), get_string('prev'));
1005 $items[] = '...';
1006 }
1007 for ($i = $start; $i <= $end; $i++) {
1008 $class = '';
1009 if ($page == $i) {
1010 $class = 'active-page';
1011 }
1012 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $i)), $i + 1, null, $class);
1013 }
1014 if ($page < ($totalpages - 1)) {
1015 $items[] = '...';
1016 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $page + 1)), get_string('next'));
1017 $items[] = $this->action_button(new moodle_url($baseurl, array('page' => $totalpages - 1)), get_string('last'));
1018 }
1019
1020 $html .= html_writer::div(join('', $items), 'listing-pagination');
1021 return $html;
1022 }
1023
1024 /**
1025 * Renderers a search result course list item.
1026 *
1027 * This function will be called for every course being displayed by course_listing.
1028 *
1029 * @param course_in_list $course The course to produce HTML for.
1030 * @param int $selectedcourse The id of the currently selected course.
1031 * @return string
1032 */
1033 public function search_listitem(course_in_list $course, $selectedcourse) {
1034
1035 $text = $course->get_formatted_name();
1036 $attributes = array(
1037 'class' => 'listitem listitem-course',
1038 'data-id' => $course->id,
1039 'data-selected' => ($selectedcourse == $course->id) ? '1' : '0',
1040 'data-visible' => $course->visible ? '1' : '0'
1041 );
1042
1043 $bulkcourseinput = array('type' => 'checkbox', 'name' => 'bc[]', 'value' => $course->id, 'class' => 'bulk-action-checkbox');
1044 $viewcourseurl = new moodle_url($this->page->url, array('courseid' => $course->id));
1045 $categoryname = coursecat::get($course->category)->get_formatted_name();
1046
1047 $html = html_writer::start_tag('li', $attributes);
1048 $html .= html_writer::start_div('clearfix');
1049 $html .= html_writer::start_div('float-left');
1050 $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp';
1051 $html .= html_writer::end_div();
1052 $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
1053 $html .= html_writer::tag('span', $categoryname, array('class' => 'float-left categoryname'));
1054 $html .= html_writer::start_div('float-right');
1055 $html .= $this->search_listitem_actions($course);
1056 $html .= html_writer::tag('span', s($course->idnumber), array('class' => 'dimmed idnumber'));
1057 $html .= html_writer::end_div();
1058 $html .= html_writer::end_div();
1059 $html .= html_writer::end_tag('li');
1060 return $html;
1061 }
1062
1063 /**
1064 * Renderers actions for individual course actions.
1065 *
1066 * @param course_in_list $course The course to renderer actions for.
1067 * @return string
1068 */
1069 public function search_listitem_actions(course_in_list $course) {
1070 $baseurl = new moodle_url(
1071 '/course/managementsearch.php',
1072 array('courseid' => $course->id, 'categoryid' => $course->category, 'sesskey' => sesskey())
1073 );
1074 $actions = array();
1075 // Edit.
1076 if ($course->can_edit()) {
1077 $actions[] = $this->output->action_icon(
1078 new moodle_url('/course/edit.php', array('id' => $course->id)),
1079 new pix_icon('t/edit', get_string('edit')),
1080 null,
1081 array('class' => 'action-edit')
1082 );
1083 }
1084 // Show/Hide.
1085 if ($course->can_change_visibility()) {
1086 if ($course->visible) {
1087 $actions[] = $this->output->action_icon(
1088 new moodle_url($baseurl, array('action' => 'hidecourse')),
1089 new pix_icon('t/show', get_string('hide')),
1090 null,
1091 array('data-action' => 'hide', 'class' => 'action-hide')
1092 );
1093 } else {
1094 $actions[] = $this->output->action_icon(
1095 new moodle_url($baseurl, array('action' => 'showcourse')),
1096 new pix_icon('t/hide', get_string('show')),
1097 null,
1098 array('data-action' => 'show', 'class' => 'action-show')
1099 );
1100 }
1101 }
1102 if (empty($actions)) {
1103 return '';
1104 }
1105 return html_writer::span(join('', $actions), 'course-item-actions item-actions');
1106 }
1107
1108}