MDL-54623 mod_assign: Unit test for list_participants()
[moodle.git] / enrol / renderer.php
blobc0c3ac85718edf0af2bb9e4363bbae026911b617
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.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * This is the main renderer for the enrol section.
20 * @package core_enrol
21 * @copyright 2010 Sam Hemelryk
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 /**
26 * This is the core renderer
28 * @copyright 2010 Sam Hemelryk
29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31 class core_enrol_renderer extends plugin_renderer_base {
33 /**
34 * Renders a course enrolment table
36 * @param course_enrolment_table $table
37 * @param moodleform $mform Form that contains filter controls
38 * @return string
40 public function render_course_enrolment_users_table(course_enrolment_users_table $table,
41 moodleform $mform) {
43 $table->initialise_javascript();
45 $buttons = $table->get_manual_enrol_buttons();
46 $buttonhtml = '';
47 if (count($buttons) > 0) {
48 $buttonhtml .= html_writer::start_tag('div', array('class' => 'enrol_user_buttons enrol-users-page-action'));
49 foreach ($buttons as $button) {
50 $buttonhtml .= $this->render($button);
52 $buttonhtml .= html_writer::end_tag('div');
55 $content = '';
56 if (!empty($buttonhtml)) {
57 $content .= $buttonhtml;
59 $content .= $mform->render();
61 $content .= $this->output->render($table->get_paging_bar());
63 // Check if the table has any bulk operations. If it does we want to wrap the table in a
64 // form so that we can capture and perform any required bulk operations.
65 if ($table->has_bulk_user_enrolment_operations()) {
66 $content .= html_writer::start_tag('form', array('action' => new moodle_url('/enrol/bulkchange.php'), 'method' => 'post'));
67 foreach ($table->get_combined_url_params() as $key => $value) {
68 if ($key == 'action') {
69 continue;
71 $content .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
73 $content .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'bulkchange'));
74 $content .= html_writer::table($table);
75 $content .= html_writer::start_tag('div', array('class' => 'singleselect bulkuserop'));
76 $content .= html_writer::start_tag('select', array('name' => 'bulkuserop'));
77 $content .= html_writer::tag('option', get_string('withselectedusers', 'enrol'), array('value' => ''));
78 $options = array('' => get_string('withselectedusers', 'enrol'));
79 foreach ($table->get_bulk_user_enrolment_operations() as $operation) {
80 $content .= html_writer::tag('option', $operation->get_title(), array('value' => $operation->get_identifier()));
82 $content .= html_writer::end_tag('select');
83 $content .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go')));
84 $content .= html_writer::end_tag('div');
86 $content .= html_writer::end_tag('form');
87 } else {
88 $content .= html_writer::table($table);
90 $content .= $this->output->render($table->get_paging_bar());
91 if (!empty($buttonhtml)) {
92 $content .= $buttonhtml;
94 return $content;
97 /**
98 * Renderers the enrol_user_button.
100 * @param enrol_user_button $button
101 * @return string XHTML
103 protected function render_enrol_user_button(enrol_user_button $button) {
104 $attributes = array('type' => 'submit',
105 'value' => $button->label,
106 'disabled' => $button->disabled ? 'disabled' : null,
107 'title' => $button->tooltip);
109 if ($button->actions) {
110 $id = html_writer::random_id('single_button');
111 $attributes['id'] = $id;
112 foreach ($button->actions as $action) {
113 $this->add_action_handler($action, $id);
116 $button->initialise_js($this->page);
118 // first the input element
119 $output = html_writer::empty_tag('input', $attributes);
121 // then hidden fields
122 $params = $button->url->params();
123 if ($button->method === 'post') {
124 $params['sesskey'] = sesskey();
126 foreach ($params as $var => $val) {
127 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val));
130 // then div wrapper for xhtml strictness
131 $output = html_writer::tag('div', $output);
133 // now the form itself around it
134 if ($button->method === 'get') {
135 $url = $button->url->out_omit_querystring(true); // url without params, the anchor part allowed
136 } else {
137 $url = $button->url->out_omit_querystring(); // url without params, the anchor part not allowed
139 if ($url === '') {
140 $url = '#'; // there has to be always some action
142 $attributes = array('method' => $button->method,
143 'action' => $url,
144 'id' => $button->formid);
145 $output = html_writer::tag('form', $output, $attributes);
147 // and finally one more wrapper with class
148 return html_writer::tag('div', $output, array('class' => $button->class));
152 * Renders a course enrolment table
154 * @param course_enrolment_table $table
155 * @return string
157 protected function render_course_enrolment_other_users_table(course_enrolment_other_users_table $table) {
159 $table->initialise_javascript();
161 $content = '';
162 $searchbutton = $table->get_user_search_button();
163 if ($searchbutton) {
164 $content .= $this->output->render($searchbutton);
166 $content .= html_writer::tag('div', get_string('otheruserdesc', 'enrol'), array('class'=>'otherusersdesc'));
167 $content .= $this->output->render($table->get_paging_bar());
168 $content .= html_writer::table($table);
169 $content .= $this->output->render($table->get_paging_bar());
170 $searchbutton = $table->get_user_search_button();
171 if ($searchbutton) {
172 $content .= $this->output->render($searchbutton);
174 return $content;
178 * Generates HTML to display the users roles and any available actions
180 * @param int $userid
181 * @param array $roles
182 * @param array $assignableroles
183 * @param moodle_url $pageurl
184 * @return string
186 public function user_roles_and_actions($userid, $roles, $assignableroles, $canassign, $pageurl) {
187 $iconenrolremove = $this->output->pix_url('t/delete');
191 // Get list of roles.
192 $rolesoutput = '';
193 foreach ($roles as $roleid=>$role) {
194 if ($canassign and (is_siteadmin() or isset($assignableroles[$roleid])) and !$role['unchangeable']) {
195 $strunassign = get_string('unassignarole', 'role', $role['text']);
196 $icon = html_writer::empty_tag('img', array('alt'=>$strunassign, 'src'=>$iconenrolremove));
197 $url = new moodle_url($pageurl, array('action'=>'unassign', 'roleid'=>$roleid, 'user'=>$userid));
198 $rolesoutput .= html_writer::tag('div', $role['text'] . html_writer::link($url, $icon, array('class'=>'unassignrolelink', 'rel'=>$roleid, 'title'=>$strunassign)), array('class'=>'role role_'.$roleid));
199 } else {
200 $rolesoutput .= html_writer::tag('div', $role['text'], array('class'=>'role unchangeable', 'rel'=>$roleid));
203 $output = '';
204 if (!empty($assignableroles) && $canassign) {
205 $roleids = array_keys($roles);
206 $hasallroles = true;
207 foreach (array_keys($assignableroles) as $key) {
208 if (!in_array($key, $roleids)) {
209 $hasallroles = false;
210 break;
213 if (!$hasallroles) {
214 $url = new moodle_url($pageurl, array('action' => 'assign', 'user' => $userid));
215 $roleicon = $this->output->pix_icon('i/assignroles', get_string('assignroles', 'role'));
216 $link = html_writer::link($url, $roleicon, array('class' => 'assignrolelink'));
217 $output = html_writer::tag('div', $link, array('class'=>'addrole'));
220 $output .= html_writer::tag('div', $rolesoutput, array('class'=>'roles'));
221 return $output;
225 * Generates the HTML to view the users groups and available group actions
227 * @param int $userid
228 * @param array $groups
229 * @param array $allgroups
230 * @param bool $canmanagegroups
231 * @param moodle_url $pageurl
232 * @return string
234 public function user_groups_and_actions($userid, $groups, $allgroups, $canmanagegroups, $pageurl) {
235 $iconenrolremove = $this->output->pix_url('t/delete');
237 $groupicon = $this->output->pix_icon('i/group', get_string('addgroup', 'group'));
239 $groupoutput = '';
240 foreach($groups as $groupid=>$name) {
241 if ($canmanagegroups and groups_remove_member_allowed($groupid, $userid)) {
242 $icon = html_writer::empty_tag('img', array('alt'=>get_string('removefromgroup', 'group', $name), 'src'=>$iconenrolremove));
243 $url = new moodle_url($pageurl, array('action'=>'removemember', 'group'=>$groupid, 'user'=>$userid));
244 $groupoutput .= html_writer::tag('div', $name . html_writer::link($url, $icon), array('class'=>'group', 'rel'=>$groupid));
245 } else {
246 $groupoutput .= html_writer::tag('div', $name, array('class'=>'group', 'rel'=>$groupid));
249 $output = '';
250 if ($canmanagegroups && (count($groups) < count($allgroups))) {
251 $url = new moodle_url($pageurl, array('action'=>'addmember', 'user'=>$userid));
252 $output .= html_writer::tag('div', html_writer::link($url, $groupicon), array('class'=>'addgroup'));
254 $output = $output.html_writer::tag('div', $groupoutput, array('class'=>'groups'));
255 return $output;
259 * Generates the HTML for the given enrolments + available actions
261 * @param int $userid
262 * @param array $enrolments
263 * @param moodle_url $pageurl
264 * @return string
266 public function user_enrolments_and_actions($enrolments) {
267 $output = '';
268 foreach ($enrolments as $ue) {
269 $enrolmentoutput = $ue['text'].' '.$ue['period'];
270 if ($ue['dimmed']) {
271 $enrolmentoutput = html_writer::tag('span', $enrolmentoutput, array('class'=>'dimmed_text'));
272 } else {
273 $enrolmentoutput = html_writer::tag('span', $enrolmentoutput);
275 foreach ($ue['actions'] as $action) {
276 $enrolmentoutput .= $this->render($action);
278 $output .= html_writer::tag('div', $enrolmentoutput, array('class'=>'enrolment'));
280 return $output;
284 * Renders a user enrolment action
285 * @param user_enrolment_action $icon
286 * @return string
288 protected function render_user_enrolment_action(user_enrolment_action $icon) {
289 return html_writer::link($icon->get_url(), $this->output->render($icon->get_icon()), $icon->get_attributes());
294 * Main course enrolment table
296 * This table is used to display the enrolment information for a course.
297 * It requires that a course enrolment manager be provided during constuct with
298 * provides all of the information for the table.
299 * The control then produces the table, the paging, and the associated JS actions
300 * for the page.
302 * @package core
303 * @subpackage enrol
304 * @copyright 2010 Sam Hemelryk
305 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
307 class course_enrolment_table extends html_table implements renderable {
310 * The get/post variable that is used to identify the page.
311 * Default: page
313 const PAGEVAR = 'page';
316 * The get/post variable to is used to identify the number of items to display
317 * per page.
318 * Default: perpage
320 const PERPAGEVAR = 'perpage';
323 * The get/post variable that is used to identify the sort field for the table.
324 * Default: sort
326 const SORTVAR = 'sort';
329 * The get/post variable that is used to identify the sort direction for the table.
330 * Default: dir
332 const SORTDIRECTIONVAR = 'dir';
335 * The default number of items per page.
336 * Default: 100
338 const DEFAULTPERPAGE = 100;
341 * The default sort, options are course_enrolment_table::$sortablefields
342 * Default: lastname
344 const DEFAULTSORT = 'lastname';
347 * The default direction
348 * Default: ASC
350 const DEFAULTSORTDIRECTION = 'ASC';
353 * The current page, starting from 0
354 * @var int
356 public $page = 0;
359 * The total number of pages
360 * @var int
362 public $pages = 0;
365 * The number of items to display per page
366 * @var int
368 public $perpage = 0;
371 * The sort field for this table, should be one of course_enrolment_table::$sortablefields
372 * @var string
374 public $sort;
377 * The sort direction, either ASC or DESC
378 * @var string
380 public $sortdirection;
383 * The course manager this table is displaying for
384 * @var course_enrolment_manager
386 protected $manager;
389 * The paging bar that controls the paging for this table
390 * @var paging_bar
392 protected $pagingbar = null;
395 * The total number of users enrolled in the course
396 * @var int
398 protected $totalusers = null;
401 * The users enrolled in this course
402 * @var array
404 protected $users = null;
407 * The fields for this table
408 * @var array
410 protected $fields = array();
413 * An array of bulk user enrolment operations
414 * @var array
416 protected $bulkoperations = array();
419 * An array of sortable fields
420 * @static
421 * @var array
423 protected static $sortablefields = array('firstname', 'lastname', 'firstnamephonetic', 'lastnamephonetic', 'middlename',
424 'alternatename', 'idnumber', 'email', 'phone1', 'phone2', 'institution', 'department', 'lastaccess', 'lastcourseaccess' );
427 * Constructs the table
429 * @param course_enrolment_manager $manager
431 public function __construct(course_enrolment_manager $manager) {
433 $this->manager = $manager;
435 $this->page = optional_param(self::PAGEVAR, 0, PARAM_INT);
436 $this->perpage = optional_param(self::PERPAGEVAR, self::DEFAULTPERPAGE, PARAM_INT);
437 $this->sort = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHANUM);
438 $this->sortdirection = optional_param(self::SORTDIRECTIONVAR, self::DEFAULTSORTDIRECTION, PARAM_ALPHA);
440 $this->attributes = array('class'=>'userenrolment');
441 if (!in_array($this->sort, self::$sortablefields)) {
442 $this->sort = self::DEFAULTSORT;
444 if ($this->page < 0) {
445 $this->page = 0;
447 if ($this->sortdirection !== 'ASC' && $this->sortdirection !== 'DESC') {
448 $this->sortdirection = self::DEFAULTSORTDIRECTION;
451 $this->id = html_writer::random_id();
453 // Collect the bulk operations for the currently filtered plugin if there is one.
454 $plugin = $manager->get_filtered_enrolment_plugin();
455 if ($plugin and enrol_is_enabled($plugin->get_name())) {
456 $this->bulkoperations = $plugin->get_bulk_operations($manager);
461 * Returns an array of enrol_user_buttons that are created by the different
462 * enrolment plugins available.
464 * @return array
466 public function get_manual_enrol_buttons() {
467 return $this->manager->get_manual_enrol_buttons();
471 * Gets the sort direction for a given field
473 * @param string $field
474 * @return string ASC or DESC
476 public function get_field_sort_direction($field) {
477 if ($field == $this->sort) {
478 return ($this->sortdirection == 'ASC')?'DESC':'ASC';
480 return self::DEFAULTSORTDIRECTION;
484 * Sets the fields for this table. These get added to the tables head as well.
486 * You can also use a multi dimensional array for this to have multiple fields
487 * in a single column
489 * @param array $fields An array of fields to set
490 * @param string $output
492 public function set_fields($fields, $output) {
493 $this->fields = $fields;
494 $this->head = array();
495 $this->colclasses = array();
496 $this->align = array();
497 $url = $this->manager->get_moodlepage()->url;
499 if (!empty($this->bulkoperations)) {
500 // If there are bulk operations add a column for checkboxes.
501 $this->head[] = '';
502 $this->colclasses[] = 'field col_bulkops';
505 foreach ($fields as $name => $label) {
506 $newlabel = '';
507 if (is_array($label)) {
508 $bits = array();
509 foreach ($label as $n => $l) {
510 if ($l === false) {
511 continue;
513 if (!in_array($n, self::$sortablefields)) {
514 $bits[] = $l;
515 } else {
516 $sorturl = new moodle_url($url, array(self::SORTVAR => $n, self::SORTDIRECTIONVAR => $this->get_field_sort_direction($n)));
517 $link = html_writer::link($sorturl, $fields[$name][$n]);
518 if ($this->sort == $n) {
519 $link .= $this->get_direction_icon($output, $n);
521 $bits[] = html_writer::tag('span', $link, array('class'=>'subheading_'.$n));
525 $newlabel = join(' / ', $bits);
526 } else {
527 if (!in_array($name, self::$sortablefields)) {
528 $newlabel = $label;
529 } else {
530 $sorturl = new moodle_url($url, array(self::SORTVAR => $name, self::SORTDIRECTIONVAR => $this->get_field_sort_direction($name)));
531 $newlabel = html_writer::link($sorturl, $fields[$name]);
532 if ($this->sort == $name) {
533 $newlabel .= $this->get_direction_icon($output, $name);
537 $this->head[] = $newlabel;
538 $this->colclasses[] = 'field col_'.$name;
542 * Sets the total number of users
544 * @param int $totalusers
546 public function set_total_users($totalusers) {
547 $this->totalusers = $totalusers;
548 $this->pages = ceil($this->totalusers / $this->perpage);
549 if ($this->page > $this->pages) {
550 $this->page = $this->pages;
554 * Sets the users for this table
556 * @param array $users
557 * @return void
559 public function set_users(array $users) {
560 $this->users = $users;
561 $hasbulkops = !empty($this->bulkoperations);
562 foreach ($users as $userid=>$user) {
563 $user = (array)$user;
564 $row = new html_table_row();
565 $row->attributes = array('class' => 'userinforow');
566 $row->id = 'user_'.$userid;
567 $row->cells = array();
568 if ($hasbulkops) {
569 // Add a checkbox into the first column.
570 $input = html_writer::empty_tag('input', array('type' => 'checkbox', 'name' => 'bulkuser[]', 'value' => $userid));
571 $row->cells[] = new html_table_cell($input);
573 foreach ($this->fields as $field => $label) {
574 if (is_array($label)) {
575 $bits = array();
576 foreach (array_keys($label) as $subfield) {
577 if (array_key_exists($subfield, $user)) {
578 $bits[] = html_writer::tag('div', $user[$subfield], array('class'=>'subfield subfield_'.$subfield));
581 if (empty($bits)) {
582 $bits[] = '&nbsp;';
584 $row->cells[] = new html_table_cell(join(' ', $bits));
585 } else {
586 if (!array_key_exists($field, $user)) {
587 $user[$field] = '&nbsp;';
589 $row->cells[] = new html_table_cell($user[$field]);
592 $this->data[] = $row;
596 public function initialise_javascript() {
597 if (has_capability('moodle/role:assign', $this->manager->get_context())) {
598 $this->manager->get_moodlepage()->requires->strings_for_js(array(
599 'assignroles',
600 'confirmunassign',
601 'confirmunassigntitle',
602 'confirmunassignyes',
603 'confirmunassignno'
604 ), 'role');
605 $modules = array('moodle-enrol-rolemanager', 'moodle-enrol-rolemanager-skin');
606 $function = 'M.enrol.rolemanager.init';
607 $arguments = array(
608 'containerId'=>$this->id,
609 'userIds'=>array_keys($this->users),
610 'courseId'=>$this->manager->get_course()->id,
611 'otherusers'=>isset($this->otherusers));
612 $this->manager->get_moodlepage()->requires->yui_module($modules, $function, array($arguments));
617 * Gets the paging bar instance for this table
619 * @return paging_bar
621 public function get_paging_bar() {
622 if ($this->pagingbar == null) {
623 $this->pagingbar = new paging_bar($this->totalusers, $this->page, $this->perpage, $this->manager->get_moodlepage()->url, self::PAGEVAR);
625 return $this->pagingbar;
629 * Gets the direction icon for the sortable field within this table
631 * @param core_renderer $output
632 * @param string $field
633 * @return string
635 protected function get_direction_icon($output, $field) {
636 $direction = self::DEFAULTSORTDIRECTION;
637 if ($this->sort == $field) {
638 $direction = $this->sortdirection;
640 if ($direction === 'ASC') {
641 return html_writer::empty_tag('img', array('alt' => '', 'class' => 'iconsort',
642 'src' => $output->pix_url('t/sort_asc')));
643 } else {
644 return html_writer::empty_tag('img', array('alt' => '', 'class' => 'iconsort',
645 'src' => $output->pix_url('t/sort_desc')));
650 * Gets the params that will need to be added to the url in order to return to this page.
652 * @return array
654 public function get_url_params() {
655 return array(
656 self::PAGEVAR => $this->page,
657 self::PERPAGEVAR => $this->perpage,
658 self::SORTVAR => $this->sort,
659 self::SORTDIRECTIONVAR => $this->sortdirection
664 * Returns an array of URL params for both the table and the manager.
666 * @return array
668 public function get_combined_url_params() {
669 return $this->get_url_params() + $this->manager->get_url_params();
673 * Sets the bulk operations for this table.
675 * @param array $bulkoperations
677 public function set_bulk_user_enrolment_operations(array $bulkoperations) {
678 $this->bulkoperations = $bulkoperations;
682 * Returns an array of bulk operations.
684 * @return array
686 public function get_bulk_user_enrolment_operations() {
687 return $this->bulkoperations;
691 * Returns true fi the table is aware of any bulk operations that can be performed on users
692 * selected from the currently filtered enrolment plugins.
694 * @return bool
696 public function has_bulk_user_enrolment_operations() {
697 return !empty($this->bulkoperations);
702 * Table control used for enrolled users
704 * @copyright 2010 Sam Hemelryk
705 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
707 class course_enrolment_users_table extends course_enrolment_table {
712 * Table used for other users
714 * Other users are users who have roles but are not enrolled.
716 * @copyright 2010 Sam Hemelryk
717 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
719 class course_enrolment_other_users_table extends course_enrolment_table {
721 public $otherusers = true;
724 * Constructs the table
726 * @param course_enrolment_manager $manager
728 public function __construct(course_enrolment_manager $manager) {
729 parent::__construct($manager);
730 $this->attributes = array('class'=>'userenrolment otheruserenrolment');
734 * Gets a button to search users and assign them roles in the course.
736 * @staticvar int $count
737 * @param int $page
738 * @return single_button
740 public function get_user_search_button() {
741 static $count = 0;
742 if (!has_capability('moodle/role:assign', $this->manager->get_context())) {
743 return false;
745 $count++;
746 $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$this->manager->get_context()->id, 'sesskey'=>sesskey()));
747 $control = new single_button($url, get_string('assignroles', 'role'), 'get');
748 $control->class = 'singlebutton assignuserrole instance'.$count;
749 if ($count == 1) {
750 $this->manager->get_moodlepage()->requires->strings_for_js(array(
751 'ajaxoneuserfound',
752 'ajaxxusersfound',
753 'ajaxnext25',
754 'enrol',
755 'enrolmentoptions',
756 'enrolusers',
757 'enrolxusers',
758 'errajaxfailedenrol',
759 'errajaxsearch',
760 'foundxcohorts',
761 'none',
762 'usersearch',
763 'unlimitedduration',
764 'startdatetoday',
765 'durationdays',
766 'enrolperiod'), 'enrol');
767 $this->manager->get_moodlepage()->requires->string_for_js('assignrole', 'role');
769 $modules = array('moodle-enrol-otherusersmanager', 'moodle-enrol-otherusersmanager-skin');
770 $function = 'M.enrol.otherusersmanager.init';
771 $arguments = array(
772 'courseId'=> $this->manager->get_course()->id,
773 'ajaxUrl' => '/enrol/ajax.php',
774 'url' => $this->manager->get_moodlepage()->url->out(false));
775 $this->manager->get_moodlepage()->requires->yui_module($modules, $function, array($arguments));
777 return $control;