MDL-67974 core_badges: Badge expiry date when hover
[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 = '';
45                 $imageurl = '';
46                 if (!empty($badge->name)) {
47                     $bname = s($badge->name);
48                 }
49                 if (!empty($badge->image)) {
50                     $imageurl = $badge->image;
51                 }
52                 if (isset($badge->assertion->badge->name)) {
53                     $bname = s($badge->assertion->badge->name);
54                 }
55                 if (isset($badge->imageUrl)) {
56                     $imageurl = $badge->imageUrl;
57                 }
58             }
60             $name = html_writer::tag('span', $bname, array('class' => 'badge-name'));
62             $image = html_writer::empty_tag('img', array('src' => $imageurl, 'class' => 'badge-image'));
63             if (!empty($badge->dateexpire) && $badge->dateexpire < time()) {
64                 $image .= $this->output->pix_icon('i/expired',
65                         get_string('expireddate', 'badges', userdate($badge->dateexpire)),
66                         'moodle',
67                         array('class' => 'expireimage'));
68                 $name .= '(' . get_string('expired', 'badges') . ')';
69             }
71             $download = $status = $push = '';
72             if (($userid == $USER->id) && !$profile) {
73                 $params = array(
74                     'download' => $badge->id,
75                     'hash' => $badge->uniquehash,
76                     'sesskey' => sesskey()
77                 );
78                 $url = new moodle_url(
79                     'mybadges.php',
80                     $params
81                 );
82                 $notexpiredbadge = (empty($badge->dateexpire) || $badge->dateexpire > time());
83                 $userbackpack = badges_get_user_backpack();
84                 if (!empty($CFG->badges_allowexternalbackpack) && $notexpiredbadge && $userbackpack) {
85                     $assertion = new moodle_url('/badges/assertion.php', array('b' => $badge->uniquehash));
86                     $action = null;
87                     if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V1) {
88                         $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
89                         $addurl = new moodle_url('#');
90                     } else if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V2P1) {
91                         $addurl = new moodle_url('/badges/backpack-export.php', array('hash' => $badge->uniquehash));
92                     } else {
93                         $addurl = new moodle_url('/badges/backpack-add.php', array('hash' => $badge->uniquehash));
94                     }
95                     $icon = new pix_icon('t/backpack', get_string('addtobackpack', 'badges'));
96                     $push = $this->output->action_icon($addurl, $icon, $action);
97                 }
99                 $download = $this->output->action_icon($url, new pix_icon('t/download', get_string('download')));
100                 if ($badge->visible) {
101                     $url = new moodle_url('mybadges.php', array('hide' => $badge->issuedid, 'sesskey' => sesskey()));
102                     $status = $this->output->action_icon($url, new pix_icon('t/hide', get_string('makeprivate', 'badges')));
103                 } else {
104                     $url = new moodle_url('mybadges.php', array('show' => $badge->issuedid, 'sesskey' => sesskey()));
105                     $status = $this->output->action_icon($url, new pix_icon('t/show', get_string('makepublic', 'badges')));
106                 }
107             }
109             if (!$profile) {
110                 $url = new moodle_url('badge.php', array('hash' => $badge->uniquehash));
111             } else {
112                 if (!$external) {
113                     $url = new moodle_url('/badges/badge.php', array('hash' => $badge->uniquehash));
114                 } else {
115                     $hash = hash('md5', $badge->hostedUrl);
116                     $url = new moodle_url('/badges/external.php', array('hash' => $hash, 'user' => $userid));
117                 }
118             }
119             $actions = html_writer::tag('div', $push . $download . $status, array('class' => 'badge-actions'));
120             $items[] = html_writer::link($url, $image . $actions . $name, array('title' => $bname));
121         }
123         return html_writer::alist($items, array('class' => 'badges'));
124     }
126     // Recipients selection form.
127     public function recipients_selection_form(user_selector_base $existinguc, user_selector_base $potentialuc) {
128         $output = '';
129         $formattributes = array();
130         $formattributes['id'] = 'recipientform';
131         $formattributes['action'] = $this->page->url;
132         $formattributes['method'] = 'post';
133         $output .= html_writer::start_tag('form', $formattributes);
134         $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
136         $existingcell = new html_table_cell();
137         $existingcell->text = $existinguc->display(true);
138         $existingcell->attributes['class'] = 'existing';
139         $actioncell = new html_table_cell();
140         $actioncell->text  = html_writer::start_tag('div', array());
141         $actioncell->text .= html_writer::empty_tag('input', array(
142                     'type' => 'submit',
143                     'name' => 'award',
144                     'value' => $this->output->larrow() . ' ' . get_string('award', 'badges'),
145                     'class' => 'actionbutton btn btn-secondary')
146                 );
147         $actioncell->text .= html_writer::empty_tag('input', array(
148                     'type' => 'submit',
149                     'name' => 'revoke',
150                     'value' => get_string('revoke', 'badges') . ' ' . $this->output->rarrow(),
151                     'class' => 'actionbutton btn btn-secondary')
152                 );
153         $actioncell->text .= html_writer::end_tag('div', array());
154         $actioncell->attributes['class'] = 'actions';
155         $potentialcell = new html_table_cell();
156         $potentialcell->text = $potentialuc->display(true);
157         $potentialcell->attributes['class'] = 'potential';
159         $table = new html_table();
160         $table->attributes['class'] = 'recipienttable boxaligncenter';
161         $table->data = array(new html_table_row(array($existingcell, $actioncell, $potentialcell)));
162         $output .= html_writer::table($table);
164         $output .= html_writer::end_tag('form');
165         return $output;
166     }
168     // Prints a badge overview infomation.
169     public function print_badge_overview($badge, $context) {
170         $display = "";
171         $languages = get_string_manager()->get_list_of_languages();
173         // Badge details.
174         $display .= $this->heading(get_string('badgedetails', 'badges'), 3);
175         $dl = array();
176         $dl[get_string('name')] = $badge->name;
177         $dl[get_string('version', 'badges')] = $badge->version;
178         $dl[get_string('language')] = $languages[$badge->language];
179         $dl[get_string('description', 'badges')] = $badge->description;
180         $dl[get_string('createdon', 'search')] = userdate($badge->timecreated);
181         $dl[get_string('badgeimage', 'badges')] = print_badge_image($badge, $context, 'large');
182         $dl[get_string('imageauthorname', 'badges')] = $badge->imageauthorname;
183         $dl[get_string('imageauthoremail', 'badges')] =
184             html_writer::tag('a', $badge->imageauthoremail, array('href' => 'mailto:' . $badge->imageauthoremail));
185         $dl[get_string('imageauthorurl', 'badges')] =
186             html_writer::link($badge->imageauthorurl, $badge->imageauthorurl, array('target' => '_blank'));
187         $dl[get_string('imagecaption', 'badges')] = $badge->imagecaption;
188         $display .= $this->definition_list($dl);
190         // Issuer details.
191         $display .= $this->heading(get_string('issuerdetails', 'badges'), 3);
192         $dl = array();
193         $dl[get_string('issuername', 'badges')] = $badge->issuername;
194         $dl[get_string('contact', 'badges')] = html_writer::tag('a', $badge->issuercontact, array('href' => 'mailto:' . $badge->issuercontact));
195         $display .= $this->definition_list($dl);
197         // Issuance details if any.
198         $display .= $this->heading(get_string('issuancedetails', 'badges'), 3);
199         if ($badge->can_expire()) {
200             if ($badge->expiredate) {
201                 $display .= get_string('expiredate', 'badges', userdate($badge->expiredate));
202             } else if ($badge->expireperiod) {
203                 if ($badge->expireperiod < 60) {
204                     $display .= get_string('expireperiods', 'badges', round($badge->expireperiod, 2));
205                 } else if ($badge->expireperiod < 60 * 60) {
206                     $display .= get_string('expireperiodm', 'badges', round($badge->expireperiod / 60, 2));
207                 } else if ($badge->expireperiod < 60 * 60 * 24) {
208                     $display .= get_string('expireperiodh', 'badges', round($badge->expireperiod / 60 / 60, 2));
209                 } else {
210                     $display .= get_string('expireperiod', 'badges', round($badge->expireperiod / 60 / 60 / 24, 2));
211                 }
212             }
213         } else {
214             $display .= get_string('noexpiry', 'badges');
215         }
217         // Criteria details if any.
218         $display .= $this->heading(get_string('bcriteria', 'badges'), 3);
219         if ($badge->has_criteria()) {
220             $display .= self::print_badge_criteria($badge);
221         } else {
222             $display .= get_string('nocriteria', 'badges');
223             if (has_capability('moodle/badges:configurecriteria', $context)) {
224                 $display .= $this->output->single_button(
225                     new moodle_url('/badges/criteria.php', array('id' => $badge->id)),
226                     get_string('addcriteria', 'badges'), 'POST', array('class' => 'activatebadge'));
227             }
228         }
230         // Awards details if any.
231         if (has_capability('moodle/badges:viewawarded', $context)) {
232             $display .= $this->heading(get_string('awards', 'badges'), 3);
233             if ($badge->has_awards()) {
234                 $url = new moodle_url('/badges/recipients.php', array('id' => $badge->id));
235                 $a = new stdClass();
236                 $a->link = $url->out();
237                 $a->count = count($badge->get_awards());
238                 $display .= get_string('numawards', 'badges', $a);
239             } else {
240                 $display .= get_string('noawards', 'badges');
241             }
243             if (has_capability('moodle/badges:awardbadge', $context) &&
244                 $badge->has_manual_award_criteria() &&
245                 $badge->is_active()) {
246                 $display .= $this->output->single_button(
247                         new moodle_url('/badges/award.php', array('id' => $badge->id)),
248                         get_string('award', 'badges'), 'POST', array('class' => 'activatebadge'));
249             }
250         }
252         $display .= self::print_badge_endorsement($badge);
253         $display .= self::print_badge_related($badge);
254         $display .= self::print_badge_alignments($badge);
256         return html_writer::div($display, null, array('id' => 'badge-overview'));
257     }
259     // Prints action icons for the badge.
260     public function print_badge_table_actions($badge, $context) {
261         $actions = "";
263         if (has_capability('moodle/badges:configuredetails', $context) && $badge->has_criteria()) {
264             // Activate/deactivate badge.
265             if ($badge->status == BADGE_STATUS_INACTIVE || $badge->status == BADGE_STATUS_INACTIVE_LOCKED) {
266                 // "Activate" will go to another page and ask for confirmation.
267                 $url = new moodle_url('/badges/action.php');
268                 $url->param('id', $badge->id);
269                 $url->param('activate', true);
270                 $url->param('sesskey', sesskey());
271                 $return = new moodle_url(qualified_me());
272                 $url->param('return', $return->out_as_local_url(false));
273                 $actions .= $this->output->action_icon($url, new pix_icon('t/show', get_string('activate', 'badges'))) . " ";
274             } else {
275                 $url = new moodle_url(qualified_me());
276                 $url->param('lock', $badge->id);
277                 $url->param('sesskey', sesskey());
278                 $actions .= $this->output->action_icon($url, new pix_icon('t/hide', get_string('deactivate', 'badges'))) . " ";
279             }
280         }
282         // Award badge manually.
283         if ($badge->has_manual_award_criteria() &&
284                 has_capability('moodle/badges:awardbadge', $context) &&
285                 $badge->is_active()) {
286             $url = new moodle_url('/badges/award.php', array('id' => $badge->id));
287             $actions .= $this->output->action_icon($url, new pix_icon('t/award', get_string('award', 'badges'))) . " ";
288         }
290         // Edit badge.
291         if (has_capability('moodle/badges:configuredetails', $context)) {
292             $url = new moodle_url('/badges/edit.php', array('id' => $badge->id, 'action' => 'badge'));
293             $actions .= $this->output->action_icon($url, new pix_icon('t/edit', get_string('edit'))) . " ";
294         }
296         // Duplicate badge.
297         if (has_capability('moodle/badges:createbadge', $context)) {
298             $url = new moodle_url('/badges/action.php', array('copy' => '1', 'id' => $badge->id, 'sesskey' => sesskey()));
299             $actions .= $this->output->action_icon($url, new pix_icon('t/copy', get_string('copy'))) . " ";
300         }
302         // Delete badge.
303         if (has_capability('moodle/badges:deletebadge', $context)) {
304             $url = new moodle_url(qualified_me());
305             $url->param('delete', $badge->id);
306             $actions .= $this->output->action_icon($url, new pix_icon('t/delete', get_string('delete'))) . " ";
307         }
309         return $actions;
310     }
312     /**
313      * Render an issued badge.
314      *
315      * @param \core_badges\output\issued_badge $ibadge
316      * @return string
317      */
318     protected function render_issued_badge(\core_badges\output\issued_badge $ibadge) {
319         global $USER, $CFG, $DB, $SITE;
320         $issued = $ibadge->issued;
321         $userinfo = $ibadge->recipient;
322         $badgeclass = $ibadge->badgeclass;
323         $badge = new badge($ibadge->badgeid);
324         $now = time();
325         if (isset($issued['expires'])) {
326             if (!is_numeric($issued['expires'])) {
327                 $issued['expires'] = strtotime($issued['expires']);
328             }
329             $expiration = $issued['expires'];
330         } else {
331             $expiration = $now + 86400;
332         }
334         $badgeimage = is_array($badgeclass['image']) ? $badgeclass['image']['id'] : $badgeclass['image'];
335         $languages = get_string_manager()->get_list_of_languages();
337         $output = '';
338         $output .= html_writer::start_tag('div', array('id' => 'badge'));
339         $output .= html_writer::start_tag('div', array('id' => 'badge-image'));
340         $output .= html_writer::empty_tag('img', array('src' => $badgeimage, 'alt' => $badge->name, 'width' => '100'));
341         if ($expiration < $now) {
342             $output .= $this->output->pix_icon('i/expired',
343             get_string('expireddate', 'badges', userdate($issued['expires'])),
344                 'moodle',
345                 array('class' => 'expireimage'));
346         }
348         if ($USER->id == $userinfo->id && !empty($CFG->enablebadges)) {
349             $output .= $this->output->single_button(
350                         new moodle_url('/badges/badge.php', array('hash' => $ibadge->hash, 'bake' => true)),
351                         get_string('download'),
352                         'POST');
353             if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now)
354                 && $userbackpack = badges_get_user_backpack($USER->id)) {
356                 if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V1) {
357                     $assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
358                     $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
359                     $attributes = array(
360                             'type'  => 'button',
361                             'class' => 'btn btn-secondary m-1',
362                             'id'    => 'addbutton',
363                             'value' => get_string('addtobackpack', 'badges'));
364                     $tobackpack = html_writer::tag('input', '', $attributes);
365                     $this->output->add_action_handler($action, 'addbutton');
366                     $output .= $tobackpack;
367                 } else {
368                     if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V2P1) {
369                         $assertion = new moodle_url('/badges/backpack-export.php', array('hash' => $ibadge->hash));
370                     } else {
371                         $assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
372                     }
373                     $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
374                     $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
375                     $output .= $tobackpack;
376                 }
377             }
378         }
379         $output .= html_writer::end_tag('div');
381         $output .= html_writer::start_tag('div', array('id' => 'badge-details'));
382         // Recipient information.
383         $output .= $this->output->heading(get_string('recipientdetails', 'badges'), 3);
384         $dl = array();
385         if ($userinfo->deleted) {
386             $strdata = new stdClass();
387             $strdata->user = fullname($userinfo);
388             $strdata->site = format_string($SITE->fullname, true, array('context' => context_system::instance()));
390             $dl[get_string('name')] = get_string('error:userdeleted', 'badges', $strdata);
391         } else {
392             $dl[get_string('name')] = fullname($userinfo);
393         }
394         $output .= $this->definition_list($dl);
396         $output .= $this->output->heading(get_string('issuerdetails', 'badges'), 3);
397         $dl = array();
398         $dl[get_string('issuername', 'badges')] = $badge->issuername;
399         if (isset($badge->issuercontact) && !empty($badge->issuercontact)) {
400             $dl[get_string('contact', 'badges')] = obfuscate_mailto($badge->issuercontact);
401         }
402         $output .= $this->definition_list($dl);
404         $output .= $this->output->heading(get_string('badgedetails', 'badges'), 3);
405         $dl = array();
406         $dl[get_string('name')] = $badge->name;
407         if (!empty($badge->version)) {
408             $dl[get_string('version', 'badges')] = $badge->version;
409         }
410         if (!empty($badge->language)) {
411             $dl[get_string('language')] = $languages[$badge->language];
412         }
413         $dl[get_string('description', 'badges')] = $badge->description;
414         if (!empty($badge->imageauthorname)) {
415             $dl[get_string('imageauthorname', 'badges')] = $badge->imageauthorname;
416         }
417         if (!empty($badge->imageauthoremail)) {
418             $dl[get_string('imageauthoremail', 'badges')] =
419                     html_writer::tag('a', $badge->imageauthoremail, array('href' => 'mailto:' . $badge->imageauthoremail));
420         }
421         if (!empty($badge->imageauthorurl)) {
422             $dl[get_string('imageauthorurl', 'badges')] =
423                     html_writer::link($badge->imageauthorurl, $badge->imageauthorurl, array('target' => '_blank'));
424         }
425         if (!empty($badge->imagecaption)) {
426             $dl[get_string('imagecaption', 'badges')] = $badge->imagecaption;
427         }
429         if ($badge->type == BADGE_TYPE_COURSE && isset($badge->courseid)) {
430             $coursename = $DB->get_field('course', 'fullname', array('id' => $badge->courseid));
431             $dl[get_string('course')] = $coursename;
432         }
433         $dl[get_string('bcriteria', 'badges')] = self::print_badge_criteria($badge);
434         $output .= $this->definition_list($dl);
436         $output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
437         $dl = array();
438         if (!is_numeric($issued['issuedOn'])) {
439             $issued['issuedOn'] = strtotime($issued['issuedOn']);
440         }
441         $dl[get_string('dateawarded', 'badges')] = userdate($issued['issuedOn']);
442         if (isset($issued['expires'])) {
443             if ($issued['expires'] < $now) {
444                 $dl[get_string('expirydate', 'badges')] = userdate($issued['expires']) . get_string('warnexpired', 'badges');
446             } else {
447                 $dl[get_string('expirydate', 'badges')] = userdate($issued['expires']);
448             }
449         }
451         // Print evidence.
452         $agg = $badge->get_aggregation_methods();
453         $evidence = $badge->get_criteria_completions($userinfo->id);
454         $eids = array_map(function($o) {
455             return $o->critid;
456         }, $evidence);
457         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
459         $items = array();
460         foreach ($badge->criteria as $type => $c) {
461             if (in_array($c->id, $eids)) {
462                 if (count($c->params) == 1) {
463                     $items[] = get_string('criteria_descr_single_' . $type , 'badges') . $c->get_details();
464                 } else {
465                     $items[] = get_string('criteria_descr_' . $type , 'badges',
466                             core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details();
467                 }
468             }
469         }
471         $dl[get_string('evidence', 'badges')] = get_string('completioninfo', 'badges') . html_writer::alist($items, array(), 'ul');
472         $output .= $this->definition_list($dl);
473         $endorsement = $badge->get_endorsement();
474         if (!empty($endorsement)) {
475             $output .= self::print_badge_endorsement($badge);
476         }
478         $relatedbadges = $badge->get_related_badges(true);
479         $items = array();
480         foreach ($relatedbadges as $related) {
481             $relatedurl = new moodle_url('/badges/overview.php', array('id' => $related->id));
482             $items[] = html_writer::link($relatedurl->out(), $related->name, array('target' => '_blank'));
483         }
484         if (!empty($items)) {
485             $output .= $this->heading(get_string('relatedbages', 'badges'), 3);
486             $output .= html_writer::alist($items, array(), 'ul');
487         }
489         $alignments = $badge->get_alignments();
490         if (!empty($alignments)) {
491             $output .= $this->heading(get_string('alignment', 'badges'), 3);
492             $items = array();
493             foreach ($alignments as $alignment) {
494                 $items[] = html_writer::link($alignment->targeturl, $alignment->targetname, array('target' => '_blank'));
495             }
496             $output .= html_writer::alist($items, array(), 'ul');
497         }
498         $output .= html_writer::end_tag('div');
500         return $output;
501     }
503     /**
504      * Render an external badge.
505      *
506      * @param \core_badges\output\external_badge $ibadge
507      * @return string
508      */
509     protected function render_external_badge(\core_badges\output\external_badge $ibadge) {
510         $issued = $ibadge->issued;
511         $assertion = $issued->assertion;
512         $issuer = $assertion->badge->issuer;
513         $userinfo = $ibadge->recipient;
514         $table = new html_table();
515         $today = strtotime(date('Y-m-d'));
517         $output = '';
518         $output .= html_writer::start_tag('div', array('id' => 'badge'));
519         $output .= html_writer::start_tag('div', array('id' => 'badge-image'));
520         if (isset($issued->imageUrl)) {
521             $issued->image = $issued->imageUrl;
522         }
523         $output .= html_writer::empty_tag('img', array('src' => $issued->image, 'width' => '100'));
524         if (isset($assertion->expires)) {
525             $expiration = is_numeric($assertion->expires) ? $assertion->expires : strtotime($assertion->expires);
526             if ($expiration < $today) {
527                 $output .= $this->output->pix_icon('i/expired',
528                         get_string('expireddate', 'badges', userdate($expiration)),
529                         'moodle',
530                         array('class' => 'expireimage'));
531             }
532         }
533         $output .= html_writer::end_tag('div');
535         $output .= html_writer::start_tag('div', array('id' => 'badge-details'));
537         // Recipient information.
538         $output .= $this->output->heading(get_string('recipientdetails', 'badges'), 3);
539         $dl = array();
540         // Technically, we should alway have a user at this point, but added an extra check just in case.
541         if ($userinfo) {
542             if (!$ibadge->valid) {
543                 $notify = $this->output->notification(get_string('recipientvalidationproblem', 'badges'), 'notifynotice');
544                 $dl[get_string('name')] = fullname($userinfo) . $notify;
545             } else {
546                 $dl[get_string('name')] = fullname($userinfo);
547             }
548         } else {
549             $notify = $this->output->notification(get_string('recipientidentificationproblem', 'badges'), 'notifynotice');
550             $dl[get_string('name')] = $notify;
551         }
552         $output .= $this->definition_list($dl);
554         $output .= $this->output->heading(get_string('issuerdetails', 'badges'), 3);
555         $dl = array();
556         $dl[get_string('issuername', 'badges')] = s($issuer->name);
557         if (isset($issuer->origin)) {
558             $dl[get_string('issuerurl', 'badges')] = html_writer::tag('a', $issuer->origin, array('href' => $issuer->origin));
559         }
561         if (isset($issuer->contact)) {
562             $dl[get_string('contact', 'badges')] = obfuscate_mailto($issuer->contact);
563         }
564         $output .= $this->definition_list($dl);
566         $output .= $this->output->heading(get_string('badgedetails', 'badges'), 3);
567         $dl = array();
568         $dl[get_string('name')] = s($assertion->badge->name);
569         $dl[get_string('description', 'badges')] = s($assertion->badge->description);
570         if (isset($assertion->badge->criteria)) {
571             $dl[get_string('bcriteria', 'badges')] = html_writer::tag(
572                 'a',
573                 s($assertion->badge->criteria),
574                 array('href' => $assertion->badge->criteria)
575             );
576         }
577         $output .= $this->definition_list($dl);
579         $dl = array();
580         if (isset($assertion->issued_on)) {
581             $issuedate = is_numeric($assertion->issued_on) ? $assertion->issued_on : strtotime($assertion->issued_on);
582             $dl[get_string('dateawarded', 'badges')] = userdate($issuedate);
583         }
584         if (isset($assertion->expires)) {
585             if ($expiration < $today) {
586                 $dl[get_string('expirydate', 'badges')] = userdate($expiration) . get_string('warnexpired', 'badges');
587             } else {
588                 $dl[get_string('expirydate', 'badges')] = userdate($expiration);
589             }
590         }
591         if (isset($assertion->evidence)) {
592             $dl[get_string('evidence', 'badges')] = html_writer::tag(
593                 'a',
594                 s($assertion->evidence),
595                 array('href' => $assertion->evidence)
596             );
597         }
598         if (!empty($dl)) {
599             $output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
600             $output .= $this->definition_list($dl);
601         }
602         $output .= html_writer::end_tag('div');
604         return $output;
605     }
607     /**
608      * Render a collection of user badges.
609      *
610      * @param \core_badges\output\badge_user_collection $badges
611      * @return string
612      */
613     protected function render_badge_user_collection(\core_badges\output\badge_user_collection $badges) {
614         global $CFG, $USER, $SITE;
615         $backpack = $badges->backpack;
616         $mybackpack = new moodle_url('/badges/mybackpack.php');
618         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
619         $htmlpagingbar = $this->render($paging);
621         // Set backpack connection string.
622         $backpackconnect = '';
623         if (!empty($CFG->badges_allowexternalbackpack) && is_null($backpack)) {
624             $backpackconnect = $this->output->box(get_string('localconnectto', 'badges', $mybackpack->out()), 'noticebox');
625         }
626         // Search box.
627         $searchform = $this->output->box($this->helper_search_form($badges->search), 'boxwidthwide boxaligncenter');
629         // Download all button.
630         $actionhtml = $this->output->single_button(
631                     new moodle_url('/badges/mybadges.php', array('downloadall' => true, 'sesskey' => sesskey())),
632                     get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
633         $downloadall = $this->output->box('', 'col-md-3');
634         $downloadall .= $this->output->box($actionhtml, 'col-md-9');
635         $downloadall = $this->output->box($downloadall, 'row ml-5');
637         // Local badges.
638         $localhtml = html_writer::start_tag('div', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
639         $sitename = format_string($SITE->fullname, true, array('context' => context_system::instance()));
640         $heading = get_string('localbadges', 'badges', $sitename);
641         $localhtml .= $this->output->heading_with_help($heading, 'localbadgesh', 'badges');
642         if ($badges->badges) {
643             $countmessage = $this->output->box(get_string('badgesearned', 'badges', $badges->totalcount));
645             $htmllist = $this->print_badges_list($badges->badges, $USER->id);
646             $localhtml .= $backpackconnect . $countmessage . $searchform;
647             $localhtml .= $htmlpagingbar . $htmllist . $htmlpagingbar . $downloadall;
648         } else {
649             $localhtml .= $searchform . $this->output->notification(get_string('nobadges', 'badges'));
650         }
651         $localhtml .= html_writer::end_tag('div');
653         // External badges.
654         $externalhtml = "";
655         if (!empty($CFG->badges_allowexternalbackpack)) {
656             $externalhtml .= html_writer::start_tag('div', array('class' => 'generalbox'));
657             $externalhtml .= $this->output->heading_with_help(get_string('externalbadges', 'badges'), 'externalbadges', 'badges');
658             if (!is_null($backpack)) {
659                 if ($backpack->totalcollections == 0) {
660                     $externalhtml .= get_string('nobackpackcollectionssummary', 'badges', $backpack);
661                 } else {
662                     if ($backpack->totalbadges == 0) {
663                         $externalhtml .= get_string('nobackpackbadgessummary', 'badges', $backpack);
664                     } else {
665                         $externalhtml .= get_string('backpackbadgessummary', 'badges', $backpack);
666                         $externalhtml .= '<br/><br/>' . $this->print_badges_list($backpack->badges, $USER->id, true, true);
667                     }
668                 }
669             } else {
670                 $externalhtml .= get_string('externalconnectto', 'badges', $mybackpack->out());
671             }
673             $externalhtml .= html_writer::end_tag('div');
674             $attr = ['class' => 'btn btn-secondary'];
675             $label = get_string('backpackbadgessettings', 'badges');
676             $backpacksettings = html_writer::link(new moodle_url('/badges/mybackpack.php'), $label, $attr);
677             $actionshtml = $this->output->box('', 'col-md-3');
678             $actionshtml .= $this->output->box($backpacksettings, 'col-md-9');
679             $actionshtml = $this->output->box($actionshtml, 'row ml-5');
680             $externalhtml .= $actionshtml;
681         }
683         return $localhtml . $externalhtml;
684     }
686     /**
687      * Render a collection of badges.
688      *
689      * @param \core_badges\output\badge_collection $badges
690      * @return string
691      */
692     protected function render_badge_collection(\core_badges\output\badge_collection $badges) {
693         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
694         $htmlpagingbar = $this->render($paging);
695         $table = new html_table();
696         $table->attributes['class'] = 'table table-bordered table-striped';
698         $sortbyname = $this->helper_sortable_heading(get_string('name'),
699                 'name', $badges->sort, $badges->dir);
700         $sortbyawarded = $this->helper_sortable_heading(get_string('awardedtoyou', 'badges'),
701                 'dateissued', $badges->sort, $badges->dir);
702         $table->head = array(
703                     get_string('badgeimage', 'badges'),
704                     $sortbyname,
705                     get_string('description', 'badges'),
706                     get_string('bcriteria', 'badges'),
707                     $sortbyawarded
708                 );
709         $table->colclasses = array('badgeimage', 'name', 'description', 'criteria', 'awards');
711         foreach ($badges->badges as $badge) {
712             $badgeimage = print_badge_image($badge, $this->page->context, 'large');
713             $name = $badge->name;
714             $description = $badge->description;
715             $criteria = self::print_badge_criteria($badge);
716             if ($badge->dateissued) {
717                 $icon = new pix_icon('i/valid',
718                             get_string('dateearned', 'badges',
719                                 userdate($badge->dateissued, get_string('strftimedatefullshort', 'core_langconfig'))));
720                 $badgeurl = new moodle_url('/badges/badge.php', array('hash' => $badge->uniquehash));
721                 $awarded = $this->output->action_icon($badgeurl, $icon, null, null, true);
722             } else {
723                 $awarded = "";
724             }
725             $row = array($badgeimage, $name, $description, $criteria, $awarded);
726             $table->data[] = $row;
727         }
729         $htmltable = html_writer::table($table);
731         return $htmlpagingbar . $htmltable . $htmlpagingbar;
732     }
734     /**
735      * Render a table of badges.
736      *
737      * @param \core_badges\output\badge_management $badges
738      * @return string
739      */
740     protected function render_badge_management(\core_badges\output\badge_management $badges) {
741         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
743         // New badge button.
744         $htmlnew = '';
745         if (has_capability('moodle/badges:createbadge', $this->page->context)) {
746             $n['type'] = $this->page->url->get_param('type');
747             $n['id'] = $this->page->url->get_param('id');
748             $btn = $this->output->single_button(new moodle_url('newbadge.php', $n), get_string('newbadge', 'badges'));
749             $htmlnew = $this->output->box($btn);
750         }
752         $htmlpagingbar = $this->render($paging);
753         $table = new html_table();
754         $table->attributes['class'] = 'table table-bordered table-striped';
756         $sortbyname = $this->helper_sortable_heading(get_string('name'),
757                 'name', $badges->sort, $badges->dir);
758         $sortbystatus = $this->helper_sortable_heading(get_string('status', 'badges'),
759                 'status', $badges->sort, $badges->dir);
760         $table->head = array(
761                 $sortbyname,
762                 $sortbystatus,
763                 get_string('bcriteria', 'badges'),
764                 get_string('awards', 'badges'),
765                 get_string('actions')
766             );
767         $table->colclasses = array('name', 'status', 'criteria', 'awards', 'actions');
769         foreach ($badges->badges as $b) {
770             $style = !$b->is_active() ? array('class' => 'dimmed') : array();
771             $forlink =  print_badge_image($b, $this->page->context) . ' ' .
772                         html_writer::start_tag('span') . $b->name . html_writer::end_tag('span');
773             $name = html_writer::link(new moodle_url('/badges/overview.php', array('id' => $b->id)), $forlink, $style);
774             $status = $b->statstring;
775             $criteria = self::print_badge_criteria($b, 'short');
777             if (has_capability('moodle/badges:viewawarded', $this->page->context)) {
778                 $awards = html_writer::link(new moodle_url('/badges/recipients.php', array('id' => $b->id)), $b->awards);
779             } else {
780                 $awards = $b->awards;
781             }
783             $actions = self::print_badge_table_actions($b, $this->page->context);
785             $row = array($name, $status, $criteria, $awards, $actions);
786             $table->data[] = $row;
787         }
788         $htmltable = html_writer::table($table);
790         return $htmlnew . $htmlpagingbar . $htmltable . $htmlpagingbar;
791     }
793     /**
794      * Prints tabs for badge editing.
795      *
796      * @param integer $badgeid The badgeid to edit.
797      * @param context $context The current context.
798      * @param string $current The currently selected tab.
799      * @return string
800      */
801     public function print_badge_tabs($badgeid, $context, $current = 'overview') {
802         global $DB;
804         $badge = new badge($badgeid);
805         $row = array();
807         $row[] = new tabobject('overview',
808                     new moodle_url('/badges/overview.php', array('id' => $badgeid)),
809                     get_string('boverview', 'badges')
810                 );
812         if (has_capability('moodle/badges:configuredetails', $context)) {
813             $row[] = new tabobject('badge',
814                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'badge')),
815                         get_string('bdetails', 'badges')
816                     );
817         }
819         if (has_capability('moodle/badges:configurecriteria', $context)) {
820             $row[] = new tabobject('criteria',
821                         new moodle_url('/badges/criteria.php', array('id' => $badgeid)),
822                         get_string('bcriteria', 'badges')
823                     );
824         }
826         if (has_capability('moodle/badges:configuremessages', $context)) {
827             $row[] = new tabobject('message',
828                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'message')),
829                         get_string('bmessage', 'badges')
830                     );
831         }
833         if (has_capability('moodle/badges:viewawarded', $context)) {
834             $awarded = $DB->count_records_sql('SELECT COUNT(b.userid)
835                                                FROM {badge_issued} b INNER JOIN {user} u ON b.userid = u.id
836                                                WHERE b.badgeid = :badgeid AND u.deleted = 0', array('badgeid' => $badgeid));
837             $row[] = new tabobject('awards',
838                         new moodle_url('/badges/recipients.php', array('id' => $badgeid)),
839                         get_string('bawards', 'badges', $awarded)
840                     );
841         }
843         if (has_capability('moodle/badges:configuredetails', $context)) {
844             $row[] = new tabobject('bendorsement',
845                 new moodle_url('/badges/endorsement.php', array('id' => $badgeid)),
846                 get_string('bendorsement', 'badges')
847             );
848         }
850         if (has_capability('moodle/badges:configuredetails', $context)) {
851             $sql = "SELECT COUNT(br.badgeid)
852                       FROM {badge_related} br
853                      WHERE (br.badgeid = :badgeid OR br.relatedbadgeid = :badgeid2)";
854             $related = $DB->count_records_sql($sql, ['badgeid' => $badgeid, 'badgeid2' => $badgeid]);
855             $row[] = new tabobject('brelated',
856                 new moodle_url('/badges/related.php', array('id' => $badgeid)),
857                 get_string('brelated', 'badges', $related)
858             );
859         }
861         if (has_capability('moodle/badges:configuredetails', $context)) {
862             $alignments = $DB->count_records_sql("SELECT COUNT(bc.id)
863                       FROM {badge_alignment} bc WHERE bc.badgeid = :badgeid", array('badgeid' => $badgeid));
864             $row[] = new tabobject('alignment',
865                 new moodle_url('/badges/alignment.php', array('id' => $badgeid)),
866                 get_string('balignment', 'badges', $alignments)
867             );
868         }
870         echo $this->tabtree($row, $current);
871     }
873     /**
874      * Prints badge status box.
875      *
876      * @param badge $badge
877      * @return Either the status box html as a string or null
878      */
879     public function print_badge_status_box(badge $badge) {
880         if (has_capability('moodle/badges:configurecriteria', $badge->get_context())) {
882             if (!$badge->has_criteria()) {
883                 $criteriaurl = new moodle_url('/badges/criteria.php', array('id' => $badge->id));
884                 $status = get_string('nocriteria', 'badges');
885                 if ($this->page->url != $criteriaurl) {
886                     $action = $this->output->single_button(
887                         $criteriaurl,
888                         get_string('addcriteria', 'badges'), 'POST', array('class' => 'activatebadge'));
889                 } else {
890                     $action = '';
891                 }
893                 $message = $status . $action;
894             } else {
895                 $status = get_string('statusmessage_' . $badge->status, 'badges');
896                 if ($badge->is_active()) {
897                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
898                                 array('id' => $badge->id, 'lock' => 1, 'sesskey' => sesskey(),
899                                       'return' => $this->page->url->out_as_local_url(false))),
900                             get_string('deactivate', 'badges'), 'POST', array('class' => 'activatebadge'));
901                 } else {
902                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
903                                 array('id' => $badge->id, 'activate' => 1, 'sesskey' => sesskey(),
904                                       'return' => $this->page->url->out_as_local_url(false))),
905                             get_string('activate', 'badges'), 'POST', array('class' => 'activatebadge'));
906                 }
908                 $message = $status . $this->output->help_icon('status', 'badges') . $action;
910             }
912             $style = $badge->is_active() ? 'generalbox statusbox active' : 'generalbox statusbox inactive';
913             return $this->output->box($message, $style);
914         }
916         return null;
917     }
919     /**
920      * Returns information about badge criteria in a list form.
921      *
922      * @param badge $badge Badge objects
923      * @param string $short Indicates whether to print full info about this badge
924      * @return string $output HTML string to output
925      */
926     public function print_badge_criteria(badge $badge, $short = '') {
927         $agg = $badge->get_aggregation_methods();
928         if (empty($badge->criteria)) {
929             return get_string('nocriteria', 'badges');
930         }
932         $overalldescr = '';
933         $overall = $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL];
934         if (!$short && !empty($overall->description)) {
935             $overalldescr = $this->output->box(
936                 format_text($overall->description, $overall->descriptionformat, array('context' => $badge->get_context())),
937                 'criteria-description'
938                 );
939         }
941         // Get the condition string.
942         if (count($badge->criteria) == 2) {
943             $condition = '';
944             if (!$short) {
945                 $condition = get_string('criteria_descr', 'badges');
946             }
947         } else {
948             $condition = get_string('criteria_descr_' . $short . BADGE_CRITERIA_TYPE_OVERALL, 'badges',
949                                       core_text::strtoupper($agg[$badge->get_aggregation_method()]));
950         }
952         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
954         $items = array();
955         // If only one criterion left, make sure its description goe to the top.
956         if (count($badge->criteria) == 1) {
957             $c = reset($badge->criteria);
958             if (!$short && !empty($c->description)) {
959                 $overalldescr = $this->output->box(
960                     format_text($c->description, $c->descriptionformat, array('context' => $badge->get_context())),
961                     'criteria-description'
962                     );
963             }
964             if (count($c->params) == 1) {
965                 $items[] = get_string('criteria_descr_single_' . $short . $c->criteriatype , 'badges') .
966                            $c->get_details($short);
967             } else {
968                 $items[] = get_string('criteria_descr_' . $short . $c->criteriatype, 'badges',
969                         core_text::strtoupper($agg[$badge->get_aggregation_method($c->criteriatype)])) .
970                         $c->get_details($short);
971             }
972         } else {
973             foreach ($badge->criteria as $type => $c) {
974                 $criteriadescr = '';
975                 if (!$short && !empty($c->description)) {
976                     $criteriadescr = $this->output->box(
977                         format_text($c->description, $c->descriptionformat, array('context' => $badge->get_context())),
978                         'criteria-description'
979                         );
980                 }
981                 if (count($c->params) == 1) {
982                     $items[] = get_string('criteria_descr_single_' . $short . $type , 'badges') .
983                                $c->get_details($short) . $criteriadescr;
984                 } else {
985                     $items[] = get_string('criteria_descr_' . $short . $type , 'badges',
986                             core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) .
987                             $c->get_details($short) .
988                             $criteriadescr;
989                 }
990             }
991         }
993         return $overalldescr . $condition . html_writer::alist($items, array(), 'ul');;
994     }
996     /**
997      * Prints criteria actions for badge editing.
998      *
999      * @param badge $badge
1000      * @return string
1001      */
1002     public function print_criteria_actions(badge $badge) {
1003         $output = '';
1004         if (!$badge->is_active() && !$badge->is_locked()) {
1005             $accepted = $badge->get_accepted_criteria();
1006             $potential = array_diff($accepted, array_keys($badge->criteria));
1008             if (!empty($potential)) {
1009                 foreach ($potential as $p) {
1010                     if ($p != 0) {
1011                         $select[$p] = get_string('criteria_' . $p, 'badges');
1012                     }
1013                 }
1014                 $output .= $this->output->single_select(
1015                     new moodle_url('/badges/criteria_settings.php', array('badgeid' => $badge->id, 'add' => true)),
1016                     'type',
1017                     $select,
1018                     '',
1019                     array('' => 'choosedots'),
1020                     null,
1021                     array('label' => get_string('addbadgecriteria', 'badges'))
1022                 );
1023             } else {
1024                 $output .= $this->output->box(get_string('nothingtoadd', 'badges'), 'clearfix');
1025             }
1026         }
1028         return $output;
1029     }
1031     /**
1032      * Renders a table with users who have earned the badge.
1033      * Based on stamps collection plugin.
1034      *
1035      * @param \core_badges\output\badge_recipients $recipients
1036      * @return string
1037      */
1038     protected function render_badge_recipients(\core_badges\output\badge_recipients $recipients) {
1039         $paging = new paging_bar($recipients->totalcount, $recipients->page, $recipients->perpage, $this->page->url, 'page');
1040         $htmlpagingbar = $this->render($paging);
1041         $table = new html_table();
1042         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1044         $sortbyfirstname = $this->helper_sortable_heading(get_string('firstname'),
1045                 'firstname', $recipients->sort, $recipients->dir);
1046         $sortbylastname = $this->helper_sortable_heading(get_string('lastname'),
1047                 'lastname', $recipients->sort, $recipients->dir);
1048         if ($this->helper_fullname_format() == 'lf') {
1049             $sortbyname = $sortbylastname . ' / ' . $sortbyfirstname;
1050         } else {
1051             $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
1052         }
1054         $sortbydate = $this->helper_sortable_heading(get_string('dateawarded', 'badges'),
1055                 'dateissued', $recipients->sort, $recipients->dir);
1057         $table->head = array($sortbyname, $sortbydate, '');
1059         foreach ($recipients->userids as $holder) {
1060             $fullname = fullname($holder);
1061             $fullname = html_writer::link(
1062                             new moodle_url('/user/profile.php', array('id' => $holder->userid)),
1063                             $fullname
1064                         );
1065             $awarded  = userdate($holder->dateissued);
1066             $badgeurl = html_writer::link(
1067                             new moodle_url('/badges/badge.php', array('hash' => $holder->uniquehash)),
1068                             get_string('viewbadge', 'badges')
1069                         );
1071             $row = array($fullname, $awarded, $badgeurl);
1072             $table->data[] = $row;
1073         }
1075         $htmltable = html_writer::table($table);
1077         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1078     }
1080     ////////////////////////////////////////////////////////////////////////////
1081     // Helper methods
1082     // Reused from stamps collection plugin
1083     ////////////////////////////////////////////////////////////////////////////
1085     /**
1086      * Renders a text with icons to sort by the given column
1087      *
1088      * This is intended for table headings.
1089      *
1090      * @param string $text    The heading text
1091      * @param string $sortid  The column id used for sorting
1092      * @param string $sortby  Currently sorted by (column id)
1093      * @param string $sorthow Currently sorted how (ASC|DESC)
1094      *
1095      * @return string
1096      */
1097     protected function helper_sortable_heading($text, $sortid = null, $sortby = null, $sorthow = null) {
1098         $out = html_writer::tag('span', $text, array('class' => 'text'));
1100         if (!is_null($sortid)) {
1101             if ($sortby !== $sortid || $sorthow !== 'ASC') {
1102                 $url = new moodle_url($this->page->url);
1103                 $url->params(array('sort' => $sortid, 'dir' => 'ASC'));
1104                 $out .= $this->output->action_icon($url,
1105                         new pix_icon('t/sort_asc', get_string('sortbyx', 'core', s($text)), null, array('class' => 'iconsort')));
1106             }
1107             if ($sortby !== $sortid || $sorthow !== 'DESC') {
1108                 $url = new moodle_url($this->page->url);
1109                 $url->params(array('sort' => $sortid, 'dir' => 'DESC'));
1110                 $out .= $this->output->action_icon($url,
1111                         new pix_icon('t/sort_desc', get_string('sortbyxreverse', 'core', s($text)), null, array('class' => 'iconsort')));
1112             }
1113         }
1114         return $out;
1115     }
1116     /**
1117      * Tries to guess the fullname format set at the site
1118      *
1119      * @return string fl|lf
1120      */
1121     protected function helper_fullname_format() {
1122         $fake = new stdClass();
1123         $fake->lastname = 'LLLL';
1124         $fake->firstname = 'FFFF';
1125         $fullname = get_string('fullnamedisplay', '', $fake);
1126         if (strpos($fullname, 'LLLL') < strpos($fullname, 'FFFF')) {
1127             return 'lf';
1128         } else {
1129             return 'fl';
1130         }
1131     }
1132     /**
1133      * Renders a search form
1134      *
1135      * @param string $search Search string
1136      * @return string HTML
1137      */
1138     protected function helper_search_form($search) {
1139         global $CFG;
1140         require_once($CFG->libdir . '/formslib.php');
1142         $mform = new MoodleQuickForm('searchform', 'POST', $this->page->url);
1144         $mform->addElement('hidden', 'sesskey', sesskey());
1146         $el[] = $mform->createElement('text', 'search', get_string('search'), array('size' => 20));
1147         $mform->setDefault('search', $search);
1148         $el[] = $mform->createElement('submit', 'submitsearch', get_string('search'));
1149         $el[] = $mform->createElement('submit', 'clearsearch', get_string('clear'));
1150         $mform->addGroup($el, 'searchgroup', get_string('searchname', 'badges'), ' ', false);
1152         ob_start();
1153         $mform->display();
1154         $out = ob_get_clean();
1156         return $out;
1157     }
1159     /**
1160      * Renders a definition list
1161      *
1162      * @param array $items the list of items to define
1163      * @param array
1164      */
1165     protected function definition_list(array $items, array $attributes = array()) {
1166         $output = html_writer::start_tag('dl', $attributes);
1167         foreach ($items as $label => $value) {
1168             $output .= html_writer::tag('dt', $label);
1169             $output .= html_writer::tag('dd', $value);
1170         }
1171         $output .= html_writer::end_tag('dl');
1172         return $output;
1173     }
1175     /**
1176      * Outputs list en badges.
1177      *
1178      * @param badge $badge Badge object.
1179      * @return string $output content endorsement to output.
1180      */
1181     protected function print_badge_endorsement(badge $badge) {
1182         $output = '';
1183         $endorsement = $badge->get_endorsement();
1184         $dl = array();
1185         $output .= $this->heading(get_string('endorsement', 'badges'), 3);
1186         if (!empty($endorsement)) {
1187             $dl[get_string('issuername', 'badges')] = $endorsement->issuername;
1188             $dl[get_string('issueremail', 'badges')] =
1189                 html_writer::tag('a', $endorsement->issueremail, array('href' => 'mailto:' . $endorsement->issueremail));
1190             $dl[get_string('issuerurl', 'badges')] = html_writer::link($endorsement->issuerurl, $endorsement->issuerurl,
1191                 array('target' => '_blank'));
1192             $dl[get_string('dateawarded', 'badges')] = userdate($endorsement->dateissued);
1193             $dl[get_string('claimid', 'badges')] = html_writer::link($endorsement->claimid, $endorsement->claimid,
1194             array('target' => '_blank'));
1195             $dl[get_string('claimcomment', 'badges')] = $endorsement->claimcomment;
1196             $output .= $this->definition_list($dl);
1197         } else {
1198             $output .= get_string('noendorsement', 'badges');
1199         }
1200         return $output;
1201     }
1203     /**
1204      * Print list badges related.
1205      *
1206      * @param badge $badge Badge objects.
1207      * @return string $output List related badges to output.
1208      */
1209     protected function print_badge_related(badge $badge) {
1210         $output = '';
1211         $relatedbadges = $badge->get_related_badges();
1212         $output .= $this->heading(get_string('relatedbages', 'badges'), 3);
1213         if (!empty($relatedbadges)) {
1214             $items = array();
1215             foreach ($relatedbadges as $related) {
1216                 $relatedurl = new moodle_url('/badges/overview.php', array('id' => $related->id));
1217                 $items[] = html_writer::link($relatedurl->out(), $related->name, array('target' => '_blank'));
1218             }
1219             $output .= html_writer::alist($items, array(), 'ul');
1220         } else {
1221             $output .= get_string('norelated', 'badges');
1222         }
1223         return $output;
1224     }
1226     /**
1227      * Print list badge alignments.
1228      *
1229      * @param badge $badge Badge objects.
1230      * @return string $output List alignments to output.
1231      */
1232     protected function print_badge_alignments(badge $badge) {
1233         $output = '';
1234         $output .= $this->heading(get_string('alignment', 'badges'), 3);
1235         $alignments = $badge->get_alignments();
1236         if (!empty($alignments)) {
1237             $items = array();
1238             foreach ($alignments as $alignment) {
1239                 $urlaligment = new moodle_url('alignment.php',
1240                     array('id' => $badge->id, 'alignmentid' => $alignment->id)
1241                 );
1242                 $items[] = html_writer::link($urlaligment, $alignment->targetname, array('target' => '_blank'));
1243             }
1244             $output .= html_writer::alist($items, array(), 'ul');
1245         } else {
1246             $output .= get_string('noalignment', 'badges');
1247         }
1248         return $output;
1249     }
1251     /**
1252      * Renders a table for related badges.
1253      *
1254      * @param \core_badges\output\badge_related $related list related badges.
1255      * @return string list related badges to output.
1256      */
1257     protected function render_badge_related(\core_badges\output\badge_related $related) {
1258         $currentbadge = new badge($related->currentbadgeid);
1259         $languages = get_string_manager()->get_list_of_languages();
1260         $paging = new paging_bar($related->totalcount, $related->page, $related->perpage, $this->page->url, 'page');
1261         $htmlpagingbar = $this->render($paging);
1262         $table = new html_table();
1263         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1264         $table->head = array(
1265             get_string('name'),
1266             get_string('version', 'badges'),
1267             get_string('language', 'badges'),
1268             get_string('type', 'badges')
1269         );
1270         if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1271             array_push($table->head, '');
1272         }
1274         foreach ($related->badges as $badge) {
1275             $badgeobject = new badge($badge->id);
1276             $style = array('title' => $badgeobject->name);
1277             if (!$badgeobject->is_active()) {
1278                 $style['class'] = 'dimmed';
1279             }
1280             $context = ($badgeobject->type == BADGE_TYPE_SITE) ?
1281                 context_system::instance() : context_course::instance($badgeobject->courseid);
1282             $forlink = print_badge_image($badgeobject, $context) . ' ' .
1283                 html_writer::start_tag('span') . $badgeobject->name . html_writer::end_tag('span');
1284             $name = html_writer::link(new moodle_url('/badges/overview.php', array('id' => $badgeobject->id)), $forlink, $style);
1286             $row = array(
1287                 $name,
1288                 $badge->version,
1289                 $badge->language ? $languages[$badge->language] : '',
1290                 $badge->type == BADGE_TYPE_COURSE ? get_string('badgesview', 'badges') : get_string('sitebadges', 'badges')
1291             );
1292             if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1293                 $action = $this->output->action_icon(
1294                     new moodle_url('related_action.php',
1295                         array(
1296                             'badgeid' => $related->currentbadgeid,
1297                             'relatedid' => $badge->id,
1298                             'action' => 'remove'
1299                         )
1300                     ), new pix_icon('t/delete', get_string('delete')));
1301                 $actions = html_writer::tag('div', $action, array('class' => 'badge-actions'));
1302                 array_push($row, $actions);
1303             }
1304             $table->data[] = $row;
1305         }
1306         $htmltable = html_writer::table($table);
1308         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1309     }
1311     /**
1312      * Renders a table with alignment.
1313      *
1314      * @param core_badges\output\badge_alignments $alignments List alignments.
1315      * @return string List alignment to output.
1316      */
1317     protected function render_badge_alignments(\core_badges\output\badge_alignments $alignments) {
1318         $currentbadge = new badge($alignments->currentbadgeid);
1319         $paging = new paging_bar($alignments->totalcount, $alignments->page, $alignments->perpage, $this->page->url, 'page');
1320         $htmlpagingbar = $this->render($paging);
1321         $table = new html_table();
1322         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1323         $table->head = array('Name', 'URL', '');
1325         foreach ($alignments->alignments as $item) {
1326             $urlaligment = new moodle_url('alignment.php',
1327                 array(
1328                     'id' => $currentbadge->id,
1329                     'alignmentid' => $item->id,
1330                 )
1331             );
1332             $row = array(
1333                 html_writer::link($urlaligment, $item->targetname),
1334                 html_writer::link($item->targeturl, $item->targeturl, array('target' => '_blank'))
1335             );
1336             if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1337                 $delete = $this->output->action_icon(
1338                     new moodle_url('alignment_action.php',
1339                         array(
1340                             'id' => $currentbadge->id,
1341                             'alignmentid' => $item->id,
1342                             'action' => 'remove'
1343                         )
1344                     ), new pix_icon('t/delete', get_string('delete')));
1345                 $edit = $this->output->action_icon(
1346                     new moodle_url('alignment.php',
1347                         array(
1348                             'id' => $currentbadge->id,
1349                             'alignmentid' => $item->id,
1350                             'action' => 'edit'
1351                         )
1352                     ), new pix_icon('t/edit', get_string('edit')));
1353                 $actions = html_writer::tag('div', $edit . $delete, array('class' => 'badge-actions'));
1354                 array_push($row, $actions);
1355             }
1356             $table->data[] = $row;
1357         }
1358         $htmltable = html_writer::table($table);
1360         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1361     }
1363     /**
1364      * Defer to template.
1365      *
1366      * @param \core_badges\output\external_backpacks_page $page
1367      * @return bool|string
1368      */
1369     public function render_external_backpacks_page(\core_badges\output\external_backpacks_page $page) {
1370         $data = $page->export_for_template($this);
1371         return parent::render_from_template('core_badges/external_backpacks_page', $data);
1372     }
1374     /**
1375      * Get the result of a backpack validation with its settings. It returns:
1376      * - A informative message if the backpack version is different from OBv2.
1377      * - A warning with the error if it's not possible to connect to this backpack.
1378      * - A successful message if the connection has worked.
1379      *
1380      * @param  int    $backpackid The backpack identifier.
1381      * @return string A message with the validation result.
1382      */
1383     public function render_test_backpack_result(int $backpackid): string {
1384         // Get the backpack.
1385         $backpack = badges_get_site_backpack($backpackid);
1387         // Add the header to the result.
1388         $result = $this->heading(get_string('testbackpack', 'badges', $backpack->backpackweburl));
1390         if ($backpack->apiversion != OPEN_BADGES_V2) {
1391             // Only OBv2 supports this validation.
1392             $result .= get_string('backpackconnectionnottested', 'badges');
1393         } else {
1394             $message = badges_verify_backpack($backpackid);
1395             if (empty($message)) {
1396                 $result .= get_string('backpackconnectionok', 'badges');
1397             } else {
1398                 $result .= $message;
1399             }
1400         }
1402         return $result;
1403     }