forked from CIT/Vmeda.Online
		
	Merge pull request #4191 from albertgasset/MOBILE-3893
MOBILE-3893 assign: Add button to remove submissions
This commit is contained in:
		
						commit
						b5b44a8a1d
					
				@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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) => {
 | 
			
		||||
 | 
			
		||||
@ -167,13 +167,20 @@
 | 
			
		||||
                        <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>
 | 
			
		||||
@ -182,7 +189,7 @@
 | 
			
		||||
                                        </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 }}
 | 
			
		||||
@ -191,12 +198,18 @@
 | 
			
		||||
                                            {{ '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">
 | 
			
		||||
 | 
			
		||||
@ -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<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).
 | 
			
		||||
     *
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
@ -237,6 +243,31 @@ export class AddonModAssignHelperProvider {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether the edited submission has no content.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign Assignment object.
 | 
			
		||||
     * @param submission Submission to inspect.
 | 
			
		||||
     * @param inputData Data entered in the submission form.
 | 
			
		||||
     * @returns Whether the submission is empty.
 | 
			
		||||
     */
 | 
			
		||||
    isSubmissionEmptyForEdit(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        inputData: CoreFormFields,
 | 
			
		||||
    ): boolean {
 | 
			
		||||
        const anyNotEmpty = submission.plugins?.some((plugin) =>
 | 
			
		||||
            !AddonModAssignSubmissionDelegate.isPluginEmptyForEdit(assign, plugin, inputData));
 | 
			
		||||
 | 
			
		||||
        // If any plugin is not empty, we consider that the submission is not empty either.
 | 
			
		||||
        if (anyNotEmpty) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If all the plugins were empty (or there were no plugins), we consider the submission to be empty.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List the participants for a single assignment, with some summary info about their submissions.
 | 
			
		||||
     *
 | 
			
		||||
@ -467,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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -724,18 +755,15 @@ export const AddonModAssignHelper = makeSingleton(AddonModAssignHelperProvider);
 | 
			
		||||
/**
 | 
			
		||||
 * Assign submission with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModAssignSubmissionFormatted =
 | 
			
		||||
    Omit<AddonModAssignSubmission, 'userid'|'groupid'> & {
 | 
			
		||||
        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.
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
 | 
			
		||||
@ -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<boolean> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // Function to store the submission to be synchronized later.
 | 
			
		||||
        const storeOffline = async (): Promise<boolean> => {
 | 
			
		||||
            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<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        userId = userId || site.getUserId();
 | 
			
		||||
 | 
			
		||||
        const params: AddonModAssignRemoveSubmissionWSParams = {
 | 
			
		||||
            assignid: assignId,
 | 
			
		||||
            userid: userId,
 | 
			
		||||
        };
 | 
			
		||||
        const result = await site.write<AddonModAssignRemoveSubmissionWSResponse>('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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,17 @@ export class AddonModAssignDefaultSubmissionHandler implements AddonModAssignSub
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
   isEmptyForEdit(
 | 
			
		||||
       assign: AddonModAssignAssign, // eslint-disable-line @typescript-eslint/no-unused-vars
 | 
			
		||||
       plugin: AddonModAssignPlugin, // eslint-disable-line @typescript-eslint/no-unused-vars
 | 
			
		||||
       inputData: CoreFormFields, // eslint-disable-line @typescript-eslint/no-unused-vars
 | 
			
		||||
    ): boolean {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,20 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a plugin has no data in the edit form.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @returns Whether the plugin is empty.
 | 
			
		||||
     */
 | 
			
		||||
    isEmptyForEdit?(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: CoreFormFields,
 | 
			
		||||
    ): boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Should clear temporary data for a cancelled submission.
 | 
			
		||||
     *
 | 
			
		||||
@ -502,6 +516,22 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        return this.executeFunctionOnEnabled(plugin.type, 'isEmpty', [assign, plugin]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a plugin has no data in the edit form
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered in the submission form.
 | 
			
		||||
     * @returns Whether the plugin is empty.
 | 
			
		||||
     */
 | 
			
		||||
    isPluginEmptyForEdit(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: CoreFormFields,
 | 
			
		||||
    ): boolean | undefined {
 | 
			
		||||
        return this.executeFunctionOnEnabled(plugin.type, 'isEmptyForEdit', [assign, plugin, inputData]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch any required data for a submission plugin.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -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 = <CoreFileUploaderStoreFilesResult>filesData.plugindata.files_filemanager;
 | 
			
		||||
                // It has offline data.
 | 
			
		||||
                let offlineFiles: FileEntry[] = [];
 | 
			
		||||
                if (offlineDataFiles.offline) {
 | 
			
		||||
                    offlineFiles = <FileEntry[]>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 = <CoreFileUploaderStoreFilesResult>offlineData.plugindata.files_filemanager;
 | 
			
		||||
                    // It has offline data.
 | 
			
		||||
                    let offlineFiles: FileEntry[] = [];
 | 
			
		||||
                    if (offlineDataFiles.offline) {
 | 
			
		||||
                        offlineFiles = <FileEntry[]>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);
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,15 @@ export class AddonModAssignSubmissionFileHandlerService implements AddonModAssig
 | 
			
		||||
        return files.length === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEmptyForEdit(assign: AddonModAssignAssign): boolean {
 | 
			
		||||
        const currentFiles = CoreFileSession.getFiles(ADDON_MOD_ASSIGN_COMPONENT, assign.id);
 | 
			
		||||
 | 
			
		||||
        return currentFiles.length == 0;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -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 = (<AddonModAssignSubmissionOnlineTextPluginData>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 = (<AddonModAssignSubmissionOnlineTextPluginData>offlineData.plugindata).onlinetext_editor.text;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // No offline data found, return online text.
 | 
			
		||||
                this.text = AddonModAssign.getSubmissionPluginText(this.plugin);
 | 
			
		||||
 | 
			
		||||
@ -58,6 +58,29 @@ export class AddonModAssignSubmissionOnlineTextHandlerService implements AddonMo
 | 
			
		||||
        return text.trim().length === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEmptyForEdit(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: AddonModAssignSubmissionOnlineTextData,
 | 
			
		||||
     ): boolean {
 | 
			
		||||
        const text = this.getTextToSubmit(plugin, inputData);
 | 
			
		||||
 | 
			
		||||
        if (CoreText.countWords(text) > 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if the online text submission contains video, audio or image elements
 | 
			
		||||
        // that can be ignored and stripped by count_words().
 | 
			
		||||
        if (/<\s*((video|audio)[^>]*>(.*?)<\s*\/\s*(video|audio)>)|(img[^>]*>)/.test(text)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user