mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 04:22:07 +02:00
MDL-43230 badges: manually revoke awarded badges
A user can be manually awarded a badge, but if given by mistake an awarded badge cannot be removed.
This commit is contained in:
parent
d9520bc04e
commit
b7374faca6
@ -43,7 +43,14 @@ if (!is_null($action)) {
|
||||
$json = ($action) ? $assertion->get_badge_class() : $assertion->get_issuer();
|
||||
} else {
|
||||
// Otherwise, get badge assertion.
|
||||
$json = $assertion->get_badge_assertion();
|
||||
$column = $DB->sql_compare_text('uniquehash', 255);
|
||||
if ($DB->record_exists_sql(sprintf('SELECT * FROM {badge_issued} WHERE %s = ?', $column), array($hash))) {
|
||||
$json = $assertion->get_badge_assertion();
|
||||
} else { // Revoked badge.
|
||||
header("HTTP/1.0 410 Gone");
|
||||
echo json_encode(array("revoked" => true));
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -31,6 +31,7 @@ require_once($CFG->dirroot . '/badges/lib/awardlib.php');
|
||||
$badgeid = required_param('id', PARAM_INT);
|
||||
$role = optional_param('role', 0, PARAM_INT);
|
||||
$award = optional_param('award', false, PARAM_BOOL);
|
||||
$revoke = optional_param('revoke', false, PARAM_BOOL);
|
||||
|
||||
require_login();
|
||||
|
||||
@ -169,6 +170,24 @@ if ($award && data_submitted() && has_capability('moodle/badges:awardbadge', $co
|
||||
}
|
||||
}
|
||||
|
||||
$recipientselector->invalidate_selected_users();
|
||||
$existingselector->invalidate_selected_users();
|
||||
$recipientselector->set_existing_recipients($existingselector->find_users(''));
|
||||
} else if ($revoke && data_submitted() && has_capability('moodle/badges:revokebadge', $context)) {
|
||||
require_sesskey();
|
||||
$users = $existingselector->get_selected_users();
|
||||
|
||||
foreach ($users as $user) {
|
||||
if (!process_manual_revoke($user->id, $USER->id, $issuerrole->roleid, $badgeid)) {
|
||||
echo $OUTPUT->error_text(get_string('error:cannotrevokebadge', 'badges'));
|
||||
} else {
|
||||
// Trigger event, badge revoked.
|
||||
$eventparams = array('objectid' => $badgeid, 'context' => $PAGE->context);
|
||||
$event = \core\event\badge_revoked::create($eventparams);
|
||||
$event->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
$recipientselector->invalidate_selected_users();
|
||||
$existingselector->invalidate_selected_users();
|
||||
$recipientselector->set_existing_recipients($existingselector->find_users(''));
|
||||
|
@ -34,43 +34,49 @@ $bake = optional_param('bake', 0, PARAM_BOOL);
|
||||
$PAGE->set_context(context_system::instance());
|
||||
$output = $PAGE->get_renderer('core', 'badges');
|
||||
|
||||
$badge = new issued_badge($id);
|
||||
|
||||
if ($bake && ($badge->recipient->id == $USER->id)) {
|
||||
$name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
|
||||
$filehash = badges_bake($id, $badge->badgeid, $USER->id, true);
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file_by_hash($filehash);
|
||||
send_stored_file($file, 0, 0, true, array('filename' => $name));
|
||||
}
|
||||
|
||||
$PAGE->set_url('/badges/badge.php', array('hash' => $id));
|
||||
$PAGE->set_pagelayout('base');
|
||||
$PAGE->set_title(get_string('issuedbadge', 'badges'));
|
||||
|
||||
if (isloggedin()) {
|
||||
$PAGE->set_heading($badge->badgeclass['name']);
|
||||
$PAGE->navbar->add($badge->badgeclass['name']);
|
||||
if ($badge->recipient->id == $USER->id) {
|
||||
$url = new moodle_url('/badges/mybadges.php');
|
||||
} else {
|
||||
$url = new moodle_url($CFG->wwwroot);
|
||||
$badge = new issued_badge($id);
|
||||
if (!empty($badge->recipient->id)) {
|
||||
if ($bake && ($badge->recipient->id == $USER->id)) {
|
||||
$name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
|
||||
$filehash = badges_bake($id, $badge->badgeid, $USER->id, true);
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file_by_hash($filehash);
|
||||
send_stored_file($file, 0, 0, true, array('filename' => $name));
|
||||
}
|
||||
navigation_node::override_active_url($url);
|
||||
|
||||
if (isloggedin()) {
|
||||
$PAGE->set_heading($badge->badgeclass['name']);
|
||||
$PAGE->navbar->add($badge->badgeclass['name']);
|
||||
if ($badge->recipient->id == $USER->id) {
|
||||
$url = new moodle_url('/badges/mybadges.php');
|
||||
} else {
|
||||
$url = new moodle_url($CFG->wwwroot);
|
||||
}
|
||||
navigation_node::override_active_url($url);
|
||||
} else {
|
||||
$PAGE->set_heading($badge->badgeclass['name']);
|
||||
$PAGE->navbar->add($badge->badgeclass['name']);
|
||||
$url = new moodle_url($CFG->wwwroot);
|
||||
navigation_node::override_active_url($url);
|
||||
}
|
||||
|
||||
// Include JS files for backpack support.
|
||||
badges_setup_backpack_js();
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
echo $output->render($badge);
|
||||
} else {
|
||||
$PAGE->set_heading($badge->badgeclass['name']);
|
||||
$PAGE->navbar->add($badge->badgeclass['name']);
|
||||
$url = new moodle_url($CFG->wwwroot);
|
||||
navigation_node::override_active_url($url);
|
||||
echo $OUTPUT->header();
|
||||
|
||||
echo $OUTPUT->container($OUTPUT->error_text(get_string('error:badgeawardnotfound', 'badges')) .
|
||||
html_writer::tag('p', $OUTPUT->close_window_button()), 'important', 'notice');
|
||||
}
|
||||
|
||||
// Include JS files for backpack support.
|
||||
badges_setup_backpack_js();
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
echo $output->render($badge);
|
||||
|
||||
// Trigger event, badge viewed.
|
||||
$other = array('badgeid' => $badge->badgeid, 'badgehash' => $id);
|
||||
$eventparams = array('context' => $PAGE->context, 'other' => $other);
|
||||
|
@ -73,7 +73,11 @@ class core_badges_assertion {
|
||||
WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
|
||||
array('hash' => $hash), IGNORE_MISSING);
|
||||
|
||||
$this->_url = new moodle_url('/badges/badge.php', array('hash' => $this->_data->uniquehash));
|
||||
if ($this->_data) {
|
||||
$this->_url = new moodle_url('/badges/badge.php', array('hash' => $this->_data->uniquehash));
|
||||
} else {
|
||||
$this->_url = new moodle_url('/badges/badge.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,6 +236,36 @@ function process_manual_award($recipientid, $issuerid, $issuerrole, $badgeid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually revoke awarded badges.
|
||||
*
|
||||
* @param int $recipientid
|
||||
* @param int $issuerid
|
||||
* @param int $issuerrole
|
||||
* @param int $badgeid
|
||||
* @return bool
|
||||
*/
|
||||
function process_manual_revoke($recipientid, $issuerid, $issuerrole, $badgeid) {
|
||||
global $DB;
|
||||
$params = array(
|
||||
'badgeid' => $badgeid,
|
||||
'issuerid' => $issuerid,
|
||||
'issuerrole' => $issuerrole,
|
||||
'recipientid' => $recipientid
|
||||
);
|
||||
if ($DB->record_exists('badge_manual_award', $params)) {
|
||||
if ($DB->delete_records('badge_manual_award', array('badgeid' => $badgeid,
|
||||
'issuerid' => $issuerid,
|
||||
'recipientid' => $recipientid))
|
||||
&& $DB->delete_records('badge_issued', array('badgeid' => $badgeid,
|
||||
'userid' => $recipientid))) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
throw new moodle_exception('error:badgenotfound', 'badges');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -115,6 +115,12 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
'value' => $this->output->larrow() . ' ' . get_string('award', 'badges'),
|
||||
'class' => 'actionbutton')
|
||||
);
|
||||
$actioncell->text .= html_writer::empty_tag('input', array(
|
||||
'type' => 'submit',
|
||||
'name' => 'revoke',
|
||||
'value' => get_string('revoke', 'badges') . ' ' . $this->output->rarrow(),
|
||||
'class' => 'actionbutton')
|
||||
);
|
||||
$actioncell->text .= html_writer::end_tag('div', array());
|
||||
$actioncell->attributes['class'] = 'actions';
|
||||
$potentialcell = new html_table_cell();
|
||||
|
@ -310,3 +310,56 @@ Feature: Award badges
|
||||
When I follow "Course 1"
|
||||
Then I should see "Course Badge 2"
|
||||
Then I should not see "Course Badge 1"
|
||||
|
||||
@javascript
|
||||
Scenario: Revoke badge
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category | groupmode |
|
||||
| Course 1 | C1 | 0 | 1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I navigate to "Add a new badge" node in "Course administration > Badges"
|
||||
And I follow "Add a new badge"
|
||||
And I set the following fields to these values:
|
||||
| Name | Course Badge |
|
||||
| Description | Course badge description |
|
||||
| issuername | Tester of course badge |
|
||||
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
|
||||
And I press "Create badge"
|
||||
And I set the field "type" to "Manual issue by role"
|
||||
And I set the field "Teacher" to "1"
|
||||
And I press "Save"
|
||||
And I press "Enable access"
|
||||
And I press "Continue"
|
||||
And I follow "Recipients (0)"
|
||||
And I press "Award badge"
|
||||
And I set the field "potentialrecipients[]" to "Student 2 (student2@example.com)"
|
||||
And I press "Award badge"
|
||||
And I set the field "potentialrecipients[]" to "Student 1 (student1@example.com)"
|
||||
When I press "Award badge"
|
||||
And I follow "Course Badge"
|
||||
Then I should see "Recipients (2)"
|
||||
And I follow "Recipients (2)"
|
||||
And I press "Award badge"
|
||||
And I set the field "existingrecipients[]" to "Student 2 (student2@example.com)"
|
||||
And I press "Remove badge"
|
||||
And I set the field "existingrecipients[]" to "Student 1 (student1@example.com)"
|
||||
When I press "Award badge"
|
||||
And I follow "Remove Badge"
|
||||
Then I should see "Recipients (0)"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Profile" in the user menu
|
||||
And I follow "Course 1"
|
||||
And I should see "Course Badge"
|
||||
|
||||
|
@ -216,9 +216,12 @@ $string['error:backpackemailnotfound'] = 'The email \'{$a}\' is not associated w
|
||||
$string['error:backpacknotavailable'] = 'Your site is not accessible from the Internet, so any badges issued from this site cannot be verified by external backpack services.';
|
||||
$string['error:backpackloginfailed'] = 'You could not be connected to an external backpack for the following reason: {$a}';
|
||||
$string['error:backpackproblem'] = 'There was a problem connecting to your backpack service provider. Please try again later.';
|
||||
$string['error:badgeawardnotfound'] = 'Cannot verify this awarded badge. This badge may have been revoked.';
|
||||
$string['error:badgenotfound'] = 'Badge not found';
|
||||
$string['error:badjson'] = 'The connection attempt returned invalid data.';
|
||||
$string['error:cannotact'] = 'Cannot activate the badge. ';
|
||||
$string['error:cannotawardbadge'] = 'Cannot award badge to a user.';
|
||||
$string['error:cannotrevokebadge'] = 'Cannot revoke badge from a user.';
|
||||
$string['error:cannotdeletecriterion'] = 'This criterion cannot be deleted. ';
|
||||
$string['error:connectionunknownreason'] = 'The connection was unsuccessful but no reason was given.';
|
||||
$string['error:clone'] = 'Cannot clone the badge.';
|
||||
@ -259,6 +262,7 @@ $string['eventbadgedisabled'] = 'Badge disabled';
|
||||
$string['eventbadgeduplicated'] = 'Badge duplicated';
|
||||
$string['eventbadgeenabled'] = 'Badge enabled';
|
||||
$string['eventbadgelistingviewed'] = 'Badge listing viewed';
|
||||
$string['eventbadgerevoked'] = 'Badge revoked';
|
||||
$string['eventbadgeupdated'] = 'Badge updated';
|
||||
$string['eventbadgeviewed'] = 'Badge viewed';
|
||||
$string['evidence'] = 'Evidence';
|
||||
@ -349,6 +353,7 @@ $string['recipientdetails'] = 'Recipient details';
|
||||
$string['recipientidentificationproblem'] = 'Cannot find a recipient of this badge among the existing users.';
|
||||
$string['recipientvalidationproblem'] = 'Current user cannot be verified as a recipient of this badge.';
|
||||
$string['relative'] = 'Relative date';
|
||||
$string['revoke'] = 'Revoke badge';
|
||||
$string['requiredcourse'] = 'At least one course should be added to the courseset criterion.';
|
||||
$string['reviewbadge'] = 'Changes in badge access';
|
||||
$string['reviewconfirm'] = '<p>This will make your badge visible to users and allow them to start earning it.</p>
|
||||
|
@ -76,6 +76,7 @@ $string['badges:deletebadge'] = 'Delete badges';
|
||||
$string['badges:earnbadge'] = 'Earn badge';
|
||||
$string['badges:manageglobalsettings'] = 'Manage badges global settings';
|
||||
$string['badges:manageownbadges'] = 'View and manage own earned badges';
|
||||
$string['badges:revokebadge'] = 'Revoke badge from a user';
|
||||
$string['badges:viewawarded'] = 'View users who earned a specific badge without being able to award a badge';
|
||||
$string['badges:viewbadges'] = 'View available badges without earning them';
|
||||
$string['badges:viewotherbadges'] = 'View public badges in other users\' profiles';
|
||||
|
118
lib/classes/event/badge_revoked.php
Normal file
118
lib/classes/event/badge_revoked.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Badge revoked event.
|
||||
*
|
||||
* @property-read array $other {
|
||||
* Extra information about event.
|
||||
*
|
||||
* - int expiredate: Badge expire timestamp.
|
||||
* - int badgeissuedid: Badge issued ID.
|
||||
* }
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2016 Matt Davidson
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Event triggered after a badge is revoked from a user.
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 3.2
|
||||
* @copyright 2016 Matt Davidson
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class badge_revoked extends base {
|
||||
|
||||
/**
|
||||
* Set basic properties for the event.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['objecttable'] = 'badge';
|
||||
$this->data['crud'] = 'c';
|
||||
$this->data['edulevel'] = self::LEVEL_TEACHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns localised general event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventbadgerevoked', 'badges');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-localised event description with id's for admin use only.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->relateduserid' has had the badge with id '$this->objectid' revoked.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relevant URL.
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
return new \moodle_url('/badges/overview.php', array('id' => $this->objectid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validations.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_data() {
|
||||
parent::validate_data();
|
||||
|
||||
if (!isset($this->relateduserid)) {
|
||||
throw new \coding_exception('The \'relateduserid\' must be set.');
|
||||
}
|
||||
|
||||
if (!isset($this->objectid)) {
|
||||
throw new \coding_exception('The \'objectid\' must be set.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get_objectid_mapping method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_objectid_mapping() {
|
||||
return array('db' => 'badge', 'restore' => 'badge');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get_other_mapping method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_other_mapping() {
|
||||
$othermapped = array();
|
||||
$othermapped['badgeissuedid'] = array('db' => 'badge_issued', 'restore' => base::NOT_MAPPED);
|
||||
|
||||
return $othermapped;
|
||||
}
|
||||
}
|
@ -2025,6 +2025,18 @@ $capabilities = array(
|
||||
)
|
||||
),
|
||||
|
||||
// Revoke badge from a user.
|
||||
'moodle/badges:revokebadge' => array(
|
||||
'riskbitmask' => RISK_SPAM,
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_COURSE,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW,
|
||||
'teacher' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
)
|
||||
),
|
||||
|
||||
// View users who earned a specific badge without being able to award a badge.
|
||||
'moodle/badges:viewawarded' => array(
|
||||
'riskbitmask' => RISK_PERSONAL,
|
||||
|
Loading…
x
Reference in New Issue
Block a user