forked from CIT/Vmeda.Online
		
	MOBILE-3636 assign: Submission plugins
This commit is contained in:
		
							parent
							
								
									3281196ec0
								
							
						
					
					
						commit
						85f79bb944
					
				@ -29,6 +29,7 @@ import { AddonModAssignModuleHandler, AddonModAssignModuleHandlerService } from
 | 
			
		||||
import { AddonModAssignPrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { AddonModAssignPushClickHandler } from './services/handlers/push-click';
 | 
			
		||||
import { AddonModAssignSyncCronHandler } from './services/handlers/sync-cron';
 | 
			
		||||
import { AddonModAssignSubmissionModule } from './submission/submission.module';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
@ -41,6 +42,7 @@ const routes: Routes = [
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        AddonModAssignComponentsModule,
 | 
			
		||||
        AddonModAssignSubmissionModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import {
 | 
			
		||||
} from '../../services/assign';
 | 
			
		||||
import { AddonModAssignHelper, AddonModAssignPluginConfig } from '../../services/assign-helper';
 | 
			
		||||
import { AddonModAssignSubmissionDelegate } from '../../services/submission-delegate';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file/ngx';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays an assignment submission plugin.
 | 
			
		||||
@ -48,7 +49,7 @@ export class AddonModAssignSubmissionPluginComponent implements OnInit {
 | 
			
		||||
    // Data to render the plugin if it isn't supported.
 | 
			
		||||
    component = AddonModAssignProvider.COMPONENT;
 | 
			
		||||
    text = '';
 | 
			
		||||
    files: CoreWSExternalFile[] = [];
 | 
			
		||||
    files: (FileEntry | CoreWSExternalFile)[] = [];
 | 
			
		||||
    notSupported = false;
 | 
			
		||||
    pluginLoaded = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -392,7 +392,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
 | 
			
		||||
        try {
 | 
			
		||||
            return AddonModAssignHelper.instance.hasFeedbackDataChanged(
 | 
			
		||||
                this.assign!,
 | 
			
		||||
                this.userSubmission,
 | 
			
		||||
                this.userSubmission!, // @todo
 | 
			
		||||
                this.feedback,
 | 
			
		||||
                this.submitId,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
@ -492,7 +492,7 @@ export class AddonModAssignHelperProvider {
 | 
			
		||||
     */
 | 
			
		||||
    async hasFeedbackDataChanged(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        submission: AddonModAssignSubmission | AddonModAssignSubmissionFormatted,
 | 
			
		||||
        feedback: AddonModAssignSubmissionFeedback,
 | 
			
		||||
        userId: number,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
@ -683,15 +683,13 @@ export class AddonModAssignHelperProvider {
 | 
			
		||||
        offline = false,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
    ): Promise<number | CoreFileUploaderStoreFilesResult> {
 | 
			
		||||
 | 
			
		||||
        if (offline) {
 | 
			
		||||
            await this.storeSubmissionFiles(assignId, folderName, files, userId, siteId);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
            return await this.storeSubmissionFiles(assignId, folderName, files, userId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.uploadFiles(assignId, files, siteId);
 | 
			
		||||
        return await this.uploadFiles(assignId, files, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { AddonModAssignOffline } from './assign-offline';
 | 
			
		||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
 | 
			
		||||
import { CoreComments } from '@features/comments/services/comments';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmaModAssign:';
 | 
			
		||||
 | 
			
		||||
@ -754,7 +755,7 @@ export class AddonModAssignProvider {
 | 
			
		||||
        promises.push(this.invalidateAssignmentUserMappingsData(assign.id, siteId));
 | 
			
		||||
        promises.push(this.invalidateAssignmentGradesData(assign.id, siteId));
 | 
			
		||||
        promises.push(this.invalidateListParticipantsData(assign.id, siteId));
 | 
			
		||||
        // @todo promises.push(CoreComments.instance.invalidateCommentsByInstance('module', assign.id, siteId));
 | 
			
		||||
        promises.push(CoreComments.instance.invalidateCommentsByInstance('module', assign.id, siteId));
 | 
			
		||||
        promises.push(this.invalidateAssignmentData(courseId, siteId));
 | 
			
		||||
        promises.push(CoreGrades.instance.invalidateAllCourseGradesData(courseId));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { AddonModAssignDefaultFeedbackHandler } from './handlers/default-feedbac
 | 
			
		||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin, AddonModAssignSavePluginData } from './assign';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { AddonModAssignSubmissionFormatted } from './assign-helper';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Interface that all feedback handlers must implement.
 | 
			
		||||
@ -264,7 +265,7 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonMod
 | 
			
		||||
     */
 | 
			
		||||
    async hasPluginDataChanged(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        submission: AddonModAssignSubmission | AddonModAssignSubmissionFormatted,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        userId: number,
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { AddonModAssignDefaultSubmissionHandler } from './handlers/default-submi
 | 
			
		||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin, AddonModAssignSavePluginData } from './assign';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { AddonModAssignSubmissionsDBRecordFormatted } from './assign-offline';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Interface that all submission handlers must implement.
 | 
			
		||||
@ -69,7 +70,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): void;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -105,9 +106,9 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: any,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): void | Promise<any>;
 | 
			
		||||
    ): void | Promise<void>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the Component to use to display the plugin data, either in read or in edit mode.
 | 
			
		||||
@ -172,7 +173,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): number | Promise<number>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -188,7 +189,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): boolean | Promise<boolean>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -232,12 +233,12 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
        pluginData: AddonModAssignSavePluginData,
 | 
			
		||||
        offline?: boolean,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): void | Promise<any>;
 | 
			
		||||
    ): void | Promise<void>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare and add to pluginData the data to send to the server based on the offline data stored.
 | 
			
		||||
@ -255,10 +256,10 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: any,
 | 
			
		||||
        pluginData: any,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        pluginData: AddonModAssignSavePluginData,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): void | Promise<any>;
 | 
			
		||||
    ): void | Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -303,7 +304,7 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): void {
 | 
			
		||||
        return this.executeFunctionOnEnabled(plugin.type, 'clearTmpData', [assign, submission, plugin, inputData]);
 | 
			
		||||
    }
 | 
			
		||||
@ -346,9 +347,9 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: any,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<any | undefined> {
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        return await this.executeFunctionOnEnabled(
 | 
			
		||||
            plugin.type,
 | 
			
		||||
            'deleteOfflineData',
 | 
			
		||||
@ -423,7 +424,7 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): Promise<number | undefined> {
 | 
			
		||||
        return await this.executeFunctionOnEnabled(
 | 
			
		||||
            plugin.type,
 | 
			
		||||
@ -445,7 +446,7 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
    ): Promise<boolean | undefined> {
 | 
			
		||||
        return await this.executeFunctionOnEnabled(
 | 
			
		||||
            plugin.type,
 | 
			
		||||
@ -520,12 +521,12 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: any,
 | 
			
		||||
        pluginData: any,
 | 
			
		||||
        inputData: Record<string, unknown>,
 | 
			
		||||
        pluginData: AddonModAssignSavePluginData,
 | 
			
		||||
        offline?: boolean,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<any | undefined> {
 | 
			
		||||
    ): Promise<void | undefined> {
 | 
			
		||||
 | 
			
		||||
        return await this.executeFunctionOnEnabled(
 | 
			
		||||
            plugin.type,
 | 
			
		||||
@ -549,10 +550,10 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: any,
 | 
			
		||||
        pluginData: any,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        pluginData: AddonModAssignSavePluginData,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<any | undefined> {
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        return this.executeFunctionOnEnabled(
 | 
			
		||||
            plugin.type,
 | 
			
		||||
@ -562,4 +563,4 @@ export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonM
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export class AddonModAssignSubmissionDelegate extends makeSingleton(AddonModAssignSubmissionDelegateService) {}
 | 
			
		||||
export const AddonModAssignSubmissionDelegate = makeSingleton(AddonModAssignSubmissionDelegateService);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								src/addons/mod/assign/submission/comments/comments.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/addons/mod/assign/submission/comments/comments.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { AddonModAssignSubmissionCommentsHandler } from './services/handler';
 | 
			
		||||
import { AddonModAssignSubmissionCommentsComponent } from './component/comments';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModAssignSubmissionDelegate } from '../../services/submission-delegate';
 | 
			
		||||
import { CoreCommentsComponentsModule } from '@features/comments/components/components.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModAssignSubmissionCommentsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCommentsComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        AddonModAssignSubmissionCommentsHandler,
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                AddonModAssignSubmissionDelegate.instance.registerHandler(AddonModAssignSubmissionCommentsHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModAssignSubmissionCommentsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    entryComponents: [
 | 
			
		||||
        AddonModAssignSubmissionCommentsComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionCommentsModule {}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<ion-item *ngIf="commentsEnabled" class="ion-text-wrap" (click)="showComments($event)" detail="false">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{plugin.name}}</h2>
 | 
			
		||||
        <core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments"
 | 
			
		||||
            [itemId]="submission.id" area="submission_comments" [title]="plugin.name" [courseId]="assign.course">
 | 
			
		||||
        </core-comments>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
@ -0,0 +1,61 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, ViewChild } from '@angular/core';
 | 
			
		||||
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
 | 
			
		||||
import { CoreCommentsCommentsComponent } from '@features/comments/components/comments/comments';
 | 
			
		||||
import { CoreComments } from '@features/comments/services/comments';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render a comments submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-assign-submission-comments',
 | 
			
		||||
    templateUrl: 'addon-mod-assign-submission-comments.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionCommentsComponent extends AddonModAssignSubmissionPluginComponent {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreCommentsCommentsComponent) commentsComponent!: CoreCommentsCommentsComponent;
 | 
			
		||||
 | 
			
		||||
    commentsEnabled: boolean;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.commentsEnabled = !CoreComments.instance.areCommentsDisabledInSite();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate the data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    invalidate(): Promise<void> {
 | 
			
		||||
        return CoreComments.instance.invalidateCommentsData(
 | 
			
		||||
            'module',
 | 
			
		||||
            this.assign.cmid,
 | 
			
		||||
            'assignsubmission_comments',
 | 
			
		||||
            this.submission.id,
 | 
			
		||||
            'submission_comments',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show the comments.
 | 
			
		||||
     */
 | 
			
		||||
    showComments(e?: Event): void {
 | 
			
		||||
        this.commentsComponent?.openComments(e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/addons/mod/assign/submission/comments/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/addons/mod/assign/submission/comments/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "pluginname": "Submission comments"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								src/addons/mod/assign/submission/comments/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/addons/mod/assign/submission/comments/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,107 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '@addons/mod/assign/services/assign';
 | 
			
		||||
import { AddonModAssignSubmissionHandler } from '@addons/mod/assign/services/submission-delegate';
 | 
			
		||||
import { Injectable, Type } from '@angular/core';
 | 
			
		||||
import { CoreComments } from '@features/comments/services/comments';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModAssignSubmissionCommentsComponent } from '../component/comments';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler for comments submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class AddonModAssignSubmissionCommentsHandlerService implements AddonModAssignSubmissionHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModAssignSubmissionCommentsHandler';
 | 
			
		||||
    type = 'comments';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the
 | 
			
		||||
     * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit
 | 
			
		||||
     * unfiltered data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Boolean or promise resolved with boolean: whether it can be edited in offline.
 | 
			
		||||
     */
 | 
			
		||||
    canEditOffline(): boolean {
 | 
			
		||||
        // This plugin is read only, but return true to prevent blocking the edition.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the Component to use to display the plugin data, either in read or in edit mode.
 | 
			
		||||
     * It's recommended to return the class of the component, but you can also return an instance of the component.
 | 
			
		||||
     *
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param edit Whether the user is editing.
 | 
			
		||||
     * @return The component (or promise resolved with component) to use, undefined if not found.
 | 
			
		||||
     */
 | 
			
		||||
    getComponent(plugin: AddonModAssignPlugin, edit = false): Type<unknown> | undefined {
 | 
			
		||||
        return edit ? undefined : AddonModAssignSubmissionCommentsComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True or promise resolved with true if enabled.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabledForEdit(): boolean{
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch any required data for the plugin.
 | 
			
		||||
     * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async prefetch(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        // Fail silently (Moodle < 3.1.1, 3.2)
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
            CoreComments.instance.getComments(
 | 
			
		||||
                'module',
 | 
			
		||||
                assign.cmid,
 | 
			
		||||
                'assignsubmission_comments',
 | 
			
		||||
                submission.id,
 | 
			
		||||
                'submission_comments',
 | 
			
		||||
                0,
 | 
			
		||||
                siteId,
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModAssignSubmissionCommentsHandler = makeSingleton(AddonModAssignSubmissionCommentsHandlerService);
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
<!-- Read only. -->
 | 
			
		||||
<ion-item class="ion-text-wrap" *ngIf="files && files.length && !edit">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ plugin.name }}</h2>
 | 
			
		||||
        <div lines="none">
 | 
			
		||||
            <core-files [files]="files" [component]="component" [componentId]="assign.cmid" [alwaysDownload]="true"></core-files>
 | 
			
		||||
        </div>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
 | 
			
		||||
<!-- Edit -->
 | 
			
		||||
<div *ngIf="edit">
 | 
			
		||||
    <ion-item-divider class="ion-text-wrap" sticky="true">
 | 
			
		||||
        <ion-label><h2>{{ plugin.name }}</h2></ion-label>
 | 
			
		||||
    </ion-item-divider>
 | 
			
		||||
    <core-attachments [files]="files" [maxSize]="maxSize" [maxSubmissions]="maxSubmissions"
 | 
			
		||||
        [component]="component" [componentId]="assign.cmid" [acceptedTypes]="acceptedTypes" [allowOffline]="allowOffline">
 | 
			
		||||
    </core-attachments>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										85
									
								
								src/addons/mod/assign/submission/file/component/file.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/addons/mod/assign/submission/file/component/file.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
 | 
			
		||||
import { AddonModAssign, AddonModAssignProvider } from '@addons/mod/assign/services/assign';
 | 
			
		||||
import { AddonModAssignHelper } from '@addons/mod/assign/services/assign-helper';
 | 
			
		||||
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreFileSession } from '@services/file-session';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { AddonModAssignSubmissionFileHandlerService } from '../services/handler';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file/ngx';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render a file submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-assign-submission-file',
 | 
			
		||||
    templateUrl: 'addon-mod-assign-submission-file.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmissionPluginComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    component = AddonModAssignProvider.COMPONENT;
 | 
			
		||||
 | 
			
		||||
    maxSize?: number;
 | 
			
		||||
    acceptedTypes?: string;
 | 
			
		||||
    maxSubmissions?: number;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    async nOnInit(): Promise<void> {
 | 
			
		||||
        // Get the offline data.
 | 
			
		||||
        const filesData = await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
            AddonModAssignOffline.instance.getSubmission(this.assign.id),
 | 
			
		||||
            undefined,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.acceptedTypes = this.data?.configs.filetypeslist;
 | 
			
		||||
        this.maxSize = this.data?.configs.maxsubmissionsizebytes
 | 
			
		||||
            ? parseInt(this.data?.configs.maxsubmissionsizebytes, 10)
 | 
			
		||||
            : undefined;
 | 
			
		||||
        this.maxSubmissions = this.data?.configs.maxfilesubmissions
 | 
			
		||||
            ? parseInt(this.data?.configs.maxfilesubmissions, 10)
 | 
			
		||||
            : 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.instance.ignoreErrors(
 | 
			
		||||
                        AddonModAssignHelper.instance.getStoredSubmissionFiles(
 | 
			
		||||
                            this.assign.id,
 | 
			
		||||
                            AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
 | 
			
		||||
                        ),
 | 
			
		||||
                        [],
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.files = offlineDataFiles.online || [];
 | 
			
		||||
                this.files = this.files.concat(offlineFiles);
 | 
			
		||||
            } else {
 | 
			
		||||
                // No offline data, get the online files.
 | 
			
		||||
                this.files = AddonModAssign.instance.getSubmissionPluginAttachments(this.plugin);
 | 
			
		||||
            }
 | 
			
		||||
        } finally  {
 | 
			
		||||
            CoreFileSession.instance.setFiles(this.component, this.assign.id, this.files);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/addons/mod/assign/submission/file/file.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/addons/mod/assign/submission/file/file.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { AddonModAssignSubmissionFileHandler } from './services/handler';
 | 
			
		||||
import { AddonModAssignSubmissionFileComponent } from './component/file';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModAssignSubmissionDelegate } from '../../services/submission-delegate';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModAssignSubmissionFileComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        AddonModAssignSubmissionFileHandler,
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                AddonModAssignSubmissionDelegate.instance.registerHandler(AddonModAssignSubmissionFileHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModAssignSubmissionFileComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    entryComponents: [
 | 
			
		||||
        AddonModAssignSubmissionFileComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionFileModule {}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/addons/mod/assign/submission/file/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/addons/mod/assign/submission/file/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "pluginname": "File submissions"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										388
									
								
								src/addons/mod/assign/submission/file/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								src/addons/mod/assign/submission/file/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,388 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    AddonModAssignAssign,
 | 
			
		||||
    AddonModAssignSubmission,
 | 
			
		||||
    AddonModAssignPlugin,
 | 
			
		||||
    AddonModAssignProvider,
 | 
			
		||||
    AddonModAssign,
 | 
			
		||||
} from '@addons/mod/assign/services/assign';
 | 
			
		||||
import { AddonModAssignHelper } from '@addons/mod/assign/services/assign-helper';
 | 
			
		||||
import { AddonModAssignOffline, AddonModAssignSubmissionsDBRecordFormatted } from '@addons/mod/assign/services/assign-offline';
 | 
			
		||||
import { AddonModAssignSubmissionHandler } from '@addons/mod/assign/services/submission-delegate';
 | 
			
		||||
import { Injectable, Type } from '@angular/core';
 | 
			
		||||
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreFileHelper } from '@services/file-helper';
 | 
			
		||||
import { CoreFileSession } from '@services/file-session';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModAssignSubmissionFileComponent } from '../component/file';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file/ngx';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler for file submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class AddonModAssignSubmissionFileHandlerService implements AddonModAssignSubmissionHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly FOLDER_NAME = 'submission_file';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModAssignSubmissionFileHandler';
 | 
			
		||||
    type = 'file';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the
 | 
			
		||||
     * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit
 | 
			
		||||
     * unfiltered data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Boolean or promise resolved with boolean: whether it can be edited in offline.
 | 
			
		||||
     */
 | 
			
		||||
    canEditOffline(): boolean {
 | 
			
		||||
        // This plugin doesn't use Moodle filters, it can be edited in offline.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a plugin has no data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return Whether the plugin is empty.
 | 
			
		||||
     */
 | 
			
		||||
    isEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean {
 | 
			
		||||
        const files = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
 | 
			
		||||
        return files.length === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Should clear temporary data for a cancelled submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     */
 | 
			
		||||
    clearTmpData(assign: AddonModAssignAssign): void {
 | 
			
		||||
        const files = CoreFileSession.instance.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
 | 
			
		||||
 | 
			
		||||
        // Clear the files in session for this assign.
 | 
			
		||||
        CoreFileSession.instance.clearFiles(AddonModAssignProvider.COMPONENT, assign.id);
 | 
			
		||||
 | 
			
		||||
        // Now delete the local files from the tmp folder.
 | 
			
		||||
        CoreFileUploader.instance.clearTmpFiles(files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This function will be called when the user wants to create a new submission based on the previous one.
 | 
			
		||||
     * It should add to pluginData the data to send to server based in the data in plugin (previous attempt).
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async copySubmissionData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionFilePluginData,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        // We need to re-upload all the existing files.
 | 
			
		||||
        const files = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
 | 
			
		||||
        // Get the itemId.
 | 
			
		||||
        pluginData.files_filemanager = await AddonModAssignHelper.instance.uploadFiles(assign.id, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the Component to use to display the plugin data, either in read or in edit mode.
 | 
			
		||||
     * It's recommended to return the class of the component, but you can also return an instance of the component.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The component (or promise resolved with component) to use, undefined if not found.
 | 
			
		||||
     */
 | 
			
		||||
    getComponent(): Type<unknown> {
 | 
			
		||||
        return AddonModAssignSubmissionFileComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete any stored data for the plugin and submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param offlineData Offline data stored.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteOfflineData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
            AddonModAssignHelper.instance.deleteStoredSubmissionFiles(
 | 
			
		||||
                assign.id,
 | 
			
		||||
                AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
 | 
			
		||||
                submission.userid,
 | 
			
		||||
                siteId,
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get files used by this plugin.
 | 
			
		||||
     * The files returned by this function will be prefetched when the user prefetches the assign.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return The files (or promise resolved with the files).
 | 
			
		||||
     */
 | 
			
		||||
    getPluginFiles(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): CoreWSExternalFile[] {
 | 
			
		||||
        return AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of data (in bytes) this plugin will send to copy a previous submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return The size (or promise resolved with size).
 | 
			
		||||
     */
 | 
			
		||||
    async getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise<number> {
 | 
			
		||||
        const files = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
 | 
			
		||||
        return CoreFileHelper.instance.getTotalFilesSize(files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of data (in bytes) this plugin will send to add or edit a submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return The size (or promise resolved with size).
 | 
			
		||||
     */
 | 
			
		||||
    async getSizeForEdit(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): Promise<number> {
 | 
			
		||||
        // Check if there's any change.
 | 
			
		||||
        if (this.hasDataChanged(assign, submission, plugin)) {
 | 
			
		||||
            const files = CoreFileSession.instance.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
 | 
			
		||||
 | 
			
		||||
            return CoreFileHelper.instance.getTotalFilesSize(files);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Nothing has changed, we won't upload any file.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the submission data has changed for this plugin.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return Boolean (or promise resolved with boolean): whether the data has changed.
 | 
			
		||||
     */
 | 
			
		||||
    async hasDataChanged(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
        const offlineData = await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
            // Check if there's any offline data.
 | 
			
		||||
            AddonModAssignOffline.instance.getSubmission(assign.id, submission.userid),
 | 
			
		||||
            undefined,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let numFiles: number;
 | 
			
		||||
        if (offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager) {
 | 
			
		||||
            const offlineDataFiles = <CoreFileUploaderStoreFilesResult>offlineData.plugindata.files_filemanager;
 | 
			
		||||
            // Has offline data, return the number of files.
 | 
			
		||||
            numFiles = offlineDataFiles.offline + offlineDataFiles.online.length;
 | 
			
		||||
        } else {
 | 
			
		||||
            // No offline data, return the number of online files.
 | 
			
		||||
            const pluginFiles = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
 | 
			
		||||
            numFiles = pluginFiles && pluginFiles.length;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const currentFiles = CoreFileSession.instance.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
 | 
			
		||||
 | 
			
		||||
        if (currentFiles.length != numFiles) {
 | 
			
		||||
            // Number of files has changed.
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const files = await this.getSubmissionFilesToSync(assign, submission, offlineData);
 | 
			
		||||
 | 
			
		||||
        // Check if there is any local file added and list has changed.
 | 
			
		||||
        return CoreFileUploader.instance.areFileListDifferent(currentFiles, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True or promise resolved with true if enabled.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabledForEdit(): boolean {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare and add to pluginData the data to send to the server based on the input data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @param offline Whether the user is editing in offline.
 | 
			
		||||
     * @param userId User ID. If not defined, site's current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async prepareSubmissionData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: AddonModAssignSubmissionFileData,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionFilePluginData,
 | 
			
		||||
        offline?: boolean,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const changed = await this.hasDataChanged(assign, submission, plugin);
 | 
			
		||||
        if (!changed) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Data has changed, we need to upload new files and re-upload all the existing files.
 | 
			
		||||
        const currentFiles = CoreFileSession.instance.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
 | 
			
		||||
        const error = CoreUtils.instance.hasRepeatedFilenames(currentFiles);
 | 
			
		||||
 | 
			
		||||
        if (error) {
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pluginData.files_filemanager = await AddonModAssignHelper.instance.uploadOrStoreFiles(
 | 
			
		||||
            assign.id,
 | 
			
		||||
            AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
 | 
			
		||||
            currentFiles,
 | 
			
		||||
            offline,
 | 
			
		||||
            userId,
 | 
			
		||||
            siteId,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare and add to pluginData the data to send to the server based on the offline data stored.
 | 
			
		||||
     * This will be used when performing a synchronization.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param offlineData Offline data stored.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async prepareSyncData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionFilePluginData,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const files = await this.getSubmissionFilesToSync(assign, submission, offlineData, siteId);
 | 
			
		||||
 | 
			
		||||
        if (files.length == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pluginData.files_filemanager = await AddonModAssignHelper.instance.uploadFiles(assign.id, files, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the file list to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param offlineData Offline data stored.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return File entries when is all resolved.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getSubmissionFilesToSync(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        offlineData?: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<(FileEntry | CoreWSExternalFile)[]> {
 | 
			
		||||
        const filesData = <CoreFileUploaderStoreFilesResult>offlineData?.plugindata.files_filemanager;
 | 
			
		||||
        if (!filesData) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Has some data to sync.
 | 
			
		||||
        let files: (FileEntry | CoreWSExternalFile)[] = filesData.online || [];
 | 
			
		||||
 | 
			
		||||
        if (filesData.offline) {
 | 
			
		||||
            // Has offline files, get them and add them to the list.
 | 
			
		||||
            const storedFiles = <FileEntry[]> await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
                AddonModAssignHelper.instance.getStoredSubmissionFiles(
 | 
			
		||||
                    assign.id,
 | 
			
		||||
                    AddonModAssignSubmissionFileHandlerService.FOLDER_NAME,
 | 
			
		||||
                    submission.userid,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                ),
 | 
			
		||||
                [],
 | 
			
		||||
            );
 | 
			
		||||
            files = files.concat(storedFiles);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return files;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModAssignSubmissionFileHandler = makeSingleton(AddonModAssignSubmissionFileHandlerService);
 | 
			
		||||
 | 
			
		||||
// Define if ever used.
 | 
			
		||||
export type AddonModAssignSubmissionFileData = Record<string, unknown>;
 | 
			
		||||
 | 
			
		||||
export type AddonModAssignSubmissionFilePluginData = {
 | 
			
		||||
    // The id of a draft area containing files for this submission. Or the offline file results.
 | 
			
		||||
    files_filemanager: number | CoreFileUploaderStoreFilesResult; // eslint-disable-line @typescript-eslint/naming-convention
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,35 @@
 | 
			
		||||
<!-- Read only -->
 | 
			
		||||
<ion-item class="ion-text-wrap" *ngIf="!edit && text">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ plugin.name }}</h2>
 | 
			
		||||
        <p *ngIf="words">{{ 'addon.mod_assign.numwords' | translate: {'$a': words} }}</p>
 | 
			
		||||
        <p>
 | 
			
		||||
            <core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
 | 
			
		||||
                [fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
 | 
			
		||||
                [courseId]="assign.course">
 | 
			
		||||
            </core-format-text>
 | 
			
		||||
        </p>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
 | 
			
		||||
<!-- Edit -->
 | 
			
		||||
<div *ngIf="edit && loaded">
 | 
			
		||||
    <ion-item-divider class="ion-text-wrap" sticky="true">
 | 
			
		||||
        <ion-label><h2>{{ plugin.name }}</h2></ion-label>
 | 
			
		||||
    </ion-item-divider>
 | 
			
		||||
    <ion-item class="ion-text-wrap" *ngIf="wordLimitEnabled && words >= 0">
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <h2>{{ 'addon.mod_assign.wordlimit' | translate }}</h2>
 | 
			
		||||
            <p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + wordLimit} }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item class="ion-text-wrap">
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <core-rich-text-editor [control]="control" [placeholder]="plugin.name"
 | 
			
		||||
                name="onlinetext_editor_text" (contentChanged)="onChange($event)" [component]="component"
 | 
			
		||||
                [componentId]="assign.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="assign.cmid"
 | 
			
		||||
                elementId="onlinetext_editor" [draftExtraParams]="{userid: currentUserId, action: 'editsubmission'}">
 | 
			
		||||
            </core-rich-text-editor>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,130 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
 | 
			
		||||
import { AddonModAssignProvider, AddonModAssign } from '@addons/mod/assign/services/assign';
 | 
			
		||||
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
 | 
			
		||||
import { Component, OnInit, ElementRef } from '@angular/core';
 | 
			
		||||
import { FormBuilder, FormControl } from '@angular/forms';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handler';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render an onlinetext submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-assign-submission-online-text',
 | 
			
		||||
    templateUrl: 'addon-mod-assign-submission-onlinetext.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    control?: FormControl;
 | 
			
		||||
    words = 0;
 | 
			
		||||
    component = AddonModAssignProvider.COMPONENT;
 | 
			
		||||
    text = '';
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    wordLimitEnabled = false;
 | 
			
		||||
    currentUserId: number;
 | 
			
		||||
    wordLimit = 0;
 | 
			
		||||
 | 
			
		||||
    protected wordCountTimeout?: number;
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        protected fb: FormBuilder,
 | 
			
		||||
        element: ElementRef,
 | 
			
		||||
    ) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.element = element.nativeElement;
 | 
			
		||||
        this.currentUserId = CoreSites.instance.getCurrentSiteUserId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    async nOnInit(): Promise<void> {
 | 
			
		||||
        // Get the text. Check if we have anything offline.
 | 
			
		||||
        const offlineData = await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
            AddonModAssignOffline.instance.getSubmission(this.assign.id),
 | 
			
		||||
            undefined,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.wordLimitEnabled = !!parseInt(this.data?.configs.wordlimitenabled || '0', 10);
 | 
			
		||||
        this.wordLimit = parseInt(this.data?.configs.wordlimit || '0');
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) {
 | 
			
		||||
                this.text = (<AddonModAssignSubmissionOnlineTextPluginData>offlineData.plugindata).onlinetext_editor.text;
 | 
			
		||||
            } else {
 | 
			
		||||
                // No offline data found, return online text.
 | 
			
		||||
                this.text = AddonModAssign.instance.getSubmissionPluginText(this.plugin);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // Set the text.
 | 
			
		||||
            if (!this.edit) {
 | 
			
		||||
                // Not editing, see full text when clicked.
 | 
			
		||||
                this.element.addEventListener('click', (e) => {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    e.stopPropagation();
 | 
			
		||||
 | 
			
		||||
                    if (this.text) {
 | 
			
		||||
                        // Open a new state with the interpolated contents.
 | 
			
		||||
                        CoreTextUtils.instance.viewText(this.plugin.name, this.text, {
 | 
			
		||||
                            component: this.component,
 | 
			
		||||
                            componentId: this.assign.cmid,
 | 
			
		||||
                            filter: true,
 | 
			
		||||
                            contextLevel: 'module',
 | 
			
		||||
                            instanceId: this.assign.cmid,
 | 
			
		||||
                            courseId: this.assign.course,
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                // Create and add the control.
 | 
			
		||||
                this.control = this.fb.control(this.text);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Calculate initial words.
 | 
			
		||||
            if (this.wordLimitEnabled) {
 | 
			
		||||
                this.words = CoreTextUtils.instance.countWords(this.text);
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Text changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param text The new text.
 | 
			
		||||
     */
 | 
			
		||||
    onChange(text: string): void {
 | 
			
		||||
        // Count words if needed.
 | 
			
		||||
        if (this.wordLimitEnabled) {
 | 
			
		||||
            // Cancel previous wait.
 | 
			
		||||
            clearTimeout(this.wordCountTimeout);
 | 
			
		||||
 | 
			
		||||
            // Wait before calculating, if the user keeps inputing we won't calculate.
 | 
			
		||||
            // This is to prevent slowing down devices, this calculation can be slow if the text is long.
 | 
			
		||||
            this.wordCountTimeout = window.setTimeout(() => {
 | 
			
		||||
                this.words = CoreTextUtils.instance.countWords(text);
 | 
			
		||||
            }, 1500);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								src/addons/mod/assign/submission/onlinetext/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/addons/mod/assign/submission/onlinetext/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
    "pluginname": "Online text submissions",
 | 
			
		||||
    "wordlimitexceeded": "The word limit for this assignment is {{$a.limit}} words and you are attempting to submit {{$a.count}} words. Please review your submission and try again."
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,48 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { AddonModAssignSubmissionOnlineTextHandler } from './services/handler';
 | 
			
		||||
import { AddonModAssignSubmissionOnlineTextComponent } from './component/onlinetext';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModAssignSubmissionDelegate } from '../../services/submission-delegate';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModAssignSubmissionOnlineTextComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        AddonModAssignSubmissionOnlineTextHandler,
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                AddonModAssignSubmissionDelegate.instance.registerHandler(AddonModAssignSubmissionOnlineTextHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModAssignSubmissionOnlineTextComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    entryComponents: [
 | 
			
		||||
        AddonModAssignSubmissionOnlineTextComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionOnlineTextModule {}
 | 
			
		||||
							
								
								
									
										323
									
								
								src/addons/mod/assign/submission/onlinetext/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								src/addons/mod/assign/submission/onlinetext/services/handler.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,323 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    AddonModAssignAssign,
 | 
			
		||||
    AddonModAssignSubmission,
 | 
			
		||||
    AddonModAssignPlugin,
 | 
			
		||||
    AddonModAssign,
 | 
			
		||||
} from '@addons/mod/assign/services/assign';
 | 
			
		||||
import { AddonModAssignHelper } from '@addons/mod/assign/services/assign-helper';
 | 
			
		||||
import { AddonModAssignOffline, AddonModAssignSubmissionsDBRecordFormatted } from '@addons/mod/assign/services/assign-offline';
 | 
			
		||||
import { AddonModAssignSubmissionHandler } from '@addons/mod/assign/services/submission-delegate';
 | 
			
		||||
import { Injectable, Type } from '@angular/core';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreFileHelper } from '@services/file-helper';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { AddonModAssignSubmissionOnlineTextComponent } from '../component/onlinetext';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler for online text submission plugin.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable( { providedIn: 'root' })
 | 
			
		||||
export class AddonModAssignSubmissionOnlineTextHandlerService implements AddonModAssignSubmissionHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModAssignSubmissionOnlineTextHandler';
 | 
			
		||||
    type = 'onlinetext';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the plugin can be edited in offline for existing submissions. In general, this should return false if the
 | 
			
		||||
     * plugin uses Moodle filters. The reason is that the app only prefetches filtered data, and the user should edit
 | 
			
		||||
     * unfiltered data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Boolean or promise resolved with boolean: whether it can be edited in offline.
 | 
			
		||||
     */
 | 
			
		||||
    canEditOffline(): boolean {
 | 
			
		||||
        // This plugin uses Moodle filters, it cannot be edited in offline.
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a plugin has no data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return Whether the plugin is empty.
 | 
			
		||||
     */
 | 
			
		||||
    isEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean {
 | 
			
		||||
        const text = AddonModAssign.instance.getSubmissionPluginText(plugin, true);
 | 
			
		||||
 | 
			
		||||
        // If the text is empty, we can ignore files because they won't be visible anyways.
 | 
			
		||||
        return text.trim().length === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This function will be called when the user wants to create a new submission based on the previous one.
 | 
			
		||||
     * It should add to pluginData the data to send to server based in the data in plugin (previous attempt).
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @param userId User ID. If not defined, site's current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async copySubmissionData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionOnlineTextPluginData,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const text = AddonModAssign.instance.getSubmissionPluginText(plugin, true);
 | 
			
		||||
        const files = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
        let itemId = 0;
 | 
			
		||||
 | 
			
		||||
        if (files.length) {
 | 
			
		||||
            // Re-upload the files.
 | 
			
		||||
            itemId = await AddonModAssignHelper.instance.uploadFiles(assign.id, files, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pluginData.onlinetext_editor = {
 | 
			
		||||
            text: text,
 | 
			
		||||
            format: 1,
 | 
			
		||||
            itemid: itemId,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the Component to use to display the plugin data, either in read or in edit mode.
 | 
			
		||||
     * It's recommended to return the class of the component, but you can also return an instance of the component.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The component (or promise resolved with component) to use, undefined if not found.
 | 
			
		||||
     */
 | 
			
		||||
    getComponent(): Type<unknown> {
 | 
			
		||||
        return AddonModAssignSubmissionOnlineTextComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get files used by this plugin.
 | 
			
		||||
     * The files returned by this function will be prefetched when the user prefetches the assign.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return The files (or promise resolved with the files).
 | 
			
		||||
     */
 | 
			
		||||
    getPluginFiles(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): CoreWSExternalFile[] {
 | 
			
		||||
        return AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of data (in bytes) this plugin will send to copy a previous submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @return The size (or promise resolved with size).
 | 
			
		||||
     */
 | 
			
		||||
    async getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise<number> {
 | 
			
		||||
        const text = AddonModAssign.instance.getSubmissionPluginText(plugin, true);
 | 
			
		||||
        const files = AddonModAssign.instance.getSubmissionPluginAttachments(plugin);
 | 
			
		||||
 | 
			
		||||
        const filesSize = await CoreFileHelper.instance.getTotalFilesSize(files);
 | 
			
		||||
 | 
			
		||||
        return text.length + filesSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of data (in bytes) this plugin will send to add or edit a submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @return The size (or promise resolved with size).
 | 
			
		||||
     */
 | 
			
		||||
    getSizeForEdit(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
    ): number {
 | 
			
		||||
        const text = AddonModAssign.instance.getSubmissionPluginText(plugin, true);
 | 
			
		||||
 | 
			
		||||
        return text.length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the text to submit.
 | 
			
		||||
     *
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @return Text to submit.
 | 
			
		||||
     */
 | 
			
		||||
    protected getTextToSubmit(plugin: AddonModAssignPlugin, inputData: AddonModAssignSubmissionOnlineTextData): string {
 | 
			
		||||
        const text = inputData.onlinetext_editor_text;
 | 
			
		||||
        const files = plugin.fileareas && plugin.fileareas[0] && plugin.fileareas[0].files || [];
 | 
			
		||||
 | 
			
		||||
        return CoreTextUtils.instance.restorePluginfileUrls(text, files || []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the submission data has changed for this plugin.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @return Boolean (or promise resolved with boolean): whether the data has changed.
 | 
			
		||||
     */
 | 
			
		||||
    async hasDataChanged(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: AddonModAssignSubmissionOnlineTextData,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
 | 
			
		||||
        // Get the original text from plugin or offline.
 | 
			
		||||
        const offlineData =
 | 
			
		||||
            await CoreUtils.instance.ignoreErrors(AddonModAssignOffline.instance.getSubmission(assign.id, submission.userid));
 | 
			
		||||
 | 
			
		||||
        let initialText = '';
 | 
			
		||||
        if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) {
 | 
			
		||||
            initialText = (<AddonModAssignSubmissionOnlineTextPluginData>offlineData.plugindata).onlinetext_editor.text;
 | 
			
		||||
        } else {
 | 
			
		||||
            // No offline data found, get text from plugin.
 | 
			
		||||
            initialText = plugin.editorfields && plugin.editorfields[0] ? plugin.editorfields[0].text : '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if text has changed.
 | 
			
		||||
        return initialText != this.getTextToSubmit(plugin, inputData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True or promise resolved with true if enabled.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled for edit on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabledForEdit(): boolean {
 | 
			
		||||
        // There's a bug in Moodle 3.1.0 that doesn't allow submitting HTML, so we'll disable this plugin in that case.
 | 
			
		||||
        // Bug was fixed in 3.1.1 minor release and in 3.2.
 | 
			
		||||
        const currentSite = CoreSites.instance.getCurrentSite();
 | 
			
		||||
 | 
			
		||||
        return !!currentSite?.isVersionGreaterEqualThan('3.1.1') || !!currentSite?.checkIfAppUsesLocalMobile();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare and add to pluginData the data to send to the server based on the input data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param inputData Data entered by the user for the submission.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @param offline Whether the user is editing in offline.
 | 
			
		||||
     * @param userId User ID. If not defined, site's current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    prepareSubmissionData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        inputData: AddonModAssignSubmissionOnlineTextData,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionOnlineTextPluginData,
 | 
			
		||||
    ): void | Promise<void> {
 | 
			
		||||
 | 
			
		||||
        let text = this.getTextToSubmit(plugin, inputData);
 | 
			
		||||
 | 
			
		||||
        // Check word limit.
 | 
			
		||||
        const configs = AddonModAssignHelper.instance.getPluginConfig(assign, 'assignsubmission', plugin.type);
 | 
			
		||||
        if (parseInt(configs.wordlimitenabled, 10)) {
 | 
			
		||||
            const words = CoreTextUtils.instance.countWords(text);
 | 
			
		||||
            const wordlimit = parseInt(configs.wordlimit, 10);
 | 
			
		||||
            if (words > wordlimit) {
 | 
			
		||||
                const params = { $a: { count: words, limit: wordlimit } };
 | 
			
		||||
                const message = Translate.instance.instant('addon.mod_assign_submission_onlinetext.wordlimitexceeded', params);
 | 
			
		||||
 | 
			
		||||
                throw new CoreError(message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add some HTML to the text if needed.
 | 
			
		||||
        text = CoreTextUtils.instance.formatHtmlLines(text);
 | 
			
		||||
 | 
			
		||||
        pluginData.onlinetext_editor = {
 | 
			
		||||
            text: text,
 | 
			
		||||
            format: 1,
 | 
			
		||||
            itemid: 0, // Can't add new files yet, so we use a fake itemid.
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare and add to pluginData the data to send to the server based on the offline data stored.
 | 
			
		||||
     * This will be used when performing a synchronization.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assign The assignment.
 | 
			
		||||
     * @param submission The submission.
 | 
			
		||||
     * @param plugin The plugin object.
 | 
			
		||||
     * @param offlineData Offline data stored.
 | 
			
		||||
     * @param pluginData Object where to store the data to send.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return If the function is async, it should return a Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    prepareSyncData(
 | 
			
		||||
        assign: AddonModAssignAssign,
 | 
			
		||||
        submission: AddonModAssignSubmission,
 | 
			
		||||
        plugin: AddonModAssignPlugin,
 | 
			
		||||
        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
			
		||||
        pluginData: AddonModAssignSubmissionOnlineTextPluginData,
 | 
			
		||||
    ): void | Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const offlinePluginData = <AddonModAssignSubmissionOnlineTextPluginData>(offlineData && offlineData.plugindata);
 | 
			
		||||
        const textData = offlinePluginData.onlinetext_editor;
 | 
			
		||||
        if (textData) {
 | 
			
		||||
            // Has some data to sync.
 | 
			
		||||
            pluginData.onlinetext_editor = textData;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModAssignSubmissionOnlineTextHandler = makeSingleton(AddonModAssignSubmissionOnlineTextHandlerService);
 | 
			
		||||
 | 
			
		||||
export type AddonModAssignSubmissionOnlineTextData = {
 | 
			
		||||
    // The text for this submission.
 | 
			
		||||
    onlinetext_editor_text: string; // eslint-disable-line @typescript-eslint/naming-convention
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonModAssignSubmissionOnlineTextPluginData = {
 | 
			
		||||
    // Editor structure.
 | 
			
		||||
    onlinetext_editor: { // eslint-disable-line @typescript-eslint/naming-convention
 | 
			
		||||
        text: string; // The text for this submission.
 | 
			
		||||
        format: number; // The format for this submission.
 | 
			
		||||
        itemid: number; // The draft area id for files attached to the submission.
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										27
									
								
								src/addons/mod/assign/submission/submission.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/addons/mod/assign/submission/submission.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { AddonModAssignSubmissionCommentsModule } from './comments/comments.module';
 | 
			
		||||
import { AddonModAssignSubmissionFileModule } from './file/file.module';
 | 
			
		||||
import { AddonModAssignSubmissionOnlineTextModule } from './onlinetext/onlinetext.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        AddonModAssignSubmissionCommentsModule,
 | 
			
		||||
        AddonModAssignSubmissionFileModule,
 | 
			
		||||
        AddonModAssignSubmissionOnlineTextModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionModule { }
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user