bf88e84197f001c5f1342b6bc329e6b44f76ab8c
[moodle.git] / badges / renderer.php
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/>.
17 /**
18  * Renderer for use with the badges output
19  *
20  * @package    core
21  * @subpackage badges
22  * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @author     Yuliya Bozhko <yuliya.bozhko@totaralms.com>
25  */
27 require_once($CFG->libdir . '/badgeslib.php');
28 require_once($CFG->libdir . '/tablelib.php');
30 /**
31  * Standard HTML output renderer for badges
32  */
33 class core_badges_renderer extends plugin_renderer_base {
35     // Outputs badges list.
36     public function print_badges_list($badges, $userid, $profile = false, $external = false) {
37         global $USER, $CFG;
38         foreach ($badges as $badge) {
39             if (!$external) {
40                 $context = ($badge->type == BADGE_TYPE_SITE) ? context_system::instance() : context_course::instance($badge->courseid);
41                 $bname = $badge->name;
42                 $imageurl = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1', false);
43             } else {
44                 $bname = $badge->assertion->badge->name;
45                 $imageurl = $badge->imageUrl;
46             }
48             $name = html_writer::tag('span', $bname, array('class' => 'badge-name'));
50             $image = html_writer::empty_tag('img', array('src' => $imageurl, 'class' => 'badge-image'));
51             if (!empty($badge->dateexpire) && $badge->dateexpire < time()) {
52                 $image .= $this->output->pix_icon('i/expired',
53                         get_string('expireddate', 'badges', userdate($badge->dateexpire)),
54                         'moodle',
55                         array('class' => 'expireimage'));
56                 $name .= '(' . get_string('expired', 'badges') . ')';
57             }
59             $download = $status = $push = '';
60             if (($userid == $USER->id) && !$profile) {
61                 $url = new moodle_url('mybadges.php', array('download' => $badge->id, 'hash' => $badge->uniquehash, 'sesskey' => sesskey()));
62                 $notexpiredbadge = (empty($badge->dateexpire) || $badge->dateexpire > time());
63                 $backpackexists = badges_user_has_backpack($USER->id);
64                 if (!empty($CFG->badges_allowexternalbackpack) && $notexpiredbadge && $backpackexists) {
65                     $assertion = new moodle_url('/badges/assertion.php', array('b' => $badge->uniquehash));
66                     $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
67                     $push = $this->output->action_icon(new moodle_url('#'), new pix_icon('t/backpack', get_string('addtobackpack', 'badges')), $action);
68                 }
70                 $download = $this->output->action_icon($url, new pix_icon('t/download', get_string('download')));
71                 if ($badge->visible) {
72                     $url = new moodle_url('mybadges.php', array('hide' => $badge->issuedid, 'sesskey' => sesskey()));
73                     $status = $this->output->action_icon($url, new pix_icon('t/hide', get_string('makeprivate', 'badges')));
74                 } else {
75                     $url = new moodle_url('mybadges.php', array('show' => $badge->issuedid, 'sesskey' => sesskey()));
76                     $status = $this->output->action_icon($url, new pix_icon('t/show', get_string('makepublic', 'badges')));
77                 }
78             }
80             if (!$profile) {
81                 $url = new moodle_url('badge.php', array('hash' => $badge->uniquehash));
82             } else {
83                 if (!$external) {
84                     $url = new moodle_url('/badges/badge.php', array('hash' => $badge->uniquehash));
85                 } else {
86                     $hash = hash('md5', $badge->hostedUrl);
87                     $url = new moodle_url('/badges/external.php', array('hash' => $hash, 'user' => $userid));
88                 }
89             }
90             $actions = html_writer::tag('div', $push . $download . $status, array('class' => 'badge-actions'));
91             $items[] = html_writer::link($url, $image . $actions . $name, array('title' => $bname));
92         }
94         return html_writer::alist($items, array('class' => 'badges'));
95     }
97     // Recipients selection form.
98     public function recipients_selection_form(user_selector_base $existinguc, user_selector_base $potentialuc) {
99         $output = '';
100         $formattributes = array();
101         $formattributes['id'] = 'recipientform';
102         $formattributes['action'] = $this->page->url;
103         $formattributes['method'] = 'post';
104         $output .= html_writer::start_tag('form', $formattributes);
105         $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
107         $existingcell = new html_table_cell();
108         $existingcell->text = $existinguc->display(true);
109         $existingcell->attributes['class'] = 'existing';
110         $actioncell = new html_table_cell();
111         $actioncell->text  = html_writer::start_tag('div', array());
112         $actioncell->text .= html_writer::empty_tag('input', array(
113                     'type' => 'submit',
114                     'name' => 'award',
115                     'value' => $this->output->larrow() . ' ' . get_string('award', 'badges'),
116                     'class' => 'actionbutton')
117                 );
118         $actioncell->text .= html_writer::end_tag('div', array());
119         $actioncell->attributes['class'] = 'actions';
120         $potentialcell = new html_table_cell();
121         $potentialcell->text = $potentialuc->display(true);
122         $potentialcell->attributes['class'] = 'potential';
124         $table = new html_table();
125         $table->attributes['class'] = 'recipienttable boxaligncenter';
126         $table->data = array(new html_table_row(array($existingcell, $actioncell, $potentialcell)));
127         $output .= html_writer::table($table);
129         $output .= html_writer::end_tag('form');
130         return $output;
131     }
133     // Prints a badge overview infomation.
134     public function print_badge_overview($badge, $context) {
135         $display = "";
137         // Badge details.
138         $display .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
139         $display .= html_writer::tag('legend', get_string('badgedetails', 'badges'), array('class' => 'bold'));
141         $detailstable = new html_table();
142         $detailstable->attributes = array('class' => 'clearfix', 'id' => 'badgedetails');
143         $detailstable->data[] = array(get_string('name') . ":", $badge->name);
144         $detailstable->data[] = array(get_string('description', 'badges') . ":", $badge->description);
145         $detailstable->data[] = array(get_string('createdon', 'search') . ":", userdate($badge->timecreated));
146         $detailstable->data[] = array(get_string('badgeimage', 'badges') . ":",
147                 print_badge_image($badge, $context, 'large'));
148         $display .= html_writer::table($detailstable);
149         $display .= html_writer::end_tag('fieldset');
151         // Issuer details.
152         $display .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
153         $display .= html_writer::tag('legend', get_string('issuerdetails', 'badges'), array('class' => 'bold'));
155         $issuertable = new html_table();
156         $issuertable->attributes = array('class' => 'clearfix', 'id' => 'badgeissuer');
157         $issuertable->data[] = array(get_string('issuername', 'badges') . ":", $badge->issuername);
158         $issuertable->data[] = array(get_string('contact', 'badges') . ":",
159                 html_writer::tag('a', $badge->issuercontact, array('href' => 'mailto:' . $badge->issuercontact)));
160         $display .= html_writer::table($issuertable);
161         $display .= html_writer::end_tag('fieldset');
163         // Issuance details if any.
164         $display .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
165         $display .= html_writer::tag('legend', get_string('issuancedetails', 'badges'), array('class' => 'bold'));
166         if ($badge->can_expire()) {
167             if ($badge->expiredate) {
168                 $display .= get_string('expiredate', 'badges', userdate($badge->expiredate));
169             } else if ($badge->expireperiod) {
170                 if ($badge->expireperiod < 60) {
171                     $display .= get_string('expireperiods', 'badges', round($badge->expireperiod, 2));
172                 } else if ($badge->expireperiod < 60 * 60) {
173                     $display .= get_string('expireperiodm', 'badges', round($badge->expireperiod / 60, 2));
174                 } else if ($badge->expireperiod < 60 * 60 * 24) {
175                     $display .= get_string('expireperiodh', 'badges', round($badge->expireperiod / 60 / 60, 2));
176                 } else {
177                     $display .= get_string('expireperiod', 'badges', round($badge->expireperiod / 60 / 60 / 24, 2));
178                 }
179             }
180         } else {
181             $display .= get_string('noexpiry', 'badges');
182         }
183         $display .= html_writer::end_tag('fieldset');
185         // Criteria details if any.
186         $display .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
187         $display .= html_writer::tag('legend', get_string('bcriteria', 'badges'), array('class' => 'bold'));
188         if ($badge->has_criteria()) {
189             $display .= self::print_badge_criteria($badge);
190         } else {
191             $display .= get_string('nocriteria', 'badges');
192             if (has_capability('moodle/badges:configurecriteria', $context)) {
193                 $display .= $this->output->single_button(
194                     new moodle_url('/badges/criteria.php', array('id' => $badge->id)),
195                     get_string('addcriteria', 'badges'), 'POST', array('class' => 'activatebadge'));
196             }
197         }
198         $display .= html_writer::end_tag('fieldset');
200         // Awards details if any.
201         if (has_capability('moodle/badges:viewawarded', $context)) {
202             $display .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
203             $display .= html_writer::tag('legend', get_string('awards', 'badges'), array('class' => 'bold'));
204             if ($badge->has_awards()) {
205                 $url = new moodle_url('/badges/recipients.php', array('id' => $badge->id));
206                 $a = new stdClass();
207                 $a->link = $url->out();
208                 $a->count = count($badge->get_awards());
209                 $display .= get_string('numawards', 'badges', $a);
210             } else {
211                 $display .= get_string('noawards', 'badges');
212             }
214             if (has_capability('moodle/badges:awardbadge', $context) &&
215                 $badge->has_manual_award_criteria() &&
216                 $badge->is_active()) {
217                 $display .= $this->output->single_button(
218                         new moodle_url('/badges/award.php', array('id' => $badge->id)),
219                         get_string('award', 'badges'), 'POST', array('class' => 'activatebadge'));
220             }
221             $display .= html_writer::end_tag('fieldset');
222         }
224         return $display;
225     }
227     // Prints action icons for the badge.
228     public function print_badge_table_actions($badge, $context) {
229         $actions = "";
231         if (has_capability('moodle/badges:configuredetails', $context)) {
232             // Activate/deactivate badge.
233             if ($badge->status == BADGE_STATUS_INACTIVE || $badge->status == BADGE_STATUS_INACTIVE_LOCKED) {
234                 $url = new moodle_url(qualified_me());
235                 $url->param('activate', $badge->id);
236                 $url->param('sesskey', sesskey());
237                 $actions .= $this->output->action_icon($url, new pix_icon('t/show', get_string('activate', 'badges'))) . " ";
238             } else {
239                 $url = new moodle_url(qualified_me());
240                 $url->param('lock', $badge->id);
241                 $url->param('sesskey', sesskey());
242                 $actions .= $this->output->action_icon($url, new pix_icon('t/hide', get_string('deactivate', 'badges'))) . " ";
243             }
244         }
246         // Award badge manually.
247         if ($badge->has_manual_award_criteria() &&
248                 has_capability('moodle/badges:awardbadge', $context) &&
249                 $badge->is_active()) {
250             $url = new moodle_url('/badges/award.php', array('id' => $badge->id));
251             $actions .= $this->output->action_icon($url, new pix_icon('t/award', get_string('award', 'badges'))) . " ";
252         }
254         // Edit badge.
255         if (has_capability('moodle/badges:configuredetails', $context)) {
256             $url = new moodle_url('/badges/edit.php', array('id' => $badge->id, 'action' => 'details'));
257             $actions .= $this->output->action_icon($url, new pix_icon('t/edit', get_string('edit'))) . " ";
258         }
260         // Duplicate badge.
261         if (has_capability('moodle/badges:createbadge', $context)) {
262             $url = new moodle_url('/badges/action.php', array('copy' => '1', 'id' => $badge->id, 'sesskey' => sesskey()));
263             $actions .= $this->output->action_icon($url, new pix_icon('t/copy', get_string('copy'))) . " ";
264         }
266         // Delete badge.
267         if (has_capability('moodle/badges:deletebadge', $context)) {
268             $url = new moodle_url(qualified_me());
269             $url->param('delete', $badge->id);
270             $actions .= $this->output->action_icon($url, new pix_icon('t/delete', get_string('delete'))) . " ";
271         }
273         return $actions;
274     }
276     // Outputs issued badge with actions available.
277     protected function render_issued_badge(issued_badge $ibadge) {
278         global $USER, $CFG, $DB;
279         $issued = $ibadge->issued;
280         $badge = new badge($ibadge->badgeid);
281         $today_date = date('Y-m-d');
282         $today = strtotime($today_date);
284         $table = new html_table();
285         $table->id = 'issued-badge-table';
287         $imagetable = new html_table();
288         $imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
289         $imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $issued['badge']['image'])));
290         if ($USER->id == $ibadge->recipient && !empty($CFG->enablebadges)) {
291             $imagetable->data[] = array($this->output->single_button(
292                         new moodle_url('/badges/badge.php', array('hash' => $ibadge->hash, 'bake' => true)),
293                         get_string('download'),
294                         'POST'));
295             $expiration = isset($issued['expires']) ? strtotime($issued['expires']) : $today + 1;
296             if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $today) && badges_user_has_backpack($USER->id)) {
297                 $assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
298                 $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
299                 $attributes = array(
300                         'type'  => 'button',
301                         'id'    => 'addbutton',
302                         'value' => get_string('addtobackpack', 'badges'));
303                 $tobackpack = html_writer::tag('input', '', $attributes);
304                 $this->output->add_action_handler($action, 'addbutton');
305                 $imagetable->data[] = array($tobackpack);
306             }
307         }
308         $datatable = new html_table();
309         $datatable->attributes = array('class' => 'badgeissuedinfo');
310         $datatable->colclasses = array('bfield', 'bvalue');
311         $datatable->data[] = array($this->output->heading(get_string('issuerdetails', 'badges'), 3), '');
312         $datatable->data[] = array(get_string('issuername', 'badges'), $badge->issuername);
313         if (isset($badge->issuercontact) && !empty($badge->issuercontact)) {
314             $datatable->data[] = array(get_string('contact', 'badges'),
315                 html_writer::tag('a', $badge->issuercontact, array('href' => 'mailto:' . $badge->issuercontact)));
316         }
317         $datatable->data[] = array($this->output->heading(get_string('badgedetails', 'badges'), 3), '');
318         $datatable->data[] = array(get_string('name'), $badge->name);
319         $datatable->data[] = array(get_string('description', 'badges'), $badge->description);
321         if ($badge->type == BADGE_TYPE_COURSE && isset($badge->courseid)) {
322             $coursename = $DB->get_field('course', 'fullname', array('id' => $badge->courseid));
323             $datatable->data[] = array(get_string('course'), $coursename);
324         }
326         $datatable->data[] = array(get_string('bcriteria', 'badges'), self::print_badge_criteria($badge));
327         $datatable->data[] = array($this->output->heading(get_string('issuancedetails', 'badges'), 3), '');
328         $datatable->data[] = array(get_string('dateawarded', 'badges'), $issued['issued_on']);
329         if (isset($issued['expires'])) {
330             $expiration = strtotime($issued['expires']);
331             if ($expiration < $today) {
332                 $cell = new html_table_cell($issued['expires'] . get_string('warnexpired', 'badges'));
333                 $cell->attributes = array('class' => 'notifyproblem warning');
334                 $datatable->data[] = array(get_string('expirydate', 'badges'), $cell);
336                 $image = html_writer::start_tag('div', array('class' => 'badge'));
337                 $image .= html_writer::empty_tag('img', array('src' => $issued['badge']['image']));
338                 $image .= $this->output->pix_icon('i/expired',
339                                 get_string('expireddate', 'badges', $issued['expires']),
340                                 'moodle',
341                                 array('class' => 'expireimage'));
342                 $image .= html_writer::end_tag('div');
343                 $imagetable->data[0] = array($image);
344             } else {
345                 $datatable->data[] = array(get_string('expirydate', 'badges'), $issued['expires']);
346             }
347         }
349         // Print evidence.
350         $agg = $badge->get_aggregation_methods();
351         $evidence = $badge->get_criteria_completions($ibadge->recipient);
352         $eids = array_map(create_function('$o', 'return $o->critid;'), $evidence);
353         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
355         $items = array();
356         foreach ($badge->criteria as $type => $c) {
357             if (in_array($c->id, $eids)) {
358                 if (count($c->params) == 1) {
359                     $items[] = get_string('criteria_descr_single_' . $type , 'badges') . $c->get_details();
360                 } else {
361                     $items[] = get_string('criteria_descr_' . $type , 'badges',
362                             strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details();
363                 }
364             }
365         }
367         $datatable->data[] = array(get_string('evidence', 'badges'),
368                 get_string('completioninfo', 'badges') .
369                 html_writer::alist($items, array(), 'ul'));
370         $table->attributes = array('class' => 'generalbox boxaligncenter issuedbadgebox');
371         $table->data[] = array(html_writer::table($imagetable), html_writer::table($datatable));
372         $htmlbadge = html_writer::table($table);
374         return $htmlbadge;
375     }
377     // Outputs external badge.
378     protected function render_external_badge(external_badge $ibadge) {
379         $issued = $ibadge->issued;
380         $assertion = $issued->assertion;
381         $issuer = $assertion->badge->issuer;
382         $table = new html_table();
384         $imagetable = new html_table();
385         $imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
386         $imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $issued->imageUrl, 'width' => '100px')));
388         $datatable = new html_table();
389         $datatable->attributes = array('class' => 'badgeissuedinfo');
390         $datatable->colclasses = array('bfield', 'bvalue');
391         $datatable->data[] = array($this->output->heading(get_string('issuerdetails', 'badges'), 3), '');
392         $datatable->data[] = array(get_string('issuername', 'badges'), $issuer->name);
393         $datatable->data[] = array(get_string('issuerurl', 'badges'),
394                 html_writer::tag('a', $issuer->origin, array('href' => $issuer->origin)));
395         if (isset($issuer->contact)) {
396             $datatable->data[] = array(get_string('contact', 'badges'),
397                 html_writer::tag('a', $issuer->contact, array('href' => 'mailto:' . $issuer->contact)));
398         }
399         $datatable->data[] = array($this->output->heading(get_string('badgedetails', 'badges'), 3), '');
400         $datatable->data[] = array(get_string('name'), $assertion->badge->name);
401         $datatable->data[] = array(get_string('description', 'badges'), $assertion->badge->description);
402         $datatable->data[] = array(get_string('bcriteria', 'badges'),
403                 html_writer::tag('a', $assertion->badge->criteria, array('href' => $assertion->badge->criteria)));
404         $datatable->data[] = array($this->output->heading(get_string('issuancedetails', 'badges'), 3), '');
405         if (isset($assertion->issued_on)) {
406             $datatable->data[] = array(get_string('dateawarded', 'badges'), $assertion->issued_on);
407         }
408         if (isset($assertion->badge->expire)) {
409             $today_date = date('Y-m-d');
410             $today = strtotime($today_date);
411             $expiration = strtotime($assertion->badge->expire);
412             if ($expiration < $today) {
413                 $cell = new html_table_cell($assertion->badge->expire . get_string('warnexpired', 'badges'));
414                 $cell->attributes = array('class' => 'notifyproblem warning');
415                 $datatable->data[] = array(get_string('expirydate', 'badges'), $cell);
417                 $image = html_writer::start_tag('div', array('class' => 'badge'));
418                 $image .= html_writer::empty_tag('img', array('src' => $issued['badge']['image']));
419                 $image .= html_writer::start_tag('span', array('class' => 'expired'))
420                             . $this->output->pix_icon('i/expired',
421                                 get_string('expireddate', 'badges', $assertion->badge->expire),
422                                 'moodle',
423                                 array('class' => 'expireimage'))
424                             . html_writer::end_tag('span');
425                 $image .= html_writer::end_tag('div');
426                 $imagetable->data[0] = array($image);
427             } else {
428                 $datatable->data[] = array(get_string('expirydate', 'badges'), $assertion->badge->expire);
429             }
430         }
431         if (isset($assertion->evidence)) {
432             $datatable->data[] = array(get_string('evidence', 'badges'),
433                 html_writer::tag('a', $assertion->evidence, array('href' => $assertion->evidence)));
434         }
435         $table->attributes = array('class' => 'generalbox boxaligncenter issuedbadgebox');
436         $table->data[] = array(html_writer::table($imagetable), html_writer::table($datatable));
437         $htmlbadge = html_writer::table($table);
439         return $htmlbadge;
440     }
442     // Outputs table of user badges.
443     protected function render_badge_user_collection(badge_user_collection $badges) {
444         global $CFG, $USER, $SITE;
445         $backpack = $badges->backpack;
446         $mybackpack = new moodle_url('/badges/mybackpack.php');
448         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
449         $htmlpagingbar = $this->render($paging);
451         // Set backpack connection string.
452         $backpackconnect = '';
453         if (!empty($CFG->badges_allowexternalbackpack) && is_null($backpack)) {
454             $backpackconnect = $this->output->box(get_string('localconnectto', 'badges', $mybackpack->out()), 'noticebox');
455         }
456         // Search box.
457         $searchform = $this->output->box($this->helper_search_form($badges->search), 'boxwidthwide boxaligncenter');
459         // Download all button.
460         $downloadall = $this->output->single_button(
461                     new moodle_url('/badges/mybadges.php', array('downloadall' => true, 'sesskey' => sesskey())),
462                     get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
464         // Local badges.
465         $localhtml = html_writer::start_tag('fieldset', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
466         $localhtml .= html_writer::tag('legend',
467                     $this->output->heading_with_help(get_string('localbadges', 'badges', $SITE->fullname), 'localbadgesh', 'badges'));
468         if ($badges->badges) {
469             $table = new html_table();
470             $table->attributes['class'] = 'statustable';
471             $table->data[] = array($this->output->heading(get_string('badgesearned', 'badges', $badges->totalcount), 4, 'activatebadge'), $downloadall);
472             $downloadbutton = html_writer::table($table);
474             $htmllist = $this->print_badges_list($badges->badges, $USER->id);
475             $localhtml .= $backpackconnect . $downloadbutton . $searchform . $htmlpagingbar . $htmllist . $htmlpagingbar;
476         } else {
477             $localhtml .= $searchform . $this->output->notification(get_string('nobadges', 'badges'));
478         }
479         $localhtml .= html_writer::end_tag('fieldset');
481         // External badges.
482         $externalhtml = "";
483         if (!empty($CFG->badges_allowexternalbackpack)) {
484             $externalhtml .= html_writer::start_tag('fieldset', array('class' => 'generalbox'));
485             $externalhtml .= html_writer::tag('legend', $this->output->heading_with_help(get_string('externalbadges', 'badges'), 'externalbadges', 'badges'));
486             if (!is_null($backpack)) {
487                 if ($backpack->totalcollections == 0) {
488                     $externalhtml .= get_string('nobackpackcollections', 'badges', $backpack);
489                 } else {
490                     if ($backpack->totalbadges == 0) {
491                         $externalhtml .= get_string('nobackpackbadges', 'badges', $backpack);
492                     } else {
493                         $externalhtml .= get_string('backpackbadges', 'badges', $backpack);
494                         $externalhtml .= '<br/><br/>' . $this->print_badges_list($backpack->badges, $USER->id, true, true);
495                     }
496                 }
497             } else {
498                 $externalhtml .= get_string('externalconnectto', 'badges', $mybackpack->out());
499             }
501             $externalhtml .= html_writer::end_tag('fieldset');
502         }
504         return $localhtml . $externalhtml;
505     }
507     // Outputs table of available badges.
508     protected function render_badge_collection(badge_collection $badges) {
509         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
510         $htmlpagingbar = $this->render($paging);
511         $table = new html_table();
512         $table->attributes['class'] = 'collection';
514         $sortbyname = $this->helper_sortable_heading(get_string('name'),
515                 'name', $badges->sort, $badges->dir);
516         $sortbyawarded = $this->helper_sortable_heading(get_string('awardedtoyou', 'badges'),
517                 'dateissued', $badges->sort, $badges->dir);
518         $table->head = array(
519                     get_string('badgeimage', 'badges'),
520                     $sortbyname,
521                     get_string('description', 'badges'),
522                     get_string('bcriteria', 'badges'),
523                     $sortbyawarded
524                 );
525         $table->colclasses = array('badgeimage', 'name', 'description', 'criteria', 'awards');
527         foreach ($badges->badges as $badge) {
528             $badgeimage = print_badge_image($badge, $this->page->context, 'large');
529             $name = $badge->name;
530             $description = $badge->description;
531             $criteria = self::print_badge_criteria($badge);
532             if ($badge->dateissued) {
533                 $icon = new pix_icon('i/valid',
534                             get_string('dateearned', 'badges',
535                                 userdate($badge->dateissued, get_string('strftimedatefullshort', 'core_langconfig'))));
536                 $badgeurl = new moodle_url('/badges/badge.php', array('hash' => $badge->uniquehash));
537                 $awarded = $this->output->action_icon($badgeurl, $icon, null, null, true);
538             } else {
539                 $awarded = "";
540             }
541             $row = array($badgeimage, $name, $description, $criteria, $awarded);
542             $table->data[] = $row;
543         }
545         $htmltable = html_writer::table($table);
547         return $htmlpagingbar . $htmltable . $htmlpagingbar;
548     }
550     // Outputs table of badges with actions available.
551     protected function render_badge_management(badge_management $badges) {
552         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
554         // New badge button.
555         $htmlnew = '';
556         if (has_capability('moodle/badges:createbadge', $this->page->context)) {
557             $n['type'] = $this->page->url->get_param('type');
558             $n['id'] = $this->page->url->get_param('id');
559             $htmlnew = $this->output->single_button(new moodle_url('newbadge.php', $n), get_string('newbadge', 'badges'));
560         }
562         $htmlpagingbar = $this->render($paging);
563         $table = new html_table();
564         $table->attributes['class'] = 'collection';
566         $sortbyname = $this->helper_sortable_heading(get_string('name'),
567                 'name', $badges->sort, $badges->dir);
568         $sortbystatus = $this->helper_sortable_heading(get_string('status', 'badges'),
569                 'status', $badges->sort, $badges->dir);
570         $table->head = array(
571                 $sortbyname,
572                 $sortbystatus,
573                 get_string('bcriteria', 'badges'),
574                 get_string('awards', 'badges'),
575                 get_string('actions')
576             );
577         $table->colclasses = array('name', 'status', 'criteria', 'awards', 'actions');
579         foreach ($badges->badges as $b) {
580             $style = !$b->is_active() ? array('class' => 'dimmed') : array();
581             $forlink =  print_badge_image($b, $this->page->context) . ' ' .
582                         html_writer::start_tag('span') . $b->name . html_writer::end_tag('span');
583             $name = html_writer::link(new moodle_url('/badges/overview.php', array('id' => $b->id)), $forlink, $style);
584             $status = $b->statstring;
585             $criteria = self::print_badge_criteria($b, 'short');
587             if (has_capability('moodle/badges:viewawarded', $this->page->context)) {
588                 $awards = html_writer::link(new moodle_url('/badges/recipients.php', array('id' => $b->id)), $b->awards);
589             } else {
590                 $awards = $b->awards;
591             }
593             $actions = self::print_badge_table_actions($b, $this->page->context);
595             $row = array($name, $status, $criteria, $awards, $actions);
596             $table->data[] = $row;
597         }
598         $htmltable = html_writer::table($table);
600         return $htmlnew . $htmlpagingbar . $htmltable . $htmlpagingbar;
601     }
603     // Prints tabs for badge editing.
604     public function print_badge_tabs($badgeid, $context, $current = 'overview') {
605         global $DB;
607         $row = array();
609         $row[] = new tabobject('overview',
610                     new moodle_url('/badges/overview.php', array('id' => $badgeid)),
611                     get_string('boverview', 'badges')
612                 );
614         if (has_capability('moodle/badges:configuredetails', $context)) {
615             $row[] = new tabobject('details',
616                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'details')),
617                         get_string('bdetails', 'badges')
618                     );
619         }
621         if (has_capability('moodle/badges:configurecriteria', $context)) {
622             $row[] = new tabobject('criteria',
623                         new moodle_url('/badges/criteria.php', array('id' => $badgeid)),
624                         get_string('bcriteria', 'badges')
625                     );
626         }
628         if (has_capability('moodle/badges:configuremessages', $context)) {
629             $row[] = new tabobject('message',
630                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'message')),
631                         get_string('bmessage', 'badges')
632                     );
633         }
635         if (has_capability('moodle/badges:viewawarded', $context)) {
636             $awarded = $DB->count_records('badge_issued', array('badgeid' => $badgeid));
637             $row[] = new tabobject('awards',
638                         new moodle_url('/badges/recipients.php', array('id' => $badgeid)),
639                         get_string('bawards', 'badges', $awarded)
640                     );
641         }
643         echo $this->tabtree($row, $current);
644     }
646     /**
647      * Prints badge status box.
648      * @return Either the status box html as a string or null
649      */
650     public function print_badge_status_box(badge $badge) {
651         if (has_capability('moodle/badges:configurecriteria', $badge->get_context())) {
652             $table = new html_table();
653             $table->attributes['class'] = 'boxaligncenter statustable';
655             if (!$badge->has_criteria()) {
656                 $criteriaurl = new moodle_url('/badges/criteria.php', array('id' => $badge->id));
657                 $status = get_string('nocriteria', 'badges');
658                 if ($this->page->url != $criteriaurl) {
659                     $action = $this->output->single_button(
660                         $criteriaurl,
661                         get_string('addcriteria', 'badges'), 'POST', array('class' => 'activatebadge'));
662                 } else {
663                     $action = '';
664                 }
665                 $row = array($status, $action);
666             } else {
667                 $status = get_string('statusmessage_' . $badge->status, 'badges');
668                 if ($badge->is_active()) {
669                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
670                                 array('id' => $badge->id, 'lock' => 1, 'sesskey' => sesskey(),
671                                       'return' => $this->page->url->out_as_local_url(false))),
672                             get_string('deactivate', 'badges'), 'POST', array('class' => 'activatebadge'));
673                 } else {
674                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
675                                 array('id' => $badge->id, 'activate' => 1, 'sesskey' => sesskey(),
676                                       'return' => $this->page->url->out_as_local_url(false))),
677                             get_string('activate', 'badges'), 'POST', array('class' => 'activatebadge'));
678                 }
679                 $row = array($status . $this->output->help_icon('status', 'badges'), $action);
680             }
681             $table->data[] = $row;
683             $style = $badge->is_active() ? 'generalbox statusbox active' : 'generalbox statusbox inactive';
684             return $this->output->box(html_writer::table($table), $style);
685         }
687         return null;
688     }
690     // Prints badge criteria.
691     public function print_badge_criteria(badge $badge, $short = '') {
692         $output = "";
693         $agg = $badge->get_aggregation_methods();
694         if (empty($badge->criteria)) {
695             return get_string('nocriteria', 'badges');
696         } else if (count($badge->criteria) == 2) {
697             if (!$short) {
698                 $output .= get_string('criteria_descr', 'badges');
699             }
700         } else {
701             $output .= get_string('criteria_descr_' . $short . BADGE_CRITERIA_TYPE_OVERALL, 'badges',
702                                     strtoupper($agg[$badge->get_aggregation_method()]));
703         }
704         $items = array();
705         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
706         foreach ($badge->criteria as $type => $c) {
707             if (count($c->params) == 1) {
708                 $items[] = get_string('criteria_descr_single_' . $short . $type , 'badges') . $c->get_details($short);
709             } else {
710                 $items[] = get_string('criteria_descr_' . $short . $type , 'badges',
711                         strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details($short);
712             }
713         }
714         $output .= html_writer::alist($items, array(), 'ul');
715         return $output;
716     }
718     // Prints criteria actions for badge editing.
719     public function print_criteria_actions(badge $badge) {
720         $table = new html_table();
721         $table->attributes = array('class' => 'boxaligncenter', 'id' => 'badgeactions');
722         $table->colclasses = array('activatebadge');
724         $actions = array();
725         if (!$badge->is_active() && !$badge->is_locked()) {
726             $accepted = $badge->get_accepted_criteria();
727             $potential = array_diff($accepted, array_keys($badge->criteria));
729             if (!empty($potential)) {
730                 foreach ($potential as $p) {
731                     if ($p != 0) {
732                         $select[$p] = get_string('criteria_' . $p, 'badges');
733                     }
734                 }
735                 $actions[] = get_string('addbadgecriteria', 'badges');
736                 $actions[] = $this->output->single_select(new moodle_url('/badges/criteria_settings.php',
737                         array('badgeid' => $badge->id, 'add' => true)), 'type', $select);
738             } else {
739                 $actions[] = $this->output->box(get_string('nothingtoadd', 'badges'), 'clearfix');
740             }
741         }
743         $table->data[] = $actions;
744         return html_writer::table($table);
745     }
747     // Renders a table with users who have earned the badge.
748     // Based on stamps collection plugin.
749     protected function render_badge_recipients(badge_recipients $recipients) {
750         $paging = new paging_bar($recipients->totalcount, $recipients->page, $recipients->perpage, $this->page->url, 'page');
751         $htmlpagingbar = $this->render($paging);
752         $table = new html_table();
753         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
755         $sortbyfirstname = $this->helper_sortable_heading(get_string('firstname'),
756                 'firstname', $recipients->sort, $recipients->dir);
757         $sortbylastname = $this->helper_sortable_heading(get_string('lastname'),
758                 'lastname', $recipients->sort, $recipients->dir);
759         if ($this->helper_fullname_format() == 'lf') {
760             $sortbyname = $sortbylastname . ' / ' . $sortbyfirstname;
761         } else {
762             $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
763         }
765         $sortbydate = $this->helper_sortable_heading(get_string('dateawarded', 'badges'),
766                 'dateissued', $recipients->sort, $recipients->dir);
768         $table->head = array($sortbyname, $sortbydate, '');
770         foreach ($recipients->userids as $holder) {
771             $fullname = fullname($holder);
772             $fullname = html_writer::link(
773                             new moodle_url('/user/profile.php', array('id' => $holder->userid)),
774                             $fullname
775                         );
776             $awarded  = userdate($holder->dateissued);
777             $badgeurl = html_writer::link(
778                             new moodle_url('/badges/badge.php', array('hash' => $holder->uniquehash)),
779                             get_string('viewbadge', 'badges')
780                         );
782             $row = array($fullname, $awarded, $badgeurl);
783             $table->data[] = $row;
784         }
786         $htmltable = html_writer::table($table);
788         return $htmlpagingbar . $htmltable . $htmlpagingbar;
789     }
791     ////////////////////////////////////////////////////////////////////////////
792     // Helper methods
793     // Reused from stamps collection plugin
794     ////////////////////////////////////////////////////////////////////////////
796     /**
797      * Renders a text with icons to sort by the given column
798      *
799      * This is intended for table headings.
800      *
801      * @param string $text    The heading text
802      * @param string $sortid  The column id used for sorting
803      * @param string $sortby  Currently sorted by (column id)
804      * @param string $sorthow Currently sorted how (ASC|DESC)
805      *
806      * @return string
807      */
808     protected function helper_sortable_heading($text, $sortid = null, $sortby = null, $sorthow = null) {
809         $out = html_writer::tag('span', $text, array('class' => 'text'));
811         if (!is_null($sortid)) {
812             if ($sortby !== $sortid || $sorthow !== 'ASC') {
813                 $url = new moodle_url($this->page->url);
814                 $url->params(array('sort' => $sortid, 'dir' => 'ASC'));
815                 $out .= $this->output->action_icon($url,
816                         new pix_icon('t/sort_asc', get_string('sortbyx', 'core', s($text)), null, array('class' => 'iconsort')));
817             }
818             if ($sortby !== $sortid || $sorthow !== 'DESC') {
819                 $url = new moodle_url($this->page->url);
820                 $url->params(array('sort' => $sortid, 'dir' => 'DESC'));
821                 $out .= $this->output->action_icon($url,
822                         new pix_icon('t/sort_desc', get_string('sortbyxreverse', 'core', s($text)), null, array('class' => 'iconsort')));
823             }
824         }
825         return $out;
826     }
827     /**
828      * Tries to guess the fullname format set at the site
829      *
830      * @return string fl|lf
831      */
832     protected function helper_fullname_format() {
833         $fake = new stdClass();
834         $fake->lastname = 'LLLL';
835         $fake->firstname = 'FFFF';
836         $fullname = get_string('fullnamedisplay', '', $fake);
837         if (strpos($fullname, 'LLLL') < strpos($fullname, 'FFFF')) {
838             return 'lf';
839         } else {
840             return 'fl';
841         }
842     }
843     /**
844      * Renders a search form
845      *
846      * @param string $search Search string
847      * @return string HTML
848      */
849     protected function helper_search_form($search) {
850         global $CFG;
851         require_once($CFG->libdir . '/formslib.php');
853         $mform = new MoodleQuickForm('searchform', 'POST', $this->page->url);
855         $mform->addElement('hidden', 'sesskey', sesskey());
857         $el[] = $mform->createElement('text', 'search', get_string('search'), array('size' => 20));
858         $mform->setDefault('search', $search);
859         $el[] = $mform->createElement('submit', 'submitsearch', get_string('search'));
860         $el[] = $mform->createElement('submit', 'clearsearch', get_string('clear'));
861         $mform->addGroup($el, 'searchgroup', get_string('searchname', 'badges'), ' ', false);
863         ob_start();
864         $mform->display();
865         $out = ob_get_clean();
867         return $out;
868     }
871 /**
872  * An issued badges for badge.php page
873  */
874 class issued_badge implements renderable {
875     /** @var issued badge */
876     public $issued;
878     /** @var badge recipient */
879     public $recipient = 0;
881     /** @var badge visibility to others */
882     public $visible = 0;
884     /** @var badge class */
885     public $badgeid = 0;
887     /** @var issued badge unique hash */
888     public $hash = "";
890     /**
891      * Initializes the badge to display
892      *
893      * @param string $hash Issued badge hash
894      */
895     public function __construct($hash) {
896         global $DB;
897         $this->issued = badges_get_issued_badge_info($hash);
898         $this->hash = $hash;
900         $rec = $DB->get_record_sql('SELECT userid, visible, badgeid
901                 FROM {badge_issued}
902                 WHERE ' . $DB->sql_compare_text('uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
903                 array('hash' => $hash), IGNORE_MISSING);
904         if ($rec) {
905             $this->recipient = $rec->userid;
906             $this->visible = $rec->visible;
907             $this->badgeid = $rec->badgeid;
908         }
909     }
912 /**
913  * An external badges for external.php page
914  */
915 class external_badge implements renderable {
916     /** @var issued badge */
917     public $issued;
919     /**
920      * Initializes the badge to display
921      *
922      * @param object $badge External badge information.
923      */
924     public function __construct($badge) {
925         $this->issued = $badge;
926     }
929 /**
930  * Badge recipients rendering class
931  */
932 class badge_recipients implements renderable {
933     /** @var string how are the data sorted */
934     public $sort = 'lastname';
936     /** @var string how are the data sorted */
937     public $dir = 'ASC';
939     /** @var int page number to display */
940     public $page = 0;
942     /** @var int number of badge recipients to display per page */
943     public $perpage = 30;
945     /** @var int the total number or badge recipients to display */
946     public $totalcount = null;
948     /** @var array internal list of  badge recipients ids */
949     public $userids = array();
950     /**
951      * Initializes the list of users to display
952      *
953      * @param array $holders List of badge holders
954      */
955     public function __construct($holders) {
956         $this->userids = $holders;
957     }
960 /**
961  * Collection of all badges for view.php page
962  */
963 class badge_collection implements renderable {
965     /** @var string how are the data sorted */
966     public $sort = 'name';
968     /** @var string how are the data sorted */
969     public $dir = 'ASC';
971     /** @var int page number to display */
972     public $page = 0;
974     /** @var int number of badges to display per page */
975     public $perpage = BADGE_PERPAGE;
977     /** @var int the total number of badges to display */
978     public $totalcount = null;
980     /** @var array list of badges */
981     public $badges = array();
983     /**
984      * Initializes the list of badges to display
985      *
986      * @param array $badges Badges to render
987      */
988     public function __construct($badges) {
989         $this->badges = $badges;
990     }
993 /**
994  * Collection of badges used at the index.php page
995  */
996 class badge_management extends badge_collection implements renderable {
999 /**
1000  * Collection of user badges used at the mybadges.php page
1001  */
1002 class badge_user_collection extends badge_collection implements renderable {
1003     /** @var array backpack settings */
1004     public $backpack = null;
1006     /** @var string search */
1007     public $search = '';
1009     /**
1010      * Initializes user badge collection.
1011      *
1012      * @param array $badges Badges to render
1013      * @param int $userid Badges owner
1014      */
1015     public function __construct($badges, $userid) {
1016         global $CFG;
1017         parent::__construct($badges);
1019         if (!empty($CFG->badges_allowexternalbackpack)) {
1020             $this->backpack = get_backpack_settings($userid, true);
1021         }
1022     }