forked from CIT/Vmeda.Online
		
	MOBILE-3636 assign: Implement assignment base
This commit is contained in:
		
							parent
							
								
									b0cf681ab6
								
							
						
					
					
						commit
						a4eefeb25a
					
				
							
								
								
									
										42
									
								
								src/addons/mod/assign/assign-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/addons/mod/assign/assign-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					// (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 { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					import { AddonModAssignComponentsModule } from './components/components.module';
 | 
				
			||||||
 | 
					import { AddonModAssignIndexPage } from './pages/index/index.page';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
 | 
					        component: AddonModAssignIndexPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId/submission-list',
 | 
				
			||||||
 | 
					        component: AddonModAssignSubmissionListPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					        AddonModAssignComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModAssignIndexPage,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModAssignLazyModule {}
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/addons/mod/assign/assign.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/addons/mod/assign/assign.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					// (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 { Routes } from '@angular/router';
 | 
				
			||||||
 | 
					import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
				
			||||||
 | 
					import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
				
			||||||
 | 
					import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
				
			||||||
 | 
					import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
				
			||||||
 | 
					import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
 | 
				
			||||||
 | 
					import { CoreCronDelegate } from '@services/cron';
 | 
				
			||||||
 | 
					import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
				
			||||||
 | 
					import { AddonModAssignComponentsModule } from './components/components.module';
 | 
				
			||||||
 | 
					import { OFFLINE_SITE_SCHEMA } from './services/database/assign';
 | 
				
			||||||
 | 
					import { AddonModAssignIndexLinkHandler } from './services/handlers/index-link';
 | 
				
			||||||
 | 
					import { AddonModAssignListLinkHandler } from './services/handlers/list-link';
 | 
				
			||||||
 | 
					import { AddonModAssignModuleHandler, AddonModAssignModuleHandlerService } from './services/handlers/module';
 | 
				
			||||||
 | 
					import { AddonModAssignPrefetchHandler } from './services/handlers/prefetch';
 | 
				
			||||||
 | 
					import { AddonModAssignPushClickHandler } from './services/handlers/push-click';
 | 
				
			||||||
 | 
					import { AddonModAssignSyncCronHandler } from './services/handlers/sync-cron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: AddonModAssignModuleHandlerService.PAGE_NAME,
 | 
				
			||||||
 | 
					        loadChildren: () => import('./assign-lazy.module').then(m => m.AddonModAssignLazyModule),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
				
			||||||
 | 
					        AddonModAssignComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    providers: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            provide: CORE_SITE_SCHEMAS,
 | 
				
			||||||
 | 
					            useValue: [OFFLINE_SITE_SCHEMA],
 | 
				
			||||||
 | 
					            multi: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            provide: APP_INITIALIZER,
 | 
				
			||||||
 | 
					            multi: true,
 | 
				
			||||||
 | 
					            deps: [],
 | 
				
			||||||
 | 
					            useFactory: () => () => {
 | 
				
			||||||
 | 
					                CoreCourseModuleDelegate.instance.registerHandler(AddonModAssignModuleHandler.instance);
 | 
				
			||||||
 | 
					                CoreContentLinksDelegate.instance.registerHandler(AddonModAssignIndexLinkHandler.instance);
 | 
				
			||||||
 | 
					                CoreContentLinksDelegate.instance.registerHandler(AddonModAssignListLinkHandler.instance);
 | 
				
			||||||
 | 
					                CoreCourseModulePrefetchDelegate.instance.registerHandler(AddonModAssignPrefetchHandler.instance);
 | 
				
			||||||
 | 
					                CoreCronDelegate.instance.register(AddonModAssignSyncCronHandler.instance);
 | 
				
			||||||
 | 
					                CorePushNotificationsDelegate.instance.registerClickHandler(AddonModAssignPushClickHandler.instance);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModAssignModule {}
 | 
				
			||||||
							
								
								
									
										47
									
								
								src/addons/mod/assign/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/addons/mod/assign/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// (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 { CommonModule } from '@angular/common';
 | 
				
			||||||
 | 
					import { FormsModule } from '@angular/forms';
 | 
				
			||||||
 | 
					import { IonicModule } from '@ionic/angular';
 | 
				
			||||||
 | 
					import { TranslateModule } from '@ngx-translate/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
				
			||||||
 | 
					import { AddonModAssignIndexComponent } from './index/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModAssignIndexComponent,
 | 
				
			||||||
 | 
					        /* AddonModAssignSubmissionComponent,
 | 
				
			||||||
 | 
					        AddonModAssignSubmissionPluginComponent,
 | 
				
			||||||
 | 
					        AddonModAssignFeedbackPluginComponent*/
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        CommonModule,
 | 
				
			||||||
 | 
					        IonicModule,
 | 
				
			||||||
 | 
					        TranslateModule.forChild(),
 | 
				
			||||||
 | 
					        FormsModule,
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					        CoreCourseComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    exports: [
 | 
				
			||||||
 | 
					        AddonModAssignIndexComponent,
 | 
				
			||||||
 | 
					        /* AddonModAssignSubmissionComponent,
 | 
				
			||||||
 | 
					        AddonModAssignSubmissionPluginComponent,
 | 
				
			||||||
 | 
					        AddonModAssignFeedbackPluginComponent */
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModAssignComponentsModule {}
 | 
				
			||||||
@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					<!-- Buttons to add to the header. -->
 | 
				
			||||||
 | 
					<core-navbar-buttons slot="end">
 | 
				
			||||||
 | 
					    <core-context-menu>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
 | 
				
			||||||
 | 
					            [href]="externalUrl" iconAction="fas-external-link-alt">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="assign && (description || (assign.introattachments && assign.introattachments.length))"
 | 
				
			||||||
 | 
					            [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()"
 | 
				
			||||||
 | 
					            iconAction="fas-arrow-right">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"
 | 
				
			||||||
 | 
					            iconAction="far-newspaper" (action)="gotoBlog()">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="loaded && !hasOffline && isOnline" [priority]="700" [content]="'core.refresh' | translate"
 | 
				
			||||||
 | 
					            (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="loaded && hasOffline && isOnline"  [priority]="600"
 | 
				
			||||||
 | 
					            [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon"
 | 
				
			||||||
 | 
					            [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)"
 | 
				
			||||||
 | 
					            [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="size" [priority]="400" [content]="'core.clearstoreddata' | translate:{$a: size}"
 | 
				
			||||||
 | 
					            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					    </core-context-menu>
 | 
				
			||||||
 | 
					</core-navbar-buttons>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- Content. -->
 | 
				
			||||||
 | 
					<core-loading [hideUntil]="loaded" class="core-loading-center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Description and intro attachments. -->
 | 
				
			||||||
 | 
					    <ion-card *ngIf="description" (click)="expandDescription($event)" class="core-clickable">
 | 
				
			||||||
 | 
					        <ion-item class="ion-text-wrap">
 | 
				
			||||||
 | 
					            <ion-label>
 | 
				
			||||||
 | 
					                <core-format-text [text]="description" [component]="component" [componentId]="componentId" maxHeight="120"
 | 
				
			||||||
 | 
					                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" (click)="expandDescription($event)">
 | 
				
			||||||
 | 
					                </core-format-text>
 | 
				
			||||||
 | 
					            </ion-label>
 | 
				
			||||||
 | 
					        </ion-item>
 | 
				
			||||||
 | 
					    </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ion-card *ngIf="assign && assign.introattachments && assign.introattachments.length">
 | 
				
			||||||
 | 
					        <core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId">
 | 
				
			||||||
 | 
					        </core-file>
 | 
				
			||||||
 | 
					    </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Assign has something offline. -->
 | 
				
			||||||
 | 
					    <ion-card class="core-warning-card" *ngIf="hasOffline">
 | 
				
			||||||
 | 
					        <ion-item>
 | 
				
			||||||
 | 
					            <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
 | 
				
			||||||
 | 
					            <ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
 | 
				
			||||||
 | 
					        </ion-item>
 | 
				
			||||||
 | 
					    </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- User can view all submissions (teacher). -->
 | 
				
			||||||
 | 
					    <ng-container *ngIf="assign && canViewAllSubmissions">
 | 
				
			||||||
 | 
					        <ion-list class="core-list-align-detail-right with-borders">
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="(groupInfo.separateGroups || groupInfo.visibleGroups)">
 | 
				
			||||||
 | 
					                <ion-label id="addon-assign-groupslabel">
 | 
				
			||||||
 | 
					                    <ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                    <ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					                <ion-select [(ngModel)]="group" (ionChange)="setGroup(group)" aria-labelledby="addon-assign-groupslabel"
 | 
				
			||||||
 | 
					                    interface="action-sheet">
 | 
				
			||||||
 | 
					                    <ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
 | 
				
			||||||
 | 
					                        {{groupOpt.name}}
 | 
				
			||||||
 | 
					                    </ion-select-option>
 | 
				
			||||||
 | 
					                </ion-select>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="timeRemaining">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <h2>{{ 'addon.mod_assign.timeremaining' | translate }}</h2>
 | 
				
			||||||
 | 
					                    <p>{{ timeRemaining }}</p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="lateSubmissions">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <h2>{{ 'addon.mod_assign.latesubmissions' | translate }}</h2>
 | 
				
			||||||
 | 
					                    <p>{{ lateSubmissions }}</p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Summary of all submissions. -->
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="summary && summary.participantcount" (click)="goToSubmissionList()" detail>
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <h2 *ngIf="assign.teamsubmission">{{ 'addon.mod_assign.numberofteams' | translate }}</h2>
 | 
				
			||||||
 | 
					                    <h2 *ngIf="!assign.teamsubmission">{{ 'addon.mod_assign.numberofparticipants' | translate }}</h2>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					                <ion-badge slot="end" *ngIf="showNumbers" color="primary">
 | 
				
			||||||
 | 
					                    {{ summary.participantcount }}
 | 
				
			||||||
 | 
					                </ion-badge>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Summary of submissions with draft status. -->
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="assign.submissiondrafts && summary && summary.submissionsenabled"
 | 
				
			||||||
 | 
					                [detail]="!showNumbers || summary.submissiondraftscount"
 | 
				
			||||||
 | 
					                (click)="goToSubmissionList(submissionStatusDraft, summary.submissiondraftscount)">
 | 
				
			||||||
 | 
					                <ion-label><h2>{{ 'addon.mod_assign.numberofdraftsubmissions' | translate }}</h2></ion-label>
 | 
				
			||||||
 | 
					                <ion-badge slot="end" *ngIf="showNumbers" color="primary">
 | 
				
			||||||
 | 
					                    {{ summary.submissiondraftscount }}
 | 
				
			||||||
 | 
					                </ion-badge>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Summary of submissions with submitted status. -->
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="summary && summary.submissionsenabled"
 | 
				
			||||||
 | 
					                [detail]="!showNumbers || summary.submissionssubmittedcount"
 | 
				
			||||||
 | 
					                (click)="goToSubmissionList(submissionStatusSubmitted, summary.submissionssubmittedcount)">
 | 
				
			||||||
 | 
					                <ion-label><h2>{{ 'addon.mod_assign.numberofsubmittedassignments' | translate }}</h2></ion-label>
 | 
				
			||||||
 | 
					                <ion-badge slot="end" *ngIf="showNumbers" color="primary">
 | 
				
			||||||
 | 
					                    {{ summary.submissionssubmittedcount }}
 | 
				
			||||||
 | 
					                </ion-badge>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Summary of submissions that need grading. -->
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap" *ngIf="summary && summary.submissionsenabled && !assign.teamsubmission && showNumbers"
 | 
				
			||||||
 | 
					                [detail]="needsGradingAvalaible"
 | 
				
			||||||
 | 
					                (click)="goToSubmissionList(needGrading, needsGradingAvalaible)">
 | 
				
			||||||
 | 
					                <ion-label><h2>{{ 'addon.mod_assign.numberofsubmissionsneedgrading' | translate }}</h2></ion-label>
 | 
				
			||||||
 | 
					                <ion-badge slot="end" color="primary">
 | 
				
			||||||
 | 
					                    {{ summary.submissionsneedgradingcount }}
 | 
				
			||||||
 | 
					                </ion-badge>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					        </ion-list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Ungrouped users. -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="assign.teamsubmission && summary && summary.warnofungroupedusers" class="core-info-card">
 | 
				
			||||||
 | 
					            <ion-item>
 | 
				
			||||||
 | 
					                <ion-icon name="fas-question-circle" slot="start"></ion-icon>
 | 
				
			||||||
 | 
					                <ion-label>{{ 'addon.mod_assign.'+summary.warnofungroupedusers | translate }}</ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- If it's a student, display his submission. -->
 | 
				
			||||||
 | 
					    <!-- @todo <addon-mod-assign-submission *ngIf="loaded && !canViewAllSubmissions && canViewOwnSubmission" [courseId]="courseId"
 | 
				
			||||||
 | 
					        [moduleId]="module.id">
 | 
				
			||||||
 | 
					    </addon-mod-assign-submission>-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</core-loading>
 | 
				
			||||||
							
								
								
									
										414
									
								
								src/addons/mod/assign/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								src/addons/mod/assign/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,414 @@
 | 
				
			|||||||
 | 
					// (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, Optional, OnDestroy, OnInit, ViewChild } from '@angular/core';
 | 
				
			||||||
 | 
					import { Params } from '@angular/router';
 | 
				
			||||||
 | 
					import { CoreSite } from '@classes/site';
 | 
				
			||||||
 | 
					import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component';
 | 
				
			||||||
 | 
					import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
 | 
				
			||||||
 | 
					import { CoreCourse } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { IonContent } from '@ionic/angular';
 | 
				
			||||||
 | 
					import { CoreGroupInfo, CoreGroups } from '@services/groups';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { CoreTextUtils } from '@services/utils/text';
 | 
				
			||||||
 | 
					import { CoreTimeUtils } from '@services/utils/time';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssign,
 | 
				
			||||||
 | 
					    AddonModAssignAssign,
 | 
				
			||||||
 | 
					    AddonModAssignGradedEventData,
 | 
				
			||||||
 | 
					    AddonModAssignProvider,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionGradingSummary,
 | 
				
			||||||
 | 
					} from '../../services/assign';
 | 
				
			||||||
 | 
					import { AddonModAssignOffline } from '../../services/assign-offline';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssignAutoSyncData,
 | 
				
			||||||
 | 
					    AddonModAssignSync,
 | 
				
			||||||
 | 
					    AddonModAssignSyncProvider,
 | 
				
			||||||
 | 
					    AddonModAssignSyncResult,
 | 
				
			||||||
 | 
					} from '../../services/assign-sync';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Component that displays an assignment.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'addon-mod-assign-index',
 | 
				
			||||||
 | 
					    templateUrl: 'addon-mod-assign-index.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // @todo @ViewChild(AddonModAssignSubmissionComponent) submissionComponent?: AddonModAssignSubmissionComponent;
 | 
				
			||||||
 | 
					    submissionComponent?: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    component = AddonModAssignProvider.COMPONENT;
 | 
				
			||||||
 | 
					    moduleName = 'assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assign?: AddonModAssignAssign; // The assign object.
 | 
				
			||||||
 | 
					    canViewAllSubmissions = false; // Whether the user can view all submissions.
 | 
				
			||||||
 | 
					    canViewOwnSubmission = false; // Whether the user can view their own submission.
 | 
				
			||||||
 | 
					    timeRemaining?: string; // Message about time remaining to submit.
 | 
				
			||||||
 | 
					    lateSubmissions?: string; // Message about late submissions.
 | 
				
			||||||
 | 
					    showNumbers = true; // Whether to show number of submissions with each status.
 | 
				
			||||||
 | 
					    summary?: AddonModAssignSubmissionGradingSummary; // The grading summary.
 | 
				
			||||||
 | 
					    needsGradingAvalaible = false; // Whether we can see the submissions that need grading.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    groupInfo: CoreGroupInfo = {
 | 
				
			||||||
 | 
					        groups: [],
 | 
				
			||||||
 | 
					        separateGroups: false,
 | 
				
			||||||
 | 
					        visibleGroups: false,
 | 
				
			||||||
 | 
					        defaultGroupId: 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Status.
 | 
				
			||||||
 | 
					    submissionStatusSubmitted = AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED;
 | 
				
			||||||
 | 
					    submissionStatusDraft = AddonModAssignProvider.SUBMISSION_STATUS_DRAFT;
 | 
				
			||||||
 | 
					    needGrading = AddonModAssignProvider.NEED_GRADING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected currentUserId?: number; // Current user ID.
 | 
				
			||||||
 | 
					    protected currentSite?: CoreSite; // Current user ID.
 | 
				
			||||||
 | 
					    protected syncEventName = AddonModAssignSyncProvider.AUTO_SYNCED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Observers.
 | 
				
			||||||
 | 
					    protected savedObserver?: CoreEventObserver;
 | 
				
			||||||
 | 
					    protected submittedObserver?: CoreEventObserver;
 | 
				
			||||||
 | 
					    protected gradedObserver?: CoreEventObserver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        protected content?: IonContent,
 | 
				
			||||||
 | 
					        @Optional() courseContentsPage?: CoreCourseContentsPage,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        super('AddonModLessonIndexComponent', content, courseContentsPage);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Component being initialized.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async ngOnInit(): Promise<void> {
 | 
				
			||||||
 | 
					        super.ngOnInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.currentUserId = CoreSites.instance.getCurrentSiteUserId();
 | 
				
			||||||
 | 
					        this.currentSite = CoreSites.instance.getCurrentSite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Listen to events.
 | 
				
			||||||
 | 
					        this.savedObserver = CoreEvents.on<any>(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, (data) => {
 | 
				
			||||||
 | 
					            if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) {
 | 
				
			||||||
 | 
					                // Assignment submission saved, refresh data.
 | 
				
			||||||
 | 
					                this.showLoadingAndRefresh(true, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, this.siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.submittedObserver = CoreEvents.on<any>(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, (data) => {
 | 
				
			||||||
 | 
					            if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) {
 | 
				
			||||||
 | 
					                // Assignment submitted, check completion.
 | 
				
			||||||
 | 
					                CoreCourse.instance.checkModuleCompletion(this.courseId!, this.module!.completiondata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Reload data since it can have offline data now.
 | 
				
			||||||
 | 
					                this.showLoadingAndRefresh(true, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, this.siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.gradedObserver = CoreEvents.on<AddonModAssignGradedEventData>(AddonModAssignProvider.GRADED_EVENT, (data) => {
 | 
				
			||||||
 | 
					            if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) {
 | 
				
			||||||
 | 
					                // Assignment graded, refresh data.
 | 
				
			||||||
 | 
					                this.showLoadingAndRefresh(true, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, this.siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.loadContent(false, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await AddonModAssign.instance.logView(this.assign!.id, this.assign!.name);
 | 
				
			||||||
 | 
					            CoreCourse.instance.checkModuleCompletion(this.courseId!, this.module!.completiondata);
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            // Ignore errors. Just don't check Module completion.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.canViewAllSubmissions) {
 | 
				
			||||||
 | 
					            // User can see all submissions, log grading view.
 | 
				
			||||||
 | 
					            CoreUtils.instance.ignoreErrors(AddonModAssign.instance.logGradingView(this.assign!.id, this.assign!.name));
 | 
				
			||||||
 | 
					        } else if (this.canViewOwnSubmission) {
 | 
				
			||||||
 | 
					            // User can only see their own submission, log view the user submission.
 | 
				
			||||||
 | 
					            CoreUtils.instance.ignoreErrors(AddonModAssign.instance.logSubmissionView(this.assign!.id, this.assign!.name));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Expand the description.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    expandDescription(ev?: Event): void {
 | 
				
			||||||
 | 
					        ev?.preventDefault();
 | 
				
			||||||
 | 
					        ev?.stopPropagation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.assign && (this.description || this.assign.introattachments)) {
 | 
				
			||||||
 | 
					            CoreTextUtils.instance.viewText(Translate.instance.instant('core.description'), this.description || '', {
 | 
				
			||||||
 | 
					                component: this.component,
 | 
				
			||||||
 | 
					                componentId: this.module!.id,
 | 
				
			||||||
 | 
					                files: this.assign.introattachments,
 | 
				
			||||||
 | 
					                filter: true,
 | 
				
			||||||
 | 
					                contextLevel: 'module',
 | 
				
			||||||
 | 
					                instanceId: this.module!.id,
 | 
				
			||||||
 | 
					                courseId: this.courseId,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get assignment data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param refresh If it's refreshing content.
 | 
				
			||||||
 | 
					     * @param sync If it should try to sync.
 | 
				
			||||||
 | 
					     * @param showErrors If show errors to the user of hide them.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get assignment data.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.assign = await AddonModAssign.instance.getAssignment(this.courseId!, this.module!.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.dataRetrieved.emit(this.assign);
 | 
				
			||||||
 | 
					            this.description = this.assign.intro;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sync) {
 | 
				
			||||||
 | 
					                // Try to synchronize the assign.
 | 
				
			||||||
 | 
					                await CoreUtils.instance.ignoreErrors(this.syncActivity(showErrors));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check if there's any offline data for this assign.
 | 
				
			||||||
 | 
					            this.hasOffline = await AddonModAssignOffline.instance.hasAssignOfflineData(this.assign.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get assignment submissions.
 | 
				
			||||||
 | 
					            const submissions = await AddonModAssign.instance.getSubmissions(this.assign.id, { cmId: this.module!.id });
 | 
				
			||||||
 | 
					            const time = CoreTimeUtils.instance.timestamp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.canViewAllSubmissions = submissions.canviewsubmissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (submissions.canviewsubmissions) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Calculate the messages to display about time remaining and late submissions.
 | 
				
			||||||
 | 
					                if (this.assign.duedate > 0) {
 | 
				
			||||||
 | 
					                    if (this.assign.duedate - time <= 0) {
 | 
				
			||||||
 | 
					                        this.timeRemaining = Translate.instance.instant('addon.mod_assign.assignmentisdue');
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        this.timeRemaining = CoreTimeUtils.instance.formatDuration(this.assign.duedate - time, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (this.assign.cutoffdate) {
 | 
				
			||||||
 | 
					                            if (this.assign.cutoffdate > time) {
 | 
				
			||||||
 | 
					                                this.lateSubmissions = Translate.instance.instant(
 | 
				
			||||||
 | 
					                                    'addon.mod_assign.latesubmissionsaccepted',
 | 
				
			||||||
 | 
					                                    { $a: CoreTimeUtils.instance.userDate(this.assign.cutoffdate * 1000) },
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                this.lateSubmissions = Translate.instance.instant('addon.mod_assign.nomoresubmissionsaccepted');
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            this.lateSubmissions = '';
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    this.timeRemaining = '';
 | 
				
			||||||
 | 
					                    this.lateSubmissions = '';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Check if groupmode is enabled to avoid showing wrong numbers.
 | 
				
			||||||
 | 
					                this.groupInfo = await CoreGroups.instance.getActivityGroupInfo(this.assign.cmid, false);
 | 
				
			||||||
 | 
					                this.showNumbers = (this.groupInfo.groups && this.groupInfo.groups.length == 0) ||
 | 
				
			||||||
 | 
					                    this.currentSite!.isVersionGreaterEqualThan('3.5');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await this.setGroup(CoreGroups.instance.validateGroupId(this.group, this.groupInfo));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // Check if the user can view their own submission.
 | 
				
			||||||
 | 
					                await AddonModAssign.instance.getSubmissionStatus(this.assign.id, { cmId: this.module!.id });
 | 
				
			||||||
 | 
					                this.canViewOwnSubmission = true;
 | 
				
			||||||
 | 
					            } catch (error) {
 | 
				
			||||||
 | 
					                this.canViewOwnSubmission = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (error.errorcode !== 'nopermission') {
 | 
				
			||||||
 | 
					                    throw error;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.fillContextMenu(refresh);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set group to see the summary.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param groupId Group ID.
 | 
				
			||||||
 | 
					     * @return Resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async setGroup(groupId: number): Promise<void> {
 | 
				
			||||||
 | 
					        this.group = groupId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissionStatus = await AddonModAssign.instance.getSubmissionStatus(this.assign!.id, {
 | 
				
			||||||
 | 
					            groupId: this.group,
 | 
				
			||||||
 | 
					            cmId: this.module!.id,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.summary = submissionStatus.gradingsummary;
 | 
				
			||||||
 | 
					        if (!this.summary) {
 | 
				
			||||||
 | 
					            this.needsGradingAvalaible = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.summary?.warnofungroupedusers === true) {
 | 
				
			||||||
 | 
					            this.summary.warnofungroupedusers = 'ungroupedusers';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            switch (this.summary?.warnofungroupedusers) {
 | 
				
			||||||
 | 
					                case AddonModAssignProvider.WARN_GROUPS_REQUIRED:
 | 
				
			||||||
 | 
					                    this.summary.warnofungroupedusers = 'ungroupedusers';
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case AddonModAssignProvider.WARN_GROUPS_OPTIONAL:
 | 
				
			||||||
 | 
					                    this.summary.warnofungroupedusers = 'ungroupedusersoptional';
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    this.summary.warnofungroupedusers = '';
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.needsGradingAvalaible =
 | 
				
			||||||
 | 
					            (submissionStatus.gradingsummary?.submissionsneedgradingcount || 0) > 0 &&
 | 
				
			||||||
 | 
					            this.currentSite!.isVersionGreaterEqualThan('3.2');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Go to view a list of submissions.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status Status to see.
 | 
				
			||||||
 | 
					     * @param count Number of submissions with the status.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    goToSubmissionList(status: string, count: number): void {
 | 
				
			||||||
 | 
					        if (typeof status != 'undefined' && !count && this.showNumbers) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const params: Params = {
 | 
				
			||||||
 | 
					            groupId: this.group || 0,
 | 
				
			||||||
 | 
					            moduleName: this.moduleName,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if (typeof status != 'undefined') {
 | 
				
			||||||
 | 
					            params.status = status;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        CoreNavigator.instance.navigate('submission-list', {
 | 
				
			||||||
 | 
					            params,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if sync has succeed from result sync data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param result Data returned by the sync function.
 | 
				
			||||||
 | 
					     * @return If succeed or not.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected hasSyncSucceed(result: AddonModAssignSyncResult): boolean {
 | 
				
			||||||
 | 
					        if (result.updated) {
 | 
				
			||||||
 | 
					            this.submissionComponent?.invalidateAndRefresh(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result.updated;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Perform the invalidate content function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async invalidateContent(): Promise<void> {
 | 
				
			||||||
 | 
					        const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(AddonModAssign.instance.invalidateAssignmentData(this.courseId!));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.assign) {
 | 
				
			||||||
 | 
					            promises.push(AddonModAssign.instance.invalidateAllSubmissionData(this.assign.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (this.canViewAllSubmissions) {
 | 
				
			||||||
 | 
					                promises.push(AddonModAssign.instance.invalidateSubmissionStatusData(this.assign.id, undefined, this.group));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises).finally(() => {
 | 
				
			||||||
 | 
					            this.submissionComponent?.invalidateAndRefresh(true);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User entered the page that contains the component.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidEnter(): void {
 | 
				
			||||||
 | 
					        super.ionViewDidEnter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.submissionComponent?.ionViewDidEnter();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User left the page that contains the component.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidLeave(): void {
 | 
				
			||||||
 | 
					        super.ionViewDidLeave();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.submissionComponent?.ionViewDidLeave();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Compares sync event data with current data to check if refresh content is needed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param syncEventData Data receiven on sync observer.
 | 
				
			||||||
 | 
					     * @return True if refresh is needed, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected isRefreshSyncNeeded(syncEventData: AddonModAssignAutoSyncData): boolean {
 | 
				
			||||||
 | 
					        if (this.assign && syncEventData.assignId == this.assign.id) {
 | 
				
			||||||
 | 
					            if (syncEventData.warnings && syncEventData.warnings.length) {
 | 
				
			||||||
 | 
					                // Show warnings.
 | 
				
			||||||
 | 
					                CoreDomUtils.instance.showErrorModal(syncEventData.warnings[0]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the sync of the activity.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async sync(): Promise<void> {
 | 
				
			||||||
 | 
					        await AddonModAssignSync.instance.syncAssign(this.assign!.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Component being destroyed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnDestroy(): void {
 | 
				
			||||||
 | 
					        super.ngOnDestroy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.savedObserver?.off();
 | 
				
			||||||
 | 
					        this.submittedObserver?.off();
 | 
				
			||||||
 | 
					        this.gradedObserver?.off();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/addons/mod/assign/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/addons/mod/assign/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "acceptsubmissionstatement": "Please accept the submission statement.",
 | 
				
			||||||
 | 
					    "addattempt": "Allow another attempt",
 | 
				
			||||||
 | 
					    "addnewattempt": "Add a new attempt",
 | 
				
			||||||
 | 
					    "addnewattemptfromprevious": "Add a new attempt based on previous submission",
 | 
				
			||||||
 | 
					    "addsubmission": "Add submission",
 | 
				
			||||||
 | 
					    "allowsubmissionsfromdate": "Allow submissions from",
 | 
				
			||||||
 | 
					    "allowsubmissionsfromdatesummary": "This assignment will accept submissions from <strong>{{$a}}</strong>",
 | 
				
			||||||
 | 
					    "allowsubmissionsanddescriptionfromdatesummary": "The assignment details and submission form will be available from <strong>{{$a}}</strong>",
 | 
				
			||||||
 | 
					    "applytoteam": "Apply grades and feedback to entire group",
 | 
				
			||||||
 | 
					    "assignmentisdue": "Assignment is due",
 | 
				
			||||||
 | 
					    "attemptnumber": "Attempt number",
 | 
				
			||||||
 | 
					    "attemptreopenmethod": "Attempts reopened",
 | 
				
			||||||
 | 
					    "attemptreopenmethod_manual": "Manually",
 | 
				
			||||||
 | 
					    "attemptreopenmethod_untilpass": "Automatically until pass",
 | 
				
			||||||
 | 
					    "attemptsettings": "Attempt settings",
 | 
				
			||||||
 | 
					    "cannotgradefromapp": "Certain grading methods are not yet supported by the app and cannot be modified.",
 | 
				
			||||||
 | 
					    "cannoteditduetostatementsubmission": "You can't add or edit a submission in the app because the submission statement could not be retrieved from the site.",
 | 
				
			||||||
 | 
					    "cannotsubmitduetostatementsubmission": "You can't make a submission in the app because the submission statement could not be retrieved from the site.",
 | 
				
			||||||
 | 
					    "confirmsubmission": "Are you sure you want to submit your work for grading? You will not be able to make any more changes.",
 | 
				
			||||||
 | 
					    "currentgrade": "Current grade in gradebook",
 | 
				
			||||||
 | 
					    "cutoffdate": "Cut-off date",
 | 
				
			||||||
 | 
					    "currentattempt": "This is attempt {{$a}}.",
 | 
				
			||||||
 | 
					    "currentattemptof": "This is attempt {{$a.attemptnumber}} ( {{$a.maxattempts}} attempts allowed ).",
 | 
				
			||||||
 | 
					    "defaultteam": "Default group",
 | 
				
			||||||
 | 
					    "duedate": "Due date",
 | 
				
			||||||
 | 
					    "duedateno": "No due date",
 | 
				
			||||||
 | 
					    "duedatereached": "The due date for this assignment has now passed",
 | 
				
			||||||
 | 
					    "editingstatus": "Editing status",
 | 
				
			||||||
 | 
					    "editsubmission": "Edit submission",
 | 
				
			||||||
 | 
					    "erroreditpluginsnotsupported": "You can't add or edit a submission in the app because certain plugins are not yet supported for editing.",
 | 
				
			||||||
 | 
					    "errorshowinginformation": "Submission information cannot be displayed.",
 | 
				
			||||||
 | 
					    "extensionduedate": "Extension due date",
 | 
				
			||||||
 | 
					    "feedbacknotsupported": "This feedback is not supported by the app and may not contain all the information.",
 | 
				
			||||||
 | 
					    "grade": "Grade",
 | 
				
			||||||
 | 
					    "graded": "Graded",
 | 
				
			||||||
 | 
					    "gradedby": "Graded by",
 | 
				
			||||||
 | 
					    "gradedfollowupsubmit": "Graded - follow up submission received",
 | 
				
			||||||
 | 
					    "gradenotsynced": "Grade not synced",
 | 
				
			||||||
 | 
					    "gradedon": "Graded on",
 | 
				
			||||||
 | 
					    "gradelocked": "This grade is locked or overridden in the gradebook.",
 | 
				
			||||||
 | 
					    "gradeoutof": "Grade out of {{$a}}",
 | 
				
			||||||
 | 
					    "gradingstatus": "Grading status",
 | 
				
			||||||
 | 
					    "groupsubmissionsettings": "Group submission settings",
 | 
				
			||||||
 | 
					    "hiddenuser": "Participant",
 | 
				
			||||||
 | 
					    "latesubmissions": "Late submissions",
 | 
				
			||||||
 | 
					    "latesubmissionsaccepted": "Allowed until {{$a}}",
 | 
				
			||||||
 | 
					    "markingworkflowstate": "Marking workflow state",
 | 
				
			||||||
 | 
					    "markingworkflowstateinmarking": "In marking",
 | 
				
			||||||
 | 
					    "markingworkflowstateinreview": "In review",
 | 
				
			||||||
 | 
					    "markingworkflowstatenotmarked": "Not marked",
 | 
				
			||||||
 | 
					    "markingworkflowstatereadyforreview": "Marking completed",
 | 
				
			||||||
 | 
					    "markingworkflowstatereadyforrelease": "Ready for release",
 | 
				
			||||||
 | 
					    "markingworkflowstatereleased": "Released",
 | 
				
			||||||
 | 
					    "modulenameplural": "Assignments",
 | 
				
			||||||
 | 
					    "multipleteams": "Member of more than one group",
 | 
				
			||||||
 | 
					    "multipleteams_desc": "The assignment requires submission in groups. You are a member of more than one group. To be able to submit you must be a member of only one group. Please contact your teacher to change your group membership.",
 | 
				
			||||||
 | 
					    "noattempt": "No attempt",
 | 
				
			||||||
 | 
					    "nomoresubmissionsaccepted": "Only allowed for participants who have been granted an extension",
 | 
				
			||||||
 | 
					    "noonlinesubmissions": "This assignment does not require you to submit anything online",
 | 
				
			||||||
 | 
					    "nosubmission": "Nothing has been submitted for this assignment",
 | 
				
			||||||
 | 
					    "notallparticipantsareshown": "Participants who have not made a submission are not shown.",
 | 
				
			||||||
 | 
					    "noteam": "Not a member of any group",
 | 
				
			||||||
 | 
					    "noteam_desc": "This assignment requires submission in groups. You are not a member of any group, so you cannot create a submission. Please contact your teacher to be added to a group.",
 | 
				
			||||||
 | 
					    "notgraded": "Not graded",
 | 
				
			||||||
 | 
					    "numberofdraftsubmissions": "Drafts",
 | 
				
			||||||
 | 
					    "numberofparticipants": "Participants",
 | 
				
			||||||
 | 
					    "numberofsubmittedassignments": "Submitted",
 | 
				
			||||||
 | 
					    "numberofsubmissionsneedgrading": "Needs grading",
 | 
				
			||||||
 | 
					    "numberofteams": "Groups",
 | 
				
			||||||
 | 
					    "numwords": "{{$a}} words",
 | 
				
			||||||
 | 
					    "outof": "{{$a.current}} out of {{$a.total}}",
 | 
				
			||||||
 | 
					    "overdue": "<font color=\"red\">Assignment is overdue by: {{$a}}</font>",
 | 
				
			||||||
 | 
					    "submissioneditable": "Student can edit this submission",
 | 
				
			||||||
 | 
					    "submissionnoteditable": "Student cannot edit this submission",
 | 
				
			||||||
 | 
					    "submissionnotsupported": "This submission is not supported by the app and may not contain all the information.",
 | 
				
			||||||
 | 
					    "submission": "Submission",
 | 
				
			||||||
 | 
					    "submissionslocked": "This assignment is not accepting submissions",
 | 
				
			||||||
 | 
					    "submissionstatus_draft": "Draft (not submitted)",
 | 
				
			||||||
 | 
					    "submissionstatusheading": "Submission status",
 | 
				
			||||||
 | 
					    "submissionstatus_marked": "Graded",
 | 
				
			||||||
 | 
					    "submissionstatus_new": "No submission",
 | 
				
			||||||
 | 
					    "submissionstatus_reopened": "Reopened",
 | 
				
			||||||
 | 
					    "submissionstatus_submitted": "Submitted for grading",
 | 
				
			||||||
 | 
					    "submissionstatus_": "No submission",
 | 
				
			||||||
 | 
					    "submissionstatus": "Submission status",
 | 
				
			||||||
 | 
					    "submissionteam": "Group",
 | 
				
			||||||
 | 
					    "submitassignment_help": "Once this assignment is submitted you will not be able to make any more changes.",
 | 
				
			||||||
 | 
					    "submitassignment": "Submit assignment",
 | 
				
			||||||
 | 
					    "submittedearly": "Assignment was submitted {{$a}} early",
 | 
				
			||||||
 | 
					    "submittedlate": "Assignment was submitted {{$a}} late",
 | 
				
			||||||
 | 
					    "syncblockedusercomponent": "user grade",
 | 
				
			||||||
 | 
					    "timemodified": "Last modified",
 | 
				
			||||||
 | 
					    "timeremaining": "Time remaining",
 | 
				
			||||||
 | 
					    "ungroupedusers": "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions.",
 | 
				
			||||||
 | 
					    "ungroupedusersoptional": "The setting 'Students submit in groups' is enabled and some users are either not a member of any group, or are a member of more than one group. Please be aware that these students will submit as members of the 'Default group'.",
 | 
				
			||||||
 | 
					    "unlimitedattempts": "Unlimited",
 | 
				
			||||||
 | 
					    "userwithid": "User with ID {{id}}",
 | 
				
			||||||
 | 
					    "userswhoneedtosubmit": "Users who need to submit: {{$a}}",
 | 
				
			||||||
 | 
					    "viewsubmission": "View submission",
 | 
				
			||||||
 | 
					    "warningsubmissionmodified": "The user submission was modified on the site.",
 | 
				
			||||||
 | 
					    "warningsubmissiongrademodified": "The submission grade was modified on the site.",
 | 
				
			||||||
 | 
					    "wordlimit": "Word limit"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/addons/mod/assign/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/addons/mod/assign/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>
 | 
				
			||||||
 | 
					            <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
 | 
				
			||||||
 | 
					            </core-format-text>
 | 
				
			||||||
 | 
					        </ion-title>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ion-buttons slot="end">
 | 
				
			||||||
 | 
					            <!-- The buttons defined by the component will be added in here. -->
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <ion-refresher slot="fixed" [disabled]="!assignComponent?.loaded" (ionRefresh)="assignComponent?.doRefresh($event)">
 | 
				
			||||||
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <addon-mod-assign-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-assign-index>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/addons/mod/assign/pages/index/index.page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/addons/mod/assign/pages/index/index.page.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					// (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, OnInit, ViewChild } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCourseWSModule } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { AddonModAssignIndexComponent } from '../../components/index/index';
 | 
				
			||||||
 | 
					import { AddonModAssignAssign } from '../../services/assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays an assign.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-assign-index',
 | 
				
			||||||
 | 
					    templateUrl: 'index.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModAssignIndexPage implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild(AddonModAssignIndexComponent) assignComponent?: AddonModAssignIndexComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    title?: string;
 | 
				
			||||||
 | 
					    module?: CoreCourseWSModule;
 | 
				
			||||||
 | 
					    courseId?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Component being initialized.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ngOnInit(): void {
 | 
				
			||||||
 | 
					        this.module = CoreNavigator.instance.getRouteParam('module');
 | 
				
			||||||
 | 
					        this.courseId = CoreNavigator.instance.getRouteNumberParam('courseId');
 | 
				
			||||||
 | 
					        this.title = this.module?.name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update some data based on the assign instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assign instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    updateData(assign: AddonModAssignAssign): void {
 | 
				
			||||||
 | 
					        this.title = assign.name || this.title;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User entered the page.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidEnter(): void {
 | 
				
			||||||
 | 
					        this.assignComponent?.ionViewDidEnter();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User left the page.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidLeave(): void {
 | 
				
			||||||
 | 
					        this.assignComponent?.ionViewDidLeave();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										727
									
								
								src/addons/mod/assign/services/assign-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										727
									
								
								src/addons/mod/assign/services/assign-helper.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,727 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
				
			||||||
 | 
					import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreWSExternalFile } from '@services/ws';
 | 
				
			||||||
 | 
					import { FileEntry } from '@ionic-native/file/ngx';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssignProvider,
 | 
				
			||||||
 | 
					    AddonModAssignAssign,
 | 
				
			||||||
 | 
					    AddonModAssignSubmission,
 | 
				
			||||||
 | 
					    AddonModAssignParticipant,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionFeedback,
 | 
				
			||||||
 | 
					    AddonModAssign,
 | 
				
			||||||
 | 
					} from './assign';
 | 
				
			||||||
 | 
					import { AddonModAssignOffline } from './assign-offline';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { CoreFile } from '@services/file';
 | 
				
			||||||
 | 
					import { CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { CoreGroups } from '@services/groups';
 | 
				
			||||||
 | 
					import { AddonModAssignSubmissionDelegate } from './submission-delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Service that provides some helper functions for assign.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignHelperProvider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a submission can be edited in offline.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission Submission.
 | 
				
			||||||
 | 
					     * @return Whether it can be edited offline.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async canEditSubmissionOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission): Promise<boolean> {
 | 
				
			||||||
 | 
					        if (!submission) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submission.status == AddonModAssignProvider.SUBMISSION_STATUS_NEW ||
 | 
				
			||||||
 | 
					                submission.status == AddonModAssignProvider.SUBMISSION_STATUS_REOPENED) {
 | 
				
			||||||
 | 
					            // It's a new submission, allow creating it in offline.
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let canEdit = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = submission.plugins
 | 
				
			||||||
 | 
					            ? submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.canPluginEditOffline(assign, submission, plugin).then((canEditPlugin) => {
 | 
				
			||||||
 | 
					                    if (!canEditPlugin) {
 | 
				
			||||||
 | 
					                        canEdit = false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return canEdit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Clear plugins temporary data because a submission was cancelled.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission Submission to clear the data for.
 | 
				
			||||||
 | 
					     * @param inputData Data entered in the submission form.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clearSubmissionPluginTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any): void {
 | 
				
			||||||
 | 
					        submission.plugins?.forEach((plugin) => {
 | 
				
			||||||
 | 
					            AddonModAssignSubmissionDelegate.instance.clearTmpData(assign, submission, plugin, inputData);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Copy the data from last submitted attempt to the current submission.
 | 
				
			||||||
 | 
					     * Since we don't have any WS for that we'll have to re-submit everything manually.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param previousSubmission Submission to copy.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async copyPreviousAttempt(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<void> {
 | 
				
			||||||
 | 
					        const pluginData: any = {};
 | 
				
			||||||
 | 
					        const promises = previousSubmission.plugins
 | 
				
			||||||
 | 
					            ? previousSubmission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.copyPluginSubmissionData(assign, plugin, pluginData))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We got the plugin data. Now we need to submit it.
 | 
				
			||||||
 | 
					        if (Object.keys(pluginData).length) {
 | 
				
			||||||
 | 
					            // There's something to save.
 | 
				
			||||||
 | 
					            return AddonModAssign.instance.saveSubmissionOnline(assign.id, pluginData);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create an empty feedback object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Feedback.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    createEmptyFeedback(): AddonModAssignSubmissionFeedback {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            grade: undefined,
 | 
				
			||||||
 | 
					            gradefordisplay: undefined,
 | 
				
			||||||
 | 
					            gradeddate: undefined,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create an empty submission object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Submission.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    createEmptySubmission(): AddonModAssignSubmissionFormatted {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            id: undefined,
 | 
				
			||||||
 | 
					            userid: undefined,
 | 
				
			||||||
 | 
					            attemptnumber: undefined,
 | 
				
			||||||
 | 
					            timecreated: undefined,
 | 
				
			||||||
 | 
					            timemodified: undefined,
 | 
				
			||||||
 | 
					            status: undefined,
 | 
				
			||||||
 | 
					            groupid: undefined,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete stored submission files for a plugin. See storeSubmissionFiles.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins).
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async deleteStoredSubmissionFiles(assignId: number, folderName: string, userId?: number, siteId?: string): Promise<void> {
 | 
				
			||||||
 | 
					        const folderPath = await AddonModAssignOffline.instance.getSubmissionPluginFolder(assignId, folderName, userId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreFile.instance.removeDir(folderPath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete all drafts of the feedback plugin data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment Id.
 | 
				
			||||||
 | 
					     * @param userId User Id.
 | 
				
			||||||
 | 
					     * @param feedback Feedback data.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async discardFeedbackPluginData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        feedback: AddonModAssignSubmissionFeedback,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = feedback.plugins
 | 
				
			||||||
 | 
					            ? feedback.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignFeedbackDelegate.instance.discardPluginFeedbackData(assignId, userId, plugin, siteId))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a submission has no content.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment object.
 | 
				
			||||||
 | 
					     * @param submission Submission to inspect.
 | 
				
			||||||
 | 
					     * @return Whether the submission is empty.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isSubmissionEmpty(assign: AddonModAssignAssign, submission?: AddonModAssignSubmission): boolean {
 | 
				
			||||||
 | 
					        if (!submission) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const anyNotEmpty = submission.plugins?.some((plugin) =>
 | 
				
			||||||
 | 
					            !AddonModAssignSubmissionDelegate.instance.isPluginEmpty(assign, plugin));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment object.
 | 
				
			||||||
 | 
					     * @param groupId Group Id.
 | 
				
			||||||
 | 
					     * @param options Other options.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the list of participants and summary of submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getParticipants(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        groupId?: number,
 | 
				
			||||||
 | 
					        options: CoreSitesCommonWSOptions = {},
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignParticipant[]> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        groupId = groupId || 0;
 | 
				
			||||||
 | 
					        options.siteId = options.siteId || CoreSites.instance.getCurrentSiteId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create new options including all existing ones.
 | 
				
			||||||
 | 
					        const modOptions: CoreCourseCommonModWSOptions = { cmId: assign.cmid, ...options };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const participants = await AddonModAssign.instance.listParticipants(assign.id, groupId, modOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (groupId || participants && participants.length > 0) {
 | 
				
			||||||
 | 
					            return participants;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If no participants returned and all groups specified, get participants by groups.
 | 
				
			||||||
 | 
					        const groupsInfo = await CoreGroups.instance.getActivityGroupInfo(assign.cmid, false, undefined, modOptions.siteId);
 | 
				
			||||||
 | 
					        [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const participantsIndexed: {[id: number]: AddonModAssignParticipant} = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = groupsInfo.groups
 | 
				
			||||||
 | 
					            ? groupsInfo.groups.map((userGroup) =>
 | 
				
			||||||
 | 
					                AddonModAssign.instance.listParticipants(assign.id, userGroup.id, modOptions).then((participantsFromList) => {
 | 
				
			||||||
 | 
					                    // Do not get repeated users.
 | 
				
			||||||
 | 
					                    participantsFromList.forEach((participant) => {
 | 
				
			||||||
 | 
					                        participantsIndexed[participant.id] = participant;
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					            :[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CoreUtils.instance.objectToArray(participantsIndexed);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get plugin config from assignment config.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment object including all config.
 | 
				
			||||||
 | 
					     * @param subtype Subtype name (assignsubmission or assignfeedback)
 | 
				
			||||||
 | 
					     * @param type Name of the subplugin.
 | 
				
			||||||
 | 
					     * @return Object containing all configurations of the subplugin selected.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginConfig(assign: AddonModAssignAssign, subtype: string, type: string): AddonModAssignPluginConfig {
 | 
				
			||||||
 | 
					        const configs: AddonModAssignPluginConfig = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assign.configs.forEach((config) => {
 | 
				
			||||||
 | 
					            if (config.subtype == subtype && config.plugin == type) {
 | 
				
			||||||
 | 
					                configs[config.name] = config.value;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return configs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get enabled subplugins.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment object including all config.
 | 
				
			||||||
 | 
					     * @param subtype Subtype name (assignsubmission or assignfeedback)
 | 
				
			||||||
 | 
					     * @return List of enabled plugins for the assign.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginsEnabled(assign: AddonModAssignAssign, subtype: string): AddonModAssignPluginsEnabled {
 | 
				
			||||||
 | 
					        const enabled: AddonModAssignPluginsEnabled = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assign.configs.forEach((config) => {
 | 
				
			||||||
 | 
					            if (config.subtype == subtype && config.name == 'enabled' && parseInt(config.value, 10) === 1) {
 | 
				
			||||||
 | 
					                // Format the plugin objects.
 | 
				
			||||||
 | 
					                enabled.push({
 | 
				
			||||||
 | 
					                    type: config.plugin,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return enabled;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a list of stored submission files. See storeSubmissionFiles.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins).
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the files.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getStoredSubmissionFiles(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        folderName: string,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<(FileEntry | DirectoryEntry)[]> {
 | 
				
			||||||
 | 
					        const folderPath = await AddonModAssignOffline.instance.getSubmissionPluginFolder(assignId, folderName, userId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CoreFile.instance.getDirectoryContents(folderPath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the size that will be uploaded to perform an attempt copy.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param previousSubmission Submission to copy.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the size.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionSizeForCopy(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<number> {
 | 
				
			||||||
 | 
					        let totalSize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = previousSubmission.plugins
 | 
				
			||||||
 | 
					            ? previousSubmission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.getPluginSizeForCopy(assign, plugin).then((size) => {
 | 
				
			||||||
 | 
					                    totalSize += (size || 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return totalSize;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the size that will be uploaded to save a submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission Submission to check data.
 | 
				
			||||||
 | 
					     * @param inputData Data entered in the submission form.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the size.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionSizeForEdit(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): Promise<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let totalSize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = submission.plugins
 | 
				
			||||||
 | 
					            ? submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.getPluginSizeForEdit(assign, submission, plugin, inputData)
 | 
				
			||||||
 | 
					                    .then((size) => {
 | 
				
			||||||
 | 
					                        totalSize += (size || 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return totalSize;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get user data for submissions since they only have userid.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment object.
 | 
				
			||||||
 | 
					     * @param submissions Submissions to get the data for.
 | 
				
			||||||
 | 
					     * @param groupId Group Id.
 | 
				
			||||||
 | 
					     * @param options Other options.
 | 
				
			||||||
 | 
					     * @return Promise always resolved. Resolve param is the formatted submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionsUserData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submissions: AddonModAssignSubmissionFormatted[] = [],
 | 
				
			||||||
 | 
					        groupId?: number,
 | 
				
			||||||
 | 
					        options: CoreSitesCommonWSOptions = {},
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionFormatted[]> {
 | 
				
			||||||
 | 
					        // Create new options including all existing ones.
 | 
				
			||||||
 | 
					        const modOptions: CoreCourseCommonModWSOptions = { cmId: assign.cmid, ...options };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const parts = await this.getParticipants(assign, groupId, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const blind = assign.blindmarking && !assign.revealidentities;
 | 
				
			||||||
 | 
					        const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					        const result: AddonModAssignSubmissionFormatted[] = [];
 | 
				
			||||||
 | 
					        const participants: {[id: number]: AddonModAssignParticipant} = CoreUtils.instance.arrayToObject(parts, 'id');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        submissions.forEach((submission) => {
 | 
				
			||||||
 | 
					            submission.submitid = submission.userid && submission.userid > 0 ? submission.userid : submission.blindid;
 | 
				
			||||||
 | 
					            if (typeof submission.submitid == 'undefined' || submission.submitid <= 0) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const participant = participants[submission.submitid];
 | 
				
			||||||
 | 
					            if (!participant) {
 | 
				
			||||||
 | 
					                // Avoid permission denied error. Participant not found on list.
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            delete participants[submission.submitid];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!blind) {
 | 
				
			||||||
 | 
					                submission.userfullname = participant.fullname;
 | 
				
			||||||
 | 
					                submission.userprofileimageurl = participant.profileimageurl;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            submission.manyGroups = !!participant.groups && participant.groups.length > 1;
 | 
				
			||||||
 | 
					            submission.noGroups = !!participant.groups && participant.groups.length == 0;
 | 
				
			||||||
 | 
					            if (participant.groupname) {
 | 
				
			||||||
 | 
					                submission.groupid = participant.groupid!;
 | 
				
			||||||
 | 
					                submission.groupname = participant.groupname;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let promise = Promise.resolve();
 | 
				
			||||||
 | 
					            if (submission.userid && submission.userid > 0 && blind) {
 | 
				
			||||||
 | 
					                // Blind but not blinded! (Moodle < 3.1.1, 3.2).
 | 
				
			||||||
 | 
					                delete submission.userid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                promise = AddonModAssign.instance.getAssignmentUserMappings(assign.id, submission.submitid, modOptions)
 | 
				
			||||||
 | 
					                    .then((blindId) => {
 | 
				
			||||||
 | 
					                        submission.blindid = blindId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            promises.push(promise.then(() => {
 | 
				
			||||||
 | 
					                // Add to the list.
 | 
				
			||||||
 | 
					                if (submission.userfullname || submission.blindid) {
 | 
				
			||||||
 | 
					                    result.push(submission);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create a submission for each participant left in the list (the participants already treated were removed).
 | 
				
			||||||
 | 
					        CoreUtils.instance.objectToArray(participants).forEach((participant: AddonModAssignParticipant) => {
 | 
				
			||||||
 | 
					            const submission = this.createEmptySubmission();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            submission.submitid = participant.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!blind) {
 | 
				
			||||||
 | 
					                submission.userid = participant.id;
 | 
				
			||||||
 | 
					                submission.userfullname = participant.fullname;
 | 
				
			||||||
 | 
					                submission.userprofileimageurl = participant.profileimageurl;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                submission.blindid = participant.id;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            submission.manyGroups = !!participant.groups && participant.groups.length > 1;
 | 
				
			||||||
 | 
					            submission.noGroups = !!participant.groups && participant.groups.length == 0;
 | 
				
			||||||
 | 
					            if (participant.groupname) {
 | 
				
			||||||
 | 
					                submission.groupid = participant.groupid!;
 | 
				
			||||||
 | 
					                submission.groupname = participant.groupname;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            submission.status = participant.submitted ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED :
 | 
				
			||||||
 | 
					                AddonModAssignProvider.SUBMISSION_STATUS_NEW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result.push(submission);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the feedback data has changed for a certain submission and assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param feedback Feedback data.
 | 
				
			||||||
 | 
					     * @param userId The user ID.
 | 
				
			||||||
 | 
					     * @return Promise resolved with true if data has changed, resolved with false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasFeedbackDataChanged(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        feedback: AddonModAssignSubmissionFeedback,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					    ): Promise<boolean> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let hasChanged = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = feedback.plugins
 | 
				
			||||||
 | 
					            ? feedback.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                this.prepareFeedbackPluginData(assign.id, userId, feedback).then(async (inputData) => {
 | 
				
			||||||
 | 
					                    const changed = await CoreUtils.instance.ignoreErrors(
 | 
				
			||||||
 | 
					                        AddonModAssignFeedbackDelegate.instance.hasPluginDataChanged(assign, submission, plugin, inputData, userId),
 | 
				
			||||||
 | 
					                        false,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    if (changed) {
 | 
				
			||||||
 | 
					                        hasChanged = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreUtils.instance.allPromises(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return hasChanged;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the submission data has changed for a certain submission and assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission Submission to check data.
 | 
				
			||||||
 | 
					     * @param inputData Data entered in the submission form.
 | 
				
			||||||
 | 
					     * @return Promise resolved with true if data has changed, resolved with false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasSubmissionDataChanged(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): Promise<boolean> {
 | 
				
			||||||
 | 
					        let hasChanged = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = submission.plugins
 | 
				
			||||||
 | 
					            ? submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.hasPluginDataChanged(assign, submission, plugin, inputData)
 | 
				
			||||||
 | 
					                    .then((changed) => {
 | 
				
			||||||
 | 
					                        if (changed) {
 | 
				
			||||||
 | 
					                            hasChanged = true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }).catch(() => {
 | 
				
			||||||
 | 
					                    // Ignore errors.
 | 
				
			||||||
 | 
					                    }))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreUtils.instance.allPromises(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return hasChanged;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and return the plugin data to send for a certain feedback and assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment Id.
 | 
				
			||||||
 | 
					     * @param userId User Id.
 | 
				
			||||||
 | 
					     * @param feedback Feedback data.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with plugin data to send to server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async prepareFeedbackPluginData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        feedback: AddonModAssignSubmissionFeedback,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const pluginData = {};
 | 
				
			||||||
 | 
					        const promises = feedback.plugins
 | 
				
			||||||
 | 
					            ? feedback.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignFeedbackDelegate.instance.preparePluginFeedbackData(assignId, userId, plugin, pluginData, siteId))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return pluginData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and return the plugin data to send for a certain submission and assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param submission Submission to check data.
 | 
				
			||||||
 | 
					     * @param inputData Data entered in the submission form.
 | 
				
			||||||
 | 
					     * @param offline True to prepare the data for an offline submission, false otherwise.
 | 
				
			||||||
 | 
					     * @return Promise resolved with plugin data to send to server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async prepareSubmissionPluginData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					        offline = false,
 | 
				
			||||||
 | 
					    ): Promise<any> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const pluginData = {};
 | 
				
			||||||
 | 
					        const promises = submission.plugins
 | 
				
			||||||
 | 
					            ? submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.preparePluginSubmissionData(
 | 
				
			||||||
 | 
					                    assign,
 | 
				
			||||||
 | 
					                    submission,
 | 
				
			||||||
 | 
					                    plugin,
 | 
				
			||||||
 | 
					                    inputData,
 | 
				
			||||||
 | 
					                    pluginData,
 | 
				
			||||||
 | 
					                    offline,
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return pluginData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Given a list of files (either online files or local files), store the local files in a local folder
 | 
				
			||||||
 | 
					     * to be submitted later.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins).
 | 
				
			||||||
 | 
					     * @param files List of files.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if success, rejected otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async storeSubmissionFiles(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        folderName: string,
 | 
				
			||||||
 | 
					        files: (CoreWSExternalFile | FileEntry)[],
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<CoreFileUploaderStoreFilesResult> {
 | 
				
			||||||
 | 
					        // Get the folder where to store the files.
 | 
				
			||||||
 | 
					        const folderPath = await AddonModAssignOffline.instance.getSubmissionPluginFolder(assignId, folderName, userId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CoreFileUploader.instance.storeFilesToUpload(folderPath, files);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Upload a file to a draft area. If the file is an online file it will be downloaded and then re-uploaded.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param file Online file or local FileEntry.
 | 
				
			||||||
 | 
					     * @param itemId Draft ID to use. Undefined or 0 to create a new draft ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the itemId.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    uploadFile(assignId: number, file: CoreWSExternalFile | FileEntry, itemId?: number, siteId?: string): Promise<number> {
 | 
				
			||||||
 | 
					        return CoreFileUploader.instance.uploadOrReuploadFile(file, itemId, AddonModAssignProvider.COMPONENT, assignId, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Given a list of files (either online files or local files), upload them to a draft area and return the draft ID.
 | 
				
			||||||
 | 
					     * Online files will be downloaded and then re-uploaded.
 | 
				
			||||||
 | 
					     * If there are no files to upload it will return a fake draft ID (1).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param files List of files.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the itemId.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    uploadFiles(assignId: number, files: (CoreWSExternalFile | FileEntry)[], siteId?: string): Promise<number> {
 | 
				
			||||||
 | 
					        return CoreFileUploader.instance.uploadOrReuploadFiles(files, AddonModAssignProvider.COMPONENT, assignId, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Upload or store some files, depending if the user is offline or not.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param folderName Name of the plugin folder. Must be unique (both in submission and feedback plugins).
 | 
				
			||||||
 | 
					     * @param files List of files.
 | 
				
			||||||
 | 
					     * @param offline True if files sould be stored for offline, false to upload them.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    uploadOrStoreFiles(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        folderName: string,
 | 
				
			||||||
 | 
					        files: (CoreWSExternalFile | FileEntry)[],
 | 
				
			||||||
 | 
					        offline = false,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (offline) {
 | 
				
			||||||
 | 
					            return this.storeSubmissionFiles(assignId, folderName, files, userId, siteId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return this.uploadFiles(assignId, files, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignHelper = makeSingleton(AddonModAssignHelperProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Assign submission with some calculated data.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignSubmissionFormatted =
 | 
				
			||||||
 | 
					    Omit<AddonModAssignSubmission, 'id' | 'userid' | 'attemptnumber' | 'timecreated' | 'timemodified' | 'status' | 'groupid'> & {
 | 
				
			||||||
 | 
					        id?: number; // Submission id.
 | 
				
			||||||
 | 
					        userid?: number; // Student id.
 | 
				
			||||||
 | 
					        attemptnumber?: number; // Attempt number.
 | 
				
			||||||
 | 
					        timecreated?: number; // Submission creation time.
 | 
				
			||||||
 | 
					        timemodified?: number; // Submission last modified time.
 | 
				
			||||||
 | 
					        status?: string; // Submission status.
 | 
				
			||||||
 | 
					        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.
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Assingment subplugins type enabled.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignPluginsEnabled = {
 | 
				
			||||||
 | 
					    type: string; // Plugin type.
 | 
				
			||||||
 | 
					}[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Assingment plugin config.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignPluginConfig = {[name: string]: string};
 | 
				
			||||||
							
								
								
									
										459
									
								
								src/addons/mod/assign/services/assign-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								src/addons/mod/assign/services/assign-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,459 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreError } from '@classes/errors/error';
 | 
				
			||||||
 | 
					import { SQLiteDBRecordValues } from '@classes/sqlitedb';
 | 
				
			||||||
 | 
					import { CoreFile } from '@services/file';
 | 
				
			||||||
 | 
					import { CoreSites } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreTextUtils } from '@services/utils/text';
 | 
				
			||||||
 | 
					import { CoreTimeUtils } from '@services/utils/time';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { AddonModAssignOutcomes, AddonModAssignSavePluginData } from './assign';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionsDBRecord,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionsGradingDBRecord,
 | 
				
			||||||
 | 
					    SUBMISSIONS_GRADES_TABLE,
 | 
				
			||||||
 | 
					    SUBMISSIONS_TABLE,
 | 
				
			||||||
 | 
					} from './database/assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Service to handle offline assign.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignOfflineProvider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete a submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if deleted, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async deleteSubmission(assignId: number, userId?: number, siteId?: string): Promise<void> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					        userId = userId || site.getUserId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await site.getDb().deleteRecords(
 | 
				
			||||||
 | 
					            SUBMISSIONS_TABLE,
 | 
				
			||||||
 | 
					            { assignid: assignId, userid: userId },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete a submission grade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if deleted, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async deleteSubmissionGrade(assignId: number, userId?: number, siteId?: string): Promise<void> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					        userId = userId || site.getUserId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await site.getDb().deleteRecords(
 | 
				
			||||||
 | 
					            SUBMISSIONS_GRADES_TABLE,
 | 
				
			||||||
 | 
					            { assignid: assignId, userid: userId },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all the assignments ids that have something to be synced.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with assignments id that have something to be synced.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getAllAssigns(siteId?: string): Promise<number[]> {
 | 
				
			||||||
 | 
					        const promises:
 | 
				
			||||||
 | 
					        Promise<AddonModAssignSubmissionsDBRecordFormatted[] | AddonModAssignSubmissionsGradingDBRecordFormatted[]>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(this.getAllSubmissions(siteId));
 | 
				
			||||||
 | 
					        promises.push(this.getAllSubmissionsGrade(siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const results = await Promise.all(promises);
 | 
				
			||||||
 | 
					        // Flatten array.
 | 
				
			||||||
 | 
					        const flatten: (AddonModAssignSubmissionsDBRecord | AddonModAssignSubmissionsGradingDBRecord)[] =
 | 
				
			||||||
 | 
					            [].concat.apply([], results);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get assign id.
 | 
				
			||||||
 | 
					        let assignIds: number[] = flatten.map((assign) => assign.assignid);
 | 
				
			||||||
 | 
					        // Get unique values.
 | 
				
			||||||
 | 
					        assignIds = assignIds.filter((id, pos) => assignIds.indexOf(id) == pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return assignIds;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all the stored submissions from all the assignments.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getAllSubmissions(siteId?: string): Promise<AddonModAssignSubmissionsDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        return this.getAssignSubmissionsFormatted(undefined, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all the stored submissions for a certain assignment.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getAssignSubmissions(assignId: number, siteId?: string): Promise<AddonModAssignSubmissionsDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        return this.getAssignSubmissionsFormatted({ assingid: assignId }, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience helper function to get stored submissions formatted.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param conditions Query conditions.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getAssignSubmissionsFormatted(
 | 
				
			||||||
 | 
					        conditions: SQLiteDBRecordValues = {},
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        const db = await CoreSites.instance.getSiteDb(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissions: AddonModAssignSubmissionsDBRecord[] = await db.getRecords(SUBMISSIONS_TABLE, conditions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse the plugin data.
 | 
				
			||||||
 | 
					        return submissions.map((submission) => ({
 | 
				
			||||||
 | 
					            assignid: submission.assignid,
 | 
				
			||||||
 | 
					            userid: submission.userid,
 | 
				
			||||||
 | 
					            courseid: submission.courseid,
 | 
				
			||||||
 | 
					            plugindata: CoreTextUtils.instance.parseJSON<AddonModAssignSavePluginData>(submission.plugindata, {}),
 | 
				
			||||||
 | 
					            onlinetimemodified: submission.onlinetimemodified,
 | 
				
			||||||
 | 
					            timecreated: submission.timecreated,
 | 
				
			||||||
 | 
					            timemodified: submission.timemodified,
 | 
				
			||||||
 | 
					            submitted: submission.submitted,
 | 
				
			||||||
 | 
					            submissionstatement: submission.submissionstatement,
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all the stored submissions grades from all the assignments.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions grades.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getAllSubmissionsGrade(siteId?: string): Promise<AddonModAssignSubmissionsGradingDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        return this.getAssignSubmissionsGradeFormatted(undefined, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get all the stored submissions grades for a certain assignment.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions grades.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getAssignSubmissionsGrade(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsGradingDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        return this.getAssignSubmissionsGradeFormatted({ assingid: assignId }, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience helper function to get stored submissions grading formatted.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param conditions Query conditions.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submissions grades.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getAssignSubmissionsGradeFormatted(
 | 
				
			||||||
 | 
					        conditions: SQLiteDBRecordValues = {},
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsGradingDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        const db = await CoreSites.instance.getSiteDb(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissions: AddonModAssignSubmissionsGradingDBRecord[] = await db.getRecords(SUBMISSIONS_GRADES_TABLE, conditions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse the plugin data and outcomes.
 | 
				
			||||||
 | 
					        return submissions.map((submission) => ({
 | 
				
			||||||
 | 
					            assignid: submission.assignid,
 | 
				
			||||||
 | 
					            userid: submission.userid,
 | 
				
			||||||
 | 
					            courseid: submission.courseid,
 | 
				
			||||||
 | 
					            grade: submission.grade,
 | 
				
			||||||
 | 
					            attemptnumber: submission.attemptnumber,
 | 
				
			||||||
 | 
					            addattempt: submission.addattempt,
 | 
				
			||||||
 | 
					            workflowstate: submission.workflowstate,
 | 
				
			||||||
 | 
					            applytoall: submission.applytoall,
 | 
				
			||||||
 | 
					            outcomes: CoreTextUtils.instance.parseJSON<AddonModAssignOutcomes>(submission.outcomes, {}),
 | 
				
			||||||
 | 
					            plugindata: CoreTextUtils.instance.parseJSON<AddonModAssignSavePluginData>(submission.plugindata, {}),
 | 
				
			||||||
 | 
					            timemodified: submission.timemodified,
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a stored submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submission.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmission(assignId: number, userId?: number, siteId?: string): Promise<AddonModAssignSubmissionsDBRecordFormatted> {
 | 
				
			||||||
 | 
					        userId = userId || CoreSites.instance.getCurrentSiteUserId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissions = await this.getAssignSubmissionsFormatted({ assignid: assignId, userid: userId }, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submissions.length) {
 | 
				
			||||||
 | 
					            return submissions[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        throw new CoreError('No records found.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the path to the folder where to store files for an offline submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the path.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionFolder(assignId: number, userId?: number, siteId?: string): Promise<string> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        userId = userId || site.getUserId();
 | 
				
			||||||
 | 
					        const siteFolderPath = CoreFile.instance.getSiteFolder(site.getId());
 | 
				
			||||||
 | 
					        const submissionFolderPath = 'offlineassign/' + assignId + '/' + userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CoreTextUtils.instance.concatenatePaths(siteFolderPath, submissionFolderPath);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a stored submission grade.
 | 
				
			||||||
 | 
					     * Submission grades are not identified using attempt number so it can retrieve the feedback for a previous attempt.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with submission grade.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionGrade(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsGradingDBRecordFormatted> {
 | 
				
			||||||
 | 
					        userId = userId || CoreSites.instance.getCurrentSiteUserId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissions = await this.getAssignSubmissionsGradeFormatted({ assignid: assignId, userid: userId }, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submissions.length) {
 | 
				
			||||||
 | 
					            return submissions[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        throw new CoreError('No records found.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the path to the folder where to store files for a certain plugin in an offline submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param pluginName Name of the plugin. Must be unique (both in submission and feedback plugins).
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the path.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getSubmissionPluginFolder(assignId: number, pluginName: string, userId?: number, siteId?: string): Promise<string> {
 | 
				
			||||||
 | 
					        const folderPath = await this.getSubmissionFolder(assignId, userId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CoreTextUtils.instance.concatenatePaths(folderPath, pluginName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the assignment has something to be synced.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with boolean: whether the assignment has something to be synced.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasAssignOfflineData(assignId: number, siteId?: string): Promise<boolean> {
 | 
				
			||||||
 | 
					        const promises:
 | 
				
			||||||
 | 
					        Promise<AddonModAssignSubmissionsDBRecordFormatted[] | AddonModAssignSubmissionsGradingDBRecordFormatted[]>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(this.getAssignSubmissions(assignId, siteId));
 | 
				
			||||||
 | 
					        promises.push(this.getAssignSubmissionsGrade(assignId, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const results = await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return results.some((result) => result.length);
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            // No offline data found.
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Mark/Unmark a submission as being submitted.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the assign belongs to.
 | 
				
			||||||
 | 
					     * @param submitted True to mark as submitted, false to mark as not submitted.
 | 
				
			||||||
 | 
					     * @param acceptStatement True to accept the submission statement, false otherwise.
 | 
				
			||||||
 | 
					     * @param timemodified The time the submission was last modified in online.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if marked, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async markSubmitted(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        submitted: boolean,
 | 
				
			||||||
 | 
					        acceptStatement: boolean,
 | 
				
			||||||
 | 
					        timemodified: number,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<number> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        userId = userId || site.getUserId();
 | 
				
			||||||
 | 
					        let submission: AddonModAssignSubmissionsDBRecord;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const savedSubmission: AddonModAssignSubmissionsDBRecordFormatted =
 | 
				
			||||||
 | 
					                await this.getSubmission(assignId, userId, site.getId());
 | 
				
			||||||
 | 
					            submission = Object.assign(savedSubmission, {
 | 
				
			||||||
 | 
					                plugindata: savedSubmission.plugindata ? JSON.stringify(savedSubmission.plugindata) : '{}',
 | 
				
			||||||
 | 
					                submitted: submitted ? 1 : 0, // Mark the submission.
 | 
				
			||||||
 | 
					                submissionstatement: acceptStatement ? 1 : 0, // Mark the submission.
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            // No submission, create an empty one.
 | 
				
			||||||
 | 
					            const now = CoreTimeUtils.instance.timestamp();
 | 
				
			||||||
 | 
					            submission = {
 | 
				
			||||||
 | 
					                assignid: assignId,
 | 
				
			||||||
 | 
					                courseid: courseId,
 | 
				
			||||||
 | 
					                userid: userId,
 | 
				
			||||||
 | 
					                onlinetimemodified: timemodified,
 | 
				
			||||||
 | 
					                timecreated: now,
 | 
				
			||||||
 | 
					                timemodified: now,
 | 
				
			||||||
 | 
					                plugindata: '{}',
 | 
				
			||||||
 | 
					                submitted: submitted ? 1 : 0, // Mark the submission.
 | 
				
			||||||
 | 
					                submissionstatement: acceptStatement ? 1 : 0, // Mark the submission.
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await site.getDb().insertRecord(SUBMISSIONS_TABLE, submission);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save a submission to be sent later.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assignment ID.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the assign belongs to.
 | 
				
			||||||
 | 
					     * @param pluginData Data to save.
 | 
				
			||||||
 | 
					     * @param timemodified The time the submission was last modified in online.
 | 
				
			||||||
 | 
					     * @param submitted True if submission has been submitted, false otherwise.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if stored, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async saveSubmission(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        pluginData: AddonModAssignSavePluginData,
 | 
				
			||||||
 | 
					        timemodified: number,
 | 
				
			||||||
 | 
					        submitted: boolean,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<number> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        userId = userId || site.getUserId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const now = CoreTimeUtils.instance.timestamp();
 | 
				
			||||||
 | 
					        const entry: AddonModAssignSubmissionsDBRecord = {
 | 
				
			||||||
 | 
					            assignid: assignId,
 | 
				
			||||||
 | 
					            courseid: courseId,
 | 
				
			||||||
 | 
					            plugindata: pluginData ? JSON.stringify(pluginData) : '{}',
 | 
				
			||||||
 | 
					            userid: userId,
 | 
				
			||||||
 | 
					            submitted: submitted ? 1 : 0,
 | 
				
			||||||
 | 
					            timecreated: now,
 | 
				
			||||||
 | 
					            timemodified: now,
 | 
				
			||||||
 | 
					            onlinetimemodified: timemodified,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await site.getDb().insertRecord(SUBMISSIONS_TABLE, entry);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save a grading to be sent later.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the assign belongs to.
 | 
				
			||||||
 | 
					     * @param grade Grade to submit.
 | 
				
			||||||
 | 
					     * @param attemptNumber Number of the attempt being graded.
 | 
				
			||||||
 | 
					     * @param addAttempt Admit the user to attempt again.
 | 
				
			||||||
 | 
					     * @param workflowState Next workflow State.
 | 
				
			||||||
 | 
					     * @param applyToAll If it's a team submission, whether the grade applies to all group members.
 | 
				
			||||||
 | 
					     * @param outcomes Object including all outcomes values. If empty, any of them will be sent.
 | 
				
			||||||
 | 
					     * @param pluginData Plugin data to save.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if stored, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async submitGradingForm(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        grade: number,
 | 
				
			||||||
 | 
					        attemptNumber: number,
 | 
				
			||||||
 | 
					        addAttempt: boolean,
 | 
				
			||||||
 | 
					        workflowState: string,
 | 
				
			||||||
 | 
					        applyToAll: boolean,
 | 
				
			||||||
 | 
					        outcomes: AddonModAssignOutcomes,
 | 
				
			||||||
 | 
					        pluginData: AddonModAssignSavePluginData,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<number> {
 | 
				
			||||||
 | 
					        const site = await CoreSites.instance.getSite(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const now = CoreTimeUtils.instance.timestamp();
 | 
				
			||||||
 | 
					        const entry: AddonModAssignSubmissionsGradingDBRecord = {
 | 
				
			||||||
 | 
					            assignid: assignId,
 | 
				
			||||||
 | 
					            userid: userId,
 | 
				
			||||||
 | 
					            courseid: courseId,
 | 
				
			||||||
 | 
					            grade: grade,
 | 
				
			||||||
 | 
					            attemptnumber: attemptNumber,
 | 
				
			||||||
 | 
					            addattempt: addAttempt ? 1 : 0,
 | 
				
			||||||
 | 
					            workflowstate: workflowState,
 | 
				
			||||||
 | 
					            applytoall: applyToAll ? 1 : 0,
 | 
				
			||||||
 | 
					            outcomes: outcomes ? JSON.stringify(outcomes) : '{}',
 | 
				
			||||||
 | 
					            plugindata: pluginData ? JSON.stringify(pluginData) : '{}',
 | 
				
			||||||
 | 
					            timemodified: now,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await site.getDb().insertRecord(SUBMISSIONS_GRADES_TABLE, entry);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignOffline = makeSingleton(AddonModAssignOfflineProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type AddonModAssignSubmissionsDBRecordFormatted = Omit<AddonModAssignSubmissionsDBRecord, 'plugindata'> & {
 | 
				
			||||||
 | 
					    plugindata: AddonModAssignSavePluginData;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type AddonModAssignSubmissionsGradingDBRecordFormatted =
 | 
				
			||||||
 | 
					    Omit<AddonModAssignSubmissionsGradingDBRecord, 'plugindata'|'outcomes'> & {
 | 
				
			||||||
 | 
					        plugindata: AddonModAssignSavePluginData;
 | 
				
			||||||
 | 
					        outcomes: AddonModAssignOutcomes;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
							
								
								
									
										572
									
								
								src/addons/mod/assign/services/assign-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										572
									
								
								src/addons/mod/assign/services/assign-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,572 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreEvents, CoreEventSiteData } from '@singletons/events';
 | 
				
			||||||
 | 
					import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
				
			||||||
 | 
					import { CoreSyncBlockedError } from '@classes/base-sync';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssignProvider,
 | 
				
			||||||
 | 
					    AddonModAssignAssign,
 | 
				
			||||||
 | 
					    AddonModAssignSubmission,
 | 
				
			||||||
 | 
					    AddonModAssign,
 | 
				
			||||||
 | 
					    AddonModAssignGetSubmissionStatusWSResponse,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionStatusOptions,
 | 
				
			||||||
 | 
					} from './assign';
 | 
				
			||||||
 | 
					import { makeSingleton, Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreCourse } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { CoreCourseActivitySyncBaseProvider } from '@features/course/classes/activity-sync';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssignOffline,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionsDBRecordFormatted,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionsGradingDBRecordFormatted,
 | 
				
			||||||
 | 
					} from './assign-offline';
 | 
				
			||||||
 | 
					import { CoreSync } from '@services/sync';
 | 
				
			||||||
 | 
					import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { CoreApp } from '@services/app';
 | 
				
			||||||
 | 
					import { CoreTextUtils } from '@services/utils/text';
 | 
				
			||||||
 | 
					import { CoreNetworkError } from '@classes/errors/network-error';
 | 
				
			||||||
 | 
					import { CoreGradesFormattedItem, CoreGradesFormattedRow, CoreGradesHelper } from '@features/grades/services/grades-helper';
 | 
				
			||||||
 | 
					import { AddonModAssignSubmissionDelegate } from './submission-delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Service to sync assigns.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignSyncProvider extends CoreCourseActivitySyncBaseProvider<AddonModAssignSyncResult> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static readonly AUTO_SYNCED = 'addon_mod_assign_autom_synced';
 | 
				
			||||||
 | 
					    static readonly MANUAL_SYNCED = 'addon_mod_assign_manual_synced';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected componentTranslate: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super('AddonModLessonSyncProvider');
 | 
				
			||||||
 | 
					        this.componentTranslate = CoreCourse.instance.translateModuleName('assign');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the sync ID for a certain user grade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param userId User the grade belongs to.
 | 
				
			||||||
 | 
					     * @return Sync ID.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getGradeSyncId(assignId: number, userId: number): string {
 | 
				
			||||||
 | 
					        return 'assignGrade#' + assignId + '#' + userId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience function to get scale selected option.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param options Possible options.
 | 
				
			||||||
 | 
					     * @param selected Selected option to search.
 | 
				
			||||||
 | 
					     * @return Index of the selected option.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected getSelectedScaleId(options: string, selected: string): number {
 | 
				
			||||||
 | 
					        let optionsList = options.split(',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        optionsList = optionsList.map((value) => value.trim());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        optionsList.unshift('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const index = options.indexOf(selected) || 0;
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return index;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if an assignment has data to synchronize.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with boolean: whether it has data to sync.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDataToSync(assignId: number, siteId?: string): Promise<boolean> {
 | 
				
			||||||
 | 
					        return AddonModAssignOffline.instance.hasAssignOfflineData(assignId, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Try to synchronize all the assignments in a certain site or in all sites.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId Site ID to sync. If not defined, sync all sites.
 | 
				
			||||||
 | 
					     * @param force Wether to force sync not depending on last execution.
 | 
				
			||||||
 | 
					     * @return Promise resolved if sync is successful, rejected if sync fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    syncAllAssignments(siteId?: string, force?: boolean): Promise<void> {
 | 
				
			||||||
 | 
					        return this.syncOnSites('all assignments', this.syncAllAssignmentsFunc.bind(this, !!force), siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sync all assignments on a site.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param force Wether to force sync not depending on last execution.
 | 
				
			||||||
 | 
					     * @param siteId Site ID to sync. If not defined, sync all sites.
 | 
				
			||||||
 | 
					     * @param Promise resolved if sync is successful, rejected if sync fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async syncAllAssignmentsFunc(force: boolean, siteId: string): Promise<void> {
 | 
				
			||||||
 | 
					        // Get all assignments that have offline data.
 | 
				
			||||||
 | 
					        const assignIds = await AddonModAssignOffline.instance.getAllAssigns(siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Try to sync all assignments.
 | 
				
			||||||
 | 
					        await Promise.all(assignIds.map(async (assignId) => {
 | 
				
			||||||
 | 
					            const result = force
 | 
				
			||||||
 | 
					                ? await this.syncAssign(assignId, siteId)
 | 
				
			||||||
 | 
					                : await this.syncAssignIfNeeded(assignId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (result?.updated) {
 | 
				
			||||||
 | 
					                CoreEvents.trigger<AddonModAssignAutoSyncData>(AddonModAssignSyncProvider.AUTO_SYNCED, {
 | 
				
			||||||
 | 
					                    assignId: assignId,
 | 
				
			||||||
 | 
					                    warnings: result.warnings,
 | 
				
			||||||
 | 
					                    gradesBlocked: result.gradesBlocked,
 | 
				
			||||||
 | 
					                }, siteId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sync an assignment only if a certain time has passed since the last time.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when the assign is synced or it doesn't need to be synced.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async syncAssignIfNeeded(assignId: number, siteId?: string): Promise<AddonModAssignSyncResult | undefined> {
 | 
				
			||||||
 | 
					        const needed = await this.isSyncNeeded(assignId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (needed) {
 | 
				
			||||||
 | 
					            return this.syncAssign(assignId, siteId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Try to synchronize an assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved in success.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async syncAssign(assignId: number, siteId?: string): Promise<AddonModAssignSyncResult> {
 | 
				
			||||||
 | 
					        siteId = siteId || CoreSites.instance.getCurrentSiteId();
 | 
				
			||||||
 | 
					        this.componentTranslate = this.componentTranslate || CoreCourse.instance.translateModuleName('assign');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.isSyncing(assignId, siteId)) {
 | 
				
			||||||
 | 
					            // There's already a sync ongoing for this assign, return the promise.
 | 
				
			||||||
 | 
					            return this.getOngoingSync(assignId, siteId)!;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Verify that assign isn't blocked.
 | 
				
			||||||
 | 
					        if (CoreSync.instance.isBlocked(AddonModAssignProvider.COMPONENT, assignId, siteId)) {
 | 
				
			||||||
 | 
					            this.logger.debug('Cannot sync assign ' + assignId + ' because it is blocked.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new CoreSyncBlockedError(Translate.instance.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.logger.debug('Try to sync assign ' + assignId + ' in site ' + siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const syncPromise = this.performSyncAssign(assignId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return this.addOngoingSync(assignId, syncPromise, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Perform the assign submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved in success.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async performSyncAssign(assignId: number, siteId: string): Promise<AddonModAssignSyncResult> {
 | 
				
			||||||
 | 
					        // Sync offline logs.
 | 
				
			||||||
 | 
					        await CoreUtils.instance.ignoreErrors(
 | 
				
			||||||
 | 
					            CoreCourseLogHelper.instance.syncActivity(AddonModAssignProvider.COMPONENT, assignId, siteId),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const result: AddonModAssignSyncResult = {
 | 
				
			||||||
 | 
					            warnings: [],
 | 
				
			||||||
 | 
					            updated: false,
 | 
				
			||||||
 | 
					            gradesBlocked: [],
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Load offline data and sync offline logs.
 | 
				
			||||||
 | 
					        const [submissions, grades] = await Promise.all([
 | 
				
			||||||
 | 
					            this.getOfflineSubmissions(assignId, siteId),
 | 
				
			||||||
 | 
					            this.getOfflineGrades(assignId, siteId),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!submissions.length && !grades.length) {
 | 
				
			||||||
 | 
					            // Nothing to sync.
 | 
				
			||||||
 | 
					            await CoreUtils.instance.ignoreErrors(this.setSyncTime(assignId, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!CoreApp.instance.isOnline()) {
 | 
				
			||||||
 | 
					            // Cannot sync in offline.
 | 
				
			||||||
 | 
					            throw new CoreNetworkError();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const courseId = submissions.length > 0 ? submissions[0].courseid : grades[0].courseid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const assign = await AddonModAssign.instance.getAssignmentById(courseId, assignId, { siteId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises = promises.concat(submissions.map(async (submission) => {
 | 
				
			||||||
 | 
					            await this.syncSubmission(assign, submission, result.warnings, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result.updated = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises = promises.concat(grades.map(async (grade) => {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await this.syncSubmissionGrade(assign, grade, result.warnings, courseId, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                result.updated = true;
 | 
				
			||||||
 | 
					            } catch (error) {
 | 
				
			||||||
 | 
					                if (error instanceof CoreSyncBlockedError) {
 | 
				
			||||||
 | 
					                    // Grade blocked, but allow finish the sync.
 | 
				
			||||||
 | 
					                    result.gradesBlocked.push(grade.userid);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    throw error;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreUtils.instance.allPromises(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (result.updated) {
 | 
				
			||||||
 | 
					            // Data has been sent to server. Now invalidate the WS calls.
 | 
				
			||||||
 | 
					            await CoreUtils.instance.ignoreErrors(AddonModAssign.instance.invalidateContent(assign.cmid, courseId, siteId));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Sync finished, set sync time.
 | 
				
			||||||
 | 
					        await CoreUtils.instance.ignoreErrors(this.setSyncTime(assignId, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // All done, return the result.
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get offline grades to be sent.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise with grades.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getOfflineGrades(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsGradingDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        // If no offline data found, return empty array.
 | 
				
			||||||
 | 
					        return CoreUtils.instance.ignoreErrors(AddonModAssignOffline.instance.getAssignSubmissionsGrade(assignId, siteId), []);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get offline submissions to be sent.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId Assign ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise with submissions.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getOfflineSubmissions(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<AddonModAssignSubmissionsDBRecordFormatted[]> {
 | 
				
			||||||
 | 
					        // If no offline data found, return empty array.
 | 
				
			||||||
 | 
					        return CoreUtils.instance.ignoreErrors(AddonModAssignOffline.instance.getAssignSubmissions(assignId, siteId), []);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Synchronize a submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param offlineData Submission offline data.
 | 
				
			||||||
 | 
					     * @param warnings List of warnings.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if success, rejected otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async syncSubmission(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
				
			||||||
 | 
					        warnings: string[],
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const userId = offlineData.userid;
 | 
				
			||||||
 | 
					        const pluginData = {};
 | 
				
			||||||
 | 
					        const options: AddonModAssignSubmissionStatusOptions = {
 | 
				
			||||||
 | 
					            userId,
 | 
				
			||||||
 | 
					            cmId: assign.cmid,
 | 
				
			||||||
 | 
					            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const status = await AddonModAssign.instance.getSubmissionStatus(assign.id, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submission = AddonModAssign.instance.getSubmissionObjectFromAttempt(assign, status.lastattempt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submission && submission.timemodified != offlineData.onlinetimemodified) {
 | 
				
			||||||
 | 
					            // The submission was modified in Moodle, discard the submission.
 | 
				
			||||||
 | 
					            this.addOfflineDataDeletedWarning(
 | 
				
			||||||
 | 
					                warnings,
 | 
				
			||||||
 | 
					                this.componentTranslate,
 | 
				
			||||||
 | 
					                assign.name,
 | 
				
			||||||
 | 
					                Translate.instance.instant('addon.mod_assign.warningsubmissionmodified'),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return this.deleteSubmissionData(assign, offlineData, submission, siteId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (submission?.plugins) {
 | 
				
			||||||
 | 
					                // Prepare plugins data.
 | 
				
			||||||
 | 
					                await Promise.all(submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                    AddonModAssignSubmissionDelegate.instance.preparePluginSyncData(
 | 
				
			||||||
 | 
					                        assign,
 | 
				
			||||||
 | 
					                        submission,
 | 
				
			||||||
 | 
					                        plugin,
 | 
				
			||||||
 | 
					                        offlineData,
 | 
				
			||||||
 | 
					                        pluginData,
 | 
				
			||||||
 | 
					                        siteId,
 | 
				
			||||||
 | 
					                    )));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Now save the submission.
 | 
				
			||||||
 | 
					            if (Object.keys(pluginData).length > 0) {
 | 
				
			||||||
 | 
					                await AddonModAssign.instance.saveSubmissionOnline(assign.id, pluginData, siteId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (assign.submissiondrafts && offlineData.submitted) {
 | 
				
			||||||
 | 
					                // The user submitted the assign manually. Submit it for grading.
 | 
				
			||||||
 | 
					                await AddonModAssign.instance.submitForGradingOnline(assign.id, !!offlineData.submissionstatement, siteId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Submission data sent, update cached data. No need to block the user for this.
 | 
				
			||||||
 | 
					            AddonModAssign.instance.getSubmissionStatus(assign.id, options);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (!error || !CoreUtils.instance.isWebServiceError(error)) {
 | 
				
			||||||
 | 
					                // Local error, reject.
 | 
				
			||||||
 | 
					                throw error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // A WebService has thrown an error, this means it cannot be submitted. Discard the submission.
 | 
				
			||||||
 | 
					            this.addOfflineDataDeletedWarning(
 | 
				
			||||||
 | 
					                warnings,
 | 
				
			||||||
 | 
					                this.componentTranslate,
 | 
				
			||||||
 | 
					                assign.name,
 | 
				
			||||||
 | 
					                CoreTextUtils.instance.getErrorMessageFromError(error) || '',
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Delete the offline data.
 | 
				
			||||||
 | 
					        await this.deleteSubmissionData(assign, offlineData, submission, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete the submission offline data (not grades).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assign.
 | 
				
			||||||
 | 
					     * @param submission Submission.
 | 
				
			||||||
 | 
					     * @param offlineData Offline data.
 | 
				
			||||||
 | 
					     * @param siteId Site ID.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async deleteSubmissionData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        offlineData: AddonModAssignSubmissionsDBRecordFormatted,
 | 
				
			||||||
 | 
					        submission?: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Delete the offline data.
 | 
				
			||||||
 | 
					        await AddonModAssignOffline.instance.deleteSubmission(assign.id, offlineData.userid, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submission?.plugins){
 | 
				
			||||||
 | 
					            // Delete plugins data.
 | 
				
			||||||
 | 
					            await Promise.all(submission.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                AddonModAssignSubmissionDelegate.instance.deletePluginOfflineData(
 | 
				
			||||||
 | 
					                    assign,
 | 
				
			||||||
 | 
					                    submission,
 | 
				
			||||||
 | 
					                    plugin,
 | 
				
			||||||
 | 
					                    offlineData,
 | 
				
			||||||
 | 
					                    siteId,
 | 
				
			||||||
 | 
					                )));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Synchronize a submission grade.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assignment.
 | 
				
			||||||
 | 
					     * @param offlineData Submission grade offline data.
 | 
				
			||||||
 | 
					     * @param warnings List of warnings.
 | 
				
			||||||
 | 
					     * @param courseId Course Id.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved if success, rejected otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async syncSubmissionGrade(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        offlineData: AddonModAssignSubmissionsGradingDBRecordFormatted,
 | 
				
			||||||
 | 
					        warnings: string[],
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const userId = offlineData.userid;
 | 
				
			||||||
 | 
					        const syncId = this.getGradeSyncId(assign.id, userId);
 | 
				
			||||||
 | 
					        const options: AddonModAssignSubmissionStatusOptions = {
 | 
				
			||||||
 | 
					            userId,
 | 
				
			||||||
 | 
					            cmId: assign.cmid,
 | 
				
			||||||
 | 
					            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if this grade sync is blocked.
 | 
				
			||||||
 | 
					        if (CoreSync.instance.isBlocked(AddonModAssignProvider.COMPONENT, syncId, siteId)) {
 | 
				
			||||||
 | 
					            this.logger.error(`Cannot sync grade for assign ${assign.id} and user ${userId} because it is blocked.!!!!`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new CoreSyncBlockedError(Translate.instance.instant(
 | 
				
			||||||
 | 
					                'core.errorsyncblocked',
 | 
				
			||||||
 | 
					                { $a: Translate.instance.instant('addon.mod_assign.syncblockedusercomponent') },
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const status = await AddonModAssign.instance.getSubmissionStatus(assign.id, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const timemodified = (status.feedback && (status.feedback.gradeddate || status.feedback.grade.timemodified)) || 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timemodified > offlineData.timemodified) {
 | 
				
			||||||
 | 
					            // The submission grade was modified in Moodle, discard it.
 | 
				
			||||||
 | 
					            this.addOfflineDataDeletedWarning(
 | 
				
			||||||
 | 
					                warnings,
 | 
				
			||||||
 | 
					                this.componentTranslate,
 | 
				
			||||||
 | 
					                assign.name,
 | 
				
			||||||
 | 
					                Translate.instance.instant('addon.mod_assign.warningsubmissiongrademodified'),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return AddonModAssignOffline.instance.deleteSubmissionGrade(assign.id, userId, siteId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If grade has been modified from gradebook, do not use offline.
 | 
				
			||||||
 | 
					        const grades: CoreGradesFormattedItem[] | CoreGradesFormattedRow[] =
 | 
				
			||||||
 | 
					            await CoreGradesHelper.instance.getGradeModuleItems(courseId, assign.cmid, userId, undefined, siteId, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const gradeInfo = await CoreCourse.instance.getModuleBasicGradeInfo(assign.cmid, siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Override offline grade and outcomes based on the gradebook data.
 | 
				
			||||||
 | 
					        grades.forEach((grade: CoreGradesFormattedItem | CoreGradesFormattedRow) => {
 | 
				
			||||||
 | 
					            if ('gradedategraded' in  grade && (grade.gradedategraded || 0) >= offlineData.timemodified) {
 | 
				
			||||||
 | 
					                if (!grade.outcomeid && !grade.scaleid) {
 | 
				
			||||||
 | 
					                    if (gradeInfo && gradeInfo.scale) {
 | 
				
			||||||
 | 
					                        offlineData.grade = this.getSelectedScaleId(gradeInfo.scale, grade.grade || '');
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        offlineData.grade = parseFloat(grade.grade || '') || undefined;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (gradeInfo && grade.outcomeid && AddonModAssign.instance.isOutcomesEditEnabled() && gradeInfo.outcomes) {
 | 
				
			||||||
 | 
					                    gradeInfo.outcomes.forEach((outcome, index) => {
 | 
				
			||||||
 | 
					                        if (outcome.scale && grade.itemnumber == index) {
 | 
				
			||||||
 | 
					                            offlineData.outcomes[grade.itemnumber] = this.getSelectedScaleId(
 | 
				
			||||||
 | 
					                                outcome.scale,
 | 
				
			||||||
 | 
					                                grade.grade || '',
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Now submit the grade.
 | 
				
			||||||
 | 
					            await AddonModAssign.instance.submitGradingFormOnline(
 | 
				
			||||||
 | 
					                assign.id,
 | 
				
			||||||
 | 
					                userId,
 | 
				
			||||||
 | 
					                offlineData.grade,
 | 
				
			||||||
 | 
					                offlineData.attemptnumber,
 | 
				
			||||||
 | 
					                !!offlineData.addattempt,
 | 
				
			||||||
 | 
					                offlineData.workflowstate,
 | 
				
			||||||
 | 
					                !!offlineData.applytoall,
 | 
				
			||||||
 | 
					                offlineData.outcomes,
 | 
				
			||||||
 | 
					                offlineData.plugindata,
 | 
				
			||||||
 | 
					                siteId,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Grades sent. Discard grades drafts.
 | 
				
			||||||
 | 
					            let promises: Promise<void | AddonModAssignGetSubmissionStatusWSResponse>[] = [];
 | 
				
			||||||
 | 
					            if (status.feedback && status.feedback.plugins) {
 | 
				
			||||||
 | 
					                promises = status.feedback.plugins.map((plugin) =>
 | 
				
			||||||
 | 
					                    AddonModAssignFeedbackDelegate.instance.discardPluginFeedbackData(assign.id, userId, plugin, siteId));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Update cached data.
 | 
				
			||||||
 | 
					            promises.push(AddonModAssign.instance.getSubmissionStatus(assign.id, options));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await CoreUtils.instance.allPromises(promises);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (!error || !CoreUtils.instance.isWebServiceError(error)) {
 | 
				
			||||||
 | 
					                // Local error, reject.
 | 
				
			||||||
 | 
					                throw error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // A WebService has thrown an error, this means it cannot be submitted. Discard the submission.
 | 
				
			||||||
 | 
					            this.addOfflineDataDeletedWarning(
 | 
				
			||||||
 | 
					                warnings,
 | 
				
			||||||
 | 
					                this.componentTranslate,
 | 
				
			||||||
 | 
					                assign.name,
 | 
				
			||||||
 | 
					                CoreTextUtils.instance.getErrorMessageFromError(error) || '',
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Delete the offline data.
 | 
				
			||||||
 | 
					        await AddonModAssignOffline.instance.deleteSubmissionGrade(assign.id, userId, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignSync = makeSingleton(AddonModAssignSyncProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Data returned by a assign sync.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignSyncResult = {
 | 
				
			||||||
 | 
					    warnings: string[]; // List of warnings.
 | 
				
			||||||
 | 
					    updated: boolean; // Whether some data was sent to the server or offline data was updated.
 | 
				
			||||||
 | 
					    courseId?: number; // Course the assign belongs to (if known).
 | 
				
			||||||
 | 
					    gradesBlocked: number[]; // Whether some grade couldn't be synced because it was blocked. UserId fields of the blocked grade.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Data passed to AUTO_SYNCED event.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignAutoSyncData = CoreEventSiteData & {
 | 
				
			||||||
 | 
					    assignId: number;
 | 
				
			||||||
 | 
					    warnings: string[];
 | 
				
			||||||
 | 
					    gradesBlocked: number[]; // Whether some grade couldn't be synced because it was blocked. UserId fields of the blocked grade.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Data passed to MANUAL_SYNCED event.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignManualSyncData = AddonModAssignAutoSyncData & {
 | 
				
			||||||
 | 
					    context: string;
 | 
				
			||||||
 | 
					    submitId?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										1855
									
								
								src/addons/mod/assign/services/assign.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1855
									
								
								src/addons/mod/assign/services/assign.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										150
									
								
								src/addons/mod/assign/services/database/assign.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/addons/mod/assign/services/database/assign.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,150 @@
 | 
				
			|||||||
 | 
					// (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 { CoreSiteSchema } from '@services/sites';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Database variables for AddonModAssignOfflineProvider.
 | 
				
			||||||
 | 
					 */export const SUBMISSIONS_TABLE = 'addon_mod_assign_submissions';
 | 
				
			||||||
 | 
					export const SUBMISSIONS_GRADES_TABLE = 'addon_mod_assign_submissions_grading';
 | 
				
			||||||
 | 
					export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
				
			||||||
 | 
					    name: 'AddonModAssignOfflineProvider',
 | 
				
			||||||
 | 
					    version: 1,
 | 
				
			||||||
 | 
					    tables: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: SUBMISSIONS_TABLE,
 | 
				
			||||||
 | 
					            columns: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'assignid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'courseid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'userid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'plugindata',
 | 
				
			||||||
 | 
					                    type: 'TEXT',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'onlinetimemodified',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'timecreated',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'timemodified',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'submitted',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'submissionstatement',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            primaryKeys: ['assignid', 'userid'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            name: SUBMISSIONS_GRADES_TABLE,
 | 
				
			||||||
 | 
					            columns: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'assignid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'courseid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'userid',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'grade',
 | 
				
			||||||
 | 
					                    type: 'REAL',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'attemptnumber',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'addattempt',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'workflowstate',
 | 
				
			||||||
 | 
					                    type: 'TEXT',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'applytoall',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'outcomes',
 | 
				
			||||||
 | 
					                    type: 'TEXT',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'plugindata',
 | 
				
			||||||
 | 
					                    type: 'TEXT',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    name: 'timemodified',
 | 
				
			||||||
 | 
					                    type: 'INTEGER',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            primaryKeys: ['assignid', 'userid'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Data about assign submissions to sync.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignSubmissionsDBRecord = {
 | 
				
			||||||
 | 
					    assignid: number; // Primary key.
 | 
				
			||||||
 | 
					    userid: number; // Primary key.
 | 
				
			||||||
 | 
					    courseid: number;
 | 
				
			||||||
 | 
					    plugindata: string;
 | 
				
			||||||
 | 
					    onlinetimemodified: number;
 | 
				
			||||||
 | 
					    timecreated: number;
 | 
				
			||||||
 | 
					    timemodified: number;
 | 
				
			||||||
 | 
					    submitted: number;
 | 
				
			||||||
 | 
					    submissionstatement?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Data about assign submission grades to sync.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AddonModAssignSubmissionsGradingDBRecord = {
 | 
				
			||||||
 | 
					    assignid: number; // Primary key.
 | 
				
			||||||
 | 
					    userid: number; // Primary key.
 | 
				
			||||||
 | 
					    courseid: number;
 | 
				
			||||||
 | 
					    grade?: number; // Real.
 | 
				
			||||||
 | 
					    attemptnumber: number;
 | 
				
			||||||
 | 
					    addattempt: number;
 | 
				
			||||||
 | 
					    workflowstate: string;
 | 
				
			||||||
 | 
					    applytoall: number;
 | 
				
			||||||
 | 
					    outcomes: string;
 | 
				
			||||||
 | 
					    plugindata: string;
 | 
				
			||||||
 | 
					    timemodified: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										374
									
								
								src/addons/mod/assign/services/feedback-delegate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								src/addons/mod/assign/services/feedback-delegate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,374 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignDefaultFeedbackHandler } from './handlers/default-feedback';
 | 
				
			||||||
 | 
					import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreWSExternalFile } from '@services/ws';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface that all feedback handlers must implement.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Name of the type of feedback the handler supports. E.g. 'file'.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Discard the draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    discardDraft?(assignId: number, userId: number, siteId?: string): void | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the Component to use to display the plugin data.
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     * @return The component (or promise resolved with component) to use, undefined if not found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getComponent?(plugin: AddonModAssignPlugin): any | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the draft saved data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Data (or promise resolved with the data).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getDraft?(assignId: number, userId: number, siteId?: string): any | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): CoreWSExternalFile[] | Promise<CoreWSExternalFile[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for the plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return The plugin name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName?(plugin: AddonModAssignPlugin): string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the feedback 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 feedback.
 | 
				
			||||||
 | 
					     * @param userId User ID of the submission.
 | 
				
			||||||
 | 
					     * @return Boolean (or promise resolved with boolean): whether the data has changed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDataChanged?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					    ): boolean | Promise<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check whether the plugin has draft data stored.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Boolean or promise resolved with boolean: whether the plugin has draft data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDraftData?(assignId: number, userId: number, siteId?: string): boolean | Promise<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prefetch?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and add to pluginData the data to send to the server based on the draft data saved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prepareFeedbackData?(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): void | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param data The data to save.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    saveDraft?(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string): void | Promise<any>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Delegate to register plugins for assign feedback.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonModAssignFeedbackHandler> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected handlerNameProperty = 'type';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        protected defaultHandler: AddonModAssignDefaultFeedbackHandler,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        super('AddonModAssignFeedbackDelegate', true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Discard the draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async discardPluginFeedbackData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the component to use for a certain feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the component to use, undefined if not found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getComponentForPlugin(plugin: AddonModAssignPlugin): Promise<any | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'getComponent', [plugin]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the draft saved data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the draft data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getPluginDraftData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, 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 Promise resolved with the files.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getPluginFiles(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<CoreWSExternalFile[]> {
 | 
				
			||||||
 | 
					        const files: CoreWSExternalFile[] | undefined =
 | 
				
			||||||
 | 
					            await this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return files || [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for a certain feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin Plugin to get the name for.
 | 
				
			||||||
 | 
					     * @return Human readable name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName(plugin: AddonModAssignPlugin): string | undefined {
 | 
				
			||||||
 | 
					        return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the feedback data has changed for a certain plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param inputData Data entered by the user for the feedback.
 | 
				
			||||||
 | 
					     * @param userId User ID of the submission.
 | 
				
			||||||
 | 
					     * @return Promise resolved with true if data has changed, resolved with false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasPluginDataChanged(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					    ): Promise<boolean | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'hasDataChanged',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, inputData, userId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check whether the plugin has draft data stored.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with true if it has draft data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasPluginDraftData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<boolean | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'hasDraftData', [assignId, userId, siteId]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a feedback plugin is supported.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param pluginType Type of the plugin.
 | 
				
			||||||
 | 
					     * @return Whether it's supported.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isPluginSupported(pluginType: string): boolean {
 | 
				
			||||||
 | 
					        return this.hasHandler(pluginType, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch any required data for a feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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<any> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and add to pluginData the data to submit for a certain feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param pluginData Object where to store the data to send.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when data has been gathered.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async preparePluginFeedbackData(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'prepareFeedbackData',
 | 
				
			||||||
 | 
					            [assignId, userId, plugin, pluginData, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assignId The assignment ID.
 | 
				
			||||||
 | 
					     * @param userId User ID.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param inputData Data to save.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when data has been saved.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async saveFeedbackDraft(
 | 
				
			||||||
 | 
					        assignId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'saveDraft',
 | 
				
			||||||
 | 
					            [assignId, userId, plugin, inputData, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignFeedbackDelegate = makeSingleton(AddonModAssignFeedbackDelegateService);
 | 
				
			||||||
							
								
								
									
										146
									
								
								src/addons/mod/assign/services/handlers/default-feedback.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/addons/mod/assign/services/handlers/default-feedback.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { AddonModAssignPlugin } from '../assign';
 | 
				
			||||||
 | 
					import { AddonModAssignFeedbackHandler } from '../feedback-delegate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Default handler used when a feedback plugin doesn't have a specific implementation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignDefaultFeedbackHandler implements AddonModAssignFeedbackHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignDefaultFeedbackHandler';
 | 
				
			||||||
 | 
					    type = 'default';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Discard the draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    discardDraft(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the Component to use to display the plugin data.
 | 
				
			||||||
 | 
					     * 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(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the draft saved data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Data (or promise resolved with the data).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getDraft(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get files used by this plugin.
 | 
				
			||||||
 | 
					     * The files returned by this function will be prefetched when the user prefetches the assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The files (or promise resolved with the files).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginFiles(): any[] {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for the plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return The plugin name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName(plugin: AddonModAssignPlugin): string {
 | 
				
			||||||
 | 
					        // Check if there's a translated string for the plugin.
 | 
				
			||||||
 | 
					        const translationId = 'addon.mod_assign_feedback_' + plugin.type + '.pluginname';
 | 
				
			||||||
 | 
					        const translation = Translate.instance.instant(translationId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (translationId != translation) {
 | 
				
			||||||
 | 
					            // Translation found, use it.
 | 
				
			||||||
 | 
					            return translation;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Fallback to WS string.
 | 
				
			||||||
 | 
					        if (plugin.name) {
 | 
				
			||||||
 | 
					            return plugin.name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the feedback data has changed for this plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Boolean (or promise resolved with boolean): whether the data has changed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDataChanged(): boolean {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check whether the plugin has draft data stored.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Boolean or promise resolved with boolean: whether the plugin has draft data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDraftData(): boolean {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch any required data for the plugin.
 | 
				
			||||||
 | 
					     * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async prefetch(): Promise<any> {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and add to pluginData the data to send to the server based on the draft data saved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prepareFeedbackData(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Save draft data of the feedback plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    saveDraft(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										210
									
								
								src/addons/mod/assign/services/handlers/default-submission.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/addons/mod/assign/services/handlers/default-submission.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { AddonModAssignPlugin } from '../assign';
 | 
				
			||||||
 | 
					import { AddonModAssignSubmissionHandler } from '../submission-delegate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Default handler used when a submission plugin doesn't have a specific implementation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignDefaultSubmissionHandler implements AddonModAssignSubmissionHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignBaseSubmissionHandler';
 | 
				
			||||||
 | 
					    type = 'base';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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 | Promise<boolean> {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a plugin has no data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Whether the plugin is empty.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isEmpty(): boolean {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Should clear temporary data for a cancelled submission.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clearTmpData(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    copySubmissionData(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete any stored data for the plugin and submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    deleteOfflineData(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get files used by this plugin.
 | 
				
			||||||
 | 
					     * The files returned by this function will be prefetched when the user prefetches the assign.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The files (or promise resolved with the files).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginFiles(): any[] {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for the plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return The plugin name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName(plugin: AddonModAssignPlugin): string {
 | 
				
			||||||
 | 
					        // Check if there's a translated string for the plugin.
 | 
				
			||||||
 | 
					        const translationId = 'addon.mod_assign_submission_' + plugin.type + '.pluginname';
 | 
				
			||||||
 | 
					        const translation = Translate.instance.instant(translationId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (translationId != translation) {
 | 
				
			||||||
 | 
					            // Translation found, use it.
 | 
				
			||||||
 | 
					            return translation;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Fallback to WS string.
 | 
				
			||||||
 | 
					        if (plugin.name) {
 | 
				
			||||||
 | 
					            return plugin.name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getSizeForCopy(): number {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getSizeForEdit(): number {
 | 
				
			||||||
 | 
					        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.
 | 
				
			||||||
 | 
					     * @param inputData Data entered by the user for the submission.
 | 
				
			||||||
 | 
					     * @return Boolean (or promise resolved with boolean): whether the data has changed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDataChanged(): boolean {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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 false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch any required data for the plugin.
 | 
				
			||||||
 | 
					     * This should NOT prefetch files. Files to be prefetched should be returned by the getPluginFiles function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async prefetch(): Promise<any> {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return If the function is async, it should return a Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prepareSyncData(): void {
 | 
				
			||||||
 | 
					        // Nothing to do.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/addons/mod/assign/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/addons/mod/assign/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handler to treat links to assign index page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignIndexLinkHandler';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super('AddonModAssign', 'assign');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignIndexLinkHandler = makeSingleton(AddonModAssignIndexLinkHandlerService);
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/addons/mod/assign/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/addons/mod/assign/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handler to treat links to assign list page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignListLinkHandlerService extends CoreContentLinksModuleListHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignListLinkHandler';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super('AddonModAssign', 'assign');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignListLinkHandler = makeSingleton(AddonModAssignListLinkHandlerService);
 | 
				
			||||||
							
								
								
									
										94
									
								
								src/addons/mod/assign/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/addons/mod/assign/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					// (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 { CoreConstants } from '@/core/constants';
 | 
				
			||||||
 | 
					import { Injectable, Type } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignIndexComponent } from '../../components/index';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { CoreCourseModule } from '@features/course/services/course-helper';
 | 
				
			||||||
 | 
					import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { AddonModAssign } from '../assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handler to support assign modules.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignModuleHandlerService implements CoreCourseModuleHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static readonly PAGE_NAME = 'mod_assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssign';
 | 
				
			||||||
 | 
					    modName = 'assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    supportedFeatures = {
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_GROUPS]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_GROUPINGS]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_MOD_INTRO]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_GRADE_OUTCOMES]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_ADVANCED_GRADING]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_PLAGIARISM]: true,
 | 
				
			||||||
 | 
					        [CoreConstants.FEATURE_COMMENT]: true,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the handler is enabled on a site level.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Whether or not the handler is enabled on a site level.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async isEnabled(): Promise<boolean> {
 | 
				
			||||||
 | 
					        return AddonModAssign.instance.isPluginEnabled();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the data required to display the module in the course contents view.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module The module object.
 | 
				
			||||||
 | 
					     * @return Data to render the module.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            icon: CoreCourse.instance.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
 | 
				
			||||||
 | 
					            title: module.name,
 | 
				
			||||||
 | 
					            class: 'addon-mod_assign-handler',
 | 
				
			||||||
 | 
					            showDownloadButton: true,
 | 
				
			||||||
 | 
					            action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void {
 | 
				
			||||||
 | 
					                options = options || {};
 | 
				
			||||||
 | 
					                options.params = options.params || {};
 | 
				
			||||||
 | 
					                Object.assign(options.params, { module });
 | 
				
			||||||
 | 
					                const routeParams = '/' + courseId + '/' + module.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                CoreNavigator.instance.navigateToSitePath(AddonModAssignModuleHandlerService.PAGE_NAME + routeParams, options);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the component to render the module. This is needed to support singleactivity course format.
 | 
				
			||||||
 | 
					     * The component returned must implement CoreCourseModuleMainComponent.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The component to use, undefined if not found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getMainComponent(): Promise<Type<unknown> | undefined> {
 | 
				
			||||||
 | 
					        return AddonModAssignIndexComponent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignModuleHandler = makeSingleton(AddonModAssignModuleHandlerService);
 | 
				
			||||||
							
								
								
									
										531
									
								
								src/addons/mod/assign/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										531
									
								
								src/addons/mod/assign/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,531 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModAssign,
 | 
				
			||||||
 | 
					    AddonModAssignAssign,
 | 
				
			||||||
 | 
					    AddonModAssignProvider,
 | 
				
			||||||
 | 
					    AddonModAssignSubmission,
 | 
				
			||||||
 | 
					    AddonModAssignSubmissionStatusOptions,
 | 
				
			||||||
 | 
					} from '../assign';
 | 
				
			||||||
 | 
					import { AddonModAssignSubmissionDelegate } from '../submission-delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignFeedbackDelegate } from '../feedback-delegate';
 | 
				
			||||||
 | 
					import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
 | 
				
			||||||
 | 
					import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { CoreWSExternalFile } from '@services/ws';
 | 
				
			||||||
 | 
					import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../assign-helper';
 | 
				
			||||||
 | 
					import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { CoreFilepool } from '@services/filepool';
 | 
				
			||||||
 | 
					import { CoreGroups } from '@services/groups';
 | 
				
			||||||
 | 
					import { AddonModAssignSync, AddonModAssignSyncResult } from '../assign-sync';
 | 
				
			||||||
 | 
					import { CoreUser } from '@features/user/services/user';
 | 
				
			||||||
 | 
					import { CoreGradesHelper } from '@features/grades/services/grades-helper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handler to prefetch assigns.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssign';
 | 
				
			||||||
 | 
					    modName = 'assign';
 | 
				
			||||||
 | 
					    component = AddonModAssignProvider.COMPONENT;
 | 
				
			||||||
 | 
					    updatesNames = /^configuration$|^.*files$|^submissions$|^grades$|^gradeitems$|^outcomes$|^comments$/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a certain module can use core_course_check_updates to check if it has updates.
 | 
				
			||||||
 | 
					     * If not defined, it will assume all modules can be checked.
 | 
				
			||||||
 | 
					     * The modules that return false will always be shown as outdated when they're downloaded.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Whether the module can use check_updates. The promise should never be rejected.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async canUseCheckUpdates(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
 | 
				
			||||||
 | 
					        // Teachers cannot use the WS because it doesn't check student submissions.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const assign = await AddonModAssign.instance.getAssignment(courseId, module.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const data = await AddonModAssign.instance.getSubmissions(assign.id, { cmId: module.id });
 | 
				
			||||||
 | 
					            if (data.canviewsubmissions) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check if the user can view their own submission.
 | 
				
			||||||
 | 
					            await AddonModAssign.instance.getSubmissionStatus(assign.id, { cmId: module.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get list of files. If not defined, we'll assume they're in module.contents.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the list of files.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSExternalFile[]> {
 | 
				
			||||||
 | 
					        const siteId = CoreSites.instance.getCurrentSiteId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const assign = await AddonModAssign.instance.getAssignment(courseId, module.id, { siteId });
 | 
				
			||||||
 | 
					            // Get intro files and attachments.
 | 
				
			||||||
 | 
					            let files = assign.introattachments || [];
 | 
				
			||||||
 | 
					            files = files.concat(this.getIntroFilesFromInstance(module, assign));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Now get the files in the submissions.
 | 
				
			||||||
 | 
					            const submissionData = await AddonModAssign.instance.getSubmissions(assign.id, { cmId: module.id, siteId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (submissionData.canviewsubmissions) {
 | 
				
			||||||
 | 
					                // Teacher, get all submissions.
 | 
				
			||||||
 | 
					                const submissions =
 | 
				
			||||||
 | 
					                    await AddonModAssignHelper.instance.getSubmissionsUserData(assign, submissionData.submissions, 0, { siteId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Get all the files in the submissions.
 | 
				
			||||||
 | 
					                const promises = submissions.map((submission) =>
 | 
				
			||||||
 | 
					                    this.getSubmissionFiles(assign, submission.submitid!, !!submission.blindid, siteId).then((submissionFiles) => {
 | 
				
			||||||
 | 
					                        files = files.concat(submissionFiles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }).catch((error) => {
 | 
				
			||||||
 | 
					                        if (error && error.errorcode == 'nopermission') {
 | 
				
			||||||
 | 
					                            // The user does not have persmission to view this submission, ignore it.
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        throw error;
 | 
				
			||||||
 | 
					                    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await Promise.all(promises);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Student, get only his/her submissions.
 | 
				
			||||||
 | 
					                const userId = CoreSites.instance.getCurrentSiteUserId();
 | 
				
			||||||
 | 
					                const blindMarking = !!assign.blindmarking && !assign.revealidentities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const submissionFiles = await this.getSubmissionFiles(assign, userId, blindMarking, siteId);
 | 
				
			||||||
 | 
					                files = files.concat(submissionFiles);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return files;
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            // Error getting data, return empty list.
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get submission files.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assign.
 | 
				
			||||||
 | 
					     * @param submitId User ID of the submission to get.
 | 
				
			||||||
 | 
					     * @param blindMarking True if blind marking, false otherwise.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved with array of files.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getSubmissionFiles(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submitId: number,
 | 
				
			||||||
 | 
					        blindMarking: boolean,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<CoreWSExternalFile[]> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const submissionStatus = await AddonModAssign.instance.getSubmissionStatusWithRetry(assign, {
 | 
				
			||||||
 | 
					            userId: submitId,
 | 
				
			||||||
 | 
					            isBlind: blindMarking,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        const userSubmission = AddonModAssign.instance.getSubmissionObjectFromAttempt(assign, submissionStatus.lastattempt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!submissionStatus.lastattempt || !userSubmission) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises: Promise<CoreWSExternalFile[]>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (userSubmission.plugins) {
 | 
				
			||||||
 | 
					            // Add submission plugin files.
 | 
				
			||||||
 | 
					            userSubmission.plugins.forEach((plugin) => {
 | 
				
			||||||
 | 
					                promises.push(AddonModAssignSubmissionDelegate.instance.getPluginFiles(assign, userSubmission, plugin, siteId));
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (submissionStatus.feedback && submissionStatus.feedback.plugins) {
 | 
				
			||||||
 | 
					            // Add feedback plugin files.
 | 
				
			||||||
 | 
					            submissionStatus.feedback.plugins.forEach((plugin) => {
 | 
				
			||||||
 | 
					                promises.push(AddonModAssignFeedbackDelegate.instance.getPluginFiles(assign, userSubmission, plugin, siteId));
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const filesLists = await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [].concat.apply([], filesLists);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Invalidate the prefetched content.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param moduleId The module ID.
 | 
				
			||||||
 | 
					     * @param courseId The course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Promise resolved when the data is invalidated.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
				
			||||||
 | 
					        await AddonModAssign.instance.invalidateContent(moduleId, courseId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Invalidate WS calls needed to determine module status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Promise resolved when invalidated.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async invalidateModule(module: CoreCourseAnyModuleData): Promise<void> {
 | 
				
			||||||
 | 
					        return CoreCourse.instance.invalidateModule(module.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Whether or not the handler is enabled on a site level.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return A boolean, or a promise resolved with a boolean, indicating if the handler is enabled.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async isEnabled(): Promise<boolean> {
 | 
				
			||||||
 | 
					        return AddonModAssign.instance.isPluginEnabled();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch a module.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prefetch(module: CoreCourseAnyModuleData, courseId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        return this.prefetchPackage(module, courseId, this.prefetchAssign.bind(this, module, courseId));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch an assignment.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async prefetchAssign(module: CoreCourseAnyModuleData, courseId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        const userId = CoreSites.instance.getCurrentSiteUserId();
 | 
				
			||||||
 | 
					        courseId = courseId || module.course || CoreSites.instance.getCurrentSiteHomeId();
 | 
				
			||||||
 | 
					        const siteId = CoreSites.instance.getCurrentSiteId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const options: CoreSitesCommonWSOptions = {
 | 
				
			||||||
 | 
					            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const modOptions: CoreCourseCommonModWSOptions = {
 | 
				
			||||||
 | 
					            cmId: module.id,
 | 
				
			||||||
 | 
					            ...options,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get assignment to retrieve all its submissions.
 | 
				
			||||||
 | 
					        const assign = await AddonModAssign.instance.getAssignment(courseId, module.id, options);
 | 
				
			||||||
 | 
					        const promises: Promise<any>[] = [];
 | 
				
			||||||
 | 
					        const blindMarking = assign.blindmarking && !assign.revealidentities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (blindMarking) {
 | 
				
			||||||
 | 
					            promises.push(
 | 
				
			||||||
 | 
					                CoreUtils.instance.ignoreErrors(AddonModAssign.instance.getAssignmentUserMappings(assign.id, -1, modOptions)),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(this.prefetchSubmissions(assign, courseId, module.id, userId, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(CoreCourseHelper.instance.getModuleCourseIdByInstance(assign.id, 'assign', siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Download intro files and attachments. Do not call getFiles because it'd call some WS twice.
 | 
				
			||||||
 | 
					        let files = assign.introattachments || [];
 | 
				
			||||||
 | 
					        files = files.concat(this.getIntroFilesFromInstance(module, assign));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(CoreFilepool.instance.addFilesToQueue(siteId, files, this.component, module.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch assign submissions.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assign.
 | 
				
			||||||
 | 
					     * @param courseId Course ID.
 | 
				
			||||||
 | 
					     * @param moduleId Module ID.
 | 
				
			||||||
 | 
					     * @param userId User ID. If not defined, site's current user.
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when prefetched, rejected otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async prefetchSubmissions(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        moduleId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					        const modOptions: CoreCourseCommonModWSOptions = {
 | 
				
			||||||
 | 
					            cmId: moduleId,
 | 
				
			||||||
 | 
					            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get submissions.
 | 
				
			||||||
 | 
					        const submissions = await AddonModAssign.instance.getSubmissions(assign.id, modOptions);
 | 
				
			||||||
 | 
					        const promises: Promise<any>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(this.prefetchParticipantSubmissions(
 | 
				
			||||||
 | 
					            assign,
 | 
				
			||||||
 | 
					            submissions.canviewsubmissions,
 | 
				
			||||||
 | 
					            submissions.submissions,
 | 
				
			||||||
 | 
					            moduleId,
 | 
				
			||||||
 | 
					            courseId,
 | 
				
			||||||
 | 
					            userId,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Prefetch own submission, we need to do this for teachers too so the response with error is cached.
 | 
				
			||||||
 | 
					        promises.push(
 | 
				
			||||||
 | 
					            this.prefetchSubmission(
 | 
				
			||||||
 | 
					                assign,
 | 
				
			||||||
 | 
					                courseId,
 | 
				
			||||||
 | 
					                moduleId,
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    userId,
 | 
				
			||||||
 | 
					                    readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					                    siteId,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                true,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected async prefetchParticipantSubmissions(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        canviewsubmissions: boolean,
 | 
				
			||||||
 | 
					        submissions: AddonModAssignSubmission[] = [],
 | 
				
			||||||
 | 
					        moduleId: number,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        userId: number,
 | 
				
			||||||
 | 
					        siteId: string,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const options: CoreSitesCommonWSOptions = {
 | 
				
			||||||
 | 
					            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					            siteId,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const modOptions: CoreCourseCommonModWSOptions = {
 | 
				
			||||||
 | 
					            cmId: moduleId,
 | 
				
			||||||
 | 
					            ...options,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Always prefetch groupInfo.
 | 
				
			||||||
 | 
					        const groupInfo = await CoreGroups.instance.getActivityGroupInfo(assign.cmid, false, undefined, siteId);
 | 
				
			||||||
 | 
					        if (!canviewsubmissions) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Teacher, prefetch all submissions.
 | 
				
			||||||
 | 
					        if (!groupInfo.groups || groupInfo.groups.length == 0) {
 | 
				
			||||||
 | 
					            groupInfo.groups = [{ id: 0, name: '' }];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const promises = groupInfo.groups.map((group) =>
 | 
				
			||||||
 | 
					            AddonModAssignHelper.instance.getSubmissionsUserData(assign, submissions, group.id, options)
 | 
				
			||||||
 | 
					                .then((submissions: AddonModAssignSubmissionFormatted[]) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const subPromises: Promise<any>[] = submissions.map((submission) => {
 | 
				
			||||||
 | 
					                        const submissionOptions = {
 | 
				
			||||||
 | 
					                            userId: submission.submitid,
 | 
				
			||||||
 | 
					                            groupId: group.id,
 | 
				
			||||||
 | 
					                            isBlind: !!submission.blindid,
 | 
				
			||||||
 | 
					                            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					                            siteId,
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return this.prefetchSubmission(assign, courseId, moduleId, submissionOptions, true);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!assign.markingworkflow) {
 | 
				
			||||||
 | 
					                        // Get assignment grades only if workflow is not enabled to check grading date.
 | 
				
			||||||
 | 
					                        subPromises.push(AddonModAssign.instance.getAssignmentGrades(assign.id, modOptions));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Prefetch the submission of the current user even if it does not exist, this will be create it.
 | 
				
			||||||
 | 
					                    if (!submissions || !submissions.find((subm: AddonModAssignSubmissionFormatted) => subm.submitid == userId)) {
 | 
				
			||||||
 | 
					                        const submissionOptions = {
 | 
				
			||||||
 | 
					                            userId,
 | 
				
			||||||
 | 
					                            groupId: group.id,
 | 
				
			||||||
 | 
					                            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
				
			||||||
 | 
					                            siteId,
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        subPromises.push(this.prefetchSubmission(assign, courseId, moduleId, submissionOptions));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return Promise.all(subPromises);
 | 
				
			||||||
 | 
					                }).then(async () => {
 | 
				
			||||||
 | 
					                    // Participiants already fetched, we don't need to ignore cache now.
 | 
				
			||||||
 | 
					                    const participants = await AddonModAssignHelper.instance.getParticipants(assign, group.id, { siteId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Fail silently (Moodle < 3.2).
 | 
				
			||||||
 | 
					                    await CoreUtils.instance.ignoreErrors(
 | 
				
			||||||
 | 
					                        CoreUser.instance.prefetchUserAvatars(participants, 'profileimageurl', siteId),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch a submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign Assign.
 | 
				
			||||||
 | 
					     * @param courseId Course ID.
 | 
				
			||||||
 | 
					     * @param moduleId Module ID.
 | 
				
			||||||
 | 
					     * @param options Other options, see getSubmissionStatusWithRetry.
 | 
				
			||||||
 | 
					     * @param resolveOnNoPermission If true, will avoid throwing if a nopermission error is raised.
 | 
				
			||||||
 | 
					     * @return Promise resolved when prefetched, rejected otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async prefetchSubmission(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        courseId: number,
 | 
				
			||||||
 | 
					        moduleId: number,
 | 
				
			||||||
 | 
					        options: AddonModAssignSubmissionStatusOptions = {},
 | 
				
			||||||
 | 
					        resolveOnNoPermission = false,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					        const submission = await AddonModAssign.instance.getSubmissionStatusWithRetry(assign, options);
 | 
				
			||||||
 | 
					        const siteId = options.siteId!;
 | 
				
			||||||
 | 
					        const userId = options.userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const promises: Promise<any>[] = [];
 | 
				
			||||||
 | 
					            const blindMarking = !!assign.blindmarking && !assign.revealidentities;
 | 
				
			||||||
 | 
					            let userIds: number[] = [];
 | 
				
			||||||
 | 
					            const userSubmission = AddonModAssign.instance.getSubmissionObjectFromAttempt(assign, submission.lastattempt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (submission.lastattempt) {
 | 
				
			||||||
 | 
					                // Get IDs of the members who need to submit.
 | 
				
			||||||
 | 
					                if (!blindMarking && submission.lastattempt.submissiongroupmemberswhoneedtosubmit) {
 | 
				
			||||||
 | 
					                    userIds = userIds.concat(submission.lastattempt.submissiongroupmemberswhoneedtosubmit);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (userSubmission && userSubmission.id) {
 | 
				
			||||||
 | 
					                    // Prefetch submission plugins data.
 | 
				
			||||||
 | 
					                    if (userSubmission.plugins) {
 | 
				
			||||||
 | 
					                        userSubmission.plugins.forEach((plugin) => {
 | 
				
			||||||
 | 
					                            // Prefetch the plugin WS data.
 | 
				
			||||||
 | 
					                            promises.push(
 | 
				
			||||||
 | 
					                                AddonModAssignSubmissionDelegate.instance.prefetch(assign, userSubmission, plugin, siteId),
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // Prefetch the plugin files.
 | 
				
			||||||
 | 
					                            promises.push(
 | 
				
			||||||
 | 
					                                AddonModAssignSubmissionDelegate.instance.getPluginFiles(assign, userSubmission, plugin, siteId)
 | 
				
			||||||
 | 
					                                    .then((files) =>
 | 
				
			||||||
 | 
					                                        CoreFilepool.instance.addFilesToQueue(siteId, files, this.component, module.id))
 | 
				
			||||||
 | 
					                                    .catch(() => {
 | 
				
			||||||
 | 
					                                        // Ignore errors.
 | 
				
			||||||
 | 
					                                    }),
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Get ID of the user who did the submission.
 | 
				
			||||||
 | 
					                    if (userSubmission.userid) {
 | 
				
			||||||
 | 
					                        userIds.push(userSubmission.userid);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Prefetch grade items.
 | 
				
			||||||
 | 
					            if (userId) {
 | 
				
			||||||
 | 
					                promises.push(CoreCourse.instance.getModuleBasicGradeInfo(moduleId, siteId).then((gradeInfo) => {
 | 
				
			||||||
 | 
					                    if (gradeInfo) {
 | 
				
			||||||
 | 
					                        promises.push(
 | 
				
			||||||
 | 
					                            CoreGradesHelper.instance.getGradeModuleItems(courseId, moduleId, userId, undefined, siteId, true),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Prefetch feedback.
 | 
				
			||||||
 | 
					            if (submission.feedback) {
 | 
				
			||||||
 | 
					                // Get profile and image of the grader.
 | 
				
			||||||
 | 
					                if (submission.feedback.grade && submission.feedback.grade.grader > 0) {
 | 
				
			||||||
 | 
					                    userIds.push(submission.feedback.grade.grader);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Prefetch feedback plugins data.
 | 
				
			||||||
 | 
					                if (submission.feedback.plugins && userSubmission && userSubmission.id) {
 | 
				
			||||||
 | 
					                    submission.feedback.plugins.forEach((plugin) => {
 | 
				
			||||||
 | 
					                        // Prefetch the plugin WS data.
 | 
				
			||||||
 | 
					                        promises.push(AddonModAssignFeedbackDelegate.instance.prefetch(assign, userSubmission, plugin, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Prefetch the plugin files.
 | 
				
			||||||
 | 
					                        promises.push(
 | 
				
			||||||
 | 
					                            AddonModAssignFeedbackDelegate.instance.getPluginFiles(assign, userSubmission, plugin, siteId)
 | 
				
			||||||
 | 
					                                .then((files) => CoreFilepool.instance.addFilesToQueue(siteId, files, this.component, module.id))
 | 
				
			||||||
 | 
					                                .catch(() => {
 | 
				
			||||||
 | 
					                                    // Ignore errors.
 | 
				
			||||||
 | 
					                                }),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Prefetch user profiles.
 | 
				
			||||||
 | 
					            promises.push(CoreUser.instance.prefetchProfiles(userIds, courseId, siteId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Promise.all(promises);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            // Ignore if the user can't view their own submission.
 | 
				
			||||||
 | 
					            if (resolveOnNoPermission && error.errorcode != 'nopermission') {
 | 
				
			||||||
 | 
					                throw error;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sync a module.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param module Module.
 | 
				
			||||||
 | 
					     * @param courseId Course ID the module belongs to
 | 
				
			||||||
 | 
					     * @param siteId Site ID. If not defined, current site.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModAssignSyncResult> {
 | 
				
			||||||
 | 
					        return AddonModAssignSync.instance.syncAssign(module.instance!, siteId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignPrefetchHandler = makeSingleton(AddonModAssignPrefetchHandlerService);
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/addons/mod/assign/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/addons/mod/assign/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
				
			||||||
 | 
					import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate';
 | 
				
			||||||
 | 
					import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
 | 
				
			||||||
 | 
					import { CoreUrlUtils } from '@services/utils/url';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { AddonModAssign } from '../assign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handler for assign push notifications clicks.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignPushClickHandlerService implements CorePushNotificationsClickHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignPushClickHandler';
 | 
				
			||||||
 | 
					    priority = 200;
 | 
				
			||||||
 | 
					    featureName = 'CoreCourseModuleDelegate_AddonModAssign';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a notification click is handled by this handler.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param notification The notification to check.
 | 
				
			||||||
 | 
					     * @return Whether the notification click is handled by this handler
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async handles(notification: NotificationData): Promise<boolean> {
 | 
				
			||||||
 | 
					        return CoreUtils.instance.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_assign' &&
 | 
				
			||||||
 | 
					                notification.name == 'assign_notification';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle the notification click.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param notification The notification to check.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async handleClick(notification: NotificationData): Promise<void> {
 | 
				
			||||||
 | 
					        const contextUrlParams = CoreUrlUtils.instance.extractUrlParams(notification.contexturl);
 | 
				
			||||||
 | 
					        const courseId = Number(notification.courseid);
 | 
				
			||||||
 | 
					        const moduleId = Number(contextUrlParams.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await CoreUtils.instance.ignoreErrors(AddonModAssign.instance.invalidateContent(moduleId, courseId, notification.site));
 | 
				
			||||||
 | 
					        await CoreCourseHelper.instance.navigateToModule(moduleId, notification.site, courseId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignPushClickHandler = makeSingleton(AddonModAssignPushClickHandlerService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NotificationData = CorePushNotificationsNotificationBasicData & {
 | 
				
			||||||
 | 
					    courseid: number;
 | 
				
			||||||
 | 
					    contexturl: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/addons/mod/assign/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/addons/mod/assign/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCronHandler } from '@services/cron';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { AddonModAssignSync } from '../assign-sync';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Synchronization cron handler.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignSyncCronHandlerService implements CoreCronHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'AddonModAssignSyncCronHandler';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the process.
 | 
				
			||||||
 | 
					     * Receives the ID of the site affected, undefined for all sites.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param siteId ID of the site affected, undefined for all sites.
 | 
				
			||||||
 | 
					     * @param force Wether the execution is forced (manual sync).
 | 
				
			||||||
 | 
					     * @return Promise resolved when done, rejected if failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    execute(siteId?: string, force?: boolean): Promise<void> {
 | 
				
			||||||
 | 
					        return AddonModAssignSync.instance.syncAllAssignments(siteId, force);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the time between consecutive executions.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Time between consecutive executions (in ms).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getInterval(): number {
 | 
				
			||||||
 | 
					        return AddonModAssignSync.instance.syncInterval;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const AddonModAssignSyncCronHandler = makeSingleton(AddonModAssignSyncCronHandlerService);
 | 
				
			||||||
							
								
								
									
										565
									
								
								src/addons/mod/assign/services/submission-delegate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								src/addons/mod/assign/services/submission-delegate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,565 @@
 | 
				
			|||||||
 | 
					// (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 { Injectable } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
 | 
				
			||||||
 | 
					import { AddonModAssignDefaultSubmissionHandler } from './handlers/default-submission';
 | 
				
			||||||
 | 
					import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
 | 
				
			||||||
 | 
					import { makeSingleton } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreWSExternalFile } from '@services/ws';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface that all submission handlers must implement.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Name of the type of submission the handler supports. E.g. 'file'.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return Boolean or promise resolved with boolean: whether it can be edited in offline.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    canEditOffline?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					    ): boolean | Promise<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Should clear temporary data for a cancelled submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param inputData Data entered by the user for the submission.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clearTmpData?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    copySubmissionData?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): void | Promise<void>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    deleteOfflineData?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        offlineData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): void | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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?: boolean,
 | 
				
			||||||
 | 
					    ): any | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): CoreWSExternalFile[] | Promise<CoreWSExternalFile[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for the plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return The plugin name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName?(plugin: AddonModAssignPlugin): string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getSizeForCopy?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					    ): number | Promise<number>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): number | Promise<number>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hasDataChanged?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): boolean | Promise<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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 | Promise<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    prefetch?(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<void>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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: any,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        offline?: boolean,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): void | Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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: any,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): void | Promise<any>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Delegate to register plugins for assign submission.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class AddonModAssignSubmissionDelegateService extends CoreDelegate<AddonModAssignSubmissionHandler> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected handlerNameProperty = 'type';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        protected defaultHandler: AddonModAssignDefaultSubmissionHandler,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        super('AddonModAssignSubmissionDelegate', true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Whether the plugin can be edited in offline for existing submissions.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return Promise resolved with boolean: whether it can be edited in offline.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async canPluginEditOffline(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					    ): Promise<boolean | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'canEditOffline', [assign, submission, plugin]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Clear some temporary data for a certain plugin because a submission was cancelled.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param submission The submission.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param inputData Data entered by the user for the submission.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clearTmpData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): void {
 | 
				
			||||||
 | 
					        return this.executeFunctionOnEnabled(plugin.type, 'clearTmpData', [assign, submission, plugin, inputData]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Copy the data from last submitted attempt to the current submission for a certain plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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 Promise resolved when the data has been copied.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async copyPluginSubmissionData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<void | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'copySubmissionData',
 | 
				
			||||||
 | 
					            [assign, plugin, pluginData, userId, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Delete offline data stored for a certain submission and plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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 Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async deletePluginOfflineData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        offlineData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'deleteOfflineData',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, offlineData, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the component to use for a certain submission plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @param edit Whether the user is editing.
 | 
				
			||||||
 | 
					     * @return Promise resolved with the component to use, undefined if not found.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getComponentForPlugin(plugin: AddonModAssignPlugin, edit?: boolean): Promise<any | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'getComponent', [plugin, edit]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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 Promise resolved with the files.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getPluginFiles(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<CoreWSExternalFile[]> {
 | 
				
			||||||
 | 
					        const files: CoreWSExternalFile[] | undefined =
 | 
				
			||||||
 | 
					            await this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return files || [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get a readable name to use for a certain submission plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param plugin Plugin to get the name for.
 | 
				
			||||||
 | 
					     * @return Human readable name.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getPluginName(plugin: AddonModAssignPlugin): string | undefined {
 | 
				
			||||||
 | 
					        return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [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 Promise resolved with size.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getPluginSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise<number | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'getSizeForCopy', [assign, plugin]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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 Promise resolved with size.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async getPluginSizeForEdit(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): Promise<number | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'getSizeForEdit',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, inputData],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if the submission data has changed for a certain 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 Promise resolved with true if data has changed, resolved with false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async hasPluginDataChanged(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					    ): Promise<boolean | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'hasDataChanged',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, inputData],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a submission plugin is supported.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param pluginType Type of the plugin.
 | 
				
			||||||
 | 
					     * @return Whether it's supported.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isPluginSupported(pluginType: string): boolean {
 | 
				
			||||||
 | 
					        return this.hasHandler(pluginType, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a submission plugin is supported for edit.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param pluginType Type of the plugin.
 | 
				
			||||||
 | 
					     * @return Whether it's supported for edit.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async isPluginSupportedForEdit(pluginType: string): Promise<boolean | undefined> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(pluginType, 'isEnabledForEdit');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check if a plugin has no data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param assign The assignment.
 | 
				
			||||||
 | 
					     * @param plugin The plugin object.
 | 
				
			||||||
 | 
					     * @return Whether the plugin is empty.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    isPluginEmpty(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): boolean | undefined {
 | 
				
			||||||
 | 
					        return this.executeFunctionOnEnabled(plugin.type, 'isEmpty', [assign, plugin]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prefetch any required data for a submission plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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> {
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and add to pluginData the data to submit for a certain submission plugin.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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 Promise resolved when data has been gathered.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async preparePluginSubmissionData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        inputData: any,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        offline?: boolean,
 | 
				
			||||||
 | 
					        userId?: number,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any | undefined> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return await this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'prepareSubmissionData',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, inputData, pluginData, offline, userId, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepare and add to pluginData the data to send to server to synchronize an offline submission.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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 Promise resolved when data has been gathered.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async preparePluginSyncData(
 | 
				
			||||||
 | 
					        assign: AddonModAssignAssign,
 | 
				
			||||||
 | 
					        submission: AddonModAssignSubmission,
 | 
				
			||||||
 | 
					        plugin: AddonModAssignPlugin,
 | 
				
			||||||
 | 
					        offlineData: any,
 | 
				
			||||||
 | 
					        pluginData: any,
 | 
				
			||||||
 | 
					        siteId?: string,
 | 
				
			||||||
 | 
					    ): Promise<any | undefined> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return this.executeFunctionOnEnabled(
 | 
				
			||||||
 | 
					            plugin.type,
 | 
				
			||||||
 | 
					            'prepareSyncData',
 | 
				
			||||||
 | 
					            [assign, submission, plugin, offlineData, pluginData, siteId],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export class AddonModAssignSubmissionDelegate extends makeSingleton(AddonModAssignSubmissionDelegateService) {}
 | 
				
			||||||
@ -14,6 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { NgModule } from '@angular/core';
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { AddonModAssignModule } from './assign/assign.module';
 | 
				
			||||||
import { AddonModBookModule } from './book/book.module';
 | 
					import { AddonModBookModule } from './book/book.module';
 | 
				
			||||||
import { AddonModLessonModule } from './lesson/lesson.module';
 | 
					import { AddonModLessonModule } from './lesson/lesson.module';
 | 
				
			||||||
import { AddonModPageModule } from './page/page.module';
 | 
					import { AddonModPageModule } from './page/page.module';
 | 
				
			||||||
@ -21,6 +22,7 @@ import { AddonModPageModule } from './page/page.module';
 | 
				
			|||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
    declarations: [],
 | 
					    declarations: [],
 | 
				
			||||||
    imports: [
 | 
					    imports: [
 | 
				
			||||||
 | 
					        AddonModAssignModule,
 | 
				
			||||||
        AddonModBookModule,
 | 
					        AddonModBookModule,
 | 
				
			||||||
        AddonModLessonModule,
 | 
					        AddonModLessonModule,
 | 
				
			||||||
        AddonModPageModule,
 | 
					        AddonModPageModule,
 | 
				
			||||||
 | 
				
			|||||||
@ -412,7 +412,7 @@ export class CoreGroupsProvider {
 | 
				
			|||||||
     * @param groupInfo Group info.
 | 
					     * @param groupInfo Group info.
 | 
				
			||||||
     * @return Group ID to use.
 | 
					     * @return Group ID to use.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    validateGroupId(groupId: number, groupInfo: CoreGroupInfo): number {
 | 
					    validateGroupId(groupId = 0, groupInfo: CoreGroupInfo): number {
 | 
				
			||||||
        if (groupId > 0 && groupInfo && groupInfo.groups && groupInfo.groups.length > 0) {
 | 
					        if (groupId > 0 && groupInfo && groupInfo.groups && groupInfo.groups.length > 0) {
 | 
				
			||||||
            // Check if the group is in the list of groups.
 | 
					            // Check if the group is in the list of groups.
 | 
				
			||||||
            if (groupInfo.groups.some((group) => groupId == group.id)) {
 | 
					            if (groupInfo.groups.some((group) => groupId == group.id)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -380,6 +380,11 @@ export class CoreNavigatorService {
 | 
				
			|||||||
        // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
 | 
					        // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
 | 
				
			||||||
        // @todo this.location.replaceState('');
 | 
					        // @todo this.location.replaceState('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        options = {
 | 
				
			||||||
 | 
					            preferCurrentTab: true,
 | 
				
			||||||
 | 
					            ...options,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        path = path.replace(/^(\.|\/main)?\//, '');
 | 
					        path = path.replace(/^(\.|\/main)?\//, '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const pathRoot = /^[^/]+/.exec(path)?.[0] ?? '';
 | 
					        const pathRoot = /^[^/]+/.exec(path)?.[0] ?? '';
 | 
				
			||||||
@ -389,7 +394,7 @@ export class CoreNavigatorService {
 | 
				
			|||||||
            false,
 | 
					            false,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (options.preferCurrentTab === false && isMainMenuTab) {
 | 
					        if (!options.preferCurrentTab && isMainMenuTab) {
 | 
				
			||||||
            return this.navigate(`/main/${path}`, options);
 | 
					            return this.navigate(`/main/${path}`, options);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user