Commit | Line | Data |
---|---|---|
27806552 YB |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * Contains classes, functions and constants used in badges. | |
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 | */ | |
26 | ||
27 | defined('MOODLE_INTERNAL') || die(); | |
28 | ||
29 | /* Include required award criteria library. */ | |
30 | require_once($CFG->dirroot . '/badges/criteria/award_criteria.php'); | |
31 | ||
32 | /* | |
33 | * Number of records per page. | |
34 | */ | |
35 | define('BADGE_PERPAGE', 50); | |
36 | ||
37 | /* | |
38 | * Badge award criteria aggregation method. | |
39 | */ | |
40 | define('BADGE_CRITERIA_AGGREGATION_ALL', 1); | |
41 | ||
42 | /* | |
43 | * Badge award criteria aggregation method. | |
44 | */ | |
45 | define('BADGE_CRITERIA_AGGREGATION_ANY', 2); | |
46 | ||
47 | /* | |
48 | * Inactive badge means that this badge cannot be earned and has not been awarded | |
49 | * yet. Its award criteria can be changed. | |
50 | */ | |
51 | define('BADGE_STATUS_INACTIVE', 0); | |
52 | ||
53 | /* | |
54 | * Active badge means that this badge can we earned, but it has not been awarded | |
55 | * yet. Can be deactivated for the purpose of changing its criteria. | |
56 | */ | |
57 | define('BADGE_STATUS_ACTIVE', 1); | |
58 | ||
59 | /* | |
60 | * Inactive badge can no longer be earned, but it has been awarded in the past and | |
61 | * therefore its criteria cannot be changed. | |
62 | */ | |
63 | define('BADGE_STATUS_INACTIVE_LOCKED', 2); | |
64 | ||
65 | /* | |
66 | * Active badge means that it can be earned and has already been awarded to users. | |
67 | * Its criteria cannot be changed any more. | |
68 | */ | |
69 | define('BADGE_STATUS_ACTIVE_LOCKED', 3); | |
70 | ||
71 | /* | |
72 | * Archived badge is considered deleted and can no longer be earned and is not | |
73 | * displayed in the list of all badges. | |
74 | */ | |
75 | define('BADGE_STATUS_ARCHIVED', 4); | |
76 | ||
77 | /* | |
78 | * Badge type for site badges. | |
79 | */ | |
80 | define('BADGE_TYPE_SITE', 1); | |
81 | ||
82 | /* | |
83 | * Badge type for course badges. | |
84 | */ | |
85 | define('BADGE_TYPE_COURSE', 2); | |
86 | ||
87 | /* | |
88 | * Badge messaging schedule options. | |
89 | */ | |
90 | define('BADGE_MESSAGE_NEVER', 0); | |
91 | define('BADGE_MESSAGE_ALWAYS', 1); | |
92 | define('BADGE_MESSAGE_DAILY', 2); | |
93 | define('BADGE_MESSAGE_WEEKLY', 3); | |
94 | define('BADGE_MESSAGE_MONTHLY', 4); | |
95 | ||
96 | /** | |
97 | * Class that represents badge. | |
98 | * | |
99 | */ | |
100 | class badge { | |
101 | /** @var int Badge id */ | |
102 | public $id; | |
103 | ||
104 | /** Values from the table 'badge' */ | |
105 | public $name; | |
106 | public $description; | |
107 | public $timecreated; | |
108 | public $timemodified; | |
109 | public $usercreated; | |
110 | public $usermodified; | |
27806552 YB |
111 | public $issuername; |
112 | public $issuerurl; | |
113 | public $issuercontact; | |
114 | public $expiredate; | |
115 | public $expireperiod; | |
116 | public $type; | |
117 | public $courseid; | |
118 | public $message; | |
119 | public $messagesubject; | |
120 | public $attachment; | |
121 | public $notification; | |
122 | public $status = 0; | |
123 | public $nextcron; | |
124 | ||
125 | /** @var array Badge criteria */ | |
126 | public $criteria = array(); | |
127 | ||
128 | /** | |
129 | * Constructs with badge details. | |
130 | * | |
131 | * @param int $badgeid badge ID. | |
132 | */ | |
133 | public function __construct($badgeid) { | |
134 | global $DB; | |
135 | $this->id = $badgeid; | |
136 | ||
137 | $data = $DB->get_record('badge', array('id' => $badgeid)); | |
138 | ||
139 | if (empty($data)) { | |
140 | print_error('error:nosuchbadge', 'badges', $badgeid); | |
141 | } | |
142 | ||
143 | foreach ((array)$data as $field => $value) { | |
144 | if (property_exists($this, $field)) { | |
145 | $this->{$field} = $value; | |
146 | } | |
147 | } | |
148 | ||
149 | $this->criteria = self::get_criteria(); | |
150 | } | |
151 | ||
152 | /** | |
153 | * Use to get context instance of a badge. | |
154 | * @return context instance. | |
155 | */ | |
156 | public function get_context() { | |
157 | if ($this->type == BADGE_TYPE_SITE) { | |
158 | return context_system::instance(); | |
159 | } else if ($this->type == BADGE_TYPE_COURSE) { | |
160 | return context_course::instance($this->courseid); | |
161 | } else { | |
162 | debugging('Something is wrong...'); | |
163 | } | |
164 | } | |
165 | ||
166 | /** | |
167 | * Return array of aggregation methods | |
168 | * @return array | |
169 | */ | |
170 | public static function get_aggregation_methods() { | |
171 | return array( | |
172 | BADGE_CRITERIA_AGGREGATION_ALL => get_string('all', 'badges'), | |
173 | BADGE_CRITERIA_AGGREGATION_ANY => get_string('any', 'badges'), | |
174 | ); | |
175 | } | |
176 | ||
177 | /** | |
178 | * Return array of accepted criteria types for this badge | |
179 | * @return array | |
180 | */ | |
181 | public function get_accepted_criteria() { | |
182 | $criteriatypes = array(); | |
183 | ||
184 | if ($this->type == BADGE_TYPE_COURSE) { | |
185 | $criteriatypes = array( | |
186 | BADGE_CRITERIA_TYPE_OVERALL, | |
187 | BADGE_CRITERIA_TYPE_MANUAL, | |
188 | BADGE_CRITERIA_TYPE_COURSE, | |
189 | BADGE_CRITERIA_TYPE_ACTIVITY | |
190 | ); | |
191 | } else if ($this->type == BADGE_TYPE_SITE) { | |
192 | $criteriatypes = array( | |
193 | BADGE_CRITERIA_TYPE_OVERALL, | |
194 | BADGE_CRITERIA_TYPE_MANUAL, | |
195 | BADGE_CRITERIA_TYPE_COURSESET, | |
196 | BADGE_CRITERIA_TYPE_PROFILE, | |
197 | ); | |
198 | } | |
199 | ||
200 | return $criteriatypes; | |
201 | } | |
202 | ||
203 | /** | |
204 | * Save/update badge information in 'badge' table only. | |
205 | * Cannot be used for updating awards and criteria settings. | |
206 | * | |
207 | * @return bool Returns true on success. | |
208 | */ | |
209 | public function save() { | |
210 | global $DB; | |
211 | ||
212 | $fordb = new stdClass(); | |
213 | foreach (get_object_vars($this) as $k => $v) { | |
214 | $fordb->{$k} = $v; | |
215 | } | |
216 | unset($fordb->criteria); | |
217 | ||
218 | $fordb->timemodified = time(); | |
219 | if ($DB->update_record_raw('badge', $fordb)) { | |
220 | return true; | |
221 | } else { | |
222 | throw new moodle_exception('error:save', 'badges'); | |
223 | return false; | |
224 | } | |
225 | } | |
226 | ||
227 | /** | |
228 | * Creates and saves a clone of badge with all its properties. | |
229 | * Clone is not active by default and has 'Copy of' attached to its name. | |
230 | * | |
231 | * @return int ID of new badge. | |
232 | */ | |
233 | public function make_clone() { | |
234 | global $DB, $USER; | |
235 | ||
236 | $fordb = new stdClass(); | |
237 | foreach (get_object_vars($this) as $k => $v) { | |
238 | $fordb->{$k} = $v; | |
239 | } | |
240 | ||
241 | $fordb->name = get_string('copyof', 'badges', $this->name); | |
242 | $fordb->status = BADGE_STATUS_INACTIVE; | |
27806552 YB |
243 | $fordb->usercreated = $USER->id; |
244 | $fordb->usermodified = $USER->id; | |
245 | $fordb->timecreated = time(); | |
246 | $fordb->timemodified = time(); | |
247 | unset($fordb->id); | |
248 | ||
249 | if ($fordb->notification > 1) { | |
250 | $fordb->nextcron = badges_calculate_message_schedule($fordb->notification); | |
251 | } | |
252 | ||
253 | $criteria = $fordb->criteria; | |
254 | unset($fordb->criteria); | |
255 | ||
256 | if ($new = $DB->insert_record('badge', $fordb, true)) { | |
257 | $newbadge = new badge($new); | |
258 | ||
259 | // Copy badge image. | |
260 | $fs = get_file_storage(); | |
261 | if ($file = $fs->get_file($this->get_context()->id, 'badges', 'badgeimage', $this->id, '/', 'f1.png')) { | |
262 | if ($imagefile = $file->copy_content_to_temp()) { | |
263 | badges_process_badge_image($newbadge, $imagefile); | |
264 | } | |
265 | } | |
266 | ||
267 | // Copy badge criteria. | |
268 | foreach ($this->criteria as $crit) { | |
269 | $crit->make_clone($new); | |
270 | } | |
271 | ||
272 | return $new; | |
273 | } else { | |
274 | throw new moodle_exception('error:clone', 'badges'); | |
275 | return false; | |
276 | } | |
277 | } | |
278 | ||
279 | /** | |
280 | * Checks if badges is active. | |
281 | * Used in badge award. | |
282 | * | |
283 | * @return bool A status indicating badge is active | |
284 | */ | |
285 | public function is_active() { | |
286 | if (($this->status == BADGE_STATUS_ACTIVE) || | |
287 | ($this->status == BADGE_STATUS_ACTIVE_LOCKED)) { | |
288 | return true; | |
289 | } | |
290 | return false; | |
291 | } | |
292 | ||
293 | /** | |
294 | * Use to get the name of badge status. | |
295 | * | |
296 | */ | |
297 | public function get_status_name() { | |
298 | return get_string('badgestatus_' . $this->status, 'badges'); | |
299 | } | |
300 | ||
301 | /** | |
302 | * Use to set badge status. | |
303 | * Only active badges can be earned/awarded/issued. | |
304 | * | |
305 | * @param int $status Status from BADGE_STATUS constants | |
306 | */ | |
307 | public function set_status($status = 0) { | |
308 | $this->status = $status; | |
309 | $this->save(); | |
310 | } | |
311 | ||
312 | /** | |
313 | * Checks if badges is locked. | |
314 | * Used in badge award and editing. | |
315 | * | |
316 | * @return bool A status indicating badge is locked | |
317 | */ | |
318 | public function is_locked() { | |
319 | if (($this->status == BADGE_STATUS_ACTIVE_LOCKED) || | |
320 | ($this->status == BADGE_STATUS_INACTIVE_LOCKED)) { | |
321 | return true; | |
322 | } | |
323 | return false; | |
324 | } | |
325 | ||
326 | /** | |
327 | * Checks if badge has been awarded to users. | |
328 | * Used in badge editing. | |
329 | * | |
330 | * @return bool A status indicating badge has been awarded at least once | |
331 | */ | |
332 | public function has_awards() { | |
333 | global $DB; | |
334 | if ($DB->record_exists('badge_issued', array('badgeid' => $this->id))) { | |
335 | return true; | |
336 | } | |
337 | return false; | |
338 | } | |
339 | ||
340 | /** | |
341 | * Gets list of users who have earned an instance of this badge. | |
342 | * | |
343 | * @return array An array of objects with information about badge awards. | |
344 | */ | |
345 | public function get_awards() { | |
346 | global $DB; | |
347 | ||
348 | $awards = $DB->get_records_sql( | |
349 | 'SELECT b.userid, b.dateissued, b.uniquehash, u.firstname, u.lastname | |
350 | FROM {badge_issued} b INNER JOIN {user} u | |
351 | ON b.userid = u.id | |
352 | WHERE b.badgeid = :badgeid', array('badgeid' => $this->id)); | |
353 | ||
354 | return $awards; | |
355 | } | |
356 | ||
357 | /** | |
358 | * Indicates whether badge has already been issued to a user. | |
359 | * | |
360 | */ | |
361 | public function is_issued($userid) { | |
362 | global $DB; | |
363 | return $DB->record_exists('badge_issued', array('badgeid' => $this->id, 'userid' => $userid)); | |
364 | } | |
365 | ||
366 | /** | |
367 | * Issue a badge to user. | |
368 | * | |
369 | * @param int $userid User who earned the badge | |
370 | * @param bool $nobake Not baking actual badges (for testing purposes) | |
371 | */ | |
372 | public function issue($userid, $nobake = false) { | |
373 | global $DB, $CFG; | |
374 | ||
375 | $now = time(); | |
376 | $issued = new stdClass(); | |
377 | $issued->badgeid = $this->id; | |
378 | $issued->userid = $userid; | |
379 | $issued->uniquehash = sha1(rand() . $userid . $this->id . $now); | |
380 | $issued->dateissued = $now; | |
381 | ||
382 | if ($this->can_expire()) { | |
383 | $issued->dateexpire = $this->calculate_expiry($now); | |
384 | } else { | |
385 | $issued->dateexpire = null; | |
386 | } | |
387 | ||
e2805314 YB |
388 | // Take into account user badges privacy settings. |
389 | // If none set, badges default visibility is set to public. | |
390 | $issued->visible = get_user_preferences('badgeprivacysetting', 1, $userid); | |
27806552 YB |
391 | |
392 | $result = $DB->insert_record('badge_issued', $issued, true); | |
393 | ||
394 | if ($result) { | |
395 | // Lock the badge, so that its criteria could not be changed any more. | |
396 | if ($this->status == BADGE_STATUS_ACTIVE) { | |
397 | $this->set_status(BADGE_STATUS_ACTIVE_LOCKED); | |
398 | } | |
399 | ||
400 | // Update details in criteria_met table. | |
401 | $compl = $this->get_criteria_completions($userid); | |
402 | foreach ($compl as $c) { | |
403 | $obj = new stdClass(); | |
404 | $obj->id = $c->id; | |
405 | $obj->issuedid = $result; | |
406 | $DB->update_record('badge_criteria_met', $obj, true); | |
407 | } | |
408 | ||
409 | if (!$nobake) { | |
410 | // Bake a badge image. | |
411 | $pathhash = badges_bake($issued->uniquehash, $this->id, $userid, true); | |
412 | ||
413 | // Notify recipients and badge creators. | |
414 | if (empty($CFG->noemailever)) { | |
415 | badges_notify_badge_award($this, $userid, $issued->uniquehash, $pathhash); | |
416 | } | |
417 | } | |
418 | } | |
419 | } | |
420 | ||
421 | /** | |
422 | * Reviews all badge criteria and checks if badge can be instantly awarded. | |
423 | * | |
424 | * @return int Number of awards | |
425 | */ | |
426 | public function review_all_criteria() { | |
427 | global $DB, $CFG; | |
428 | $awards = 0; | |
429 | ||
430 | // Raise timelimit as this could take a while for big web sites. | |
431 | set_time_limit(0); | |
432 | raise_memory_limit(MEMORY_HUGE); | |
433 | ||
434 | // For site level badges, get all active site users who can earn this badge and haven't got it yet. | |
435 | if ($this->type == BADGE_TYPE_SITE) { | |
436 | $sql = 'SELECT DISTINCT u.id, bi.badgeid | |
437 | FROM {user} u | |
438 | LEFT JOIN {badge_issued} bi | |
439 | ON u.id = bi.userid AND bi.badgeid = :badgeid | |
440 | WHERE bi.badgeid IS NULL AND u.id != :guestid AND u.deleted = 0'; | |
441 | $toearn = $DB->get_fieldset_sql($sql, array('badgeid' => $this->id, 'guestid' => $CFG->siteguest)); | |
442 | } else { | |
443 | // For course level badges, get users who can earn this badge in the course. | |
444 | // These are all enrolled users with capability moodle/badges:earnbadge. | |
445 | $earned = $DB->get_fieldset_select('badge_issued', 'userid AS id', 'badgeid = :badgeid', array('badgeid' => $this->id)); | |
446 | $users = get_enrolled_users($this->get_context(), 'moodle/badges:earnbadge', 0, 'u.id'); | |
447 | $toearn = array_diff(array_keys($users), $earned); | |
448 | } | |
449 | ||
450 | foreach ($toearn as $uid) { | |
451 | $toreview = false; | |
452 | foreach ($this->criteria as $crit) { | |
453 | if ($crit->criteriatype != BADGE_CRITERIA_TYPE_OVERALL) { | |
454 | if ($crit->review($uid)) { | |
455 | $crit->mark_complete($uid); | |
456 | if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) { | |
457 | $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid); | |
458 | $this->issue($uid); | |
459 | $awards++; | |
460 | break; | |
461 | } else { | |
462 | $toreview = true; | |
463 | continue; | |
464 | } | |
465 | } else { | |
466 | if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) { | |
467 | continue; | |
468 | } else { | |
469 | break; | |
470 | } | |
471 | } | |
472 | } | |
473 | } | |
474 | // Review overall if it is required. | |
475 | if ($toreview && $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($uid)) { | |
476 | $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid); | |
477 | $this->issue($uid); | |
478 | $awards++; | |
479 | } | |
480 | } | |
481 | ||
482 | return $awards; | |
483 | } | |
484 | ||
485 | /** | |
486 | * Gets an array of completed criteria from 'badge_criteria_met' table. | |
487 | * | |
488 | * @param int $userid Completions for a user | |
489 | * @return array Records of criteria completions | |
490 | */ | |
491 | public function get_criteria_completions($userid) { | |
492 | global $DB; | |
493 | $completions = array(); | |
494 | $sql = "SELECT bcm.id, bcm.critid | |
495 | FROM {badge_criteria_met} bcm | |
496 | INNER JOIN {badge_criteria} bc ON bcm.critid = bc.id | |
497 | WHERE bc.badgeid = :badgeid AND bcm.userid = :userid "; | |
498 | $completions = $DB->get_records_sql($sql, array('badgeid' => $this->id, 'userid' => $userid)); | |
499 | ||
500 | return $completions; | |
501 | } | |
502 | ||
503 | /** | |
504 | * Checks if badges has award criteria set up. | |
505 | * | |
506 | * @return bool A status indicating badge has at least one criterion | |
507 | */ | |
508 | public function has_criteria() { | |
509 | if (count($this->criteria) > 0) { | |
510 | return true; | |
511 | } | |
512 | return false; | |
513 | } | |
514 | ||
515 | /** | |
516 | * Returns badge award criteria | |
517 | * | |
518 | * @return array An array of badge criteria | |
519 | */ | |
520 | public function get_criteria() { | |
521 | global $DB; | |
522 | $criteria = array(); | |
523 | ||
524 | if ($records = (array)$DB->get_records('badge_criteria', array('badgeid' => $this->id))) { | |
525 | foreach ($records as $record) { | |
526 | $criteria[$record->criteriatype] = award_criteria::build((array)$record); | |
527 | } | |
528 | } | |
529 | ||
530 | return $criteria; | |
531 | } | |
532 | ||
533 | /** | |
534 | * Get aggregation method for badge criteria | |
535 | * | |
536 | * @param int $criteriatype If none supplied, get overall aggregation method (optional) | |
537 | * @return int One of BADGE_CRITERIA_AGGREGATION_ALL or BADGE_CRITERIA_AGGREGATION_ANY | |
538 | */ | |
539 | public function get_aggregation_method($criteriatype = 0) { | |
540 | global $DB; | |
541 | $params = array('badgeid' => $this->id, 'criteriatype' => $criteriatype); | |
542 | $aggregation = $DB->get_field('badge_criteria', 'method', $params, IGNORE_MULTIPLE); | |
543 | ||
544 | if (!$aggregation) { | |
545 | return BADGE_CRITERIA_AGGREGATION_ALL; | |
546 | } | |
547 | ||
548 | return $aggregation; | |
549 | } | |
550 | ||
551 | /** | |
552 | * Checks if badge has expiry period or date set up. | |
553 | * | |
554 | * @return bool A status indicating badge can expire | |
555 | */ | |
556 | public function can_expire() { | |
557 | if ($this->expireperiod || $this->expiredate) { | |
558 | return true; | |
559 | } | |
560 | return false; | |
561 | } | |
562 | ||
563 | /** | |
564 | * Calculates badge expiry date based on either expirydate or expiryperiod. | |
565 | * | |
566 | * @param int $timestamp Time of badge issue | |
567 | * @return int A timestamp | |
568 | */ | |
569 | public function calculate_expiry($timestamp) { | |
570 | $expiry = null; | |
571 | ||
572 | if (isset($this->expiredate)) { | |
573 | $expiry = $this->expiredate; | |
574 | } else if (isset($this->expireperiod)) { | |
575 | $expiry = $timestamp + $this->expireperiod; | |
576 | } | |
577 | ||
578 | return $expiry; | |
579 | } | |
580 | ||
581 | /** | |
582 | * Checks if badge has manual award criteria set. | |
583 | * | |
584 | * @return bool A status indicating badge can be awarded manually | |
585 | */ | |
586 | public function has_manual_award_criteria() { | |
587 | foreach ($this->criteria as $criterion) { | |
588 | if ($criterion->criteriatype == BADGE_CRITERIA_TYPE_MANUAL) { | |
589 | return true; | |
590 | } | |
591 | } | |
592 | return false; | |
593 | } | |
594 | ||
595 | /** | |
596 | * Marks the badge as archived. | |
597 | * For reporting and historical purposed we cannot completely delete badges. | |
598 | * We will just change their status to BADGE_STATUS_ARCHIVED. | |
599 | */ | |
600 | public function delete() { | |
601 | $this->status = BADGE_STATUS_ARCHIVED; | |
602 | $this->save(); | |
603 | } | |
604 | } | |
605 | ||
606 | /** | |
607 | * Sends notifications to users about awarded badges. | |
608 | * | |
609 | * @param badge $badge Badge that was issued | |
610 | * @param int $userid Recipient ID | |
611 | * @param string $issued Unique hash of an issued badge | |
612 | * @param string $filepathhash File path hash of an issued badge for attachments | |
613 | */ | |
614 | function badges_notify_badge_award(badge $badge, $userid, $issued, $filepathhash) { | |
615 | global $CFG, $DB; | |
616 | ||
617 | $admin = get_admin(); | |
618 | $userfrom = new stdClass(); | |
619 | $userfrom->id = $admin->id; | |
620 | $userfrom->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : $admin->email; | |
a327f25e AG |
621 | foreach (get_all_user_name_fields() as $addname) { |
622 | $userfrom->$addname = !empty($CFG->badges_defaultissuername) ? '' : $admin->$addname; | |
623 | } | |
27806552 | 624 | $userfrom->firstname = !empty($CFG->badges_defaultissuername) ? $CFG->badges_defaultissuername : $admin->firstname; |
27806552 YB |
625 | $userfrom->maildisplay = true; |
626 | ||
627 | $issuedlink = html_writer::link(new moodle_url('/badges/badge.php', array('hash' => $issued)), $badge->name); | |
628 | $userto = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); | |
629 | ||
630 | $params = new stdClass(); | |
631 | $params->badgename = $badge->name; | |
632 | $params->username = fullname($userto); | |
633 | $params->badgelink = $issuedlink; | |
634 | $message = badge_message_from_template($badge->message, $params); | |
635 | $plaintext = format_text_email($message, FORMAT_HTML); | |
636 | ||
637 | if ($badge->attachment && $filepathhash) { | |
638 | $fs = get_file_storage(); | |
639 | $file = $fs->get_file_by_hash($filepathhash); | |
640 | $attachment = $file->copy_content_to_temp(); | |
641 | email_to_user($userto, | |
642 | $userfrom, | |
643 | $badge->messagesubject, | |
644 | $plaintext, | |
645 | $message, | |
646 | str_replace($CFG->dataroot, '', $attachment), | |
647 | str_replace(' ', '_', $badge->name) . ".png" | |
648 | ); | |
649 | @unlink($attachment); | |
650 | } else { | |
651 | email_to_user($userto, $userfrom, $badge->messagesubject, $plaintext, $message); | |
652 | } | |
653 | ||
654 | // Notify badge creator about the award if they receive notifications every time. | |
655 | if ($badge->notification == 1) { | |
656 | $creator = $DB->get_record('user', array('id' => $badge->usercreated), '*', MUST_EXIST); | |
657 | $a = new stdClass(); | |
658 | $a->user = fullname($userto); | |
659 | $a->link = $issuedlink; | |
660 | $creatormessage = get_string('creatorbody', 'badges', $a); | |
661 | $creatorsubject = get_string('creatorsubject', 'badges', $badge->name); | |
662 | ||
663 | $eventdata = new stdClass(); | |
664 | $eventdata->component = 'moodle'; | |
665 | $eventdata->name = 'instantmessage'; | |
666 | $eventdata->userfrom = $userfrom; | |
667 | $eventdata->userto = $creator; | |
668 | $eventdata->notification = 1; | |
669 | $eventdata->subject = $creatorsubject; | |
670 | $eventdata->fullmessage = $creatormessage; | |
671 | $eventdata->fullmessageformat = FORMAT_PLAIN; | |
672 | $eventdata->fullmessagehtml = format_text($creatormessage, FORMAT_HTML); | |
673 | $eventdata->smallmessage = ''; | |
674 | ||
675 | message_send($eventdata); | |
676 | $DB->set_field('badge_issued', 'issuernotified', time(), array('badgeid' => $badge->id, 'userid' => $userid)); | |
677 | } | |
678 | } | |
679 | ||
680 | /** | |
681 | * Caclulates date for the next message digest to badge creators. | |
682 | * | |
683 | * @param in $schedule Type of message schedule BADGE_MESSAGE_DAILY|BADGE_MESSAGE_WEEKLY|BADGE_MESSAGE_MONTHLY. | |
684 | * @return int Timestamp for next cron | |
685 | */ | |
686 | function badges_calculate_message_schedule($schedule) { | |
687 | $nextcron = 0; | |
688 | ||
689 | switch ($schedule) { | |
690 | case BADGE_MESSAGE_DAILY: | |
691 | $nextcron = time() + 60 * 60 * 24; | |
692 | break; | |
693 | case BADGE_MESSAGE_WEEKLY: | |
694 | $nextcron = time() + 60 * 60 * 24 * 7; | |
695 | break; | |
696 | case BADGE_MESSAGE_MONTHLY: | |
697 | $nextcron = time() + 60 * 60 * 24 * 7 * 30; | |
698 | break; | |
699 | } | |
700 | ||
701 | return $nextcron; | |
702 | } | |
703 | ||
704 | /** | |
705 | * Replaces variables in a message template and returns text ready to be emailed to a user. | |
706 | * | |
707 | * @param string $message Message body. | |
708 | * @return string Message with replaced values | |
709 | */ | |
710 | function badge_message_from_template($message, $params) { | |
711 | $msg = $message; | |
712 | foreach ($params as $key => $value) { | |
713 | $msg = str_replace("%$key%", $value, $msg); | |
714 | } | |
715 | ||
716 | return $msg; | |
717 | } | |
718 | ||
719 | /** | |
720 | * Get all badges. | |
721 | * | |
722 | * @param int Type of badges to return | |
723 | * @param int Course ID for course badges | |
724 | * @param string $sort An SQL field to sort by | |
725 | * @param string $dir The sort direction ASC|DESC | |
726 | * @param int $page The page or records to return | |
727 | * @param int $perpage The number of records to return per page | |
728 | * @param int $user User specific search | |
729 | * @return array $badge Array of records matching criteria | |
730 | */ | |
731 | function badges_get_badges($type, $courseid = 0, $sort = '', $dir = '', $page = 0, $perpage = BADGE_PERPAGE, $user = 0) { | |
732 | global $DB; | |
733 | $records = array(); | |
734 | $params = array(); | |
735 | $where = "b.status != :deleted AND b.type = :type "; | |
736 | $params['deleted'] = BADGE_STATUS_ARCHIVED; | |
737 | ||
738 | $userfields = array('b.id, b.name, b.status'); | |
739 | $usersql = ""; | |
740 | if ($user != 0) { | |
741 | $userfields[] = 'bi.dateissued'; | |
742 | $userfields[] = 'bi.uniquehash'; | |
743 | $usersql = " LEFT JOIN {badge_issued} bi ON b.id = bi.badgeid AND bi.userid = :userid "; | |
744 | $params['userid'] = $user; | |
745 | $where .= " AND (b.status = 1 OR b.status = 3) "; | |
746 | } | |
747 | $fields = implode(', ', $userfields); | |
748 | ||
749 | if ($courseid != 0 ) { | |
750 | $where .= "AND b.courseid = :courseid "; | |
751 | $params['courseid'] = $courseid; | |
752 | } | |
753 | ||
754 | $sorting = (($sort != '' && $dir != '') ? 'ORDER BY ' . $sort . ' ' . $dir : ''); | |
755 | $params['type'] = $type; | |
756 | ||
757 | $sql = "SELECT $fields FROM {badge} b $usersql WHERE $where $sorting"; | |
758 | $records = $DB->get_records_sql($sql, $params, $page * $perpage, $perpage); | |
759 | ||
760 | $badges = array(); | |
761 | foreach ($records as $r) { | |
762 | $badge = new badge($r->id); | |
763 | $badges[$r->id] = $badge; | |
764 | if ($user != 0) { | |
765 | $badges[$r->id]->dateissued = $r->dateissued; | |
766 | $badges[$r->id]->uniquehash = $r->uniquehash; | |
767 | } else { | |
768 | $badges[$r->id]->awards = $DB->count_records('badge_issued', array('badgeid' => $badge->id)); | |
769 | $badges[$r->id]->statstring = $badge->get_status_name(); | |
770 | } | |
771 | } | |
772 | return $badges; | |
773 | } | |
774 | ||
775 | /** | |
776 | * Get badges for a specific user. | |
777 | * | |
778 | * @param int $userid User ID | |
779 | * @param int $courseid Badges earned by a user in a specific course | |
780 | * @param int $page The page or records to return | |
781 | * @param int $perpage The number of records to return per page | |
782 | * @param string $search A simple string to search for | |
783 | * @param bool $onlypublic Return only public badges | |
784 | * @return array of badges ordered by decreasing date of issue | |
785 | */ | |
786 | function badges_get_user_badges($userid, $courseid = 0, $page = 0, $perpage = 0, $search = '', $onlypublic = false) { | |
787 | global $DB; | |
788 | $badges = array(); | |
789 | ||
790 | $params[] = $userid; | |
791 | $sql = 'SELECT | |
792 | bi.uniquehash, | |
793 | bi.dateissued, | |
794 | bi.dateexpire, | |
795 | bi.id as issuedid, | |
796 | bi.visible, | |
797 | u.email, | |
798 | b.* | |
799 | FROM | |
800 | {badge} b, | |
801 | {badge_issued} bi, | |
802 | {user} u | |
803 | WHERE b.id = bi.badgeid | |
804 | AND u.id = bi.userid | |
805 | AND bi.userid = ?'; | |
806 | ||
807 | if (!empty($search)) { | |
808 | $sql .= ' AND (' . $DB->sql_like('b.name', '?', false) . ') '; | |
809 | $params[] = "%$search%"; | |
810 | } | |
811 | if ($onlypublic) { | |
812 | $sql .= ' AND (bi.visible = 1) '; | |
813 | } | |
814 | ||
815 | if ($courseid != 0) { | |
816 | $sql .= ' AND (b.courseid = ' . $courseid . ') '; | |
817 | } | |
818 | $sql .= ' ORDER BY bi.dateissued DESC'; | |
819 | $badges = $DB->get_records_sql($sql, $params, $page * $perpage, $perpage); | |
820 | ||
821 | return $badges; | |
822 | } | |
823 | ||
824 | /** | |
825 | * Get issued badge details for assertion URL | |
826 | * | |
827 | * @param string $hash | |
828 | */ | |
829 | function badges_get_issued_badge_info($hash) { | |
830 | global $DB, $CFG; | |
831 | ||
832 | $a = array(); | |
833 | ||
834 | $record = $DB->get_record_sql(' | |
835 | SELECT | |
836 | bi.dateissued, | |
837 | bi.dateexpire, | |
838 | u.email, | |
7e06ea6a YB |
839 | b.*, |
840 | bb.email as backpackemail | |
27806552 | 841 | FROM |
7e06ea6a YB |
842 | {badge} b |
843 | JOIN {badge_issued} bi | |
844 | ON b.id = bi.badgeid | |
845 | JOIN {user} u | |
846 | ON u.id = bi.userid | |
847 | LEFT JOIN {badge_backpack} bb | |
848 | ON bb.userid = bi.userid | |
849 | WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40), | |
27806552 YB |
850 | array('hash' => $hash), IGNORE_MISSING); |
851 | ||
852 | if ($record) { | |
853 | if ($record->type == BADGE_TYPE_SITE) { | |
854 | $context = context_system::instance(); | |
855 | } else { | |
856 | $context = context_course::instance($record->courseid); | |
857 | } | |
858 | ||
859 | $url = new moodle_url('/badges/badge.php', array('hash' => $hash)); | |
7e06ea6a | 860 | $email = empty($record->backpackemail) ? $record->email : $record->backpackemail; |
27806552 YB |
861 | |
862 | // Recipient's email is hashed: <algorithm>$<hash(email + salt)>. | |
7e06ea6a YB |
863 | $a['recipient'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt); |
864 | $a['salt'] = $CFG->badges_badgesalt; | |
27806552 YB |
865 | |
866 | if ($record->dateexpire) { | |
867 | $a['expires'] = date('Y-m-d', $record->dateexpire); | |
868 | } | |
869 | ||
870 | $a['issued_on'] = date('Y-m-d', $record->dateissued); | |
871 | $a['evidence'] = $url->out(); // Issued badge URL. | |
872 | $a['badge'] = array(); | |
873 | $a['badge']['version'] = '0.5.0'; // Version of OBI specification, 0.5.0 - current beta. | |
874 | $a['badge']['name'] = $record->name; | |
875 | $a['badge']['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $record->id, '/', 'f1')->out(); | |
876 | $a['badge']['description'] = $record->description; | |
877 | $a['badge']['criteria'] = $url->out(); // Issued badge URL. | |
878 | $a['badge']['issuer'] = array(); | |
879 | $a['badge']['issuer']['origin'] = $record->issuerurl; | |
880 | $a['badge']['issuer']['name'] = $record->issuername; | |
881 | $a['badge']['issuer']['contact'] = $record->issuercontact; | |
882 | } | |
883 | ||
884 | return $a; | |
885 | } | |
886 | ||
887 | /** | |
888 | * Extends the course administration navigation with the Badges page | |
889 | * | |
890 | * @param navigation_node $coursenode | |
891 | * @param object $course | |
892 | */ | |
893 | function badges_add_course_navigation(navigation_node $coursenode, stdClass $course) { | |
894 | global $CFG, $SITE; | |
895 | ||
896 | $coursecontext = context_course::instance($course->id); | |
897 | $isfrontpage = (!$coursecontext || $course->id == $SITE->id); | |
898 | ||
60d72efb | 899 | if (!empty($CFG->enablebadges) && !empty($CFG->badges_allowcoursebadges) && !$isfrontpage) { |
27806552 YB |
900 | if (has_capability('moodle/badges:configuredetails', $coursecontext)) { |
901 | $coursenode->add(get_string('coursebadges', 'badges'), null, | |
902 | navigation_node::TYPE_CONTAINER, null, 'coursebadges', | |
903 | new pix_icon('i/badge', get_string('coursebadges', 'badges'))); | |
904 | ||
905 | if (has_capability('moodle/badges:viewawarded', $coursecontext)) { | |
19a9f2ea | 906 | $url = new moodle_url('/badges/index.php', |
27806552 YB |
907 | array('type' => BADGE_TYPE_COURSE, 'id' => $course->id)); |
908 | ||
909 | $coursenode->get('coursebadges')->add(get_string('managebadges', 'badges'), $url, | |
910 | navigation_node::TYPE_SETTING, null, 'coursebadges'); | |
911 | } | |
912 | ||
913 | if (has_capability('moodle/badges:createbadge', $coursecontext)) { | |
19a9f2ea | 914 | $url = new moodle_url('/badges/newbadge.php', |
27806552 YB |
915 | array('type' => BADGE_TYPE_COURSE, 'id' => $course->id)); |
916 | ||
917 | $coursenode->get('coursebadges')->add(get_string('newbadge', 'badges'), $url, | |
918 | navigation_node::TYPE_SETTING, null, 'newbadge'); | |
919 | } | |
19a9f2ea YB |
920 | } else if (has_capability('moodle/badges:awardbadge', $coursecontext)) { |
921 | $coursenode->add(get_string('coursebadges', 'badges'), null, | |
922 | navigation_node::TYPE_CONTAINER, null, 'coursebadges', | |
923 | new pix_icon('i/badge', get_string('coursebadges', 'badges'))); | |
924 | ||
925 | $url = new moodle_url('/badges/index.php', | |
926 | array('type' => BADGE_TYPE_COURSE, 'id' => $course->id)); | |
927 | ||
928 | $coursenode->get('coursebadges')->add(get_string('managebadges', 'badges'), $url, | |
929 | navigation_node::TYPE_SETTING, null, 'coursebadges'); | |
27806552 YB |
930 | } |
931 | } | |
932 | } | |
933 | ||
27806552 YB |
934 | /** |
935 | * Triggered when 'user_updated' event happens. | |
936 | * | |
937 | * @param object $eventdata Holds all information about a user. | |
938 | * @return boolean | |
939 | */ | |
940 | function badges_award_handle_profile_criteria_review(stdClass $eventdata) { | |
941 | global $DB, $CFG; | |
942 | ||
943 | if (!empty($CFG->enablebadges)) { | |
944 | $userid = $eventdata->id; | |
945 | ||
946 | if ($rs = $DB->get_records('badge_criteria', array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE))) { | |
947 | foreach ($rs as $r) { | |
948 | $badge = new badge($r->badgeid); | |
949 | if (!$badge->is_active() || $badge->is_issued($userid)) { | |
950 | continue; | |
951 | } | |
952 | ||
953 | if ($badge->criteria[BADGE_CRITERIA_TYPE_PROFILE]->review($userid)) { | |
954 | $badge->criteria[BADGE_CRITERIA_TYPE_PROFILE]->mark_complete($userid); | |
955 | ||
956 | if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { | |
957 | $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); | |
958 | $badge->issue($userid); | |
959 | } | |
960 | } | |
961 | } | |
962 | } | |
963 | } | |
964 | ||
965 | return true; | |
966 | } | |
967 | ||
968 | /** | |
969 | * Triggered when badge is manually awarded. | |
970 | * | |
971 | * @param object $data | |
972 | * @return boolean | |
973 | */ | |
974 | function badges_award_handle_manual_criteria_review(stdClass $data) { | |
975 | $criteria = $data->crit; | |
976 | $userid = $data->userid; | |
977 | $badge = new badge($criteria->badgeid); | |
978 | ||
979 | if (!$badge->is_active() || $badge->is_issued($userid)) { | |
980 | return true; | |
981 | } | |
982 | ||
983 | if ($criteria->review($userid)) { | |
984 | $criteria->mark_complete($userid); | |
985 | ||
986 | if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { | |
987 | $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); | |
988 | $badge->issue($userid); | |
989 | } | |
990 | } | |
991 | ||
992 | return true; | |
993 | } | |
994 | ||
995 | /** | |
996 | * Process badge image from form data | |
997 | * | |
998 | * @param badge $badge Badge object | |
999 | * @param string $iconfile Original file | |
1000 | */ | |
1001 | function badges_process_badge_image(badge $badge, $iconfile) { | |
1002 | global $CFG, $USER; | |
1003 | require_once($CFG->libdir. '/gdlib.php'); | |
1004 | ||
1005 | if (!empty($CFG->gdversion)) { | |
e50220f8 | 1006 | process_new_icon($badge->get_context(), 'badges', 'badgeimage', $badge->id, $iconfile); |
27806552 YB |
1007 | @unlink($iconfile); |
1008 | ||
1009 | // Clean up file draft area after badge image has been saved. | |
1010 | $context = context_user::instance($USER->id, MUST_EXIST); | |
1011 | $fs = get_file_storage(); | |
1012 | $fs->delete_area_files($context->id, 'user', 'draft'); | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | /** | |
1017 | * Print badge image. | |
1018 | * | |
1019 | * @param badge $badge Badge object | |
1020 | * @param stdClass $context | |
1021 | * @param string $size | |
1022 | */ | |
1023 | function print_badge_image(badge $badge, stdClass $context, $size = 'small') { | |
1024 | $fsize = ($size == 'small') ? 'f2' : 'f1'; | |
1025 | ||
1026 | $imageurl = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', $fsize, false); | |
1027 | // Appending a random parameter to image link to forse browser reload the image. | |
b143259e YB |
1028 | $imageurl->param('refresh', rand(1, 10000)); |
1029 | $attributes = array('src' => $imageurl, 'alt' => s($badge->name), 'class' => 'activatebadge'); | |
27806552 YB |
1030 | |
1031 | return html_writer::empty_tag('img', $attributes); | |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * Bake issued badge. | |
1036 | * | |
1037 | * @param string $hash Unique hash of an issued badge. | |
1038 | * @param int $badgeid ID of the original badge. | |
1039 | * @param int $userid ID of badge recipient (optional). | |
1040 | * @param boolean $pathhash Return file pathhash instead of image url (optional). | |
1041 | * @return string|url Returns either new file path hash or new file URL | |
1042 | */ | |
1043 | function badges_bake($hash, $badgeid, $userid = 0, $pathhash = false) { | |
1044 | global $CFG, $USER; | |
1045 | require_once(dirname(dirname(__FILE__)) . '/badges/lib/bakerlib.php'); | |
1046 | ||
1047 | $badge = new badge($badgeid); | |
1048 | $badge_context = $badge->get_context(); | |
1049 | $userid = ($userid) ? $userid : $USER->id; | |
1050 | $user_context = context_user::instance($userid); | |
1051 | ||
1052 | $fs = get_file_storage(); | |
1053 | if (!$fs->file_exists($user_context->id, 'badges', 'userbadge', $badge->id, '/', $hash . '.png')) { | |
1054 | if ($file = $fs->get_file($badge_context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1.png')) { | |
1055 | $contents = $file->get_content(); | |
1056 | ||
1057 | $filehandler = new PNG_MetaDataHandler($contents); | |
1058 | $assertion = new moodle_url('/badges/assertion.php', array('b' => $hash)); | |
1059 | if ($filehandler->check_chunks("tEXt", "openbadges")) { | |
1060 | // Add assertion URL tExt chunk. | |
1061 | $newcontents = $filehandler->add_chunks("tEXt", "openbadges", $assertion->out(false)); | |
1062 | $fileinfo = array( | |
1063 | 'contextid' => $user_context->id, | |
1064 | 'component' => 'badges', | |
1065 | 'filearea' => 'userbadge', | |
1066 | 'itemid' => $badge->id, | |
1067 | 'filepath' => '/', | |
1068 | 'filename' => $hash . '.png', | |
1069 | ); | |
1070 | ||
1071 | // Create a file with added contents. | |
1072 | $newfile = $fs->create_file_from_string($fileinfo, $newcontents); | |
1073 | if ($pathhash) { | |
1074 | return $newfile->get_pathnamehash(); | |
1075 | } | |
1076 | } | |
1077 | } else { | |
1078 | debugging('Error baking badge image!'); | |
1079 | } | |
1080 | } | |
1081 | ||
1082 | $fileurl = moodle_url::make_pluginfile_url($user_context->id, 'badges', 'userbadge', $badge->id, '/', $hash, true); | |
1083 | return $fileurl; | |
1084 | } | |
1085 | ||
1086 | /** | |
e2805314 | 1087 | * Returns external backpack settings and badges from this backpack. |
27806552 YB |
1088 | * |
1089 | * @param int $userid Backpack user ID. | |
1090 | * @return null|object Returns null is there is no backpack or object with backpack settings. | |
1091 | */ | |
1092 | function get_backpack_settings($userid) { | |
1093 | global $DB; | |
1094 | require_once(dirname(dirname(__FILE__)) . '/badges/lib/backpacklib.php'); | |
1095 | ||
e2805314 | 1096 | $record = $DB->get_record('badge_backpack', array('userid' => $userid)); |
27806552 YB |
1097 | if ($record) { |
1098 | $backpack = new OpenBadgesBackpackHandler($record); | |
1099 | $out = new stdClass(); | |
1100 | $out->backpackurl = $backpack->get_url(); | |
e2805314 YB |
1101 | |
1102 | if ($collections = $DB->get_records('badge_external', array('backpackid' => $record->id))) { | |
1103 | $out->totalcollections = count($collections); | |
1104 | $out->totalbadges = 0; | |
1105 | $out->badges = array(); | |
1106 | foreach ($collections as $collection) { | |
1107 | $badges = $backpack->get_badges($collection->collectionid); | |
1108 | if (isset($badges->badges)) { | |
1109 | $out->badges = array_merge($out->badges, $badges->badges); | |
1110 | $out->totalbadges += count($out->badges); | |
1111 | } else { | |
1112 | $out->badges = array_merge($out->badges, array()); | |
1113 | } | |
1114 | } | |
1115 | } else { | |
1116 | $out->totalbadges = 0; | |
1117 | $out->totalcollections = 0; | |
1118 | } | |
1119 | ||
27806552 YB |
1120 | return $out; |
1121 | } | |
1122 | ||
1123 | return null; | |
1124 | } | |
1125 | ||
1126 | /** | |
1127 | * Download all user badges in zip archive. | |
1128 | * | |
1129 | * @param int $userid ID of badge owner. | |
1130 | */ | |
1131 | function badges_download($userid) { | |
1132 | global $CFG, $DB; | |
1133 | $context = context_user::instance($userid); | |
1134 | $records = $DB->get_records('badge_issued', array('userid' => $userid)); | |
1135 | ||
1136 | // Get list of files to download. | |
1137 | $fs = get_file_storage(); | |
1138 | $filelist = array(); | |
1139 | foreach ($records as $issued) { | |
1140 | $badge = new badge($issued->badgeid); | |
1141 | // Need to make image name user-readable and unique using filename safe characters. | |
1142 | $name = $badge->name . ' ' . userdate($issued->dateissued, '%d %b %Y') . ' ' . hash('crc32', $badge->id); | |
1143 | $name = str_replace(' ', '_', $name); | |
1144 | if ($file = $fs->get_file($context->id, 'badges', 'userbadge', $issued->badgeid, '/', $issued->uniquehash . '.png')) { | |
1145 | $filelist[$name . '.png'] = $file; | |
1146 | } | |
1147 | } | |
1148 | ||
1149 | // Zip files and sent them to a user. | |
1150 | $tempzip = tempnam($CFG->tempdir.'/', 'mybadges'); | |
1151 | $zipper = new zip_packer(); | |
1152 | if ($zipper->archive_to_pathname($filelist, $tempzip)) { | |
1153 | send_temp_file($tempzip, 'badges.zip'); | |
1154 | } else { | |
1155 | debugging("Problems with archiving the files."); | |
1156 | } | |
1157 | } | |
1158 | ||
1159 | /** | |
1160 | * Print badges on user profile page. | |
1161 | * | |
1162 | * @param int $userid User ID. | |
1163 | * @param int $courseid Course if we need to filter badges (optional). | |
1164 | */ | |
1165 | function profile_display_badges($userid, $courseid = 0) { | |
1166 | global $CFG, $PAGE, $USER, $SITE; | |
1167 | require_once($CFG->dirroot . '/badges/renderer.php'); | |
1168 | ||
1169 | if ($USER->id == $userid || has_capability('moodle/badges:viewotherbadges', context_user::instance($USER->id))) { | |
1170 | $records = badges_get_user_badges($userid, $courseid, null, null, null, true); | |
1171 | $renderer = new core_badges_renderer($PAGE, ''); | |
1172 | ||
1173 | // Print local badges. | |
1174 | if ($records) { | |
1175 | $left = get_string('localbadgesp', 'badges', $SITE->fullname); | |
1176 | $right = $renderer->print_badges_list($records, $userid, true); | |
1177 | echo html_writer::tag('dt', $left); | |
1178 | echo html_writer::tag('dd', $right); | |
1179 | } | |
1180 | ||
1181 | // Print external badges. | |
60d72efb | 1182 | if ($courseid == 0 && !empty($CFG->badges_allowexternalbackpack)) { |
27806552 YB |
1183 | $backpack = get_backpack_settings($userid); |
1184 | if (isset($backpack->totalbadges) && $backpack->totalbadges !== 0) { | |
1185 | $left = get_string('externalbadgesp', 'badges'); | |
1186 | $right = $renderer->print_badges_list($backpack->badges, $userid, true, true); | |
1187 | echo html_writer::tag('dt', $left); | |
1188 | echo html_writer::tag('dd', $right); | |
1189 | } | |
1190 | } | |
1191 | } | |
1192 | } | |
1193 | ||
1194 | /** | |
1195 | * Checks if badges can be pushed to external backpack. | |
1196 | * | |
1197 | * @return string Code of backpack accessibility status. | |
1198 | */ | |
1199 | function badges_check_backpack_accessibility() { | |
1200 | global $CFG; | |
1201 | include_once $CFG->libdir . '/filelib.php'; | |
1202 | ||
1203 | // Using fake assertion url to check whether backpack can access the web site. | |
1204 | $fakeassertion = new moodle_url('/badges/assertion.php', array('b' => 'abcd1234567890')); | |
1205 | ||
1206 | // Curl request to http://backpack.openbadges.org/baker. | |
1207 | $curl = new curl(); | |
1208 | $options = array( | |
1209 | 'FRESH_CONNECT' => true, | |
1210 | 'RETURNTRANSFER' => true, | |
27806552 | 1211 | 'HEADER' => 0, |
dd4a197e | 1212 | 'CONNECTTIMEOUT' => 2, |
27806552 YB |
1213 | ); |
1214 | $location = 'http://backpack.openbadges.org/baker'; | |
1215 | $out = $curl->get($location, array('assertion' => $fakeassertion->out(false)), $options); | |
1216 | ||
1217 | $data = json_decode($out); | |
1218 | if (!empty($curl->error)) { | |
1219 | return 'curl-request-timeout'; | |
1220 | } else { | |
1221 | if (isset($data->code) && $data->code == 'http-unreachable') { | |
1222 | return 'http-unreachable'; | |
1223 | } else { | |
1224 | return 'available'; | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | return false; | |
1229 | } | |
e2805314 YB |
1230 | |
1231 | /** | |
1232 | * Checks if user has external backpack connected. | |
1233 | * | |
1234 | * @param int $userid ID of a user. | |
1235 | * @return bool True|False whether backpack connection exists. | |
1236 | */ | |
1237 | function badges_user_has_backpack($userid) { | |
1238 | global $DB; | |
1239 | return $DB->record_exists('badge_backpack', array('userid' => $userid)); | |
1240 | } | |
7deff81f YB |
1241 | |
1242 | /** | |
1243 | * Handles what happens to the course badges when a course is deleted. | |
1244 | * | |
1245 | * @param int $courseid course ID. | |
1246 | * @return void. | |
1247 | */ | |
1248 | function badges_handle_course_deletion($courseid) { | |
1249 | global $CFG, $DB; | |
1250 | include_once $CFG->libdir . '/filelib.php'; | |
1251 | ||
1252 | $systemcontext = context_system::instance(); | |
1253 | $coursecontext = context_course::instance($courseid); | |
1254 | $fs = get_file_storage(); | |
1255 | ||
1256 | // Move badges images to the system context. | |
1257 | $fs->move_area_files_to_new_context($coursecontext->id, $systemcontext->id, 'badges', 'badgeimage'); | |
1258 | ||
1259 | // Get all course badges. | |
1260 | $badges = $DB->get_records('badge', array('type' => BADGE_TYPE_COURSE, 'courseid' => $courseid)); | |
1261 | foreach ($badges as $badge) { | |
1262 | // Archive badges in this course. | |
1263 | $toupdate = new stdClass(); | |
1264 | $toupdate->id = $badge->id; | |
1265 | $toupdate->type = BADGE_TYPE_SITE; | |
1266 | $toupdate->courseid = null; | |
1267 | $toupdate->status = BADGE_STATUS_ARCHIVED; | |
1268 | $DB->update_record('badge', $toupdate); | |
1269 | } | |
1270 | } | |
2c910861 YB |
1271 | |
1272 | /** | |
1273 | * Loads JS files required for backpack support. | |
1274 | * | |
1275 | * @uses $CFG, $PAGE | |
1276 | * @return void | |
1277 | */ | |
1278 | function badges_setup_backpack_js() { | |
1279 | global $CFG, $PAGE; | |
1280 | if (!empty($CFG->badges_allowexternalbackpack)) { | |
1281 | $PAGE->requires->string_for_js('error:backpackproblem', 'badges'); | |
1282 | $protocol = (strpos($CFG->wwwroot, 'https://') === 0) ? 'https://' : 'http://'; | |
1283 | $PAGE->requires->js(new moodle_url($protocol . 'backpack.openbadges.org/issuer.js'), true); | |
1284 | $PAGE->requires->js('/badges/backpack.js', true); | |
1285 | } | |
1286 | } |