Merge pull request #3097 from dpalou/MOBILE-3958

Mobile 3958
main
Pau Ferrer Ocaña 2022-02-07 15:10:49 +01:00 committed by GitHub
commit 8bae313a3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 99 deletions

View File

@ -1734,6 +1734,7 @@
"core.grades.calculatedgrade": "grades",
"core.grades.category": "grades",
"core.grades.contributiontocoursetotal": "grades",
"core.grades.fail": "grades",
"core.grades.feedback": "grades",
"core.grades.grade": "grades",
"core.grades.gradeitem": "grades",
@ -1744,6 +1745,7 @@
"core.grades.nogradesreturned": "grades",
"core.grades.nooutcome": "grades",
"core.grades.outcome": "grades",
"core.grades.pass": "grades",
"core.grades.percentage": "grades",
"core.grades.range": "grades",
"core.grades.rank": "grades",

View File

@ -196,7 +196,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @param response Response of get submission status.
*/
protected calculateTimeRemaining(response: AddonModAssignGetSubmissionStatusWSResponse): void {
if (this.assign!.duedate <= 0) {
if (!this.assign) {
return;
}
if (this.assign.duedate <= 0) {
this.timeRemaining = '';
this.timeRemainingClass = '';
@ -206,7 +210,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
const time = CoreTimeUtils.timestamp();
const dueDate = response.lastattempt?.extensionduedate
? response.lastattempt.extensionduedate
: this.assign!.duedate;
: this.assign.duedate;
const timeRemaining = dueDate - time;
if (timeRemaining > 0) {
@ -276,6 +280,10 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* Copy a previous attempt and then go to edit.
*/
async copyPrevious(): Promise<void> {
if (!this.assign) {
return;
}
if (!CoreApp.isOnline()) {
CoreDomUtils.showErrorModal('core.networkerrormsg', true);
@ -291,7 +299,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
let modal = await CoreDomUtils.showModalLoading();
const size = await CoreUtils.ignoreErrors(
AddonModAssignHelper.getSubmissionSizeForCopy(this.assign!, previousSubmission),
AddonModAssignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission),
-1,
); // Error calculating size, return -1.
@ -309,15 +317,15 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
modal = await CoreDomUtils.showModalLoading('core.sending', true);
try {
await AddonModAssignHelper.copyPreviousAttempt(this.assign!, previousSubmission);
await AddonModAssignHelper.copyPreviousAttempt(this.assign, previousSubmission);
// Now go to edit.
this.goToEdit();
if (!this.assign!.submissiondrafts) {
if (!this.assign.submissiondrafts && this.userSubmission) {
// No drafts allowed, so it was submitted. Trigger event.
CoreEvents.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, {
assignmentId: this.assign!.id,
submissionId: this.userSubmission!.id,
assignmentId: this.assign.id,
submissionId: this.userSubmission.id,
userId: this.currentUserId,
}, this.siteId);
} else {
@ -337,8 +345,8 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @return Promise resolved when done.
*/
protected async discardDrafts(): Promise<void> {
if (this.feedback && this.feedback.plugins) {
await AddonModAssignHelper.discardFeedbackPluginData(this.assign!.id, this.submitId, this.feedback);
if (this.assign && this.feedback && this.feedback.plugins) {
await AddonModAssignHelper.discardFeedbackPluginData(this.assign.id, this.submitId, this.feedback);
}
}
@ -363,7 +371,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @return Promise resolved with boolean: whether there's data to save.
*/
protected async hasDataToSave(isSubmit = false): Promise<boolean> {
if (!this.canSaveGrades || !this.loaded) {
if (!this.canSaveGrades || !this.loaded || !this.assign) {
return false;
}
@ -379,7 +387,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
}
// Check if outcomes changed.
if (this.gradeInfo && this.gradeInfo.outcomes) {
if (this.gradeInfo?.outcomes) {
for (const x in this.gradeInfo.outcomes) {
const outcome = this.gradeInfo.outcomes[x];
@ -396,7 +404,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
try {
return AddonModAssignHelper.hasFeedbackDataChanged(
this.assign!,
this.assign,
this.userSubmission,
this.feedback,
this.submitId,
@ -435,13 +443,13 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
promises.push(AddonModAssign.invalidateAssignmentData(this.courseId));
if (this.assign) {
promises.push(AddonModAssign.invalidateSubmissionStatusData(
this.assign!.id,
this.assign.id,
this.submitId,
undefined,
!!this.blindId,
));
promises.push(AddonModAssign.invalidateAssignmentUserMappingsData(this.assign!.id));
promises.push(AddonModAssign.invalidateListParticipantsData(this.assign!.id));
promises.push(AddonModAssign.invalidateAssignmentUserMappingsData(this.assign.id));
promises.push(AddonModAssign.invalidateListParticipantsData(this.assign.id));
}
promises.push(CoreGradesHelper.invalidateGradeModuleItems(this.courseId, this.submitId));
promises.push(CoreCourse.invalidateModule(this.moduleId));
@ -534,13 +542,13 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
}
// Treat last attempt.
promises = this.treatLastAttempt(submissionStatus);
promises = this.treatLastAttempt(submissionStatus, this.lastAttempt);
// Calculate the time remaining.
this.calculateTimeRemaining(submissionStatus);
// Load the feedback.
promises.push(this.loadFeedback(submissionStatus.feedback));
promises.push(this.loadFeedback(this.assign, submissionStatus.feedback));
// Check if there's any unsupported plugin for editing.
if (!this.userSubmission || !this.userSubmission.plugins) {
@ -575,8 +583,12 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @return Promise resolved when done.
*/
protected async loadSubmissionOfflineData(): Promise<void> {
if (!this.assign) {
return;
}
try {
const submission = await AddonModAssignOffline.getSubmission(this.assign!.id, this.submitId);
const submission = await AddonModAssignOffline.getSubmission(this.assign.id, this.submitId);
this.hasOffline = submission && submission.plugindata && Object.keys(submission.plugindata).length > 0;
@ -591,10 +603,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
/**
* Load the data to render the feedback and grade.
*
* @param assign Assign data.
* @param feedback The feedback data from the submission status.
* @return Promise resolved when done.
*/
protected async loadFeedback(feedback?: AddonModAssignSubmissionFeedback): Promise<void> {
protected async loadFeedback(assign: AddonModAssignAssign, feedback?: AddonModAssignSubmissionFeedback): Promise<void> {
this.grade = {
method: '',
modified: 0,
@ -654,22 +667,22 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
// Treat the grade info.
await this.treatGradeInfo();
const isManual = this.assign!.attemptreopenmethod == AddonModAssignAttemptReopenMethodValues.MANUAL;
const isUnlimited = this.assign!.maxattempts == AddonModAssignProvider.UNLIMITED_ATTEMPTS;
const isLessThanMaxAttempts = !!this.userSubmission && (this.userSubmission.attemptnumber < (this.assign!.maxattempts - 1));
const isManual = assign.attemptreopenmethod == AddonModAssignAttemptReopenMethodValues.MANUAL;
const isUnlimited = assign.maxattempts == AddonModAssignProvider.UNLIMITED_ATTEMPTS;
const isLessThanMaxAttempts = !!this.userSubmission && (this.userSubmission.attemptnumber < (assign.maxattempts - 1));
this.allowAddAttempt = isManual && (!this.userSubmission || isUnlimited || isLessThanMaxAttempts);
if (this.assign!.teamsubmission) {
if (assign.teamsubmission) {
this.grade.applyToAll = true;
this.originalGrades.applyToAll = true;
}
if (this.assign!.markingworkflow && this.grade.gradingStatus) {
if (assign.markingworkflow && this.grade.gradingStatus) {
this.workflowStatusTranslationId =
AddonModAssign.getSubmissionGradingStatusTranslationId(this.grade.gradingStatus);
}
if (this.lastAttempt?.gradingstatus == 'graded' && !this.assign!.markingworkflow && this.userSubmission && feedback) {
if (this.lastAttempt?.gradingstatus == 'graded' && !assign.markingworkflow && this.userSubmission && feedback) {
if (feedback.gradeddate < this.userSubmission.timemodified) {
this.lastAttempt.gradingstatus = AddonModAssignGradingStates.GRADED_FOLLOWUP_SUBMIT;
@ -685,7 +698,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
if (!this.feedback || !this.feedback.plugins) {
// Feedback plugins not present, we have to use assign configs to detect the plugins used.
this.feedback = AddonModAssignHelper.createEmptyFeedback();
this.feedback.plugins = AddonModAssignHelper.getPluginsEnabled(this.assign!, 'assignfeedback');
this.feedback.plugins = AddonModAssignHelper.getPluginsEnabled(assign, 'assignfeedback');
}
// Check if there's any offline data for this submission.
@ -700,7 +713,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
// Submission grades aren't identified by attempt number so it can retrieve the feedback for a previous attempt.
// The app will not treat that as an special case.
const submissionGrade = await CoreUtils.ignoreErrors(
AddonModAssignOffline.getSubmissionGrade(this.assign!.id, this.submitId),
AddonModAssignOffline.getSubmissionGrade(assign.id, this.submitId),
);
this.hasOfflineGrade = false;
@ -725,10 +738,10 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
if (submissionGrade.outcomes && Object.keys(submissionGrade.outcomes).length && this.gradeInfo?.outcomes) {
this.gradeInfo.outcomes.forEach((outcome) => {
if (submissionGrade.outcomes[outcome.itemNumber!] !== undefined) {
if (outcome.itemNumber !== undefined && submissionGrade.outcomes[outcome.itemNumber] !== undefined) {
// If outcome has been modified from gradebook, do not use offline.
if (outcome.modified! < submissionGrade.timemodified) {
outcome.selectedId = submissionGrade.outcomes[outcome.itemNumber!];
if ((outcome.modified || 0) < submissionGrade.timemodified) {
outcome.selectedId = submissionGrade.outcomes[outcome.itemNumber];
this.originalGrades.outcomes[outcome.id] = outcome.selectedId;
}
}
@ -752,11 +765,15 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @param status Submission status.
*/
protected setStatusNameAndClass(status: AddonModAssignGetSubmissionStatusWSResponse): void {
if (!this.assign) {
return;
}
if (this.hasOffline || this.submittedOffline) {
// Offline data.
this.statusTranslated = Translate.instant('core.notsent');
this.statusColor = 'warning';
} else if (!this.assign!.teamsubmission) {
} else if (!this.assign.teamsubmission) {
// Single submission.
if (this.userSubmission && this.userSubmission.status != this.statusNew) {
@ -775,7 +792,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
} else {
// Team submission.
if (!status.lastattempt?.submissiongroup && this.assign!.preventsubmissionnotingroup) {
if (!status.lastattempt?.submissiongroup && this.assign.preventsubmissionnotingroup) {
this.statusTranslated = Translate.instant('addon.mod_assign.nosubmission');
this.statusColor = AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_SUBMISSION);
} else if (this.userSubmission && this.userSubmission.status != this.statusNew) {
@ -816,7 +833,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
* @param acceptStatement Whether the statement has been accepted.
*/
async submitForGrading(acceptStatement: boolean): Promise<void> {
if (this.assign!.requiresubmissionstatement && !acceptStatement) {
if (!this.assign || !this.userSubmission) {
return;
}
if (this.assign.requiresubmissionstatement && !acceptStatement) {
CoreDomUtils.showErrorModal('addon.mod_assign.acceptsubmissionstatement', true);
return;
@ -830,17 +851,17 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
try {
await AddonModAssign.submitForGrading(
this.assign!.id,
this.assign.id,
this.courseId,
acceptStatement,
this.userSubmission!.timemodified,
this.userSubmission.timemodified,
this.hasOffline,
);
// Submitted, trigger event.
CoreEvents.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, {
assignmentId: this.assign!.id,
submissionId: this.userSubmission!.id,
assignmentId: this.assign.id,
submissionId: this.userSubmission.id,
userId: this.currentUserId,
}, this.siteId);
} catch (error) {
@ -861,7 +882,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
async submitGrade(): Promise<void> {
// Check if there's something to be saved.
const modified = await this.hasDataToSave(true);
if (!modified) {
if (!modified || !this.assign) {
return;
}
@ -880,22 +901,21 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
const modal = await CoreDomUtils.showModalLoading('core.sending', true);
(this.gradeInfo?.outcomes || []).forEach((outcome) => {
if (outcome.itemNumber) {
outcomes[outcome.itemNumber] = outcome.selectedId!;
if (outcome.itemNumber && outcome.selectedId) {
outcomes[outcome.itemNumber] = outcome.selectedId;
}
});
let pluginData: AddonModAssignSavePluginData = {};
try {
if (this.feedback && this.feedback.plugins) {
pluginData =
await AddonModAssignHelper.prepareFeedbackPluginData(this.assign!.id, this.submitId, this.feedback);
pluginData = await AddonModAssignHelper.prepareFeedbackPluginData(this.assign.id, this.submitId, this.feedback);
}
try {
// We have all the data, now send it.
await AddonModAssign.submitGradingForm(
this.assign!.id,
this.assign.id,
this.submitId,
this.courseId,
grade || 0,
@ -914,7 +934,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
this.invalidateAndRefresh(true);
CoreEvents.trigger(AddonModAssignProvider.GRADED_EVENT, {
assignmentId: this.assign!.id,
assignmentId: this.assign.id,
submissionId: this.submitId,
userId: this.currentUserId,
}, this.siteId);
@ -939,21 +959,21 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
this.isGrading = true;
// Make sure outcomes is an array.
this.gradeInfo.outcomes = this.gradeInfo.outcomes || [];
const gradeInfo = this.gradeInfo;
gradeInfo.outcomes = gradeInfo.outcomes || [];
// Check if grading method is simple or not.
if (this.gradeInfo.advancedgrading && this.gradeInfo.advancedgrading[0] &&
this.gradeInfo.advancedgrading[0].method !== undefined) {
this.grade.method = this.gradeInfo.advancedgrading[0].method || 'simple';
if (gradeInfo.advancedgrading && gradeInfo.advancedgrading[0] && gradeInfo.advancedgrading[0].method !== undefined) {
this.grade.method = gradeInfo.advancedgrading[0].method || 'simple';
} else {
this.grade.method = 'simple';
}
this.canSaveGrades = this.grade.method == 'simple'; // Grades can be saved if simple grading.
if (this.gradeInfo.scale) {
if (gradeInfo.scale) {
this.grade.scale =
CoreUtils.makeMenuFromList(this.gradeInfo.scale, Translate.instant('core.nograde'));
CoreUtils.makeMenuFromList(gradeInfo.scale, Translate.instant('core.nograde'));
} else {
// Format the grade.
this.grade.grade = CoreUtils.formatFloat(this.grade.grade);
@ -964,8 +984,8 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
}
// Treat outcomes.
if (this.gradeInfo.outcomes) {
this.gradeInfo.outcomes.forEach((outcome) => {
if (gradeInfo.outcomes) {
gradeInfo.outcomes.forEach((outcome) => {
if (outcome.scale) {
outcome.options =
CoreUtils.makeMenuFromList<number>(
@ -986,7 +1006,8 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
grades.forEach((grade: CoreGradesFormattedItem) => {
if (!grade.outcomeid && !grade.scaleid) {
const gradeFormatted = grade.gradeformatted || '';
// Clean HTML tags, grade can contain an icon.
const gradeFormatted = CoreTextUtils.cleanTags(grade.gradeformatted || '');
// Not using outcomes or scale, get the numeric grade.
if (this.grade.scale) {
this.grade.gradebookGrade = CoreUtils.formatFloat(
@ -1004,7 +1025,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
} else if (grade.outcomeid) {
// Only show outcomes with info on it, outcomeid could be null if outcomes are disabled on site.
this.gradeInfo!.outcomes && this.gradeInfo!.outcomes.forEach((outcome) => {
gradeInfo.outcomes?.forEach((outcome) => {
if (outcome.id == String(grade.outcomeid)) {
outcome.selected = grade.gradeformatted;
outcome.modified = grade.gradedategraded;
@ -1017,38 +1038,42 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
outcomes.push(outcome);
}
});
this.gradeInfo!.disabled = grade.gradeislocked || grade.gradeisoverridden;
gradeInfo.disabled = grade.gradeislocked || grade.gradeisoverridden;
}
});
this.gradeInfo.outcomes = outcomes;
gradeInfo.outcomes = outcomes;
}
/**
* Treat the last attempt.
*
* @param submissionStatus Response of get submission status.
* @param lastAttempt Last attempt (if any).
* @param promises List where to add the promises.
*/
protected treatLastAttempt(submissionStatus: AddonModAssignGetSubmissionStatusWSResponse): Promise<void>[] {
protected treatLastAttempt(
submissionStatus: AddonModAssignGetSubmissionStatusWSResponse,
lastAttempt?: AddonModAssignSubmissionAttemptFormatted,
): Promise<void>[] {
const promises: Promise<void>[] =[];
if (!submissionStatus.lastattempt) {
if (!lastAttempt || !this.assign) {
return [];
}
const submissionStatementMissing = !!this.assign!.requiresubmissionstatement &&
this.assign!.submissionstatement === undefined;
const submissionStatementMissing = !!this.assign.requiresubmissionstatement &&
this.assign.submissionstatement === undefined;
this.canSubmit = !this.isSubmittedForGrading && !this.submittedOffline && (submissionStatus.lastattempt.cansubmit ||
(this.hasOffline && AddonModAssign.canSubmitOffline(this.assign!, submissionStatus)));
this.canSubmit = !this.isSubmittedForGrading && !this.submittedOffline && (lastAttempt.cansubmit ||
(this.hasOffline && AddonModAssign.canSubmitOffline(this.assign, submissionStatus)));
this.canEdit = !this.isSubmittedForGrading && submissionStatus.lastattempt.canedit &&
(!this.submittedOffline || !this.assign!.submissiondrafts);
this.canEdit = !this.isSubmittedForGrading && lastAttempt.canedit &&
(!this.submittedOffline || !this.assign.submissiondrafts);
// Get submission statement if needed.
if (this.assign!.requiresubmissionstatement && this.assign!.submissiondrafts && this.submitId == this.currentUserId) {
this.submissionStatement = this.assign!.submissionstatement;
if (this.assign.requiresubmissionstatement && this.assign.submissiondrafts && this.submitId == this.currentUserId) {
this.submissionStatement = this.assign.submissionstatement;
this.acceptStatement = false;
} else {
this.submissionStatement = undefined;
@ -1056,26 +1081,26 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
}
// Show error if submission statement should be shown but it couldn't be retrieved.
this.showErrorStatementEdit = submissionStatementMissing && !this.assign!.submissiondrafts &&
this.showErrorStatementEdit = submissionStatementMissing && !this.assign.submissiondrafts &&
this.submitId == this.currentUserId;
this.showErrorStatementSubmit = submissionStatementMissing && !!this.assign!.submissiondrafts;
this.showErrorStatementSubmit = submissionStatementMissing && !!this.assign.submissiondrafts;
this.userSubmission = AddonModAssign.getSubmissionObjectFromAttempt(this.assign!, submissionStatus.lastattempt);
this.userSubmission = AddonModAssign.getSubmissionObjectFromAttempt(this.assign, lastAttempt);
if (this.assign!.attemptreopenmethod != this.attemptReopenMethodNone && this.userSubmission) {
if (this.assign.attemptreopenmethod != this.attemptReopenMethodNone && this.userSubmission) {
this.currentAttempt = this.userSubmission.attemptnumber + 1;
}
this.setStatusNameAndClass(submissionStatus);
if (this.assign!.teamsubmission) {
if (submissionStatus.lastattempt.submissiongroup) {
if (this.assign.teamsubmission) {
if (lastAttempt.submissiongroup) {
// Get the name of the group.
promises.push(CoreGroups.getActivityAllowedGroups(this.assign!.cmid).then((result) => {
const group = result.groups.find((group) => group.id == submissionStatus.lastattempt!.submissiongroup);
promises.push(CoreGroups.getActivityAllowedGroups(this.assign.cmid).then((result) => {
const group = result.groups.find((group) => group.id === lastAttempt.submissiongroup);
if (group) {
this.lastAttempt!.submissiongroupname = group.name;
lastAttempt.submissiongroupname = group.name;
}
return;
@ -1085,9 +1110,9 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
// Get the members that need to submit.
if (this.userSubmission &&
this.userSubmission.status != this.statusNew &&
submissionStatus.lastattempt.submissiongroupmemberswhoneedtosubmit
lastAttempt.submissiongroupmemberswhoneedtosubmit
) {
submissionStatus.lastattempt.submissiongroupmemberswhoneedtosubmit.forEach((member) => {
lastAttempt.submissiongroupmemberswhoneedtosubmit.forEach((member) => {
if (!this.blindMarking) {
promises.push(CoreUser.getProfile(member, this.courseId).then((profile) => {
this.membersToSubmit.push(profile);
@ -1100,23 +1125,20 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
}
// Get grading text and color.
this.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(
submissionStatus.lastattempt.gradingstatus,
);
this.gradingColor = AddonModAssign.getSubmissionGradingStatusColor(submissionStatus.lastattempt.gradingstatus);
this.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(lastAttempt.gradingstatus);
this.gradingColor = AddonModAssign.getSubmissionGradingStatusColor(lastAttempt.gradingstatus);
// Get the submission plugins.
if (this.userSubmission) {
if (!this.assign!.teamsubmission ||
!submissionStatus.lastattempt.submissiongroup ||
!this.assign!.preventsubmissionnotingroup
if (!this.assign.teamsubmission ||
!lastAttempt.submissiongroup ||
!this.assign.preventsubmissionnotingroup
) {
if (this.previousAttempt && this.previousAttempt.submission!.plugins &&
this.userSubmission.status == this.statusReopened) {
if (this.previousAttempt?.submission?.plugins && this.userSubmission.status === this.statusReopened) {
// Get latest attempt if available.
this.submissionPlugins = this.previousAttempt.submission!.plugins;
this.submissionPlugins = this.previousAttempt.submission.plugins;
} else {
this.submissionPlugins = this.userSubmission.plugins!;
this.submissionPlugins = this.userSubmission.plugins || [];
}
}
}
@ -1134,7 +1156,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
return;
}
const syncId = AddonModAssignSync.getGradeSyncId(this.assign!.id, this.submitId);
const syncId = AddonModAssignSync.getGradeSyncId(this.assign.id, this.submitId);
if (block) {
CoreSync.blockOperation(AddonModAssignProvider.COMPONENT, syncId);

View File

@ -6,6 +6,7 @@
"calculatedgrade": "Calculated grade",
"category": "Category",
"contributiontocoursetotal": "Contribution to course total",
"fail": "Fail",
"feedback": "Feedback",
"grade": "Grade",
"gradeitem": "Grade item",
@ -16,6 +17,7 @@
"nogradesreturned": "No grades returned",
"nooutcome": "No outcome",
"outcome": "Outcome",
"pass": "Pass",
"percentage": "Percentage",
"range": "Range",
"rank": "Rank",

View File

@ -49,8 +49,8 @@
<span [innerHTML]="row.gradeitem"></span>
</th>
<ng-container *ngFor="let column of columns">
<td *ngIf="column.name !== 'gradeitem' && column.name !== 'feedback' && row[column.name] != undefined"
[class]="'ion-text-start core-grades-table-' + column.name"
<td *ngIf="column.name !== 'gradeitem' && column.name !== 'feedback' && column.name !== 'grade' &&
row[column.name] != undefined" [class]="'ion-text-start core-grades-table-' + column.name"
[class.ion-hide-md-down]="column.hiddenPhone" [innerHTML]="row[column.name]">
</td>
<td *ngIf="column.name === 'feedback' && row.feedback !== undefined"
@ -59,6 +59,12 @@
[contextInstanceId]="courseId">
</core-format-text>
</td>
<td *ngIf="column.name === 'grade'" [class.ion-hide-md-down]="column.hiddenPhone"
class="ion-text-start core-grades-table-grade {{row.gradeClass}}">
<ion-icon *ngIf="row.gradeIcon" [name]="row.gradeIcon" [attr.aria-label]="row.gradeIconAlt">
</ion-icon>
<span [innerHTML]="row[column.name]"></span>
</td>
</ng-container>
</ng-container>
</tr>

View File

@ -42,7 +42,7 @@
}
th, td {
@include padding(10px, 10px, 10px, null);
@include padding(8px, 8px, 8px, null);
vertical-align: top;
white-space: normal;
text-align: start;
@ -55,7 +55,7 @@
}
thead #gradeitem {
@include padding(null, null, null, 23px);
@include padding(null, null, null, 24px);
}
tbody th {
@ -63,11 +63,11 @@
}
tbody #gradeitem {
@include padding(null, null, null, 5px);
@include padding(null, null, null, 4px);
}
.core-grades-table-gradeitem {
@include padding(null, null, null, 5px);
@include padding(null, null, null, 4px);
font-weight: bold;
&.column-itemname {
@ -90,7 +90,7 @@
}
span {
@include margin(null, null, null, 5px);
@include margin(null, null, null, 4px);
}
.expandable-status-icon {
@ -101,7 +101,7 @@
}
.core-grades-table-feedback {
@include padding(null, null, null, 5px);
@include padding(null, null, null, 4px);
.no-overflow {
overflow: auto;
@ -109,6 +109,12 @@
}
.core-grades-table-grade {
ion-icon {
@include padding(null, 4px, null, null);
}
}
.dimmed_text,
.hidden {
opacity: .7;

View File

@ -108,7 +108,7 @@ export class CoreGradesHelperProvider {
let content = String(column.content);
if (name == 'itemname') {
if (name === 'itemname') {
const itemNameColumn = <CoreGradesTableItemNameColumn> column;
row.id = parseInt(itemNameColumn.id.split('_')[1], 10);
@ -123,6 +123,20 @@ export class CoreGradesHelperProvider {
content = content.replace(/<\/span>/gi, '\n');
content = CoreTextUtils.cleanTags(content);
name = 'gradeitem';
} else if (name === 'grade') {
// Add the pass/fail class if present.
row.gradeClass = column.class.includes('gradepass') ? 'text-success' :
(column.class.includes('gradefail') ? 'text-danger' : '');
if (content.includes('fa-check')) {
row.gradeIcon = 'fas-check';
row.gradeIconAlt = Translate.instant('core.grades.pass');
content = CoreTextUtils.cleanTags(content);
} else if (content.includes('fa-times')) {
row.gradeIcon = 'fas-times';
row.gradeIconAlt = Translate.instant('core.grades.fail');
content = CoreTextUtils.cleanTags(content);
}
} else {
content = CoreTextUtils.replaceNewLines(content, '<br>');
}
@ -721,6 +735,9 @@ export type CoreGradesFormattedTableRow = CoreGradesFormattedRowCommonData & {
ariaLabel?: string;
expandable?: boolean;
expanded?: boolean;
gradeClass?: string;
gradeIcon?: string;
gradeIconAlt?: string;
};
export type CoreGradesFormattedTableColumn = {

View File

@ -357,7 +357,7 @@ export class CoreH5PContentValidator {
}
// Remove temporary files suffix.
if (file.path.substring(-4, 4) === '#tmp') {
if (file.path.slice(-4) === '#tmp') {
file.path = file.path.substring(0, file.path.length - 4);
}