From a39918daae3506830d49d86b4f0fc9be203d2eab Mon Sep 17 00:00:00 2001 From: Eiz Eddin Al Katrib Date: Fri, 17 Feb 2017 14:48:02 +0000 Subject: [PATCH] MDL-37361 completion: Enabled overriding activity completion status --- completion/classes/external.php | 120 ++++++++++++++++++ lang/en/completion.php | 3 + lang/en/role.php | 1 + .../course_module_completion_updated.php | 9 +- lib/completionlib.php | 26 +++- lib/db/access.php | 9 ++ lib/db/install.xml | 1 + lib/db/services.php | 8 ++ lib/db/upgrade.php | 14 ++ pix/i/completion-auto-n-override.png | Bin 0 -> 228 bytes pix/i/completion-auto-n-override.svg | 3 + pix/i/completion-auto-y-override.png | Bin 0 -> 328 bytes pix/i/completion-auto-y-override.svg | 3 + pix/i/completion-manual-n-override.png | Bin 0 -> 206 bytes pix/i/completion-manual-n-override.svg | 3 + pix/i/completion-manual-y-override.png | Bin 0 -> 295 bytes pix/i/completion-manual-y-override.svg | 3 + .../amd/build/completion_override.min.js | 1 + .../progress/amd/src/completion_override.js | 85 +++++++++++++ report/progress/index.php | 62 ++++++++- report/progress/styles.css | 5 + version.php | 2 +- 22 files changed, 343 insertions(+), 15 deletions(-) create mode 100644 pix/i/completion-auto-n-override.png create mode 100644 pix/i/completion-auto-n-override.svg create mode 100644 pix/i/completion-auto-y-override.png create mode 100644 pix/i/completion-auto-y-override.svg create mode 100644 pix/i/completion-manual-n-override.png create mode 100644 pix/i/completion-manual-n-override.svg create mode 100644 pix/i/completion-manual-y-override.png create mode 100644 pix/i/completion-manual-y-override.svg create mode 100644 report/progress/amd/build/completion_override.min.js create mode 100644 report/progress/amd/src/completion_override.js diff --git a/completion/classes/external.php b/completion/classes/external.php index 7979e61f3b8..685c3b9928a 100644 --- a/completion/classes/external.php +++ b/completion/classes/external.php @@ -114,6 +114,126 @@ class core_completion_external extends external_api { ); } + /** + * Describes the parameters for override_activity_completion_status. + * + * @return external_external_function_parameters + * @since Moodle 3.1 + */ + public static function override_activity_completion_status_parameters() { + return new external_function_parameters ( + array( + 'userid' => new external_value(PARAM_INT, 'user id'), + 'cmid' => new external_value(PARAM_INT, 'course module id'), + 'newstate' => new external_value(PARAM_INT, 'the new activity completion state'), + ) + ); + } + + /** + * Update completion status for a user in an activity. + * @param int $userid User id + * @param int $cmid Course module id + * @param int $newstate Activity completion + * @return array Result and possible warnings + * @since Moodle 3.1 + * @throws moodle_exception + */ + public static function override_activity_completion_status($userid, $cmid, $newstate) { + global $OUTPUT, $DB, $USER; + + // Validate and normalize parameters. + $params = self::validate_parameters(self::override_activity_completion_status_parameters(), + array('userid' => $userid, 'cmid' => $cmid, 'newstate' => $newstate)); + $userid = $params['userid']; + $cmid = $params['cmid']; + $newstate = $params['newstate']; + + $warnings = array(); + + $context = context_module::instance($cmid); + self::validate_context($context); + + list($course, $cm) = get_course_and_cm_from_cmid($cmid); + + // Set up completion object and check it is enabled. + $completion = new completion_info($course); + if (!$completion->is_enabled()) { + throw new moodle_exception('completionnotenabled', 'completion'); + } + + // Update completion state. + $completion->update_state($cm, $newstate, $userid, true); + + // Get activity completion data. + $completiondata = $completion->get_data($cm, false, $userid); + $state = $completiondata->completionstate; + $overrideby = $completiondata->overrideby; + $date = userdate($completiondata->timemodified); + + // Work out how it corresponds to an icon. + switch($state) { + case COMPLETION_INCOMPLETE : + $completiontype = 'n'.($overrideby ? '-override' : ''); + break; + case COMPLETION_COMPLETE : + $completiontype = 'y'.($overrideby ? '-override' : ''); + break; + case COMPLETION_COMPLETE_PASS : + $completiontype = 'pass'; + break; + case COMPLETION_COMPLETE_FAIL : + $completiontype = 'fail'; + break; + } + + $completionicon = 'completion-'. + ($cm->completion == COMPLETION_TRACKING_AUTOMATIC ? 'auto' : 'manual'). + '-'.$completiontype; + + $overridebyuser = $DB->get_record('user', array('id' => $USER->id), '*', MUST_EXIST); + $describe = get_string('completion-' . $completiontype, 'completion', fullname($overridebyuser)); + $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); + $a = new StdClass; + $a->state = $describe; + $a->date = $date; + $a->user = fullname($user); + $a->activity = $cm->id; + $fulldescribe = get_string('progress-title', 'completion', $a); + + $img = ''.s($describe).''; + + // Set data values for next completion change. + $otherstate = ($state == COMPLETION_COMPLETE) ? COMPLETION_INCOMPLETE : COMPLETION_COMPLETE; + $changecompl = $userid . '-' . $cmid . '-' . $otherstate; + + $result = array(); + $result['status'] = true; + $result['warnings'] = $warnings; + $result['changecompl'] = $changecompl; + $result['img'] = $img; + return $result; + } + + /** + * Describes the override_activity_completion_status return value. + * + * @return external_single_structure + * @since Moodle 3.1 + */ + public static function override_activity_completion_status_returns() { + + return new external_single_structure( + array( + 'status' => new external_value(PARAM_BOOL, 'Status, true if success'), + 'warnings' => new external_warnings(), + 'changecompl' => new external_value(PARAM_ALPHANUMEXT, 'The new completion change data'), + 'img' => new external_value(PARAM_RAW, 'Image element to replace existing one'), + ) + ); + } + /** * Returns description of method parameters * diff --git a/lang/en/completion.php b/lang/en/completion.php index 4bd47420cae..db94880724f 100644 --- a/lang/en/completion.php +++ b/lang/en/completion.php @@ -39,6 +39,7 @@ $string['aggregationmethod'] = 'Aggregation method'; $string['all'] = 'All'; $string['any'] = 'Any'; $string['approval'] = 'Approval'; +$string['areyousureoverridecompletion'] = 'Are you sure you want to override the current completion state of this activity for this user and mark it "{$a}"?'; $string['badautocompletion'] = 'When you select automatic completion, you must also enable at least one requirement (below).'; $string['bulkactivitycompletion'] = 'Bulk edit activity completion'; $string['bulkactivitydetail'] = 'Select the activities you wish to bulk edit.'; @@ -67,8 +68,10 @@ $string['completion-alt-manual-n'] = 'Not completed: {$a}. Select to mark as com $string['completion-alt-manual-y'] = 'Completed: {$a}. Select to mark as not complete.'; $string['completion-fail'] = 'Completed (did not achieve pass grade)'; $string['completion-n'] = 'Not completed'; +$string['completion-n-override'] = 'Not completed (overrride by {$a})'; $string['completion-pass'] = 'Completed (achieved pass grade)'; $string['completion-y'] = 'Completed'; +$string['completion-y-override'] = 'Completed (overrride by {$a})'; $string['completion_automatic'] = 'Show activity as complete when conditions are met'; $string['completion_help'] = 'If enabled, activity completion is tracked, either manually or automatically, based on certain conditions. Multiple conditions may be set if desired. If so, the activity will only be considered complete when ALL conditions are met. diff --git a/lang/en/role.php b/lang/en/role.php index 7af4a201029..3ac79943bf1 100644 --- a/lang/en/role.php +++ b/lang/en/role.php @@ -172,6 +172,7 @@ $string['course:managegroups'] = 'Manage groups'; $string['course:managescales'] = 'Manage scales'; $string['course:markcomplete'] = 'Mark users as complete in course completion'; $string['course:movesections'] = 'Move sections'; +$string['course:overridecompletion'] = 'Override activity completion status'; $string['course:publish'] = 'Publish a course'; $string['course:renameroles'] = 'Rename roles'; $string['course:request'] = 'Request new courses'; diff --git a/lib/classes/event/course_module_completion_updated.php b/lib/classes/event/course_module_completion_updated.php index 5142621612a..7ac1ff91490 100644 --- a/lib/classes/event/course_module_completion_updated.php +++ b/lib/classes/event/course_module_completion_updated.php @@ -66,8 +66,13 @@ class course_module_completion_updated extends base { * @return string */ public function get_description() { - return "The user with id '$this->userid' updated the completion state for the course module with id '$this->contextinstanceid' " . - "for the user with id '$this->relateduserid'."; + if (isset($this->other['overrideby']) && $this->other['overrideby']) { + return "The user with id '{$this->userid}' overrode the completion state to '{$this->other['completionstate']}' ". + "for the course module with id '{$this->contextinstanceid}' for the user with id '{$this->relateduserid}'."; + } else { + return "The user with id '{$this->userid}' updated the completion state for the course module with id " . + "'{$this->contextinstanceid}' for the user with id '{$this->relateduserid}'."; + } } /** diff --git a/lib/completionlib.php b/lib/completionlib.php index c8bf8177e86..d5d93c27ca1 100644 --- a/lib/completionlib.php +++ b/lib/completionlib.php @@ -548,9 +548,10 @@ class completion_info { * result. For manual events, COMPLETION_COMPLETE or COMPLETION_INCOMPLETE * must be used; these directly set the specified state. * @param int $userid User ID to be updated. Default 0 = current user + * @param bool $override Whether manually overriding the existing completion state. * @return void */ - public function update_state($cm, $possibleresult=COMPLETION_UNKNOWN, $userid=0) { + public function update_state($cm, $possibleresult=COMPLETION_UNKNOWN, $userid=0, $override = false) { global $USER; // Do nothing if completion is not enabled for that activity @@ -569,8 +570,9 @@ class completion_info { return; } - if ($cm->completion == COMPLETION_TRACKING_MANUAL) { - // For manual tracking we set the result directly + if ($cm->completion == COMPLETION_TRACKING_MANUAL || $override) { + // For manual tracking, or if overriding the completion state manually, + // we set the result directly. switch($possibleresult) { case COMPLETION_COMPLETE: case COMPLETION_INCOMPLETE: @@ -581,14 +583,22 @@ class completion_info { } } else { - // Automatic tracking; get new state - $newstate = $this->internal_get_state($cm, $userid, $current); + // Automatic tracking. + if ($current->overrideby) { + // If the current completion state has been set by override, do nothing + // as we don't want it to be changed automatically. + return; + } else { + // Get new state. + $newstate = $this->internal_get_state($cm, $userid, $current); + } } // If changed, update if ($newstate != $current->completionstate) { $current->completionstate = $newstate; $current->timemodified = time(); + $current->overrideby = $override ? $USER->id : null; $this->internal_set_data($cm, $current); } } @@ -958,6 +968,7 @@ class completion_info { $data['userid'] = $userid; $data['completionstate'] = 0; $data['viewed'] = 0; + $data['overrideby'] = null; $data['timemodified'] = 0; } $cacheddata[$othercm->id] = $data; @@ -980,6 +991,7 @@ class completion_info { $data['userid'] = $userid; $data['completionstate'] = 0; $data['viewed'] = 0; + $data['overrideby'] = null; $data['timemodified'] = 0; } @@ -1047,7 +1059,9 @@ class completion_info { 'context' => $cmcontext, 'relateduserid' => $data->userid, 'other' => array( - 'relateduserid' => $data->userid + 'relateduserid' => $data->userid, + 'overrideby' => $data->overrideby, + 'completionstate' => $data->completionstate ) )); $event->add_record_snapshot('course_modules_completion', $data); diff --git a/lib/db/access.php b/lib/db/access.php index 7b3a37a38e7..0d72b980ebb 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -1929,6 +1929,15 @@ $capabilities = array( 'manager' => CAP_ALLOW ) ), + 'moodle/course:overridecompletion' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), 'moodle/community:add' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, diff --git a/lib/db/install.xml b/lib/db/install.xml index bd36ac76690..7b537c53e9d 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -322,6 +322,7 @@ + diff --git a/lib/db/services.php b/lib/db/services.php index 2cf3f73a3a4..2f2814e8c16 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -276,6 +276,14 @@ $functions = array( 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_completion_override_activity_completion_status' => array( + 'classname' => 'core_completion_external', + 'methodname' => 'override_activity_completion_status', + 'description' => 'Update completion status for a user in an activity by overriding it.', + 'type' => 'write', + 'capabilities' => 'moodle/course:overridecompletion', + 'ajax' => true, + ), 'core_course_create_categories' => array( 'classname' => 'core_course_external', 'methodname' => 'create_categories', diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index d0fb99f05b2..b695f3565c5 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2601,5 +2601,19 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2017092900.00); } + if ($oldversion < 2017100600.01) { + // Define field override to be added to course_modules_completion. + $table = new xmldb_table('course_modules_completion'); + $field = new xmldb_field('overrideby', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'viewed'); + + // Conditionally launch add field override. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2017100600.01); + } + return true; } diff --git a/pix/i/completion-auto-n-override.png b/pix/i/completion-auto-n-override.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb98cea7a5af71790d531760c7febe30cd991a2 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0px`u5 z7sn8b(`P4N6l7B5agpy~y}_z^SZK+j=B_sbF^^8NGSgVEx}?KLmXeK_c+use}+ zS`ed}O$f8nw~5VC^ZC9fcI6eb8m)SG@#6Ypj&a$H9Mgg(PYs+~{#DeHS-jze%{kdi z(|sEym2c>n%&*?K&m%=(|NaN_+ba0kWgVFwJb2M`;oa@io_dQr%9pHJ* YTQ$dCSI|)S1JH#Gp00i_>zopr0MqkTG5`Po literal 0 HcmV?d00001 diff --git a/pix/i/completion-auto-n-override.svg b/pix/i/completion-auto-n-override.svg new file mode 100644 index 00000000000..6100638d09e --- /dev/null +++ b/pix/i/completion-auto-n-override.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/pix/i/completion-auto-y-override.png b/pix/i/completion-auto-y-override.png new file mode 100644 index 0000000000000000000000000000000000000000..aeca4edf099ab026444eec8caacfe8d42af37549 GIT binary patch literal 328 zcmV-O0k{5%P)s;$x=F(JD|{7yga(g z>OcYNFj^?!EKD5RuoPOKzPX(5%Pvp=Js_7(j=#bHePN7gm23c}iXcf$pt^0~-Fh{+ zbs7)1z!WHf;d&C?KEMUAkqu|ldXIo&zAuMCcoNNk4M5XC?wljw@&L}$2o8X@&G*cBdA(A1q!r&y#3P(PHk=6Z|ng_Yq;e!B7y9W aY0RDr7IM&1hWZTv0000 +]> \ No newline at end of file diff --git a/pix/i/completion-manual-n-override.png b/pix/i/completion-manual-n-override.png new file mode 100644 index 0000000000000000000000000000000000000000..6f95e981a5ce6ca54f50e258ac8b84210ac92fa4 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0pkRZi zi(`nz>9>=1^EMa=ILmi1-cY)}kxBW0i;D0;jx`R`4R0uj)QC7dnjG-xl-0>=kyV|+ zUd+6SMxDW@1Q(w9%)DUZw%tA*Aq{L?4F8*dneEZfWW4nDhl$PVcwZ@#JGTti9k?9- zA@d*)yS{_f!Zm_B*}PQPPqC)>i9Sm +]> \ No newline at end of file diff --git a/pix/i/completion-manual-y-override.png b/pix/i/completion-manual-y-override.png new file mode 100644 index 0000000000000000000000000000000000000000..bdbc46b188baef66e93eb3f85b7190907115f5cf GIT binary patch literal 295 zcmV+?0oeYDP)Z`y~=SsLxSuuo6(;sz;Xn?`~manQRzR|4Z8pU002ovPDHLkV1hxke~$nF literal 0 HcmV?d00001 diff --git a/pix/i/completion-manual-y-override.svg b/pix/i/completion-manual-y-override.svg new file mode 100644 index 00000000000..69270ba3e50 --- /dev/null +++ b/pix/i/completion-manual-y-override.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/report/progress/amd/build/completion_override.min.js b/report/progress/amd/build/completion_override.min.js new file mode 100644 index 00000000000..d5213636583 --- /dev/null +++ b/report/progress/amd/build/completion_override.min.js @@ -0,0 +1 @@ +define(["jquery","core/ajax","core/str","core/notification"],function(a,b,c,d){return{update:function(){a("#completion-progress a.changecompl").on("click",function(e){e.preventDefault();var f=a(this),g=f.data("changecompl"),h=g.split("-"),i=h[0],j=h[1],k=h[2],l=1==k?"completion-y":"completion-n";c.get_strings([{key:l,component:"completion"}]).done(function(e){c.get_strings([{key:"confirm",component:"moodle"},{key:"areyousureoverridecompletion",component:"completion",param:e[0]},{key:"yes",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){f.append('
');var c=b.call([{methodname:"core_completion_override_activity_completion_status",args:{userid:i,cmid:j,newstate:k}}]);c[0].then(function(b){f.data("changecompl",b.changecompl),f.attr("data-changecompl",b.changecompl),f.children("img").replaceWith(b.img),a(".ajaxworking").remove()}).fail(d.exception)})}).fail(d.exception)}).fail(d.exception)})}}}); \ No newline at end of file diff --git a/report/progress/amd/src/completion_override.js b/report/progress/amd/src/completion_override.js new file mode 100644 index 00000000000..410f8d15624 --- /dev/null +++ b/report/progress/amd/src/completion_override.js @@ -0,0 +1,85 @@ +// 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 . + +/** + * AMD module to handle overriding activity completion status. + * + * @module report_progress/completion_override + * @package report_progress + * @copyright 2016 onwards Eiz Eddin Al Katrib + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 3.1 + */ +define(['jquery', 'core/ajax', 'core/str', 'core/notification'], + function($, ajax, str, notification) { + return /** @alias module:report_progress/completion_override */ { + + /** + * Change the activity completion state. + * + * @method change + */ + update: function() { + + $('#completion-progress a.changecompl').on('click', function(e) { + e.preventDefault(); + + var el = $(this); + var changecompl = el.data('changecompl'); + var changecomplfields = changecompl.split('-'); + var userid = changecomplfields[0]; + var cmid = changecomplfields[1]; + var newstate = changecomplfields[2]; + var newstatestr = (newstate == 1) ? 'completion-y' : 'completion-n'; + + str.get_strings([ + {key: newstatestr, component: 'completion'} + ]).done(function(strings) { + str.get_strings([ + {key: 'confirm', component: 'moodle'}, + {key: 'areyousureoverridecompletion', component: 'completion', param: strings[0]}, + {key: 'yes', component: 'moodle'}, + {key: 'cancel', component: 'moodle'} + ]).done(function(strings) { + notification.confirm( + strings[0], // Confirm. + strings[1], // Message. + strings[2], // Yes. + strings[3], // Cancel. + function() { + el.append('
'); + + var promise = ajax.call([{ + methodname: 'core_completion_override_activity_completion_status', + args: { + userid: userid, cmid: cmid, newstate: newstate + } + }]); + + promise[0].then(function(results) { + el.data('changecompl', results.changecompl); + el.attr('data-changecompl', results.changecompl); + el.children("img").replaceWith(results.img); + $('.ajaxworking').remove(); + }).fail(notification.exception); + } + ); + }).fail(notification.exception); + }).fail(notification.exception); + + }); + } + }; +}); diff --git a/report/progress/index.php b/report/progress/index.php index 2895e1343f1..064461cacc0 100644 --- a/report/progress/index.php +++ b/report/progress/index.php @@ -51,6 +51,9 @@ $sifirst = optional_param('sifirst', 'all', PARAM_NOTAGS); $silast = optional_param('silast', 'all', PARAM_NOTAGS); $start = optional_param('start', 0, PARAM_INT); +// Action. +$changecompl = optional_param('changecompl', '', PARAM_ALPHANUMEXT); + // Whether to show extra user identity information $extrafields = get_extra_user_fields($context); $leftcols = 1 + count($extrafields); @@ -74,6 +77,12 @@ if ($format !== '') { if ($start !== 0) { $url->param('start', $start); } +if ($sifirst !== 'all') { + $url->param('sifirst', $sifirst); +} +if ($silast !== 'all') { + $url->param('silast', $silast); +} $PAGE->set_url($url); $PAGE->set_pagelayout('report'); @@ -94,6 +103,20 @@ $reportsurl = $CFG->wwwroot.'/course/report.php?id='.$course->id; $completion = new completion_info($course); $activities = $completion->get_activities(); +if ($changecompl) { + if ($changecompl) { + require_capability('moodle/course:overridecompletion', $context); + require_sesskey(); + list($userid, $cmid, $newstate) = preg_split('/-/', $changecompl, 3); + // Make sure the activity and user are tracked. + if (isset($activities[$cmid]) && + $completion->get_num_tracked_users('u.id = :userid', array('userid' => (int)$userid), $group)) { + $completion->update_state($activities[$cmid], $newstate, $userid, true); + } + redirect($PAGE->url); + } +} + if ($sifirst !== 'all') { set_user_preference('ifirst', $sifirst); } @@ -173,6 +196,7 @@ if ($csv && $grandtotal && count($activities)>0) { // Only show CSV if there are $PAGE->set_title($strcompletion); $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); + $PAGE->requires->js_call_amd('report_progress/completion_override', 'update'); // Handle groups (if enabled) groups_print_course_menu($course,$CFG->wwwroot.'/report/progress/?course='.$course->id); @@ -363,25 +387,40 @@ foreach($progress as $user) { if (array_key_exists($activity->id,$user->progress)) { $thisprogress=$user->progress[$activity->id]; $state=$thisprogress->completionstate; + $overrideby = $thisprogress->overrideby; $date=userdate($thisprogress->timemodified); } else { $state=COMPLETION_INCOMPLETE; + $overrideby = 0; $date=''; } // Work out how it corresponds to an icon switch($state) { - case COMPLETION_INCOMPLETE : $completiontype='n'; break; - case COMPLETION_COMPLETE : $completiontype='y'; break; - case COMPLETION_COMPLETE_PASS : $completiontype='pass'; break; - case COMPLETION_COMPLETE_FAIL : $completiontype='fail'; break; + case COMPLETION_INCOMPLETE : + $completiontype = 'n'.($overrideby ? '-override' : ''); + break; + case COMPLETION_COMPLETE : + $completiontype = 'y'.($overrideby ? '-override' : ''); + break; + case COMPLETION_COMPLETE_PASS : + $completiontype = 'pass'; + break; + case COMPLETION_COMPLETE_FAIL : + $completiontype = 'fail'; + break; } $completionicon='completion-'. ($activity->completion==COMPLETION_TRACKING_AUTOMATIC ? 'auto' : 'manual'). '-'.$completiontype; - $describe = get_string('completion-' . $completiontype, 'completion'); + if ($overrideby) { + $overridebyuser = $DB->get_record('user', array('id' => $overrideby), '*', MUST_EXIST); + $describe = get_string('completion-' . $completiontype, 'completion', fullname($overridebyuser)); + } else { + $describe = get_string('completion-' . $completiontype, 'completion'); + } $a=new StdClass; $a->state=$describe; $a->date=$date; @@ -392,8 +431,19 @@ foreach($progress as $user) { if ($csv) { print $sep.csv_quote($describe).$sep.csv_quote($date); } else { + $celltext = ''.s($describe).''; + if (has_capability('moodle/course:overridecompletion', $context) && + $state != COMPLETION_COMPLETE_PASS && $state != COMPLETION_COMPLETE_FAIL) { + $newstate = ($state == COMPLETION_COMPLETE) ? COMPLETION_INCOMPLETE : COMPLETION_COMPLETE; + $changecompl = $user->id . '-' . $activity->id . '-' . $newstate; + $url = new moodle_url($PAGE->url, array('sesskey' => sesskey(), + 'changecompl' => $changecompl)); + $celltext = html_writer::link($url, $celltext, array('class' => 'changecompl', + 'data-changecompl' => $changecompl)); + } print ''. - $OUTPUT->pix_icon('i/' . $completionicon, $fulldescribe) . ''; + $celltext . ''; } } diff --git a/report/progress/styles.css b/report/progress/styles.css index ede47d068dc..425d0674ef8 100644 --- a/report/progress/styles.css +++ b/report/progress/styles.css @@ -59,4 +59,9 @@ #page-report-progress-index .modicon { padding-top: 5px; } + +#page-report-progress-index #completion-progress td a .ajaxworking { + height: 16px; + background: url([[pix:i/ajaxloader]]) no-repeat; +} /*rtl:end:ignore*/ diff --git a/version.php b/version.php index a8c58ddd6fb..0a6f2d6f3f8 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2017100600.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2017100600.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.