diff --git a/amd/build/page_info.min.js b/amd/build/page_info.min.js index fa0e2b8..7e7da74 100644 --- a/amd/build/page_info.min.js +++ b/amd/build/page_info.min.js @@ -1 +1 @@ -define([],function(){var a={failGrades:!1,incompleteGrades:!1,defaultIncomplete:!1,lastAttendDates:!1,incompleteDeadlineDates:!1,gradeModes:!1,init:function(a,b,c,d,e){this.failGrades=a,this.incompleteGrades=b,this.defaultIncomplete=c,this.incompleteDeadlineDates={start:new Date(d.start),userstart:d.userstart,end:new Date(d.end),userend:d.userend},this.lastAttendDates={start:new Date(e.start),userstart:e.userstart,end:new Date(e.end),userend:e.userend}},gradeIsIncomplete:function(a){return!!this.incompleteGrades.hasOwnProperty(a)},gradeIsFailure:function(a){return!!this.failGrades.hasOwnProperty(a)},isAllowedIncompleteGrade:function(a){return!(!a||this.gradeIsIncomplete(a))},isAllowedIncompleteDeadline:function(a){var b=new Date(a);return!(bthis.incompleteDeadlineDates.end)},isAllowedLastAttendDate:function(a){var b=new Date(a);return!(bthis.lastAttendDates.end)},getStringLastAttendDates:function(){return{start:this.lastAttendDates.userstart,end:this.lastAttendDates.userend}},getStringIncompleteDeadline:function(){return{start:this.incompleteDeadlineDates.userstart,end:this.incompleteDeadlineDates.userend}},getIncompleteGrade:function(){return"F"}};return a}); \ No newline at end of file +define([],function(){var a={failGrades:!1,incompleteGrades:!1,defaultIncomplete:!1,lastAttendDates:!1,incompleteDeadlineDates:!1,gradeModes:!1,init:function(a,b,c,d,e){this.failGrades=a,this.incompleteGrades=b,this.defaultIncomplete=c;var f=d.start;f!==!1&&(f=new Date(f));var g=d.end;g!==!1&&(g=new Date(g)),this.incompleteDeadlineDates={start:f,userstart:d.userstart,end:g,userend:d.userend},this.lastAttendDates={start:new Date(e.start),userstart:e.userstart,end:new Date(e.end),userend:e.userend}},gradeIsIncomplete:function(a){return!!this.incompleteGrades.hasOwnProperty(a)},gradeIsFailure:function(a){return!!this.failGrades.hasOwnProperty(a)},isAllowedIncompleteGrade:function(a){return!(!a||this.gradeIsIncomplete(a))},isAllowedIncompleteDeadline:function(a){var b=new Date(a).getTime(),c=this.incompleteDeadlineDates.start.getTime();return!(this.incompleteDeadlineDates.start!==!1&&bthis.incompleteDeadlineDates.end.getTime())},isAllowedLastAttendDate:function(a){var b=new Date(a);return!(bthis.lastAttendDates.end)},getStringLastAttendDates:function(){return{start:this.lastAttendDates.userstart,end:this.lastAttendDates.userend}},getIncompleteDeadlineStringName:function(){return this.incompleteDeadlineDates.start===!1?"invalid_incomplete_date_end":this.incompleteDeadlineDates.end===!1?"invalid_incomplete_date_start":this.incompleteDeadlineDates.start.getTime()===this.incompleteDeadlineDates.end.getTime()?"invalid_incomplete_date_single":"invalid_incomplete_date_range"},getStringIncompleteDeadline:function(){return{start:this.incompleteDeadlineDates.userstart,end:this.incompleteDeadlineDates.userend}},getIncompleteGrade:function(){return"F"}};return a}); \ No newline at end of file diff --git a/amd/build/row_control.min.js b/amd/build/row_control.min.js index 01973e2..2c358ea 100644 --- a/amd/build/row_control.min.js +++ b/amd/build/row_control.min.js @@ -1 +1 @@ -define(["jquery","gradeexport_ilp_push/page_info","core/ajax","core/notification","core/templates","core/str","core/log"],function(a,b,c,d,e,f,g){var h={failGrades:!1,incompleteGrades:!1,initAll:function(){a(".gradingtable .usergraderow").each(function(){h.initRow(a(this))})},initRow:function(c){var d=c.find(".gradeselect").eq(0);d.change(function(){var d=a(this).val();b.gradeIsIncomplete(d)?(c.find(".incomplete").show(),c.find(".fail").hide()):b.gradeIsFailure(d)?(c.find(".incomplete").hide(),c.find(".fail").show()):(c.find(".incomplete").hide(),c.find(".fail").hide()),h.updateVerification(c)}),c.find(".incompletedeadline input").eq(0).change(function(){h.updateVerification(c)}),c.find(".datelastattended input").eq(0).change(function(){h.updateVerification(c)}),c.find(".incompletegradeselect").eq(0).change(function(){h.updateVerification(c)}),c.find(".grademode .currentgrademode").eq(0).click(function(){h.showGradeModeSelector(c)}),c.find(".grademode .grademodeselect").eq(0).change(function(){h.changeGradeMode(c)}),c.find(".grademode .grademodeselect").eq(0).focusout(function(){h.hideGradeModeSelector(c)}),c.find(".togglehistory a").eq(0).click(function(a){a.preventDefault(),h.toggleHistory(c)}),h.updateVerification(c)},updateFormWarning:function(a,b,c,d){var h=a.find(b);if(h)return h=h.eq(0),c?void f.get_string(c,"gradeexport_ilp_push",d).done(function(a){e.renderPix("i/warning","core",a).then(function(a){h.html(a)}).fail(function(){g.debug("Failed to fetch the warning icon")})}):void h.html("")},updateVerification:function(a){if(!a.hasClass("locked")){var c=a.find(".gradeselect").eq(0),d=c.val(),e=a.find(".confirmcheckbox").eq(0),f=a.find(".grade .letter").eq(0).data("grade-key"),g=a.find(".grade .notequal").eq(0);f!=d?g.show():g.hide();var i=!1,j=!1,k="";if(d||(j=!0,k="invalid_grade"),h.updateFormWarning(a,".gradeerror",k),b.gradeIsIncomplete(d)){i=a.find(".incompletedeadline input").eq(0),k="",""==i.val()?(j=!0,k="invalid_incomplete_date"):b.isAllowedIncompleteDeadline(i.val())||(j=!0,k="invalid_incomplete_date"),h.updateFormWarning(a,".incompletedateerror",k,b.getStringIncompleteDeadline());var l=a.find(".incompletegradeselect").eq(0);k="",l.val()?b.isAllowedIncompleteGrade(l.val())||(j=!0,k="invalid_incomplete_grade_wrong"):(j=!0,k="invalid_incomplete_grade_missing"),h.updateFormWarning(a,".incompletegradeerror",k,b.getIncompleteGrade())}else b.gradeIsFailure(d)&&(i=a.find(".datelastattended input").eq(0),k="",""==i.val()?(j=!0,k="invalid_datelastattended"):b.isAllowedLastAttendDate(i.val())||(j=!0,k="invalid_datelastattended"),h.updateFormWarning(a,".datelastattendederror",k,b.getStringLastAttendDates()));j?e.prop("disabled",!0):e.prop("disabled",!1)}},showGradeModeSelector:function(a){var b=a.find(".grademode .currentgrademode"),c=a.find(".grademode .grademodeselect");b.hide(),c.show()},hideGradeModeSelector:function(a){var b=a.find(".grademode .currentgrademode"),c=a.find(".grademode .grademodeselect");b.show(),c.hide()},changeGradeMode:function(b){var f=b.find(".grademode .currentgrademode"),g=b.find(".grademode .grademodeselect"),i=g.find("select").eq(0).val(),j=b.data("user-id"),k=b.data("row-id"),l=b.closest("form").find('input[name="courseid"]').val();c.call([{methodname:"gradeexport_ilp_push_update_row_grade_mode",args:{rowid:k,grademodeid:i,studentid:j,courseid:l},done:function(c){return c.warnings.length>0?(d.alert(c.warnings[0].message,c.warnings[0].item),f.show(),void g.hide()):void e.render("gradeexport_ilp_push/user_row",JSON.parse(c.rowdata)).then(function(c,d){return b.siblings('tr[data-row-id="'+k+'"]').remove(),e.replaceNode(b,c,d),b=a('.gradingtable .usergraderow[data-row-id="'+k+'"]').eq(0),h.initRow(b),null}).fail(function(a){d.exception(a)})},fail:d.exception}])},toggleHistory:function(b){var c=a("#historyrow-"+b.data("row-id")+" td.historycell"),d=c.find(".historydiv");c.is(":hidden")?(c.slideDown(100),d.slideDown(400)):(d.animate({height:0},400,function(){d.hide(),d.height("auto")}),c.hide(400))}};return h}); \ No newline at end of file +define(["jquery","gradeexport_ilp_push/page_info","core/ajax","core/notification","core/templates","core/str","core/log"],function(a,b,c,d,e,f,g){var h={failGrades:!1,incompleteGrades:!1,initAll:function(){a(".gradingtable .usergraderow").each(function(){h.initRow(a(this))})},initRow:function(c){var d=c.find(".gradeselect").eq(0);d.change(function(){var d=a(this).val();b.gradeIsIncomplete(d)?(c.find(".incomplete").show(),c.find(".fail").hide()):b.gradeIsFailure(d)?(c.find(".incomplete").hide(),c.find(".fail").show()):(c.find(".incomplete").hide(),c.find(".fail").hide()),h.updateVerification(c)}),c.find(".incompletedeadline input").eq(0).change(function(){h.updateVerification(c)}),c.find(".datelastattended input").eq(0).change(function(){h.updateVerification(c)}),c.find(".incompletegradeselect").eq(0).change(function(){h.updateVerification(c)}),c.find(".grademode .currentgrademode").eq(0).click(function(){h.showGradeModeSelector(c)}),c.find(".grademode .grademodeselect").eq(0).change(function(){h.changeGradeMode(c)}),c.find(".grademode .grademodeselect").eq(0).focusout(function(){h.hideGradeModeSelector(c)}),c.find(".togglehistory a").eq(0).click(function(a){a.preventDefault(),h.toggleHistory(c)}),h.updateVerification(c)},updateFormWarning:function(a,b,c,d){var h=a.find(b);if(h)return h=h.eq(0),c?void f.get_string(c,"gradeexport_ilp_push",d).done(function(a){e.renderPix("i/warning","core",a).then(function(a){h.html(a)}).fail(function(){g.debug("Failed to fetch the warning icon")})}):void h.html("")},updateVerification:function(a){if(!a.hasClass("locked")){var c=a.find(".gradeselect").eq(0),d=c.val(),e=a.find(".confirmcheckbox").eq(0),f=a.find(".grade .letter").eq(0).data("grade-key"),g=a.find(".grade .notequal").eq(0);f!=d?g.show():g.hide();var i=!1,j=!1,k="";if(d||(j=!0,k="invalid_grade"),h.updateFormWarning(a,".gradeerror",k),b.gradeIsIncomplete(d)){i=a.find(".incompletedeadline input").eq(0),k="",""==i.val()?(j=!0,k="invalid_incomplete_date"):b.isAllowedIncompleteDeadline(i.val())||(j=!0,k="invalid_incomplete_date"),""!=k&&(k=b.getIncompleteDeadlineStringName()),h.updateFormWarning(a,".incompletedateerror",k,b.getStringIncompleteDeadline());var l=a.find(".incompletegradeselect").eq(0);k="",l.val()?b.isAllowedIncompleteGrade(l.val())||(j=!0,k="invalid_incomplete_grade_wrong"):(j=!0,k="invalid_incomplete_grade_missing"),h.updateFormWarning(a,".incompletegradeerror",k,b.getIncompleteGrade())}else b.gradeIsFailure(d)&&(i=a.find(".datelastattended input").eq(0),k="",""==i.val()?(j=!0,k="invalid_datelastattended"):b.isAllowedLastAttendDate(i.val())||(j=!0,k="invalid_datelastattended"),h.updateFormWarning(a,".datelastattendederror",k,b.getStringLastAttendDates()));j?e.prop("disabled",!0):e.prop("disabled",!1)}},showGradeModeSelector:function(a){var b=a.find(".grademode .currentgrademode"),c=a.find(".grademode .grademodeselect");b.hide(),c.show()},hideGradeModeSelector:function(a){var b=a.find(".grademode .currentgrademode"),c=a.find(".grademode .grademodeselect");b.show(),c.hide()},changeGradeMode:function(b){var f=b.find(".grademode .currentgrademode"),g=b.find(".grademode .grademodeselect"),i=g.find("select").eq(0).val(),j=b.data("user-id"),k=b.data("row-id"),l=b.closest("form").find('input[name="courseid"]').val();c.call([{methodname:"gradeexport_ilp_push_update_row_grade_mode",args:{rowid:k,grademodeid:i,studentid:j,courseid:l},done:function(c){return c.warnings.length>0?(d.alert(c.warnings[0].message,c.warnings[0].item),f.show(),void g.hide()):void e.render("gradeexport_ilp_push/user_row",JSON.parse(c.rowdata)).then(function(c,d){return b.siblings('tr[data-row-id="'+k+'"]').remove(),e.replaceNode(b,c,d),b=a('.gradingtable .usergraderow[data-row-id="'+k+'"]').eq(0),h.initRow(b),null}).fail(function(a){d.exception(a)})},fail:d.exception}])},toggleHistory:function(b){var c=a("#historyrow-"+b.data("row-id")+" td.historycell"),d=c.find(".historydiv");c.is(":hidden")?(c.slideDown(100),d.slideDown(400)):(d.animate({height:0},400,function(){d.hide(),d.height("auto")}),c.hide(400))}};return h}); \ No newline at end of file diff --git a/amd/src/page_info.js b/amd/src/page_info.js index bcf9f7a..4ed569c 100644 --- a/amd/src/page_info.js +++ b/amd/src/page_info.js @@ -37,9 +37,19 @@ define([], function() { this.failGrades = failGrades; this.incompleteGrades = incompleteGrades; this.defaultIncomplete = defaultIncomplete; - this.incompleteDeadlineDates = {start: new Date(deadlineDates.start), + + var start = deadlineDates.start; + if (start !== false) { + start = new Date(start); + } + var end = deadlineDates.end; + if (end !== false) { + end = new Date(end); + } + + this.incompleteDeadlineDates = {start: start, userstart: deadlineDates.userstart, - end: new Date(deadlineDates.end), + end: end, userend: deadlineDates.userend}; this.lastAttendDates = {start: new Date(lastAttendDates.start), userstart: lastAttendDates.userstart, @@ -72,13 +82,14 @@ define([], function() { }, isAllowedIncompleteDeadline: function(value) { - var date = new Date(value); + var date = new Date(value).getTime(); + var st = this.incompleteDeadlineDates.start.getTime(); - if (date < this.incompleteDeadlineDates.start) { + if ((this.incompleteDeadlineDates.start !== false) && (date < st)) { return false; } - if (date > this.incompleteDeadlineDates.end) { + if ((this.incompleteDeadlineDates.end !== false) && (date > this.incompleteDeadlineDates.end.getTime())) { return false; } @@ -104,6 +115,20 @@ define([], function() { end: this.lastAttendDates.userend}; }, + getIncompleteDeadlineStringName: function() { + if (this.incompleteDeadlineDates.start === false) { + return 'invalid_incomplete_date_end'; + } + if (this.incompleteDeadlineDates.end === false) { + return 'invalid_incomplete_date_start'; + } + if (this.incompleteDeadlineDates.start.getTime() === this.incompleteDeadlineDates.end.getTime()) { + return 'invalid_incomplete_date_single'; + } + + return 'invalid_incomplete_date_range'; + }, + getStringIncompleteDeadline: function() { return {start: this.incompleteDeadlineDates.userstart, end: this.incompleteDeadlineDates.userend}; diff --git a/amd/src/row_control.js b/amd/src/row_control.js index 465212e..ee6913c 100755 --- a/amd/src/row_control.js +++ b/amd/src/row_control.js @@ -157,6 +157,9 @@ define(['jquery', 'gradeexport_ilp_push/page_info', 'core/ajax', 'core/notificat errorCode = 'invalid_incomplete_date'; } + if (errorCode != '') { + errorCode = pageInfo.getIncompleteDeadlineStringName(); + } RowController.updateFormWarning(row, '.incompletedateerror', errorCode, pageInfo.getStringIncompleteDeadline()); var incompleteSelect = row.find('.incompletegradeselect').eq(0); diff --git a/classes/banner_grades.php b/classes/banner_grades.php index 9efcb44..125d324 100644 --- a/classes/banner_grades.php +++ b/classes/banner_grades.php @@ -30,6 +30,7 @@ use stdClass; use gradeexport_ilp_push\local\data\grade_mode; +use gradeexport_ilp_push\local\sis_interface; /** * Deals with the interaction of banner grading. @@ -219,30 +220,61 @@ public static function get_allowed_last_attend_dates($course, $format = false, $ return $dates; } + /** + * Return the earliest and latest valid incomplete dates for the provided course. + * + * @param object $course The course object to reference. + * @param string $format The strftime format string to use. + * @param int $tz The timezone number to use. + * @return object The return object with keys of 'start' and 'end' timestamps. + * Start set to 'false' will indicate before end (inclusive) + * End set to 'false' will indicate after start (inclusive) + */ public static function get_allowed_last_incomplete_deadline_dates($course, $format = false, $tz = 99) { - $dates = new stdClass(); + $sis = sis_interface\factory::instance(); - if (empty($course->enddate)) { - // We just have to guess. TODO better. - $courseend = $course->startdate + (3600 * 24 * 7 * 16); - } else { - $courseend = $course->enddate; + // First see if there are SIS dates. + $dates = $sis->get_allowed_last_incomplete_deadline_dates($course); + + // If that didn't return anything, then get settings date. + if ($dates === false) { + $dates = new stdClass(); + + // Temp placeholder for 4/27/2020. + $dates->start = 1587988800; + $dates->end = 1587988800; } - // The day after the end of the course is the first allowed date (I think TODO). - $courseend += 3600 * 24; + // If we still don't have dates, we are going to make them up. + if ($dates === false) { + $dates = new stdClass(); + + if (empty($course->enddate)) { + // We just have to guess. TODO better. + $courseend = $course->startdate + (3600 * 24 * 7 * 16); + } else { + $courseend = $course->enddate; + } + + // The day after the end of the course is the first allowed date (I think TODO). + $courseend += 3600 * 24; - $dates->start = $courseend; + $dates->start = $courseend; - $dates->end = $courseend + (3600 * 24 * 380); + $dates->end = $courseend + (3600 * 24 * 380); + } if (!$format) { return $dates; } // Convert to a format. - $dates->start = date_format_string($dates->start, $format, $tz); - $dates->end = date_format_string($dates->end, $format, $tz); + if ($dates->start !== false) { + $dates->start = date_format_string($dates->start, $format, $tz); + } + if ($dates->end !== false) { + $dates->end = date_format_string($dates->end, $format, $tz); + } return $dates; } diff --git a/classes/local/sis_interface/base.php b/classes/local/sis_interface/base.php index fd8cb51..4b4b87f 100644 --- a/classes/local/sis_interface/base.php +++ b/classes/local/sis_interface/base.php @@ -117,6 +117,20 @@ public function teacher_allowed_to_grade_course($user, $course) { return true; } + /** + * A function that allows the SIS to override the allowed incomplete dates for a course. + * + * Override to implement. + * + * @param object $course The course object to reference. + * @return object The return object with keys of 'start' and 'end' timestamps. + * Start set to 'false' will indicate before end (inclusive) + * End set to 'false' will indicate after start (inclusive) + */ + public function get_allowed_last_incomplete_deadline_dates($course) { + return false; + } + } diff --git a/classes/rule_validator.php b/classes/rule_validator.php index ee14e07..7c1fdb8 100644 --- a/classes/rule_validator.php +++ b/classes/rule_validator.php @@ -91,13 +91,14 @@ public static function validate_row(saved_grade $grade, grade_exporter $exporter if (is_null($grade->incompletedeadline)) { // TODO - this might not be required... $results['errors']['incompletedeadline'] = get_string('invalid_incomplete_date_missing', 'gradeexport_ilp_push'); - } else if ($grade->incompletedeadline < $incompletedeadline->start) { + } else if (($incompletedeadline->start !== false) && ($grade->incompletedeadline < $incompletedeadline->start)) { // If present, the incomplete date must be within a certain timeline (settings dependent). // TODO better date ranges and error messages... $results['errors']['incompletedeadline'] = get_string('invalid_incomplete_date_early', 'gradeexport_ilp_push'); - } else if ($grade->incompletedeadline > $incompletedeadline->end) { + } else if (($incompletedeadline->end !== false) && ($grade->incompletedeadline > $incompletedeadline->end)) { $results['errors']['incompletedeadline'] = get_string('invalid_incomplete_date_late', 'gradeexport_ilp_push'); } + } return $results; diff --git a/classes/settings.php b/classes/settings.php index 9d5d5c9..de67af4 100644 --- a/classes/settings.php +++ b/classes/settings.php @@ -41,6 +41,10 @@ class settings { protected $settings = null; + protected $termid = null; + + protected $termsettings = null; + /** * A static factory for the settings object. * @@ -54,6 +58,10 @@ public static function get_settings() { return static::$settingobj; } + protected function set_term_id($termid) { + + } + protected function __construct() { $this->settings = get_config('gradeexport_ilp_push'); } diff --git a/lang/en/gradeexport_ilp_push.php b/lang/en/gradeexport_ilp_push.php index 5175839..7e8c717 100644 --- a/lang/en/gradeexport_ilp_push.php +++ b/lang/en/gradeexport_ilp_push.php @@ -100,6 +100,10 @@ $string['invalid_incomplete_grade'] = 'Default incomplete grade must be set to "{$a}".'; $string['invalid_incomplete_date'] = 'Incomplete deadline must be between {$a->start} and {$a->end}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; +$string['invalid_incomplete_date_range'] = 'Incomplete deadline must be between {$a->start} and {$a->end}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; +$string['invalid_incomplete_date_start'] = 'Incomplete deadline must be on or after {$a->start}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; +$string['invalid_incomplete_date_end'] = 'Incomplete deadline must be on or before {$a->end}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; +$string['invalid_incomplete_date_single'] = 'Incomplete deadline must be on {$a->start}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; $string['invalid_datelastattended'] = 'Date last attended must be between {$a->start} and {$a->end}. Please use MM/DD/YYYY or YYYY-MM-DD format.'; $string['grader_no_id'] = 'The current user has no SIS ID, and so cannot export grades to Banner.';