Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOBILE-3893 assign: Add button to remove submissions #4191

Merged
merged 2 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/langindex.json
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,12 @@
"addon.mod_assign.numwords": "moodle",
"addon.mod_assign.outof": "assign",
"addon.mod_assign.overdue": "assign",
"addon.mod_assign.removesubmission": "assign",
"addon.mod_assign.removesubmissionconfirm": "assign",
"addon.mod_assign.removesubmissionconfirmwithtimelimit": "assign",
"addon.mod_assign.submission": "assign",
"addon.mod_assign.submissioneditable": "assign",
"addon.mod_assign.submissionempty": "assign",
"addon.mod_assign.submissionnoteditable": "assign",
"addon.mod_assign.submissionnotsupported": "local_moodlemobileapp",
"addon.mod_assign.submissionslocked": "assign",
Expand Down
12 changes: 12 additions & 0 deletions src/addons/mod/assign/components/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
ADDON_MOD_ASSIGN_GRADED_EVENT,
ADDON_MOD_ASSIGN_PAGE_NAME,
ADDON_MOD_ASSIGN_STARTED_EVENT,
ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT,
ADDON_MOD_ASSIGN_SUBMISSION_SAVED_EVENT,
ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT,
ADDON_MOD_ASSIGN_WARN_GROUPS_OPTIONAL,
Expand Down Expand Up @@ -126,6 +127,17 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
this.siteId,
);

this.savedObserver = CoreEvents.on(
ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT,
(data) => {
if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) {
// Assignment submission removed, refresh data.
this.showLoadingAndRefresh(true, false);
}
},
this.siteId,
);

this.submittedObserver = CoreEvents.on(
ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT,
(data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,20 @@ <h2>{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}</h2>
<div class="list-item-limited-width" *ngIf="canEdit || canSubmit">
<ng-container *ngIf="canEdit">
<ng-container *ngIf=" !unsupportedEditPlugins.length && !showErrorStatementEdit">
<!-- If has offline data, show edit. -->
<ion-button expand="block" class="ion-text-wrap" *ngIf="hasOffline" (click)="goToEdit()">
{{ 'addon.mod_assign.editsubmission' | translate }}
</ion-button>
<!-- If has offline data, show edit and remove. -->
<div *ngIf="editedOffline" class="adaptable-buttons-row">
<ion-button expand="block" class="ion-margin ion-text-wrap" (click)="goToEdit()">
{{ 'addon.mod_assign.editsubmission' | translate }}
</ion-button>
<ion-button *ngIf="isRemoveAvailable" expand="block" class="ion-margin ion-text-wrap"
(click)="remove()">
{{ 'addon.mod_assign.removesubmission' | translate }}
</ion-button>
</div>
<!-- If no submission or is new, show add submission. -->
<ion-button expand="block" class="ion-text-wrap" (click)="goToEdit()" *ngIf="!hasOffline &&
(!userSubmission || !userSubmission!.status || userSubmission!.status === statusNew)">
<ion-button expand="block" class="ion-text-wrap" (click)="goToEdit()" *ngIf="!editedOffline &&
(removedOffline || !userSubmission || !userSubmission!.status ||
userSubmission!.status === statusNew)">
<ng-container *ngIf="!assign?.timelimit || userSubmission?.timestarted">
{{ 'addon.mod_assign.addsubmission' | translate }}
</ng-container>
Expand All @@ -182,7 +189,7 @@ <h2>{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}</h2>
</ng-container>
</ion-button>
<!-- If reopened, show addfromprevious and addnewattempt. -->
<ng-container *ngIf="!hasOffline && userSubmission?.status === statusReopened">
<ng-container *ngIf="!editedOffline && !removedOffline && userSubmission?.status === statusReopened">
<ion-button *ngIf="!isPreviousAttemptEmpty" expand="block" class="ion-text-wrap"
(click)="copyPrevious()">
{{ 'addon.mod_assign.addnewattemptfromprevious' | translate }}
Expand All @@ -191,12 +198,18 @@ <h2>{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}</h2>
{{ 'addon.mod_assign.addnewattempt' | translate }}
</ion-button>
</ng-container>
<!-- Else show editsubmission. -->
<ion-button expand="block" class="ion-text-wrap" *ngIf="!hasOffline && userSubmission &&
userSubmission!.status && userSubmission!.status !== statusNew &&
userSubmission!.status !== statusReopened" (click)="goToEdit()">
{{ 'addon.mod_assign.editsubmission' | translate }}
</ion-button>
<!-- Else show editsubmission and removesubmission. -->
<div *ngIf="!editedOffline && !removedOffline && userSubmission && userSubmission!.status
&& userSubmission!.status !== statusNew && userSubmission!.status !== statusReopened"
class="adaptable-buttons-row">
<ion-button expand="block" class="ion-margin ion-text-wrap" (click)="goToEdit()">
{{ 'addon.mod_assign.editsubmission' | translate }}
</ion-button>
<ion-button *ngIf="isRemoveAvailable" expand="block" class="ion-margin ion-text-wrap"
(click)="remove()">
{{ 'addon.mod_assign.removesubmission' | translate }}
</ion-button>
</div>
</ng-container>
<ion-item class="core-danger-item ion-text-wrap"
*ngIf="(unsupportedEditPlugins.length && !showErrorStatementEdit)|| showErrorStatementEdit">
Expand Down
72 changes: 59 additions & 13 deletions src/addons/mod/assign/components/submission/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
ADDON_MOD_ASSIGN_GRADED_EVENT,
ADDON_MOD_ASSIGN_MANUAL_SYNCED,
ADDON_MOD_ASSIGN_PAGE_NAME,
ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT,
ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT,
ADDON_MOD_ASSIGN_UNLIMITED_ATTEMPTS,
} from '../../constants';
Expand Down Expand Up @@ -96,8 +97,9 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
isSubmittedForGrading = false; // Whether the submission has been submitted for grading.
acceptStatement = false; // Statement accepted (for grading).
feedback?: AddonModAssignSubmissionFeedbackFormatted; // The feedback.
hasOffline = false; // Whether there is offline data.
editedOffline = false; // Whether the submission was added or edited in offline.
submittedOffline = false; // Whether it was submitted in offline.
removedOffline = false; // Whether the submission was removed in offline.
fromDate?: string; // Readable date when the assign started accepting submissions.
currentAttempt = 0; // The current attempt number.
maxAttemptsText: string; // The text for maximum attempts.
Expand All @@ -108,6 +110,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
membersToSubmitBlind: number[] = []; // Team members that need to submit the assignment (blindmarking).
canSubmit = false; // Whether the user can submit for grading.
canEdit = false; // Whether the user can edit the submission.
isRemoveAvailable = false; // Whether WS to remove submission is available.
submissionStatement?: string; // The submission statement.
showErrorStatementEdit = false; // Whether to show an error in edit due to submission statement.
showErrorStatementSubmit = false; // Whether to show an error in submit due to submission statement.
Expand Down Expand Up @@ -406,6 +409,47 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
);
}

