Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'MDL-25718_recover_grades_master' of git://github.com/an…

…dyjdavis/moodle
  • Loading branch information...
commit d2e360a95b9a248327841eac7cab53bdb8e4f3da 2 parents 9ff913b + 13ba903
Petr Skoda skodak authored
6 enrol/ajax.php
View
@@ -154,6 +154,8 @@
$roleid = optional_param('role', null, PARAM_INT);
$duration = optional_param('duration', 0, PARAM_INT);
$startdate = optional_param('startdate', 0, PARAM_INT);
+ $recovergrades = optional_param('recovergrades', 0, PARAM_INT);
+
if (empty($roleid)) {
$roleid = null;
}
@@ -185,6 +187,10 @@
$plugin = $plugins[$instance->enrol];
if ($plugin->allow_enrol($instance) && has_capability('enrol/'.$plugin->get_name().':enrol', $context)) {
$plugin->enrol_user($instance, $user->id, $roleid, $timestart, $timeend);
+ if ($recovergrades) {
+ require_once($CFG->libdir.'/gradelib.php');
+ grade_recover_history_grades($user->id, $instance->courseid);
+ }
} else {
throw new enrol_ajax_exception('enrolnotpermitted');
}
2  enrol/locallib.php
View
@@ -613,6 +613,8 @@ public function get_group($groupid) {
* @return bool
*/
public function edit_enrolment($userenrolment, $data) {
+ //Only allow editing if the user has the appropriate capability
+ //Already checked in /enrol/users.php but checking again in case this function is called from elsewhere
list($instance, $plugin) = $this->get_user_enrolment_components($userenrolment);
if ($instance && $plugin && $plugin->allow_manage($instance) && has_capability("enrol/$instance->enrol:manage", $this->context)) {
if (!isset($data->status)) {
7 enrol/renderer.php
View
@@ -626,6 +626,7 @@ public function get_cohort_enrolment_control() {
* @return single_button|url_select
*/
public function get_enrolment_selector() {
+ global $CFG;
static $count = 0;
$instances = $this->manager->get_enrolment_instances();
@@ -686,7 +687,8 @@ public function get_enrolment_selector() {
'startdatetoday',
'durationdays',
'enrolperiod',
- 'finishenrollingusers'), 'enrol');
+ 'finishenrollingusers',
+ 'recovergrades'), 'enrol');
$this->moodlepage->requires->string_for_js('assignroles', 'role');
$this->moodlepage->requires->string_for_js('startingfrom', 'moodle');
@@ -698,7 +700,8 @@ public function get_enrolment_selector() {
'ajaxurl'=>'/enrol/ajax.php',
'url'=>$this->moodlepage->url->out(false),
'optionsStartDate'=>$startdateoptions,
- 'defaultRole'=>$instance->roleid);
+ 'defaultRole'=>$instance->roleid,
+ 'disableGradeHistory'=>$CFG->disablegradehistory);
$this->moodlepage->requires->yui_module($modules, $function, array($arguments));
}
return $control;
5 enrol/users.php
View
@@ -62,7 +62,7 @@
switch ($action) {
/**
- * Unenrols a user from this course
+ * Unenrols a user from this course (includes removing all of their grades)
*/
case 'unenrol':
$ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
@@ -73,6 +73,7 @@
} else {
$user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
$yesurl = new moodle_url($PAGE->url, array('action'=>'unenrol', 'ue'=>$ue->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+
$message = get_string('unenrolconfirm', 'enrol', array('user'=>fullname($user, true), 'course'=>format_string($course->fullname)));
$pagetitle = get_string('unenrol', 'enrol');
$pagecontent = $OUTPUT->confirm($message, $yesurl, $PAGE->url);
@@ -166,6 +167,8 @@
*/
case 'edit':
$ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
+
+ //Only show the edit form if the user has the appropriate capability
list($instance, $plugin) = $manager->get_user_enrolment_components($ue);
if ($instance && $plugin && $plugin->allow_manage($instance) && has_capability("enrol/$instance->enrol:manage", $manager->get_context())) {
$user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
16 enrol/yui/enrolmentmanager/enrolmentmanager.js
View
@@ -20,7 +20,8 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
DEFAULTROLE : 'defaultRole',
DEFAULTSTARTDATE : 'defaultStartDate',
DEFAULTDURATION : 'defaultDuration',
- ASSIGNABLEROLES : 'assignableRoles'
+ ASSIGNABLEROLES : 'assignableRoles',
+ DISABLEGRADEHISTORY : 'disableGradeHistory'
};
/** CSS classes for nodes in structure **/
var CSS = {
@@ -48,6 +49,8 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
ODD : 'odd',
EVEN : 'even',
HIDDEN : 'hidden',
+ RECOVERGRADESCHECK : 'uep-recovergradescheck',
+ RECOVERGRADESCHECKTITLE : 'uep-recovergradeschecktitle',
SEARCHOPTIONS : 'uep-searchoptions',
COLLAPSIBLEHEADING : 'collapsibleheading',
COLLAPSIBLEAREA : 'collapsiblearea',
@@ -70,6 +73,11 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
_loadingNode : null,
_escCloseEvent : null,
initializer : function(config) {
+ recovergradescheckbox = null;
+ if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
+ recovergradescheckbox = Y.Node.create('<div class="'+CSS.RECOVERGRADESCHECK+'"><input type="checkbox" id="recovergrades" name="recovergrades" /><span class="'+CSS.RECOVERGRADESCHECKTITLE+'"><label for="recovergrades">'+M.str.enrol.recovergrades+'</label></span></div>');
+ }
+
this.set(UEP.BASE, Y.Node.create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
.append(Y.Node.create('<div class="'+CSS.WRAP+'"></div>')
.append(Y.Node.create('<div class="'+CSS.HEADER+' header"></div>')
@@ -80,6 +88,7 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
.append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.ROLE+'">'+M.str.role.assignroles+'</div>')
.append(Y.Node.create('<select><option value="">'+M.str.enrol.none+'</option></select>'))
)
+ .append(recovergradescheckbox)
.append(Y.Node.create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
.append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.str.enrol.enrolmentoptions+'</div>'))
.append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
@@ -367,6 +376,8 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
params['role'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.ROLE+' select').get('value');
params['startdate'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.STARTDATE+' select').get('value');
params['duration'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.DURATION+' select').get('value');
+ params['recovergrades'] = this.get(UEP.BASE).one('#recovergrades').get('checked')?1:0;
+
Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
method:'POST',
data:build_querystring(params),
@@ -487,6 +498,9 @@ YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
},
optionsStartDate : {
value : []
+ },
+ disableGradeHistory : {
+ value : 0
}
}
});
1  lang/en/enrol.php
View
@@ -77,6 +77,7 @@
$string['periodend'] = 'until {$a}';
$string['periodstart'] = 'from {$a}';
$string['periodstartend'] = 'from {$a->start} until {$a->end}';
+$string['recovergrades'] = 'Recover user\'s old grades if possible';
$string['rolefromthiscourse'] = '{$a->role} (Assigned in this course)';
$string['rolefrommetacourse'] = '{$a->role} (Inherited from parent course)';
$string['rolefromcategory'] = '{$a->role} (Inherited from course category)';
84 lib/gradelib.php
View
@@ -914,6 +914,76 @@ function grade_force_site_regrading() {
}
/**
+ * Recover a user's grades from grade_grades_history
+ * @param int $userid the user ID whose grades we want to recover
+ * @param int $courseid the relevant course
+ * @return bool true if successful or false if there was an error or no grades could be recovered
+ */
+function grade_recover_history_grades($userid, $courseid) {
+ global $CFG, $DB;
+
+ if ($CFG->disablegradehistory) {
+ debugging('Attempting to recover grades when grade history is disabled.');
+ return false;
+ }
+
+ //Were grades recovered? Flag to return.
+ $recoveredgrades = false;
+
+ //Check the user is enrolled in this course
+ //Dont bother checking if they have a gradeable role. They may get one later so recover
+ //whatever grades they have now just in case.
+ $course_context = get_context_instance(CONTEXT_COURSE, $courseid);
+ if (!is_enrolled($course_context, $userid)) {
+ debugging('Attempting to recover the grades of a user who is deleted or not enrolled. Skipping recover.');
+ return false;
+ }
+
+ //Check for existing grades for this user in this course
+ //Recovering grades when the user already has grades can lead to duplicate indexes and bad data
+ //In the future we could move the existing grades to the history table then recover the grades from before then
+ $sql = "SELECT gg.id FROM {grade_grades} gg
+ JOIN {grade_items} gi ON gi.id = gg.itemid
+ WHERE gi.courseid = :courseid AND gg.userid = :userid";
+ $params = array('userid' => $userid, 'courseid' => $courseid);
+ if ($DB->record_exists_sql($sql, $params)) {
+ debugging('Attempting to recover the grades of a user who already has grades. Skipping recover.');
+ return false;
+ } else {
+ //Retrieve the user's old grades
+ //have history ID as first column to guarantee we a unique first column
+ $sql = "SELECT h.id, gi.itemtype, gi.itemmodule, gi.iteminstance as iteminstance, gi.itemnumber, h.source, h.itemid, h.userid, h.rawgrade, h.rawgrademax, h.rawgrademin, h.rawscaleid, h.usermodified, h.finalgrade, h.hidden, h.locked, h.locktime, h.exported, h.overridden, h.excluded, h.feedback, h.feedbackformat, h.information, h.informationformat, h.timemodified, itemcreated.tm AS timecreated
+ FROM {grade_grades_history} h
+ JOIN (SELECT itemid, MAX(id) AS id FROM {grade_grades_history}
+ WHERE userid = :userid1 GROUP BY itemid) maxquery ON h.id = maxquery.id AND h.itemid = maxquery.itemid
+ JOIN {grade_items} gi ON gi.id = h.itemid
+ JOIN (SELECT itemid, MAX(timemodified) AS tm FROM {grade_grades_history} WHERE userid = :userid2 AND action = :insertaction GROUP BY itemid) itemcreated ON itemcreated.itemid = h.itemid
+ WHERE gi.courseid = :courseid";
+ $params = array('userid1' => $userid, 'userid2' => $userid , 'insertaction' => GRADE_HISTORY_INSERT, 'courseid' => $courseid);
+ $oldgrades = $DB->get_records_sql($sql, $params);
+
+ //now move the old grades to the grade_grades table
+ foreach ($oldgrades as $oldgrade) {
+ unset($oldgrade->id);
+
+ $grade = new grade_grade($oldgrade, false);//2nd arg false as dont want to try and retrieve a record from the DB
+ $grade->insert($oldgrade->source);
+
+ //dont include default empty grades created when activities are created
+ if (!empty($oldgrade->finalgrade) || !empty($oldgrade->feedback)) {
+ $recoveredgrades = true;
+ }
+ }
+ }
+
+ //Some activities require manual grade synching (moving grades from the activity into the gradebook)
+ //If the student was deleted when synching was done they may have grades in the activity that haven't been moved across
+ grade_grab_course_grades($courseid, null, $userid);
+
+ return $recoveredgrades;
+}
+
+/**
* Updates all final grades in course.
*
* @param int $courseid
@@ -1032,14 +1102,12 @@ function grade_regrade_final_grades($courseid, $userid=null, $updated_item=null)
/**
* Refetches data from all course activities
- *
- * @global object
- * @global object
- * @param int $courseid
- * @param string $modname
+ * @param int $courseid the course ID
+ * @param string $modname limit the grade fetch to a single module type
+ * @param int $userid limit the grade fetch to a single user
* @return void
*/
-function grade_grab_course_grades($courseid, $modname=null) {
+function grade_grab_course_grades($courseid, $modname=null, $userid=0) {
global $CFG, $DB;
if ($modname) {
@@ -1050,7 +1118,7 @@ function grade_grab_course_grades($courseid, $modname=null) {
if ($modinstances = $DB->get_records_sql($sql, $params)) {
foreach ($modinstances as $modinstance) {
- grade_update_mod_grades($modinstance);
+ grade_update_mod_grades($modinstance, $userid);
}
}
return;
@@ -1075,7 +1143,7 @@ function grade_grab_course_grades($courseid, $modname=null) {
if ($modinstances = $DB->get_records_sql($sql, $params)) {
foreach ($modinstances as $modinstance) {
- grade_update_mod_grades($modinstance);
+ grade_update_mod_grades($modinstance, $userid);
}
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.