MDL-65959 badge: Enable OB2.1 *connection* with cross domain ability
[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         $expiration = isset($issued['expires']) ? $issued['expires'] : $now + 86400;
326         $badgeimage = is_array($badgeclass['image']) ? $badgeclass['image']['id'] : $badgeclass['image'];
327         $languages = get_string_manager()->get_list_of_languages();
329         $output = '';
330         $output .= html_writer::start_tag('div', array('id' => 'badge'));
331         $output .= html_writer::start_tag('div', array('id' => 'badge-image'));
332         $output .= html_writer::empty_tag('img', array('src' => $badgeimage, 'alt' => $badge->name, 'width' => '100'));
333         if ($expiration < $now) {
334             $output .= $this->output->pix_icon('i/expired',
335             get_string('expireddate', 'badges', userdate($issued['expires'])),
336                 'moodle',
337                 array('class' => 'expireimage'));
338         }
340         if ($USER->id == $userinfo->id && !empty($CFG->enablebadges)) {
341             $output .= $this->output->single_button(
342                         new moodle_url('/badges/badge.php', array('hash' => $ibadge->hash, 'bake' => true)),
343                         get_string('download'),
344                         'POST');
345             if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now)
346                 && $userbackpack = badges_get_user_backpack($USER->id)) {
348                 if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V1) {
349                     $assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
350                     $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
351                     $attributes = array(
352                             'type'  => 'button',
353                             'class' => 'btn btn-secondary m-1',
354                             'id'    => 'addbutton',
355                             'value' => get_string('addtobackpack', 'badges'));
356                     $tobackpack = html_writer::tag('input', '', $attributes);
357                     $this->output->add_action_handler($action, 'addbutton');
358                     $output .= $tobackpack;
359                 } else {
360                     if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V2P1) {
361                         $assertion = new moodle_url('/badges/backpack-export.php', array('hash' => $ibadge->hash));
362                     } else {
363                         $assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
364                     }
365                     $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
366                     $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
367                     $output .= $tobackpack;
368                 }
369             }
370         }
371         $output .= html_writer::end_tag('div');
373         $output .= html_writer::start_tag('div', array('id' => 'badge-details'));
374         // Recipient information.
375         $output .= $this->output->heading(get_string('recipientdetails', 'badges'), 3);
376         $dl = array();
377         if ($userinfo->deleted) {
378             $strdata = new stdClass();
379             $strdata->user = fullname($userinfo);
380             $strdata->site = format_string($SITE->fullname, true, array('context' => context_system::instance()));
382             $dl[get_string('name')] = get_string('error:userdeleted', 'badges', $strdata);
383         } else {
384             $dl[get_string('name')] = fullname($userinfo);
385         }
386         $output .= $this->definition_list($dl);
388         $output .= $this->output->heading(get_string('issuerdetails', 'badges'), 3);
389         $dl = array();
390         $dl[get_string('issuername', 'badges')] = $badge->issuername;
391         if (isset($badge->issuercontact) && !empty($badge->issuercontact)) {
392             $dl[get_string('contact', 'badges')] = obfuscate_mailto($badge->issuercontact);
393         }
394         $output .= $this->definition_list($dl);
396         $output .= $this->output->heading(get_string('badgedetails', 'badges'), 3);
397         $dl = array();
398         $dl[get_string('name')] = $badge->name;
399         if (!empty($badge->version)) {
400             $dl[get_string('version', 'badges')] = $badge->version;
401         }
402         if (!empty($badge->language)) {
403             $dl[get_string('language')] = $languages[$badge->language];
404         }
405         $dl[get_string('description', 'badges')] = $badge->description;
406         if (!empty($badge->imageauthorname)) {
407             $dl[get_string('imageauthorname', 'badges')] = $badge->imageauthorname;
408         }
409         if (!empty($badge->imageauthoremail)) {
410             $dl[get_string('imageauthoremail', 'badges')] =
411                     html_writer::tag('a', $badge->imageauthoremail, array('href' => 'mailto:' . $badge->imageauthoremail));
412         }
413         if (!empty($badge->imageauthorurl)) {
414             $dl[get_string('imageauthorurl', 'badges')] =
415                     html_writer::link($badge->imageauthorurl, $badge->imageauthorurl, array('target' => '_blank'));
416         }
417         if (!empty($badge->imagecaption)) {
418             $dl[get_string('imagecaption', 'badges')] = $badge->imagecaption;
419         }
421         if ($badge->type == BADGE_TYPE_COURSE && isset($badge->courseid)) {
422             $coursename = $DB->get_field('course', 'fullname', array('id' => $badge->courseid));
423             $dl[get_string('course')] = $coursename;
424         }
425         $dl[get_string('bcriteria', 'badges')] = self::print_badge_criteria($badge);
426         $output .= $this->definition_list($dl);
428         $output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
429         $dl = array();
430         if (!is_numeric($issued['issuedOn'])) {
431             $issued['issuedOn'] = strtotime($issued['issuedOn']);
432         }
433         $dl[get_string('dateawarded', 'badges')] = userdate($issued['issuedOn']);
434         if (isset($issued['expires'])) {
435             if (!is_numeric($issued['expires'])) {
436                 $issued['expires'] = strtotime($issued['expires']);
437             }
438             if ($issued['expires'] < $now) {
439                 $dl[get_string('expirydate', 'badges')] = userdate($issued['expires']) . get_string('warnexpired', 'badges');
441             } else {
442                 $dl[get_string('expirydate', 'badges')] = userdate($issued['expires']);
443             }
444         }
446         // Print evidence.
447         $agg = $badge->get_aggregation_methods();
448         $evidence = $badge->get_criteria_completions($userinfo->id);
449         $eids = array_map(function($o) {
450             return $o->critid;
451         }, $evidence);
452         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
454         $items = array();
455         foreach ($badge->criteria as $type => $c) {
456             if (in_array($c->id, $eids)) {
457                 if (count($c->params) == 1) {
458                     $items[] = get_string('criteria_descr_single_' . $type , 'badges') . $c->get_details();
459                 } else {
460                     $items[] = get_string('criteria_descr_' . $type , 'badges',
461                             core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) . $c->get_details();
462                 }
463             }
464         }
466         $dl[get_string('evidence', 'badges')] = get_string('completioninfo', 'badges') . html_writer::alist($items, array(), 'ul');
467         $output .= $this->definition_list($dl);
468         $endorsement = $badge->get_endorsement();
469         if (!empty($endorsement)) {
470             $output .= self::print_badge_endorsement($badge);
471         }
473         $relatedbadges = $badge->get_related_badges(true);
474         $items = array();
475         foreach ($relatedbadges as $related) {
476             $relatedurl = new moodle_url('/badges/overview.php', array('id' => $related->id));
477             $items[] = html_writer::link($relatedurl->out(), $related->name, array('target' => '_blank'));
478         }
479         if (!empty($items)) {
480             $output .= $this->heading(get_string('relatedbages', 'badges'), 3);
481             $output .= html_writer::alist($items, array(), 'ul');
482         }
484         $alignments = $badge->get_alignments();
485         if (!empty($alignments)) {
486             $output .= $this->heading(get_string('alignment', 'badges'), 3);
487             $items = array();
488             foreach ($alignments as $alignment) {
489                 $items[] = html_writer::link($alignment->targeturl, $alignment->targetname, array('target' => '_blank'));
490             }
491             $output .= html_writer::alist($items, array(), 'ul');
492         }
493         $output .= html_writer::end_tag('div');
495         return $output;
496     }
498     /**
499      * Render an external badge.
500      *
501      * @param \core_badges\output\external_badge $ibadge
502      * @return string
503      */
504     protected function render_external_badge(\core_badges\output\external_badge $ibadge) {
505         $issued = $ibadge->issued;
506         $assertion = $issued->assertion;
507         $issuer = $assertion->badge->issuer;
508         $userinfo = $ibadge->recipient;
509         $table = new html_table();
510         $today = strtotime(date('Y-m-d'));
512         $output = '';
513         $output .= html_writer::start_tag('div', array('id' => 'badge'));
514         $output .= html_writer::start_tag('div', array('id' => 'badge-image'));
515         if (isset($issued->imageUrl)) {
516             $issued->image = $issued->imageUrl;
517         }
518         $output .= html_writer::empty_tag('img', array('src' => $issued->image, 'width' => '100'));
519         if (isset($assertion->expires)) {
520             $expiration = is_numeric($assertion->expires) ? $assertion->expires : strtotime($assertion->expires);
521             if ($expiration < $today) {
522                 $output .= $this->output->pix_icon('i/expired',
523                         get_string('expireddate', 'badges', userdate($expiration)),
524                         'moodle',
525                         array('class' => 'expireimage'));
526             }
527         }
528         $output .= html_writer::end_tag('div');
530         $output .= html_writer::start_tag('div', array('id' => 'badge-details'));
532         // Recipient information.
533         $output .= $this->output->heading(get_string('recipientdetails', 'badges'), 3);
534         $dl = array();
535         // Technically, we should alway have a user at this point, but added an extra check just in case.
536         if ($userinfo) {
537             if (!$ibadge->valid) {
538                 $notify = $this->output->notification(get_string('recipientvalidationproblem', 'badges'), 'notifynotice');
539                 $dl[get_string('name')] = fullname($userinfo) . $notify;
540             } else {
541                 $dl[get_string('name')] = fullname($userinfo);
542             }
543         } else {
544             $notify = $this->output->notification(get_string('recipientidentificationproblem', 'badges'), 'notifynotice');
545             $dl[get_string('name')] = $notify;
546         }
547         $output .= $this->definition_list($dl);
549         $output .= $this->output->heading(get_string('issuerdetails', 'badges'), 3);
550         $dl = array();
551         $dl[get_string('issuername', 'badges')] = s($issuer->name);
552         if (isset($issuer->origin)) {
553             $dl[get_string('issuerurl', 'badges')] = html_writer::tag('a', $issuer->origin, array('href' => $issuer->origin));
554         }
556         if (isset($issuer->contact)) {
557             $dl[get_string('contact', 'badges')] = obfuscate_mailto($issuer->contact);
558         }
559         $output .= $this->definition_list($dl);
561         $output .= $this->output->heading(get_string('badgedetails', 'badges'), 3);
562         $dl = array();
563         $dl[get_string('name')] = s($assertion->badge->name);
564         $dl[get_string('description', 'badges')] = s($assertion->badge->description);
565         if (isset($assertion->badge->criteria)) {
566             $dl[get_string('bcriteria', 'badges')] = html_writer::tag(
567                 'a',
568                 s($assertion->badge->criteria),
569                 array('href' => $assertion->badge->criteria)
570             );
571         }
572         $output .= $this->definition_list($dl);
574         $dl = array();
575         if (isset($assertion->issued_on)) {
576             $issuedate = is_numeric($assertion->issued_on) ? $assertion->issued_on : strtotime($assertion->issued_on);
577             $dl[get_string('dateawarded', 'badges')] = userdate($issuedate);
578         }
579         if (isset($assertion->expires)) {
580             if ($expiration < $today) {
581                 $dl[get_string('expirydate', 'badges')] = userdate($expiration) . get_string('warnexpired', 'badges');
582             } else {
583                 $dl[get_string('expirydate', 'badges')] = userdate($expiration);
584             }
585         }
586         if (isset($assertion->evidence)) {
587             $dl[get_string('evidence', 'badges')] = html_writer::tag(
588                 'a',
589                 s($assertion->evidence),
590                 array('href' => $assertion->evidence)
591             );
592         }
593         if (!empty($dl)) {
594             $output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
595             $output .= $this->definition_list($dl);
596         }
597         $output .= html_writer::end_tag('div');
599         return $output;
600     }
602     /**
603      * Render a collection of user badges.
604      *
605      * @param \core_badges\output\badge_user_collection $badges
606      * @return string
607      */
608     protected function render_badge_user_collection(\core_badges\output\badge_user_collection $badges) {
609         global $CFG, $USER, $SITE;
610         $backpack = $badges->backpack;
611         $mybackpack = new moodle_url('/badges/mybackpack.php');
613         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
614         $htmlpagingbar = $this->render($paging);
616         // Set backpack connection string.
617         $backpackconnect = '';
618         if (!empty($CFG->badges_allowexternalbackpack) && is_null($backpack)) {
619             $backpackconnect = $this->output->box(get_string('localconnectto', 'badges', $mybackpack->out()), 'noticebox');
620         }
621         // Search box.
622         $searchform = $this->output->box($this->helper_search_form($badges->search), 'boxwidthwide boxaligncenter');
624         // Download all button.
625         $actionhtml = $this->output->single_button(
626                     new moodle_url('/badges/mybadges.php', array('downloadall' => true, 'sesskey' => sesskey())),
627                     get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
628         $downloadall = $this->output->box('', 'col-md-3');
629         $downloadall .= $this->output->box($actionhtml, 'col-md-9');
630         $downloadall = $this->output->box($downloadall, 'row ml-5');
632         // Local badges.
633         $localhtml = html_writer::start_tag('div', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
634         $sitename = format_string($SITE->fullname, true, array('context' => context_system::instance()));
635         $heading = get_string('localbadges', 'badges', $sitename);
636         $localhtml .= $this->output->heading_with_help($heading, 'localbadgesh', 'badges');
637         if ($badges->badges) {
638             $countmessage = $this->output->box(get_string('badgesearned', 'badges', $badges->totalcount));
640             $htmllist = $this->print_badges_list($badges->badges, $USER->id);
641             $localhtml .= $backpackconnect . $countmessage . $searchform;
642             $localhtml .= $htmlpagingbar . $htmllist . $htmlpagingbar . $downloadall;
643         } else {
644             $localhtml .= $searchform . $this->output->notification(get_string('nobadges', 'badges'));
645         }
646         $localhtml .= html_writer::end_tag('div');
648         // External badges.
649         $externalhtml = "";
650         if (!empty($CFG->badges_allowexternalbackpack)) {
651             $externalhtml .= html_writer::start_tag('div', array('class' => 'generalbox'));
652             $externalhtml .= $this->output->heading_with_help(get_string('externalbadges', 'badges'), 'externalbadges', 'badges');
653             if (!is_null($backpack)) {
654                 if ($backpack->totalcollections == 0) {
655                     $externalhtml .= get_string('nobackpackcollectionssummary', 'badges', $backpack);
656                 } else {
657                     if ($backpack->totalbadges == 0) {
658                         $externalhtml .= get_string('nobackpackbadgessummary', 'badges', $backpack);
659                     } else {
660                         $externalhtml .= get_string('backpackbadgessummary', 'badges', $backpack);
661                         $externalhtml .= '<br/><br/>' . $this->print_badges_list($backpack->badges, $USER->id, true, true);
662                     }
663                 }
664             } else {
665                 $externalhtml .= get_string('externalconnectto', 'badges', $mybackpack->out());
666             }
668             $externalhtml .= html_writer::end_tag('div');
669             $attr = ['class' => 'btn btn-secondary'];
670             $label = get_string('backpackbadgessettings', 'badges');
671             $backpacksettings = html_writer::link(new moodle_url('/badges/mybackpack.php'), $label, $attr);
672             $actionshtml = $this->output->box('', 'col-md-3');
673             $actionshtml .= $this->output->box($backpacksettings, 'col-md-9');
674             $actionshtml = $this->output->box($actionshtml, 'row ml-5');
675             $externalhtml .= $actionshtml;
676         }
678         return $localhtml . $externalhtml;
679     }
681     /**
682      * Render a collection of badges.
683      *
684      * @param \core_badges\output\badge_collection $badges
685      * @return string
686      */
687     protected function render_badge_collection(\core_badges\output\badge_collection $badges) {
688         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
689         $htmlpagingbar = $this->render($paging);
690         $table = new html_table();
691         $table->attributes['class'] = 'table table-bordered table-striped';
693         $sortbyname = $this->helper_sortable_heading(get_string('name'),
694                 'name', $badges->sort, $badges->dir);
695         $sortbyawarded = $this->helper_sortable_heading(get_string('awardedtoyou', 'badges'),
696                 'dateissued', $badges->sort, $badges->dir);
697         $table->head = array(
698                     get_string('badgeimage', 'badges'),
699                     $sortbyname,
700                     get_string('description', 'badges'),
701                     get_string('bcriteria', 'badges'),
702                     $sortbyawarded
703                 );
704         $table->colclasses = array('badgeimage', 'name', 'description', 'criteria', 'awards');
706         foreach ($badges->badges as $badge) {
707             $badgeimage = print_badge_image($badge, $this->page->context, 'large');
708             $name = $badge->name;
709             $description = $badge->description;
710             $criteria = self::print_badge_criteria($badge);
711             if ($badge->dateissued) {
712                 $icon = new pix_icon('i/valid',
713                             get_string('dateearned', 'badges',
714                                 userdate($badge->dateissued, get_string('strftimedatefullshort', 'core_langconfig'))));
715                 $badgeurl = new moodle_url('/badges/badge.php', array('hash' => $badge->uniquehash));
716                 $awarded = $this->output->action_icon($badgeurl, $icon, null, null, true);
717             } else {
718                 $awarded = "";
719             }
720             $row = array($badgeimage, $name, $description, $criteria, $awarded);
721             $table->data[] = $row;
722         }
724         $htmltable = html_writer::table($table);
726         return $htmlpagingbar . $htmltable . $htmlpagingbar;
727     }
729     /**
730      * Render a table of badges.
731      *
732      * @param \core_badges\output\badge_management $badges
733      * @return string
734      */
735     protected function render_badge_management(\core_badges\output\badge_management $badges) {
736         $paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
738         // New badge button.
739         $htmlnew = '';
740         if (has_capability('moodle/badges:createbadge', $this->page->context)) {
741             $n['type'] = $this->page->url->get_param('type');
742             $n['id'] = $this->page->url->get_param('id');
743             $btn = $this->output->single_button(new moodle_url('newbadge.php', $n), get_string('newbadge', 'badges'));
744             $htmlnew = $this->output->box($btn);
745         }
747         $htmlpagingbar = $this->render($paging);
748         $table = new html_table();
749         $table->attributes['class'] = 'table table-bordered table-striped';
751         $sortbyname = $this->helper_sortable_heading(get_string('name'),
752                 'name', $badges->sort, $badges->dir);
753         $sortbystatus = $this->helper_sortable_heading(get_string('status', 'badges'),
754                 'status', $badges->sort, $badges->dir);
755         $table->head = array(
756                 $sortbyname,
757                 $sortbystatus,
758                 get_string('bcriteria', 'badges'),
759                 get_string('awards', 'badges'),
760                 get_string('actions')
761             );
762         $table->colclasses = array('name', 'status', 'criteria', 'awards', 'actions');
764         foreach ($badges->badges as $b) {
765             $style = !$b->is_active() ? array('class' => 'dimmed') : array();
766             $forlink =  print_badge_image($b, $this->page->context) . ' ' .
767                         html_writer::start_tag('span') . $b->name . html_writer::end_tag('span');
768             $name = html_writer::link(new moodle_url('/badges/overview.php', array('id' => $b->id)), $forlink, $style);
769             $status = $b->statstring;
770             $criteria = self::print_badge_criteria($b, 'short');
772             if (has_capability('moodle/badges:viewawarded', $this->page->context)) {
773                 $awards = html_writer::link(new moodle_url('/badges/recipients.php', array('id' => $b->id)), $b->awards);
774             } else {
775                 $awards = $b->awards;
776             }
778             $actions = self::print_badge_table_actions($b, $this->page->context);
780             $row = array($name, $status, $criteria, $awards, $actions);
781             $table->data[] = $row;
782         }
783         $htmltable = html_writer::table($table);
785         return $htmlnew . $htmlpagingbar . $htmltable . $htmlpagingbar;
786     }
788     /**
789      * Prints tabs for badge editing.
790      *
791      * @param integer $badgeid The badgeid to edit.
792      * @param context $context The current context.
793      * @param string $current The currently selected tab.
794      * @return string
795      */
796     public function print_badge_tabs($badgeid, $context, $current = 'overview') {
797         global $DB;
799         $badge = new badge($badgeid);
800         $row = array();
802         $row[] = new tabobject('overview',
803                     new moodle_url('/badges/overview.php', array('id' => $badgeid)),
804                     get_string('boverview', 'badges')
805                 );
807         if (has_capability('moodle/badges:configuredetails', $context)) {
808             $row[] = new tabobject('details',
809                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'badge')),
810                         get_string('bdetails', 'badges')
811                     );
812         }
814         if (has_capability('moodle/badges:configurecriteria', $context)) {
815             $row[] = new tabobject('criteria',
816                         new moodle_url('/badges/criteria.php', array('id' => $badgeid)),
817                         get_string('bcriteria', 'badges')
818                     );
819         }
821         if (has_capability('moodle/badges:configuremessages', $context)) {
822             $row[] = new tabobject('message',
823                         new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'message')),
824                         get_string('bmessage', 'badges')
825                     );
826         }
828         if (has_capability('moodle/badges:viewawarded', $context)) {
829             $awarded = $DB->count_records_sql('SELECT COUNT(b.userid)
830                                                FROM {badge_issued} b INNER JOIN {user} u ON b.userid = u.id
831                                                WHERE b.badgeid = :badgeid AND u.deleted = 0', array('badgeid' => $badgeid));
832             $row[] = new tabobject('awards',
833                         new moodle_url('/badges/recipients.php', array('id' => $badgeid)),
834                         get_string('bawards', 'badges', $awarded)
835                     );
836         }
838         if (has_capability('moodle/badges:configuredetails', $context)) {
839             $row[] = new tabobject('bendorsement',
840                 new moodle_url('/badges/endorsement.php', array('id' => $badgeid)),
841                 get_string('bendorsement', 'badges')
842             );
843         }
845         if (has_capability('moodle/badges:configuredetails', $context)) {
846             $sql = "SELECT COUNT(br.badgeid)
847                       FROM {badge_related} br
848                      WHERE (br.badgeid = :badgeid OR br.relatedbadgeid = :badgeid2)";
849             $related = $DB->count_records_sql($sql, ['badgeid' => $badgeid, 'badgeid2' => $badgeid]);
850             $row[] = new tabobject('brelated',
851                 new moodle_url('/badges/related.php', array('id' => $badgeid)),
852                 get_string('brelated', 'badges', $related)
853             );
854         }
856         if (has_capability('moodle/badges:configuredetails', $context)) {
857             $alignments = $DB->count_records_sql("SELECT COUNT(bc.id)
858                       FROM {badge_alignment} bc WHERE bc.badgeid = :badgeid", array('badgeid' => $badgeid));
859             $row[] = new tabobject('balignment',
860                 new moodle_url('/badges/alignment.php', array('id' => $badgeid)),
861                 get_string('balignment', 'badges', $alignments)
862             );
863         }
865         echo $this->tabtree($row, $current);
866     }
868     /**
869      * Prints badge status box.
870      *
871      * @param badge $badge
872      * @return Either the status box html as a string or null
873      */
874     public function print_badge_status_box(badge $badge) {
875         if (has_capability('moodle/badges:configurecriteria', $badge->get_context())) {
877             if (!$badge->has_criteria()) {
878                 $criteriaurl = new moodle_url('/badges/criteria.php', array('id' => $badge->id));
879                 $status = get_string('nocriteria', 'badges');
880                 if ($this->page->url != $criteriaurl) {
881                     $action = $this->output->single_button(
882                         $criteriaurl,
883                         get_string('addcriteria', 'badges'), 'POST', array('class' => 'activatebadge'));
884                 } else {
885                     $action = '';
886                 }
888                 $message = $status . $action;
889             } else {
890                 $status = get_string('statusmessage_' . $badge->status, 'badges');
891                 if ($badge->is_active()) {
892                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
893                                 array('id' => $badge->id, 'lock' => 1, 'sesskey' => sesskey(),
894                                       'return' => $this->page->url->out_as_local_url(false))),
895                             get_string('deactivate', 'badges'), 'POST', array('class' => 'activatebadge'));
896                 } else {
897                     $action = $this->output->single_button(new moodle_url('/badges/action.php',
898                                 array('id' => $badge->id, 'activate' => 1, 'sesskey' => sesskey(),
899                                       'return' => $this->page->url->out_as_local_url(false))),
900                             get_string('activate', 'badges'), 'POST', array('class' => 'activatebadge'));
901                 }
903                 $message = $status . $this->output->help_icon('status', 'badges') . $action;
905             }
907             $style = $badge->is_active() ? 'generalbox statusbox active' : 'generalbox statusbox inactive';
908             return $this->output->box($message, $style);
909         }
911         return null;
912     }
914     /**
915      * Returns information about badge criteria in a list form.
916      *
917      * @param badge $badge Badge objects
918      * @param string $short Indicates whether to print full info about this badge
919      * @return string $output HTML string to output
920      */
921     public function print_badge_criteria(badge $badge, $short = '') {
922         $agg = $badge->get_aggregation_methods();
923         if (empty($badge->criteria)) {
924             return get_string('nocriteria', 'badges');
925         }
927         $overalldescr = '';
928         $overall = $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL];
929         if (!$short && !empty($overall->description)) {
930             $overalldescr = $this->output->box(
931                 format_text($overall->description, $overall->descriptionformat, array('context' => $badge->get_context())),
932                 'criteria-description'
933                 );
934         }
936         // Get the condition string.
937         if (count($badge->criteria) == 2) {
938             $condition = '';
939             if (!$short) {
940                 $condition = get_string('criteria_descr', 'badges');
941             }
942         } else {
943             $condition = get_string('criteria_descr_' . $short . BADGE_CRITERIA_TYPE_OVERALL, 'badges',
944                                       core_text::strtoupper($agg[$badge->get_aggregation_method()]));
945         }
947         unset($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
949         $items = array();
950         // If only one criterion left, make sure its description goe to the top.
951         if (count($badge->criteria) == 1) {
952             $c = reset($badge->criteria);
953             if (!$short && !empty($c->description)) {
954                 $overalldescr = $this->output->box(
955                     format_text($c->description, $c->descriptionformat, array('context' => $badge->get_context())),
956                     'criteria-description'
957                     );
958             }
959             if (count($c->params) == 1) {
960                 $items[] = get_string('criteria_descr_single_' . $short . $c->criteriatype , 'badges') .
961                            $c->get_details($short);
962             } else {
963                 $items[] = get_string('criteria_descr_' . $short . $c->criteriatype, 'badges',
964                         core_text::strtoupper($agg[$badge->get_aggregation_method($c->criteriatype)])) .
965                         $c->get_details($short);
966             }
967         } else {
968             foreach ($badge->criteria as $type => $c) {
969                 $criteriadescr = '';
970                 if (!$short && !empty($c->description)) {
971                     $criteriadescr = $this->output->box(
972                         format_text($c->description, $c->descriptionformat, array('context' => $badge->get_context())),
973                         'criteria-description'
974                         );
975                 }
976                 if (count($c->params) == 1) {
977                     $items[] = get_string('criteria_descr_single_' . $short . $type , 'badges') .
978                                $c->get_details($short) . $criteriadescr;
979                 } else {
980                     $items[] = get_string('criteria_descr_' . $short . $type , 'badges',
981                             core_text::strtoupper($agg[$badge->get_aggregation_method($type)])) .
982                             $c->get_details($short) .
983                             $criteriadescr;
984                 }
985             }
986         }
988         return $overalldescr . $condition . html_writer::alist($items, array(), 'ul');;
989     }
991     /**
992      * Prints criteria actions for badge editing.
993      *
994      * @param badge $badge
995      * @return string
996      */
997     public function print_criteria_actions(badge $badge) {
998         $output = '';
999         if (!$badge->is_active() && !$badge->is_locked()) {
1000             $accepted = $badge->get_accepted_criteria();
1001             $potential = array_diff($accepted, array_keys($badge->criteria));
1003             if (!empty($potential)) {
1004                 foreach ($potential as $p) {
1005                     if ($p != 0) {
1006                         $select[$p] = get_string('criteria_' . $p, 'badges');
1007                     }
1008                 }
1009                 $output .= $this->output->single_select(
1010                     new moodle_url('/badges/criteria_settings.php', array('badgeid' => $badge->id, 'add' => true)),
1011                     'type',
1012                     $select,
1013                     '',
1014                     array('' => 'choosedots'),
1015                     null,
1016                     array('label' => get_string('addbadgecriteria', 'badges'))
1017                 );
1018             } else {
1019                 $output .= $this->output->box(get_string('nothingtoadd', 'badges'), 'clearfix');
1020             }
1021         }
1023         return $output;
1024     }
1026     /**
1027      * Renders a table with users who have earned the badge.
1028      * Based on stamps collection plugin.
1029      *
1030      * @param \core_badges\output\badge_recipients $recipients
1031      * @return string
1032      */
1033     protected function render_badge_recipients(\core_badges\output\badge_recipients $recipients) {
1034         $paging = new paging_bar($recipients->totalcount, $recipients->page, $recipients->perpage, $this->page->url, 'page');
1035         $htmlpagingbar = $this->render($paging);
1036         $table = new html_table();
1037         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1039         $sortbyfirstname = $this->helper_sortable_heading(get_string('firstname'),
1040                 'firstname', $recipients->sort, $recipients->dir);
1041         $sortbylastname = $this->helper_sortable_heading(get_string('lastname'),
1042                 'lastname', $recipients->sort, $recipients->dir);
1043         if ($this->helper_fullname_format() == 'lf') {
1044             $sortbyname = $sortbylastname . ' / ' . $sortbyfirstname;
1045         } else {
1046             $sortbyname = $sortbyfirstname . ' / ' . $sortbylastname;
1047         }
1049         $sortbydate = $this->helper_sortable_heading(get_string('dateawarded', 'badges'),
1050                 'dateissued', $recipients->sort, $recipients->dir);
1052         $table->head = array($sortbyname, $sortbydate, '');
1054         foreach ($recipients->userids as $holder) {
1055             $fullname = fullname($holder);
1056             $fullname = html_writer::link(
1057                             new moodle_url('/user/profile.php', array('id' => $holder->userid)),
1058                             $fullname
1059                         );
1060             $awarded  = userdate($holder->dateissued);
1061             $badgeurl = html_writer::link(
1062                             new moodle_url('/badges/badge.php', array('hash' => $holder->uniquehash)),
1063                             get_string('viewbadge', 'badges')
1064                         );
1066             $row = array($fullname, $awarded, $badgeurl);
1067             $table->data[] = $row;
1068         }
1070         $htmltable = html_writer::table($table);
1072         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1073     }
1075     ////////////////////////////////////////////////////////////////////////////
1076     // Helper methods
1077     // Reused from stamps collection plugin
1078     ////////////////////////////////////////////////////////////////////////////
1080     /**
1081      * Renders a text with icons to sort by the given column
1082      *
1083      * This is intended for table headings.
1084      *
1085      * @param string $text    The heading text
1086      * @param string $sortid  The column id used for sorting
1087      * @param string $sortby  Currently sorted by (column id)
1088      * @param string $sorthow Currently sorted how (ASC|DESC)
1089      *
1090      * @return string
1091      */
1092     protected function helper_sortable_heading($text, $sortid = null, $sortby = null, $sorthow = null) {
1093         $out = html_writer::tag('span', $text, array('class' => 'text'));
1095         if (!is_null($sortid)) {
1096             if ($sortby !== $sortid || $sorthow !== 'ASC') {
1097                 $url = new moodle_url($this->page->url);
1098                 $url->params(array('sort' => $sortid, 'dir' => 'ASC'));
1099                 $out .= $this->output->action_icon($url,
1100                         new pix_icon('t/sort_asc', get_string('sortbyx', 'core', s($text)), null, array('class' => 'iconsort')));
1101             }
1102             if ($sortby !== $sortid || $sorthow !== 'DESC') {
1103                 $url = new moodle_url($this->page->url);
1104                 $url->params(array('sort' => $sortid, 'dir' => 'DESC'));
1105                 $out .= $this->output->action_icon($url,
1106                         new pix_icon('t/sort_desc', get_string('sortbyxreverse', 'core', s($text)), null, array('class' => 'iconsort')));
1107             }
1108         }
1109         return $out;
1110     }
1111     /**
1112      * Tries to guess the fullname format set at the site
1113      *
1114      * @return string fl|lf
1115      */
1116     protected function helper_fullname_format() {
1117         $fake = new stdClass();
1118         $fake->lastname = 'LLLL';
1119         $fake->firstname = 'FFFF';
1120         $fullname = get_string('fullnamedisplay', '', $fake);
1121         if (strpos($fullname, 'LLLL') < strpos($fullname, 'FFFF')) {
1122             return 'lf';
1123         } else {
1124             return 'fl';
1125         }
1126     }
1127     /**
1128      * Renders a search form
1129      *
1130      * @param string $search Search string
1131      * @return string HTML
1132      */
1133     protected function helper_search_form($search) {
1134         global $CFG;
1135         require_once($CFG->libdir . '/formslib.php');
1137         $mform = new MoodleQuickForm('searchform', 'POST', $this->page->url);
1139         $mform->addElement('hidden', 'sesskey', sesskey());
1141         $el[] = $mform->createElement('text', 'search', get_string('search'), array('size' => 20));
1142         $mform->setDefault('search', $search);
1143         $el[] = $mform->createElement('submit', 'submitsearch', get_string('search'));
1144         $el[] = $mform->createElement('submit', 'clearsearch', get_string('clear'));
1145         $mform->addGroup($el, 'searchgroup', get_string('searchname', 'badges'), ' ', false);
1147         ob_start();
1148         $mform->display();
1149         $out = ob_get_clean();
1151         return $out;
1152     }
1154     /**
1155      * Renders a definition list
1156      *
1157      * @param array $items the list of items to define
1158      * @param array
1159      */
1160     protected function definition_list(array $items, array $attributes = array()) {
1161         $output = html_writer::start_tag('dl', $attributes);
1162         foreach ($items as $label => $value) {
1163             $output .= html_writer::tag('dt', $label);
1164             $output .= html_writer::tag('dd', $value);
1165         }
1166         $output .= html_writer::end_tag('dl');
1167         return $output;
1168     }
1170     /**
1171      * Outputs list en badges.
1172      *
1173      * @param badge $badge Badge object.
1174      * @return string $output content endorsement to output.
1175      */
1176     protected function print_badge_endorsement(badge $badge) {
1177         $output = '';
1178         $endorsement = $badge->get_endorsement();
1179         $dl = array();
1180         $output .= $this->heading(get_string('endorsement', 'badges'), 3);
1181         if (!empty($endorsement)) {
1182             $dl[get_string('issuername', 'badges')] = $endorsement->issuername;
1183             $dl[get_string('issueremail', 'badges')] =
1184                 html_writer::tag('a', $endorsement->issueremail, array('href' => 'mailto:' . $endorsement->issueremail));
1185             $dl[get_string('issuerurl', 'badges')] = html_writer::link($endorsement->issuerurl, $endorsement->issuerurl,
1186                 array('target' => '_blank'));
1187             $dl[get_string('dateawarded', 'badges')] = userdate($endorsement->dateissued);
1188             $dl[get_string('claimid', 'badges')] = html_writer::link($endorsement->claimid, $endorsement->claimid,
1189             array('target' => '_blank'));
1190             $dl[get_string('claimcomment', 'badges')] = $endorsement->claimcomment;
1191             $output .= $this->definition_list($dl);
1192         } else {
1193             $output .= get_string('noendorsement', 'badges');
1194         }
1195         return $output;
1196     }
1198     /**
1199      * Print list badges related.
1200      *
1201      * @param badge $badge Badge objects.
1202      * @return string $output List related badges to output.
1203      */
1204     protected function print_badge_related(badge $badge) {
1205         $output = '';
1206         $relatedbadges = $badge->get_related_badges();
1207         $output .= $this->heading(get_string('relatedbages', 'badges'), 3);
1208         if (!empty($relatedbadges)) {
1209             $items = array();
1210             foreach ($relatedbadges as $related) {
1211                 $relatedurl = new moodle_url('/badges/overview.php', array('id' => $related->id));
1212                 $items[] = html_writer::link($relatedurl->out(), $related->name, array('target' => '_blank'));
1213             }
1214             $output .= html_writer::alist($items, array(), 'ul');
1215         } else {
1216             $output .= get_string('norelated', 'badges');
1217         }
1218         return $output;
1219     }
1221     /**
1222      * Print list badge alignments.
1223      *
1224      * @param badge $badge Badge objects.
1225      * @return string $output List alignments to output.
1226      */
1227     protected function print_badge_alignments(badge $badge) {
1228         $output = '';
1229         $output .= $this->heading(get_string('alignment', 'badges'), 3);
1230         $alignments = $badge->get_alignments();
1231         if (!empty($alignments)) {
1232             $items = array();
1233             foreach ($alignments as $alignment) {
1234                 $urlaligment = new moodle_url('alignment.php',
1235                     array('id' => $badge->id, 'alignmentid' => $alignment->id)
1236                 );
1237                 $items[] = html_writer::link($urlaligment, $alignment->targetname, array('target' => '_blank'));
1238             }
1239             $output .= html_writer::alist($items, array(), 'ul');
1240         } else {
1241             $output .= get_string('noalignment', 'badges');
1242         }
1243         return $output;
1244     }
1246     /**
1247      * Renders a table for related badges.
1248      *
1249      * @param \core_badges\output\badge_related $related list related badges.
1250      * @return string list related badges to output.
1251      */
1252     protected function render_badge_related(\core_badges\output\badge_related $related) {
1253         $currentbadge = new badge($related->currentbadgeid);
1254         $languages = get_string_manager()->get_list_of_languages();
1255         $paging = new paging_bar($related->totalcount, $related->page, $related->perpage, $this->page->url, 'page');
1256         $htmlpagingbar = $this->render($paging);
1257         $table = new html_table();
1258         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1259         $table->head = array(
1260             get_string('name'),
1261             get_string('version', 'badges'),
1262             get_string('language', 'badges'),
1263             get_string('type', 'badges')
1264         );
1265         if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1266             array_push($table->head, '');
1267         }
1269         foreach ($related->badges as $badge) {
1270             $badgeobject = new badge($badge->id);
1271             $style = array('title' => $badgeobject->name);
1272             if (!$badgeobject->is_active()) {
1273                 $style['class'] = 'dimmed';
1274             }
1275             $context = ($badgeobject->type == BADGE_TYPE_SITE) ?
1276                 context_system::instance() : context_course::instance($badgeobject->courseid);
1277             $forlink = print_badge_image($badgeobject, $context) . ' ' .
1278                 html_writer::start_tag('span') . $badgeobject->name . html_writer::end_tag('span');
1279             $name = html_writer::link(new moodle_url('/badges/overview.php', array('id' => $badgeobject->id)), $forlink, $style);
1281             $row = array(
1282                 $name,
1283                 $badge->version,
1284                 $badge->language ? $languages[$badge->language] : '',
1285                 $badge->type == BADGE_TYPE_COURSE ? get_string('badgesview', 'badges') : get_string('sitebadges', 'badges')
1286             );
1287             if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1288                 $action = $this->output->action_icon(
1289                     new moodle_url('related_action.php',
1290                         array(
1291                             'badgeid' => $related->currentbadgeid,
1292                             'relatedid' => $badge->id,
1293                             'action' => 'remove'
1294                         )
1295                     ), new pix_icon('t/delete', get_string('delete')));
1296                 $actions = html_writer::tag('div', $action, array('class' => 'badge-actions'));
1297                 array_push($row, $actions);
1298             }
1299             $table->data[] = $row;
1300         }
1301         $htmltable = html_writer::table($table);
1303         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1304     }
1306     /**
1307      * Renders a table with alignment.
1308      *
1309      * @param core_badges\output\badge_alignments $alignments List alignments.
1310      * @return string List alignment to output.
1311      */
1312     protected function render_badge_alignments(\core_badges\output\badge_alignments $alignments) {
1313         $currentbadge = new badge($alignments->currentbadgeid);
1314         $paging = new paging_bar($alignments->totalcount, $alignments->page, $alignments->perpage, $this->page->url, 'page');
1315         $htmlpagingbar = $this->render($paging);
1316         $table = new html_table();
1317         $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide';
1318         $table->head = array('Name', 'URL', '');
1320         foreach ($alignments->alignments as $item) {
1321             $urlaligment = new moodle_url('alignment.php',
1322                 array(
1323                     'id' => $currentbadge->id,
1324                     'alignmentid' => $item->id,
1325                 )
1326             );
1327             $row = array(
1328                 html_writer::link($urlaligment, $item->targetname),
1329                 html_writer::link($item->targeturl, $item->targeturl, array('target' => '_blank'))
1330             );
1331             if (!$currentbadge->is_active() && !$currentbadge->is_locked()) {
1332                 $delete = $this->output->action_icon(
1333                     new moodle_url('alignment_action.php',
1334                         array(
1335                             'id' => $currentbadge->id,
1336                             'alignmentid' => $item->id,
1337                             'action' => 'remove'
1338                         )
1339                     ), new pix_icon('t/delete', get_string('delete')));
1340                 $edit = $this->output->action_icon(
1341                     new moodle_url('alignment.php',
1342                         array(
1343                             'id' => $currentbadge->id,
1344                             'alignmentid' => $item->id,
1345                             'action' => 'edit'
1346                         )
1347                     ), new pix_icon('t/edit', get_string('edit')));
1348                 $actions = html_writer::tag('div', $edit . $delete, array('class' => 'badge-actions'));
1349                 array_push($row, $actions);
1350             }
1351             $table->data[] = $row;
1352         }
1353         $htmltable = html_writer::table($table);
1355         return $htmlpagingbar . $htmltable . $htmlpagingbar;
1356     }
1358     /**
1359      * Defer to template.
1360      *
1361      * @param \core_badges\output\external_backpacks_page $page
1362      * @return bool|string
1363      */
1364     public function render_external_backpacks_page(\core_badges\output\external_backpacks_page $page) {
1365         $data = $page->export_for_template($this);
1366         return parent::render_from_template('core_badges/external_backpacks_page', $data);
1367     }
1369     /**
1370      * Get the result of a backpack validation with its settings. It returns:
1371      * - A informative message if the backpack version is different from OBv2.
1372      * - A warning with the error if it's not possible to connect to this backpack.
1373      * - A successful message if the connection has worked.
1374      *
1375      * @param  int    $backpackid The backpack identifier.
1376      * @return string A message with the validation result.
1377      */
1378     public function render_test_backpack_result(int $backpackid): string {
1379         // Get the backpack.
1380         $backpack = badges_get_site_backpack($backpackid);
1382         // Add the header to the result.
1383         $result = $this->heading(get_string('testbackpack', 'badges', $backpack->backpackweburl));
1385         if ($backpack->apiversion != OPEN_BADGES_V2) {
1386             // Only OBv2 supports this validation.
1387             $result .= get_string('backpackconnectionnottested', 'badges');
1388         } else {
1389             $message = badges_verify_backpack($backpackid);
1390             if (empty($message)) {
1391                 $result .= get_string('backpackconnectionok', 'badges');
1392             } else {
1393                 $result .= $message;
1394             }
1395         }
1397         return $result;
1398     }