/**
* Remove submisson.
*/
async remove(): Promise<void> {
if (!this.assign || !this.userSubmission) {
return;
}
const message = this.assign?.timelimit ?
'addon.mod_assign.removesubmissionconfirmwithtimelimit' :
'addon.mod_assign.removesubmissionconfirm';
try {
await CoreDomUtils.showDeleteConfirm(message);
} catch {
return;
}

const modal = await CoreLoadings.show('core.sending', true);

try {
const sent = await AddonModAssign.removeSubmission(this.assign, this.userSubmission);

if (sent) {
CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'assign' });
}

CoreEvents.trigger(
ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT,
{
assignmentId: this.assign.id,
submissionId: this.userSubmission.id,
userId: this.currentUserId,
},
CoreSites.getCurrentSiteId(),
);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'Error removing submission.');
} finally {
modal.dismiss();
}
}

/**
* Check if there's data to save (grade).
*
Expand Down Expand Up @@ -633,13 +677,14 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
try {
const submission = await AddonModAssignOffline.getSubmission(this.assign.id, this.submitId);

this.hasOffline = submission && submission.plugindata && Object.keys(submission.plugindata).length > 0;

this.submittedOffline = !!submission?.submitted;
this.removedOffline = submission && Object.keys(submission.plugindata).length == 0;
this.editedOffline = submission && !this.removedOffline;
this.submittedOffline = !!submission?.submitted && !this.removedOffline;
} catch (error) {
// No offline data found.
this.hasOffline = false;
this.editedOffline = false;
this.submittedOffline = false;
this.removedOffline = false;
}
}

Expand Down Expand Up @@ -821,14 +866,14 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
return;
}

if (this.hasOffline || this.submittedOffline) {
// Offline data.
if (this.editedOffline || this.submittedOffline) {
// Added, edited or submitted offline.
this.statusTranslated = Translate.instant('core.notsent');
this.statusColor = CoreIonicColorNames.WARNING;
} else if (!this.assign.teamsubmission) {

// Single submission.
if (this.userSubmission && this.userSubmission.status != this.statusNew) {
if (this.userSubmission && this.userSubmission.status != this.statusNew && !this.removedOffline) {
this.statusTranslated = Translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
} else {
Expand All @@ -844,10 +889,10 @@ 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.removedOffline) {
this.statusTranslated = Translate.instant('addon.mod_assign.nosubmission');
this.statusColor = AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_SUBMISSION);
} else if (this.userSubmission && this.userSubmission.status != this.statusNew) {
} else if (this.userSubmission && this.userSubmission.status != this.statusNew && !this.removedOffline) {
this.statusTranslated = Translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
} else {
Expand Down Expand Up @@ -907,7 +952,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
this.courseId,
acceptStatement,
this.userSubmission.timemodified,
this.hasOffline,
this.editedOffline,
);

// Submitted, trigger event.
Expand Down Expand Up @@ -1142,11 +1187,12 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
this.assign.requiresubmissionstatement = 0;
}

this.canSubmit = !this.isSubmittedForGrading && !this.submittedOffline && (lastAttempt.cansubmit ||
(this.hasOffline && AddonModAssign.canSubmitOffline(this.assign, submissionStatus)));
this.canSubmit = !this.isSubmittedForGrading && !this.submittedOffline && !this.removedOffline &&
(lastAttempt.cansubmit || (this.editedOffline && AddonModAssign.canSubmitOffline(this.assign, submissionStatus)));

this.canEdit = !this.isSubmittedForGrading && lastAttempt.canedit &&
(!this.submittedOffline || !this.assign.submissiondrafts);
this.isRemoveAvailable = AddonModAssign.isRemoveSubmissionAvailable();

// Get submission statement if needed.
if (this.assign.requiresubmissionstatement && this.assign.submissiondrafts && this.submitId == this.currentUserId) {
Expand Down
1 change: 1 addition & 0 deletions src/addons/mod/assign/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const ADDON_MOD_ASSIGN_WARN_GROUPS_OPTIONAL = 'warnoptional';

// Events.
export const ADDON_MOD_ASSIGN_SUBMISSION_SAVED_EVENT = 'addon_mod_assign_submission_saved';
export const ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT = 'addon_mod_assign_submission_removed';
export const ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT = 'addon_mod_assign_submitted_for_grading';
export const ADDON_MOD_ASSIGN_GRADED_EVENT = 'addon_mod_assign_graded';
export const ADDON_MOD_ASSIGN_STARTED_EVENT = 'addon_mod_assign_started';
Expand Down
4 changes: 4 additions & 0 deletions src/addons/mod/assign/lang.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@
"numwords": "{{$a}} words",
"outof": "{{$a.current}} out of {{$a.total}}",
"overdue": "Assignment is overdue by: {{$a}}",
"removesubmission": "Remove submission",
"removesubmissionconfirm": "Are you sure you want to remove your submission?",
"removesubmissionconfirmwithtimelimit": "Are you sure you want to remove your submission? Please note that this will not reset your time limit.",
"submission": "Submission",
"submissioneditable": "Student can edit this submission",
"submissionempty": "Nothing was submitted",
"submissionnoteditable": "Student cannot edit this submission",
"submissionnotsupported": "This submission is not supported by the app and may not contain all the information.",
"submissionslocked": "This assignment is not accepting submissions",
Expand Down
15 changes: 6 additions & 9 deletions src/addons/mod/assign/pages/edit/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,15 +231,8 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave {
this.timeLimitEndTime = 0;
}

try {
// Check if there's any offline data for this submission.
const offlineData = await AddonModAssignOffline.getSubmission(this.assign.id, this.userId);

this.hasOffline = offlineData?.plugindata && Object.keys(offlineData.plugindata).length > 0;
} catch {
// No offline data found.
this.hasOffline = false;
}
// Check if there's any offline data for this submission.
this.hasOffline = await CoreUtils.promiseWorks(AddonModAssignOffline.getSubmission(this.assign.id, this.userId));

CoreAnalytics.logEvent({
type: CoreAnalyticsEventType.VIEW_ITEM,
Expand Down Expand Up @@ -398,6 +391,10 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy, CanLeave {
throw Translate.instant('addon.mod_assign.acceptsubmissionstatement');
}

if (AddonModAssignHelper.isSubmissionEmptyForEdit(this.assign!, this.userSubmission!, inputData)) {
throw Translate.instant('addon.mod_assign.submissionempty');
}

let modal = await CoreLoadings.show();
let size = -1;

Expand Down
Loading