diff --git a/scripts/langindex.json b/scripts/langindex.json
index 2b2ebb48f..620d2c299 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -430,6 +430,9 @@
"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",
diff --git a/src/addons/mod/assign/components/index/index.ts b/src/addons/mod/assign/components/index/index.ts
index e343bbaff..eb31d7487 100644
--- a/src/addons/mod/assign/components/index/index.ts
+++ b/src/addons/mod/assign/components/index/index.ts
@@ -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,
@@ -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) => {
diff --git a/src/addons/mod/assign/components/submission/addon-mod-assign-submission.html b/src/addons/mod/assign/components/submission/addon-mod-assign-submission.html
index 0ee574a3f..ad7abb287 100644
--- a/src/addons/mod/assign/components/submission/addon-mod-assign-submission.html
+++ b/src/addons/mod/assign/components/submission/addon-mod-assign-submission.html
@@ -167,13 +167,20 @@
-
-
- {{ 'addon.mod_assign.editsubmission' | translate }}
-
+
+
+
+ {{ 'addon.mod_assign.editsubmission' | translate }}
+
+
+ {{ 'addon.mod_assign.removesubmission' | translate }}
+
+
-
+
{{ 'addon.mod_assign.addsubmission' | translate }}
@@ -182,7 +189,7 @@
-
+
{{ 'addon.mod_assign.addnewattemptfromprevious' | translate }}
@@ -191,12 +198,18 @@
{{ 'addon.mod_assign.addnewattempt' | translate }}
-
-
- {{ 'addon.mod_assign.editsubmission' | translate }}
-
+
+
+
+ {{ 'addon.mod_assign.editsubmission' | translate }}
+
+
+ {{ 'addon.mod_assign.removesubmission' | translate }}
+
+
diff --git a/src/addons/mod/assign/components/submission/submission.ts b/src/addons/mod/assign/components/submission/submission.ts
index f5fdd8df2..1341860e8 100644
--- a/src/addons/mod/assign/components/submission/submission.ts
+++ b/src/addons/mod/assign/components/submission/submission.ts
@@ -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';
@@ -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.
@@ -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.
@@ -406,6 +409,47 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
);
}
+ /**
+ * Remove submisson.
+ */
+ async remove(): Promise {
+ 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).
*
@@ -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;
}
}
@@ -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 {
@@ -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 {
@@ -907,7 +952,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
this.courseId,
acceptStatement,
this.userSubmission.timemodified,
- this.hasOffline,
+ this.editedOffline,
);
// Submitted, trigger event.
@@ -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) {
diff --git a/src/addons/mod/assign/constants.ts b/src/addons/mod/assign/constants.ts
index 52797e854..914a1a998 100644
--- a/src/addons/mod/assign/constants.ts
+++ b/src/addons/mod/assign/constants.ts
@@ -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';
diff --git a/src/addons/mod/assign/lang.json b/src/addons/mod/assign/lang.json
index fa94b86fc..e1e2cf0aa 100644
--- a/src/addons/mod/assign/lang.json
+++ b/src/addons/mod/assign/lang.json
@@ -82,6 +82,9 @@
"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",
diff --git a/src/addons/mod/assign/pages/edit/edit.ts b/src/addons/mod/assign/pages/edit/edit.ts
index 5949a7446..6b6154c43 100644
--- a/src/addons/mod/assign/pages/edit/edit.ts
+++ b/src/addons/mod/assign/pages/edit/edit.ts
@@ -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,
diff --git a/src/addons/mod/assign/services/assign-helper.ts b/src/addons/mod/assign/services/assign-helper.ts
index 8c5d55b35..a816ecab6 100644
--- a/src/addons/mod/assign/services/assign-helper.ts
+++ b/src/addons/mod/assign/services/assign-helper.ts
@@ -81,6 +81,12 @@ export class AddonModAssignHelperProvider {
return true;
}
+ if (await CoreUtils.promiseWorks(AddonModAssignOffline.getSubmission(assign.id, submission.userid))) {
+ // Submission was saved or deleted offline, allow editing it or creating a new one.
+ return true;
+ }
+
+ // Submission was created online, check if plugins allow editing it.
let canEdit = true;
const promises = submission.plugins
@@ -492,7 +498,7 @@ export class AddonModAssignHelperProvider {
submission.manyGroups = !!participant.groups && participant.groups.length > 1;
submission.noGroups = !!participant.groups && participant.groups.length == 0;
if (participant.groupname) {
- submission.groupid = participant.groupid;
+ submission.groupid = participant.groupid ?? 0;
submission.groupname = participant.groupname;
}
@@ -749,18 +755,15 @@ export const AddonModAssignHelper = makeSingleton(AddonModAssignHelperProvider);
/**
* Assign submission with some calculated data.
*/
-export type AddonModAssignSubmissionFormatted =
- Omit & {
- userid?: number; // Student id.
- groupid?: number; // Group id.
- blindid?: number; // Calculated in the app. Blindid of the user that did the submission.
- submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission.
- userfullname?: string; // Calculated in the app. Full name of the user that did the submission.
- userprofileimageurl?: string; // Calculated in the app. Avatar of the user that did the submission.
- manyGroups?: boolean; // Calculated in the app. Whether the user belongs to more than 1 group.
- noGroups?: boolean; // Calculated in the app. Whether the user doesn't belong to any group.
- groupname?: string; // Calculated in the app. Name of the group the submission belongs to.
- };
+export interface AddonModAssignSubmissionFormatted extends AddonModAssignSubmission {
+ blindid?: number; // Calculated in the app. Blindid of the user that did the submission.
+ submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission.
+ userfullname?: string; // Calculated in the app. Full name of the user that did the submission.
+ userprofileimageurl?: string; // Calculated in the app. Avatar of the user that did the submission.
+ manyGroups?: boolean; // Calculated in the app. Whether the user belongs to more than 1 group.
+ noGroups?: boolean; // Calculated in the app. Whether the user doesn't belong to any group.
+ groupname?: string; // Calculated in the app. Name of the group the submission belongs to.
+}
/**
* Assignment plugin config.
diff --git a/src/addons/mod/assign/services/assign-sync.ts b/src/addons/mod/assign/services/assign-sync.ts
index 19137968e..063e3f1cd 100644
--- a/src/addons/mod/assign/services/assign-sync.ts
+++ b/src/addons/mod/assign/services/assign-sync.ts
@@ -331,27 +331,29 @@ export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvid
}
try {
- if (submission?.plugins) {
- // Prepare plugins data.
- await Promise.all(submission.plugins.map((plugin) =>
- AddonModAssignSubmissionDelegate.preparePluginSyncData(
- assign,
- submission,
- plugin,
- offlineData,
- pluginData,
- siteId,
- )));
- }
+ if (Object.keys(offlineData.plugindata).length == 0) {
+ await AddonModAssign.removeSubmissionOnline(assign.id, offlineData.userid, siteId);
+ } else {
+ if (submission?.plugins) {
+ // Prepare plugins data.
+ await Promise.all(submission.plugins.map((plugin) =>
+ AddonModAssignSubmissionDelegate.preparePluginSyncData(
+ assign,
+ submission,
+ plugin,
+ offlineData,
+ pluginData,
+ siteId,
+ )));
+ }
- // Now save the submission.
- if (Object.keys(pluginData).length > 0) {
+ // Now save the submission.
await AddonModAssign.saveSubmissionOnline(assign.id, pluginData, siteId);
- }
- if (assign.submissiondrafts && offlineData.submitted) {
- // The user submitted the assign manually. Submit it for grading.
- await AddonModAssign.submitForGradingOnline(assign.id, !!offlineData.submissionstatement, siteId);
+ if (assign.submissiondrafts && offlineData.submitted) {
+ // The user submitted the assign manually. Submit it for grading.
+ await AddonModAssign.submitForGradingOnline(assign.id, !!offlineData.submissionstatement, siteId);
+ }
}
// Submission data sent, update cached data. No need to block the user for this.
diff --git a/src/addons/mod/assign/services/assign.ts b/src/addons/mod/assign/services/assign.ts
index 976ca8abe..a4d5216cd 100644
--- a/src/addons/mod/assign/services/assign.ts
+++ b/src/addons/mod/assign/services/assign.ts
@@ -42,6 +42,7 @@ import {
ADDON_MOD_ASSIGN_GRADED_EVENT,
ADDON_MOD_ASSIGN_MANUAL_SYNCED,
ADDON_MOD_ASSIGN_STARTED_EVENT,
+ ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT,
ADDON_MOD_ASSIGN_SUBMISSION_SAVED_EVENT,
ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT,
} from '../constants';
@@ -55,6 +56,7 @@ declare module '@singletons/events' {
*/
export interface CoreEventsData {
[ADDON_MOD_ASSIGN_SUBMISSION_SAVED_EVENT]: AddonModAssignSubmissionSavedEventData;
+ [ADDON_MOD_ASSIGN_SUBMISSION_REMOVED_EVENT]: AddonModAssignSubmissionRemovedEventData;
[ADDON_MOD_ASSIGN_SUBMITTED_FOR_GRADING_EVENT]: AddonModAssignSubmittedForGradingEventData;
[ADDON_MOD_ASSIGN_GRADED_EVENT]: AddonModAssignGradedEventData;
[ADDON_MOD_ASSIGN_STARTED_EVENT]: AddonModAssignStartedEventData;
@@ -1314,6 +1316,125 @@ export class AddonModAssignProvider {
}
}
+ /**
+ * Remove the assignment submission of a user.
+ *
+ * @param assign Assign.
+ * @param submission Last online submission.
+ * @param siteId Site ID. If not defined, current site.
+ * @returns Promise resolved with true if sent to server, resolved with false if stored in offline.
+ * @since 4.5
+ */
+ async removeSubmission(
+ assign: AddonModAssignAssign,
+ submission: AddonModAssignSubmission,
+ siteId?: string,
+ ): Promise {
+ siteId = siteId || CoreSites.getCurrentSiteId();
+
+ // Function to store the submission to be synchronized later.
+ const storeOffline = async (): Promise => {
+ await AddonModAssignOffline.saveSubmission(
+ assign.id,
+ assign.course,
+ {},
+ submission.timemodified,
+ !!assign.submissiondrafts,
+ submission.userid,
+ siteId,
+ );
+
+ return false;
+ };
+
+ if (submission.status === AddonModAssignSubmissionStatusValues.NEW ||
+ submission.status == AddonModAssignSubmissionStatusValues.REOPENED) {
+ // The submission was created offline and not synced, just delete the offline submission.
+ await AddonModAssignOffline.deleteSubmission(assign.id, submission.userid, siteId);
+
+ return false;
+ }
+
+ if (!CoreNetwork.isOnline()) {
+ // App is offline, store the action.
+ return storeOffline();
+ }
+
+ try {
+ // If there's an offline submission, discard it first.
+ const offlineData = await AddonModAssignOffline.getSubmission(assign.id, submission.userid, siteId);
+
+ if (offlineData) {
+ if (submission.plugins) {
+ // Delete all plugin data.
+ await Promise.all(submission.plugins.map((plugin) =>
+ AddonModAssignSubmissionDelegate.deletePluginOfflineData(
+ assign,
+ submission,
+ plugin,
+ offlineData,
+ siteId,
+ )));
+ }
+
+ await AddonModAssignOffline.deleteSubmission(assign.id, submission.userid, siteId);
+ }
+
+ await this.removeSubmissionOnline(assign.id, submission.userid, siteId);
+
+ return true;
+ } catch (error) {
+ if (error && !CoreUtils.isWebServiceError(error)) {
+ // Couldn't connect to server, store in offline.
+ return storeOffline();
+ } else {
+ // The WebService has thrown an error or offline not supported, reject.
+ throw error;
+ }
+ }
+ }
+
+ /**
+ * Remove the assignment submission of a user.
+ *
+ * @param assignId Assign ID.
+ * @param userId User ID. If not defined, current user.
+ * @param siteId Site ID. If not defined, current site.
+ * @returns Promise resolved when submitted, rejected otherwise.
+ * @since 4.5
+ */
+ async removeSubmissionOnline(assignId: number, userId?: number, siteId?: string): Promise {
+ const site = await CoreSites.getSite(siteId);
+ userId = userId || site.getUserId();
+
+ const params: AddonModAssignRemoveSubmissionWSParams = {
+ assignid: assignId,
+ userid: userId,
+ };
+ const result = await site.write('mod_assign_remove_submission', params);
+
+ if (!result.status) {
+ if (result.warnings?.length) {
+ throw new CoreWSError(result.warnings[0]);
+ } else {
+ throw new CoreError('Error removing assignment submission.');
+ }
+ }
+ }
+
+ /**
+ * Returns whether or not remove submission WS available or not.
+ *
+ * @param site Site. If not defined, current site.
+ * @returns If WS is available.
+ * @since 4.5
+ */
+ isRemoveSubmissionAvailable(site?: CoreSite): boolean {
+ site = site ?? CoreSites.getRequiredCurrentSite();
+
+ return site.wsAvailable('mod_assign_remove_submission');
+ }
+
}
export const AddonModAssign = makeSingleton(AddonModAssignProvider);
@@ -1755,6 +1876,22 @@ type AddonModAssignStartSubmissionWSParams = {
assignid: number; // Assignment instance id.
};
+/**
+ * Params of mod_assign_remove_submission WS.
+ */
+type AddonModAssignRemoveSubmissionWSParams = {
+ userid: number; // User id.
+ assignid: number; // Assignment instance id.
+};
+
+/**
+ * Data returned by mod_assign_remove_submission WS.
+ */
+export type AddonModAssignRemoveSubmissionWSResponse = {
+ status: boolean;
+ warnings?: CoreWSExternalWarning[];
+};
+
/**
* Data returned by mod_assign_start_submission WS.
*
@@ -1784,6 +1921,11 @@ export type AddonModAssignSubmittedForGradingEventData = {
*/
export type AddonModAssignSubmissionSavedEventData = AddonModAssignSubmittedForGradingEventData;
+/**
+ * Data sent by SUBMISSION_REMOVED_EVENT event.
+ */
+export type AddonModAssignSubmissionRemovedEventData = AddonModAssignSubmittedForGradingEventData;
+
/**
* Data sent by GRADED_EVENT event.
*/
diff --git a/src/addons/mod/assign/submission/file/component/file.ts b/src/addons/mod/assign/submission/file/component/file.ts
index 588c7e9ee..e142c842f 100644
--- a/src/addons/mod/assign/submission/file/component/file.ts
+++ b/src/addons/mod/assign/submission/file/component/file.ts
@@ -54,28 +54,31 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
: undefined;
// Get the offline data.
- const filesData = await CoreUtils.ignoreErrors(
+ const offlineData = await CoreUtils.ignoreErrors(
AddonModAssignOffline.getSubmission(this.assign.id),
undefined,
);
try {
- if (filesData && filesData.plugindata && filesData.plugindata.files_filemanager) {
- const offlineDataFiles = filesData.plugindata.files_filemanager;
- // It has offline data.
- let offlineFiles: FileEntry[] = [];
- if (offlineDataFiles.offline) {
- offlineFiles = await CoreUtils.ignoreErrors(
- AddonModAssignHelper.getStoredSubmissionFiles(
- this.assign.id,
- AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
- ),
- [],
- );
- }
+ if (offlineData) {
+ // Offline submission, get files if submission is not removed.
+ if (offlineData.plugindata.files_filemanager) {
+ const offlineDataFiles = offlineData.plugindata.files_filemanager;
+ // It has offline data.
+ let offlineFiles: FileEntry[] = [];
+ if (offlineDataFiles.offline) {
+ offlineFiles = await CoreUtils.ignoreErrors(
+ AddonModAssignHelper.getStoredSubmissionFiles(
+ this.assign.id,
+ AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
+ ),
+ [],
+ );
+ }
- this.files = offlineDataFiles.online || [];
- this.files = this.files.concat(offlineFiles);
+ this.files = offlineDataFiles.online || [];
+ this.files = this.files.concat(offlineFiles);
+ }
} else {
// No offline data, get the online files.
this.files = AddonModAssign.getSubmissionPluginAttachments(this.plugin);
diff --git a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts
index 47760672d..04447b4d3 100644
--- a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts
+++ b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts
@@ -69,8 +69,11 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
this.wordLimit = parseInt(this.configs?.wordlimit || '0');
try {
- if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) {
- this.text = (offlineData.plugindata).onlinetext_editor.text;
+ if (offlineData && offlineData.plugindata) {
+ // Offline submission, get text if submission is not removed.
+ if (offlineData.plugindata.onlinetext_editor) {
+ this.text = (offlineData.plugindata).onlinetext_editor.text;
+ }
} else {
// No offline data found, return online text.
this.text = AddonModAssign.getSubmissionPluginText(this.plugin);
diff --git a/src/addons/mod/assign/tests/behat/basic_usage.feature b/src/addons/mod/assign/tests/behat/basic_usage.feature
index 94bce1779..9cbf92c17 100755
--- a/src/addons/mod/assign/tests/behat/basic_usage.feature
+++ b/src/addons/mod/assign/tests/behat/basic_usage.feature
@@ -120,6 +120,17 @@ Feature: Test basic usage of assignment activity in app
Then I should find "Online text submissions" in the app
And I should find "Submission test 2nd attempt" in the app
+ @lms_from4.5
+ Scenario: Remove submission (online text)
+ Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
+ And I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test" in the app
+ And I press "Save" in the app
+
+ When I press "Remove submission" in the app
+ And I press "DELETE" in the app
+ Then I should find "No attempt" in the app
+
Scenario: Add submission offline (online text) & Submit for grading offline & Sync submissions
Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
When I press "Add submission" in the app
@@ -164,3 +175,73 @@ Feature: Test basic usage of assignment activity in app
Then I should find "Submitted for grading" in the app
And I should find "Submission test edited offline" in the app
But I should not find "This Assignment has offline data to be synchronised." in the app
+
+ @lms_from4.5
+ Scenario: Remove submission offline and syncrhonize it
+ Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
+ And I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test" in the app
+ And I press "Save" in the app
+ Then I should find "Draft (not submitted)" in the app
+
+ # Remove submission added online.
+ When I switch network connection to offline
+ And I press "Remove submission" in the app
+ And I press "DELETE" in the app
+ Then I should find "No attempt" in the app
+ And I should find "This Assignment has offline data to be synchronised." in the app
+
+ # Synchronize submission removal.
+ When I switch network connection to wifi
+ And I press the back button in the app
+ And I press "assignment1" in the app
+ Then I should find "No attempt" in the app
+ But I should not find "This Assignment has offline data to be synchronised." in the app
+
+ # Remove submission added offline (while offline)
+ Given I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test offline" in the app
+ And I switch network connection to offline
+ And I press "Save" in the app
+
+ When I press "Remove submission" in the app
+ And I press "DELETE" in the app
+ Then I should find "No attempt" in the app
+ But I should not find "This Assignment has offline data to be synchronised." in the app
+
+ # Remove submission added offline (while online before synchronising)
+ Given I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test offline" in the app
+ And I switch network connection to offline
+ And I press "Save" in the app
+ And I switch network connection to wifi
+
+ When I press "Remove submission" in the app
+ And I press "DELETE" in the app
+ Then I should find "No attempt" in the app
+ But I should not find "This Assignment has offline data to be synchronised." in the app
+
+ @lms_from4.5
+ Scenario: Add submission offline after removing a submission offline
+ Given I entered the assign activity "assignment1" on course "Course 1" as "student1" in the app
+ When I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test online" in the app
+ And I press "Save" in the app
+ And I switch network connection to offline
+ And I press "Remove submission" in the app
+ And I press "DELETE" in the app
+ Then I should find "This Assignment has offline data to be synchronised." in the app
+ And I should find "No attempt" in the app
+
+ When I press "Add submission" in the app
+ And I set the field "Online text submissions" to "Submission test offline" in the app
+ And I press "Save" in the app
+ And I press "OK" in the app
+ Then I should find "This Assignment has offline data to be synchronised." in the app
+ And I should find "Submission test offline" in the app
+
+ When I switch network connection to wifi
+ And I go back in the app
+ And I press "assignment1" in the app
+ Then I should find "Submission test offline" in the app
+ But I should not find "This Assignment has offline data to be synchronised." in the app