forked from CIT/Vmeda.Online
		
	MOBILE-3657 workshop: Index page
This commit is contained in:
		
							parent
							
								
									8e7b148205
								
							
						
					
					
						commit
						8db22cc54a
					
				
							
								
								
									
										46
									
								
								src/addons/mod/workshop/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/addons/mod/workshop/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { AddonModWorkshopIndexComponent } from './index/index';
 | 
			
		||||
import { AddonModWorkshopSubmissionComponent } from './submission/submission';
 | 
			
		||||
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModWorkshopPhaseInfoComponent } from './phase/phase';
 | 
			
		||||
import { AddonModWorkshopAssessmentComponent } from './assessment/assessment';
 | 
			
		||||
import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strategy/assessment-strategy';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModWorkshopIndexComponent,
 | 
			
		||||
        AddonModWorkshopSubmissionComponent,
 | 
			
		||||
        AddonModWorkshopPhaseInfoComponent,
 | 
			
		||||
        AddonModWorkshopAssessmentComponent,
 | 
			
		||||
        AddonModWorkshopAssessmentStrategyComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCourseComponentsModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModWorkshopIndexComponent,
 | 
			
		||||
        AddonModWorkshopSubmissionComponent,
 | 
			
		||||
        AddonModWorkshopPhaseInfoComponent,
 | 
			
		||||
        AddonModWorkshopAssessmentComponent,
 | 
			
		||||
        AddonModWorkshopAssessmentStrategyComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopComponentsModule {}
 | 
			
		||||
@ -0,0 +1,245 @@
 | 
			
		||||
<!-- 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="description" [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]="200" [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">
 | 
			
		||||
    <ion-card class="with-borders" *ngIf="phases">
 | 
			
		||||
        <ion-item (click)="viewPhaseInfo()" detail="true">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2 class="ion-text-wrap">{{ phases[workshop!.phase].title }}</h2>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <ng-container *ngIf="phases && phases[workshop!.phase] && phases[workshop!.phase].tasks &&
 | 
			
		||||
            phases[workshop!.phase].tasks.length">
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngFor="let task of phases[workshop!.phase].tasks"
 | 
			
		||||
                [class.item-dimmed]="task.code == 'submit' && !showSubmit" (click)="runTask(task)" detail="false">
 | 
			
		||||
                <ion-icon slot="start" name="far-circle" *ngIf="task.completed == null"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-times-circle" color="danger" *ngIf="task.completed == ''"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-info-circle" color="info" *ngIf="task.completed == 'info'"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-check-circle" color="success" *ngIf="task.completed == '1'"></ion-icon>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{task.title}}</h2>
 | 
			
		||||
                    <p *ngIf="task.details" [innerHTML]="task.details"></p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-icon slot="end" *ngIf="task.link && task.code != 'submit'" name="fas-external-link-alt"></ion-icon>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </ion-card>
 | 
			
		||||
 | 
			
		||||
    <!-- 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>
 | 
			
		||||
 | 
			
		||||
    <!-- Description (setup phase only) -->
 | 
			
		||||
    <ion-card *ngIf="description && workshop && workshop!.phase == PHASE_SETUP">
 | 
			
		||||
        <ion-item class="ion-text-wrap">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2>{{ 'core.description' | translate }}</h2>
 | 
			
		||||
                <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
 | 
			
		||||
                    [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </ion-card>
 | 
			
		||||
 | 
			
		||||
    <div *ngIf="access && workshop && workshop!.phase >= PHASE_SUBMISSION">
 | 
			
		||||
        <!-- CLOSED PHASE -->
 | 
			
		||||
        <ng-container *ngIf="workshop!.phase >= PHASE_CLOSED">
 | 
			
		||||
            <ion-card *ngIf="workshop!.conclusion">
 | 
			
		||||
                <ion-item class="ion-text-wrap">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>{{ 'addon.mod_workshop.conclusion' | translate }}</h2>
 | 
			
		||||
                        <core-format-text fullOnClick="true" [component]="component" [componentId]="module.id"
 | 
			
		||||
                            [text]="workshop!.conclusion" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                            [courseId]="courseId">
 | 
			
		||||
                        </core-format-text>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
 | 
			
		||||
            <ion-card class="with-borders" *ngIf="userGrades">
 | 
			
		||||
                <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                    <ion-label><h2>{{ 'addon.mod_workshop.yourgrades' | translate }}</h2></ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="userGrades.submissionlongstrgrade">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>{{ 'addon.mod_workshop.submissiongrade' | translate }}</h2>
 | 
			
		||||
                        <p>{{ userGrades.submissionlongstrgrade }}</p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="userGrades.assessmentlongstrgrade">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>{{ 'addon.mod_workshop.gradinggrade' | translate }}</h2>
 | 
			
		||||
                        <p>{{ userGrades.assessmentlongstrgrade }}</p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
        <!-- SUBMISSION PHASE -->
 | 
			
		||||
        <ion-card *ngIf="workshop!.phase == PHASE_SUBMISSION && workshop!.instructauthors">
 | 
			
		||||
            <ion-item class="ion-text-wrap">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h2>
 | 
			
		||||
                    <core-format-text fullOnClick="true" [component]="component" [componentId]="module.id"
 | 
			
		||||
                        [text]="workshop!.instructauthors" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                        [courseId]="courseId">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-card>
 | 
			
		||||
 | 
			
		||||
        <ion-card *ngIf="canSubmit">
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="!submission">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ 'addon.mod_workshop.yoursubmission' | translate }}</h2>
 | 
			
		||||
                    <p>{{ 'addon.mod_workshop.noyoursubmission' | translate }}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
 | 
			
		||||
            <ng-container *ngIf="submission">
 | 
			
		||||
                <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                    <ion-label><h2>{{ 'addon.mod_workshop.yoursubmission' | translate }}</h2></ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <addon-mod-workshop-submission [submission]="submission" [courseId]="workshop!.course" [module]="module"
 | 
			
		||||
                    [workshop]="workshop" [access]="access">
 | 
			
		||||
                </addon-mod-workshop-submission>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ion-card>
 | 
			
		||||
 | 
			
		||||
        <!-- Show only on current phase -->
 | 
			
		||||
        <ng-container *ngIf="workshop!.phase == PHASE_SUBMISSION">
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="showSubmit">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <ion-button expand="block" *ngIf="access.creatingsubmissionallowed && !submission" (click)="gotoSubmit()">
 | 
			
		||||
                        <ion-icon slot="start" name="fas-plus"></ion-icon>
 | 
			
		||||
                        {{ 'addon.mod_workshop.createsubmission' | translate }}
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
                    <ion-button expand="block" *ngIf="access.modifyingsubmissionallowed && submission" (click)="gotoSubmit()">
 | 
			
		||||
                        <ion-icon slot="start" name="fas-edit"></ion-icon>
 | 
			
		||||
                        {{ 'addon.mod_workshop.editsubmission' | translate }}
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngIf="workshop!.phase >= PHASE_CLOSED">
 | 
			
		||||
            <ion-card class="with-borders" *ngIf="publishedSubmissions && publishedSubmissions.length">
 | 
			
		||||
                <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                    <ion-label><h2>{{ 'addon.mod_workshop.publishedsubmissions' | translate }}</h2></ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ng-container *ngFor="let submission of publishedSubmissions">
 | 
			
		||||
                    <addon-mod-workshop-submission [submission]="submission" [courseId]="workshop!.course" [module]="module"
 | 
			
		||||
                        [workshop]="workshop" [access]="access" summary="true" class="core-as-item">
 | 
			
		||||
                    </addon-mod-workshop-submission>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
        <!-- ASSESSMENT PHASE -->
 | 
			
		||||
        <ng-container *ngIf="workshop!.phase >= PHASE_ASSESSMENT">
 | 
			
		||||
            <ion-card *ngIf="workshop!.phase == PHASE_ASSESSMENT && workshop!.instructreviewers">
 | 
			
		||||
                <ion-item class="ion-text-wrap">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h2>
 | 
			
		||||
                        <core-format-text fullOnClick="true" [component]="component" [componentId]="module.id"
 | 
			
		||||
                            [text]="workshop!.instructreviewers" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                            [courseId]="courseId">
 | 
			
		||||
                        </core-format-text>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
 | 
			
		||||
            <ion-card class="with-borders" *ngIf="canAssess">
 | 
			
		||||
                <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                    <ion-label><h2>{{ 'addon.mod_workshop.assignedassessments' | translate }}</h2></ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="!assessments || !assessments.length">
 | 
			
		||||
                    <ion-label><p>{{ 'addon.mod_workshop.assignedassessmentsnone' | translate }}</p></ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ng-container *ngFor="let assessment of (assessments || [])">
 | 
			
		||||
                    <addon-mod-workshop-submission [submission]="assessment.submission" [assessment]="assessment"
 | 
			
		||||
                        [courseId]="workshop!.course" [module]="module" [workshop]="workshop" [access]="access" summary="true"
 | 
			
		||||
                        class="core-as-item">
 | 
			
		||||
                    </addon-mod-workshop-submission>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ion-card >
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
        <!-- MULTIPLE PHASES SUBMISSION OR GREATER only teachers -->
 | 
			
		||||
        <ion-card class="with-borders" *ngIf="access.canviewallsubmissions && workshop!.phase >= PHASE_SUBMISSION &&
 | 
			
		||||
            ((grades && grades.length) || (groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)))">
 | 
			
		||||
            <ion-item-divider class="ion-text-wrap">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2 *ngIf="workshop!.phase == PHASE_SUBMISSION">{{ 'addon.mod_workshop.submissionsreport' | translate }}</h2>
 | 
			
		||||
                    <h2 *ngIf="workshop!.phase > PHASE_SUBMISSION">{{ 'addon.mod_workshop.gradesreport' | translate }}</h2>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item-divider>
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
 | 
			
		||||
                <ion-label id="addon-workshop-groupslabel" *ngIf="groupInfo.separateGroups">
 | 
			
		||||
                    {{ 'core.groupsseparate' | translate }}
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-label id="addon-workshop-groupslabel" *ngIf="groupInfo.visibleGroups">
 | 
			
		||||
                    {{ 'core.groupsvisible' | translate }}
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-select [(ngModel)]="group" (ionChange)="setGroup(group)" aria-labelledby="addon-workshop-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>
 | 
			
		||||
 | 
			
		||||
            <ng-container *ngFor="let submission of grades">
 | 
			
		||||
                <addon-mod-workshop-submission [submission]="submission" [courseId]="workshop!.course" [module]="module"
 | 
			
		||||
                    [workshop]="workshop" [access]="access" summary="true" class="core-as-item">
 | 
			
		||||
                </addon-mod-workshop-submission>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <ion-grid *ngIf="page > 0 || hasNextPage">
 | 
			
		||||
                <ion-row class="ion-align-items-center">
 | 
			
		||||
                    <ion-col *ngIf="page > 0">
 | 
			
		||||
                        <ion-button expand="block" fill="outline" (click)="gotoSubmissionsPage(page! -1)">
 | 
			
		||||
                            <ion-icon name="fas-chevron-left" slot="start"></ion-icon>
 | 
			
		||||
                            {{ 'core.previous' | translate }}
 | 
			
		||||
                        </ion-button>
 | 
			
		||||
                    </ion-col>
 | 
			
		||||
                    <ion-col *ngIf="hasNextPage">
 | 
			
		||||
                        <ion-button expand="block" (click)="gotoSubmissionsPage(page! + 1)">
 | 
			
		||||
                            {{ 'core.next' | translate }}
 | 
			
		||||
                            <ion-icon name="fas-chevron-right" slot="end"></ion-icon>
 | 
			
		||||
                        </ion-button>
 | 
			
		||||
                    </ion-col>
 | 
			
		||||
                </ion-row>
 | 
			
		||||
            </ion-grid>
 | 
			
		||||
        </ion-card>
 | 
			
		||||
    </div>
 | 
			
		||||
</core-loading>
 | 
			
		||||
							
								
								
									
										555
									
								
								src/addons/mod/workshop/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								src/addons/mod/workshop/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,555 @@
 | 
			
		||||
// (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, Input, OnDestroy, OnInit, Optional } from '@angular/core';
 | 
			
		||||
import { Params } from '@angular/router';
 | 
			
		||||
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 { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { ModalController, Platform } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
import { AddonModWorkshopModuleHandlerService } from '../../services/handlers/module';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshopPhase,
 | 
			
		||||
    AddonModWorkshop,
 | 
			
		||||
    AddonModWorkshopData,
 | 
			
		||||
    AddonModWorkshopGetWorkshopAccessInformationWSResponse,
 | 
			
		||||
    AddonModWorkshopPhaseData,
 | 
			
		||||
    AddonModWorkshopGetGradesWSResponse,
 | 
			
		||||
    AddonModWorkshopAssessmentSavedChangedEventData,
 | 
			
		||||
    AddonModWorkshopSubmissionChangedEventData,
 | 
			
		||||
    AddonModWorkshopGradesData,
 | 
			
		||||
    AddonModWorkshopPhaseTaskData,
 | 
			
		||||
    AddonModWorkshopReviewer,
 | 
			
		||||
} from '../../services/workshop';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopHelper,
 | 
			
		||||
    AddonModWorkshopSubmissionAssessmentWithFormData,
 | 
			
		||||
    AddonModWorkshopSubmissionDataWithOfflineData,
 | 
			
		||||
} from '../../services/workshop-helper';
 | 
			
		||||
import { AddonModWorkshopOffline, AddonModWorkshopOfflineSubmission } from '../../services/workshop-offline';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopSyncProvider,
 | 
			
		||||
    AddonModWorkshopSync,
 | 
			
		||||
    AddonModWorkshopAutoSyncData,
 | 
			
		||||
    AddonModWorkshopSyncResult,
 | 
			
		||||
} from '../../services/workshop-sync';
 | 
			
		||||
import { AddonModWorkshopPhaseInfoComponent } from '../phase/phase';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays a workshop index page.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-workshop-index',
 | 
			
		||||
    templateUrl: 'addon-mod-workshop-index.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() group = 0;
 | 
			
		||||
 | 
			
		||||
    component = AddonModWorkshopProvider.COMPONENT;
 | 
			
		||||
    moduleName = 'workshop';
 | 
			
		||||
 | 
			
		||||
    workshop?: AddonModWorkshopData;
 | 
			
		||||
    page = 0;
 | 
			
		||||
    access?: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    phases?: Record<string, AddonModWorkshopPhaseData>;
 | 
			
		||||
    grades: AddonModWorkshopSubmissionDataWithOfflineData[] = [];
 | 
			
		||||
    assessments: AddonModWorkshopSubmissionAssessmentWithFormData[] = [];
 | 
			
		||||
    userGrades?: AddonModWorkshopGetGradesWSResponse;
 | 
			
		||||
    publishedSubmissions: AddonModWorkshopSubmissionDataWithOfflineData[] = [];
 | 
			
		||||
    submission?: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    groupInfo: CoreGroupInfo = {
 | 
			
		||||
        groups: [],
 | 
			
		||||
        separateGroups: false,
 | 
			
		||||
        visibleGroups: false,
 | 
			
		||||
        defaultGroupId: 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    canSubmit = false;
 | 
			
		||||
    showSubmit = false;
 | 
			
		||||
    canAssess = false;
 | 
			
		||||
    hasNextPage = false;
 | 
			
		||||
 | 
			
		||||
    readonly PHASE_SETUP = AddonModWorkshopPhase.PHASE_SETUP;
 | 
			
		||||
    readonly PHASE_SUBMISSION = AddonModWorkshopPhase.PHASE_SUBMISSION;
 | 
			
		||||
    readonly PHASE_ASSESSMENT = AddonModWorkshopPhase.PHASE_ASSESSMENT;
 | 
			
		||||
    readonly PHASE_EVALUATION = AddonModWorkshopPhase.PHASE_EVALUATION;
 | 
			
		||||
    readonly PHASE_CLOSED = AddonModWorkshopPhase.PHASE_CLOSED;
 | 
			
		||||
 | 
			
		||||
    protected offlineSubmissions: AddonModWorkshopOfflineSubmission[] = [];
 | 
			
		||||
    protected obsSubmissionChanged: CoreEventObserver;
 | 
			
		||||
    protected obsAssessmentSaved: CoreEventObserver;
 | 
			
		||||
    protected appResumeSubscription: Subscription;
 | 
			
		||||
    protected syncObserver?: CoreEventObserver;
 | 
			
		||||
    protected syncEventName = AddonModWorkshopSyncProvider.AUTO_SYNCED;
 | 
			
		||||
 | 
			
		||||
    constructor (
 | 
			
		||||
    @Optional() content: IonContent,
 | 
			
		||||
        @Optional() courseContentsPage?: CoreCourseContentsPage,
 | 
			
		||||
    ) {
 | 
			
		||||
        super('AddonModWorkshopIndexComponent', content, courseContentsPage);
 | 
			
		||||
 | 
			
		||||
        // Listen to submission and assessment changes.
 | 
			
		||||
        this.obsSubmissionChanged = CoreEvents.on(AddonModWorkshopProvider.SUBMISSION_CHANGED, (data) => {
 | 
			
		||||
            this.eventReceived(data);
 | 
			
		||||
        }, this.siteId);
 | 
			
		||||
 | 
			
		||||
        // Listen to submission and assessment changes.
 | 
			
		||||
        this.obsAssessmentSaved = CoreEvents.on(AddonModWorkshopProvider.ASSESSMENT_SAVED, (data) => {
 | 
			
		||||
            this.eventReceived(data);
 | 
			
		||||
        }, this.siteId);
 | 
			
		||||
 | 
			
		||||
        // Since most actions will take the user out of the app, we should refresh the view when the app is resumed.
 | 
			
		||||
        this.appResumeSubscription = Platform.resume.subscribe(() => {
 | 
			
		||||
            this.showLoadingAndRefresh(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Refresh workshop on sync.
 | 
			
		||||
        this.syncObserver = CoreEvents.on(AddonModWorkshopSyncProvider.AUTO_SYNCED, (data) => {
 | 
			
		||||
            // Update just when all database is synced.
 | 
			
		||||
            this.eventReceived(data);
 | 
			
		||||
        }, this.siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        super.ngOnInit();
 | 
			
		||||
 | 
			
		||||
        await this.loadContent(false, true);
 | 
			
		||||
        if (!this.workshop) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await AddonModWorkshop.logView(this.workshop.id, this.workshop.name);
 | 
			
		||||
            CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function called when we receive an event of submission changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param data Data received by the event.
 | 
			
		||||
     */
 | 
			
		||||
    protected eventReceived(
 | 
			
		||||
        data: AddonModWorkshopAutoSyncData |
 | 
			
		||||
        AddonModWorkshopSubmissionChangedEventData |
 | 
			
		||||
        AddonModWorkshopAssessmentSavedChangedEventData,
 | 
			
		||||
    ): void {
 | 
			
		||||
        if (this.workshop?.id === data.workshopId) {
 | 
			
		||||
            this.showLoadingAndRefresh(true);
 | 
			
		||||
 | 
			
		||||
            // Check completion since it could be configured to complete once the user adds a new discussion or replies.
 | 
			
		||||
            CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Perform the invalidate content function.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async invalidateContent(): Promise<void> {
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonModWorkshop.invalidateWorkshopData(this.courseId));
 | 
			
		||||
        if (this.workshop) {
 | 
			
		||||
            promises.push(AddonModWorkshop.invalidateWorkshopAccessInformationData(this.workshop.id));
 | 
			
		||||
            promises.push(AddonModWorkshop.invalidateUserPlanPhasesData(this.workshop.id));
 | 
			
		||||
            if (this.canSubmit) {
 | 
			
		||||
                promises.push(AddonModWorkshop.invalidateSubmissionsData(this.workshop.id));
 | 
			
		||||
            }
 | 
			
		||||
            if (this.access?.canviewallsubmissions) {
 | 
			
		||||
                promises.push(AddonModWorkshop.invalidateGradeReportData(this.workshop.id));
 | 
			
		||||
                promises.push(CoreGroups.invalidateActivityAllowedGroups(this.workshop.coursemodule));
 | 
			
		||||
                promises.push(CoreGroups.invalidateActivityGroupMode(this.workshop.coursemodule));
 | 
			
		||||
            }
 | 
			
		||||
            if (this.canAssess) {
 | 
			
		||||
                promises.push(AddonModWorkshop.invalidateReviewerAssesmentsData(this.workshop.id));
 | 
			
		||||
            }
 | 
			
		||||
            promises.push(AddonModWorkshop.invalidateGradesData(this.workshop.id));
 | 
			
		||||
            promises.push(AddonModWorkshop.invalidateWorkshopWSData(this.workshop.id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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: AddonModWorkshopAutoSyncData): boolean {
 | 
			
		||||
        if (this.workshop && syncEventData.workshopId == this.workshop.id) {
 | 
			
		||||
            // Refresh the data.
 | 
			
		||||
            this.content?.scrollToTop();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download feedback contents.
 | 
			
		||||
     *
 | 
			
		||||
     * @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> {
 | 
			
		||||
        try {
 | 
			
		||||
            this.workshop = await AddonModWorkshop.getWorkshop(this.courseId, this.module.id);
 | 
			
		||||
 | 
			
		||||
            this.description = this.workshop.intro;
 | 
			
		||||
            this.dataRetrieved.emit(this.workshop);
 | 
			
		||||
 | 
			
		||||
            if (sync) {
 | 
			
		||||
                // Try to synchronize the feedback.
 | 
			
		||||
                await this.syncActivity(showErrors);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check if there are answers stored in offline.
 | 
			
		||||
            this.access = await AddonModWorkshop.getWorkshopAccessInformation(this.workshop.id, { cmId: this.module.id });
 | 
			
		||||
 | 
			
		||||
            if (this.access.canviewallsubmissions) {
 | 
			
		||||
                this.groupInfo = await CoreGroups.getActivityGroupInfo(this.workshop.coursemodule);
 | 
			
		||||
                this.group = CoreGroups.validateGroupId(this.group, this.groupInfo);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.phases = await AddonModWorkshop.getUserPlanPhases(this.workshop.id, { cmId: this.module.id });
 | 
			
		||||
 | 
			
		||||
            this.phases[this.workshop.phase].tasks.forEach((task) => {
 | 
			
		||||
                if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) {
 | 
			
		||||
                    // Add links to manage examples.
 | 
			
		||||
                    task.link = this.externalUrl!;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Check if there are info stored in offline.
 | 
			
		||||
            this.hasOffline = await AddonModWorkshopOffline.hasWorkshopOfflineData(this.workshop.id);
 | 
			
		||||
            if (this.hasOffline) {
 | 
			
		||||
                this.offlineSubmissions = await AddonModWorkshopOffline.getSubmissions(this.workshop.id);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.offlineSubmissions = [];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await this.setPhaseInfo();
 | 
			
		||||
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.fillContextMenu(refresh);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves and shows submissions grade page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param page Page number to be retrieved.
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async gotoSubmissionsPage(page: number): Promise<void> {
 | 
			
		||||
        const report = await AddonModWorkshop.getGradesReport(this.workshop!.id, {
 | 
			
		||||
            groupId: this.group,
 | 
			
		||||
            page,
 | 
			
		||||
            cmId: this.module.id,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const numEntries = (report && report.grades && report.grades.length) || 0;
 | 
			
		||||
 | 
			
		||||
        this.page = page;
 | 
			
		||||
 | 
			
		||||
        this.hasNextPage = numEntries >= AddonModWorkshopProvider.PER_PAGE && ((this.page + 1) *
 | 
			
		||||
            AddonModWorkshopProvider.PER_PAGE) < report.totalcount;
 | 
			
		||||
 | 
			
		||||
        const grades: AddonModWorkshopGradesData[] = report.grades || [];
 | 
			
		||||
 | 
			
		||||
        this.grades = [];
 | 
			
		||||
 | 
			
		||||
        await Promise.all(grades.map(async (grade) => {
 | 
			
		||||
            const submission: AddonModWorkshopSubmissionDataWithOfflineData = {
 | 
			
		||||
                id: grade.submissionid,
 | 
			
		||||
                workshopid: this.workshop!.id,
 | 
			
		||||
                example: false,
 | 
			
		||||
                authorid: grade.userid,
 | 
			
		||||
                timecreated: grade.submissionmodified,
 | 
			
		||||
                timemodified: grade.submissionmodified,
 | 
			
		||||
                title: grade.submissiontitle,
 | 
			
		||||
                content: '',
 | 
			
		||||
                contenttrust: 0,
 | 
			
		||||
                attachment: 0,
 | 
			
		||||
                grade: grade.submissiongrade,
 | 
			
		||||
                gradeover: grade.submissiongradeover,
 | 
			
		||||
                gradeoverby: grade.submissiongradeoverby,
 | 
			
		||||
                published: !!grade.submissionpublished,
 | 
			
		||||
                gradinggrade: grade.gradinggrade,
 | 
			
		||||
                late: 0,
 | 
			
		||||
                reviewedby: this.parseReviewer(grade.reviewedby),
 | 
			
		||||
                reviewerof: this.parseReviewer(grade.reviewerof),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (this.workshop!.phase == AddonModWorkshopPhase.PHASE_ASSESSMENT) {
 | 
			
		||||
                submission.reviewedbydone = grade.reviewedby?.reduce((a, b) => a + (b.grade ? 1 : 0), 0) || 0;
 | 
			
		||||
                submission.reviewerofdone = grade.reviewerof?.reduce((a, b) => a + (b.grade ? 1 : 0), 0) || 0;
 | 
			
		||||
                submission.reviewedbycount = grade.reviewedby?.length || 0;
 | 
			
		||||
                submission.reviewerofcount = grade.reviewerof?.length || 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const offlineData = await AddonModWorkshopHelper.applyOfflineData(submission, this.offlineSubmissions);
 | 
			
		||||
 | 
			
		||||
            if (typeof offlineData != 'undefined') {
 | 
			
		||||
                this.grades!.push(offlineData);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected parseReviewer(reviewers: AddonModWorkshopReviewer[] = []): AddonModWorkshopSubmissionAssessmentWithFormData[] {
 | 
			
		||||
        return reviewers.map((reviewer: AddonModWorkshopReviewer) => {
 | 
			
		||||
            const parsed: AddonModWorkshopSubmissionAssessmentWithFormData = {
 | 
			
		||||
                grade: reviewer.grade,
 | 
			
		||||
                gradinggrade: reviewer.gradinggrade,
 | 
			
		||||
                gradinggradeover: reviewer.gradinggradeover,
 | 
			
		||||
                id: reviewer.assessmentid,
 | 
			
		||||
                reviewerid: reviewer.userid,
 | 
			
		||||
                submissionid: reviewer.submissionid,
 | 
			
		||||
                weight: reviewer.weight,
 | 
			
		||||
                timecreated: 0,
 | 
			
		||||
                timemodified: 0,
 | 
			
		||||
                feedbackauthor: '',
 | 
			
		||||
                gradinggradeoverby: 0,
 | 
			
		||||
                feedbackattachmentfiles: [],
 | 
			
		||||
                feedbackcontentfiles: [],
 | 
			
		||||
                feedbackauthorattachment: 0,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return parsed;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open task.
 | 
			
		||||
     *
 | 
			
		||||
     * @param task Task to be done.
 | 
			
		||||
     */
 | 
			
		||||
    runTask(task: AddonModWorkshopPhaseTaskData): void {
 | 
			
		||||
        if (task.code == 'submit') {
 | 
			
		||||
            this.gotoSubmit();
 | 
			
		||||
        } else if (task.link) {
 | 
			
		||||
            CoreUtils.openInBrowser(task.link);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to submit page.
 | 
			
		||||
     */
 | 
			
		||||
    gotoSubmit(): void {
 | 
			
		||||
        if (this.canSubmit && ((this.access!.creatingsubmissionallowed && !this.submission) ||
 | 
			
		||||
                (this.access!.modifyingsubmissionallowed && this.submission))) {
 | 
			
		||||
            const params: Params = {
 | 
			
		||||
                module: this.module,
 | 
			
		||||
                access: this.access,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const submissionId = this.submission?.id || 0;
 | 
			
		||||
            CoreNavigator.navigateToSitePath(
 | 
			
		||||
                AddonModWorkshopModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.module.id}/${submissionId}/edit`,
 | 
			
		||||
                { params },
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * View Phase info.
 | 
			
		||||
     */
 | 
			
		||||
    async viewPhaseInfo(): Promise<void> {
 | 
			
		||||
        if (this.phases) {
 | 
			
		||||
            const modal = await ModalController.create({
 | 
			
		||||
                component: AddonModWorkshopPhaseInfoComponent,
 | 
			
		||||
                componentProps: {
 | 
			
		||||
                    phases: CoreUtils.objectToArray(this.phases),
 | 
			
		||||
                    workshopPhase: this.workshop!.phase,
 | 
			
		||||
                    externalUrl: this.externalUrl,
 | 
			
		||||
                    showSubmit: this.showSubmit,
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            await modal.present();
 | 
			
		||||
 | 
			
		||||
            const result = await modal.onDidDismiss();
 | 
			
		||||
            if (result.data === true) {
 | 
			
		||||
                this.gotoSubmit();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set group to see the workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param groupId Group Id.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async setGroup(groupId: number): Promise<void> {
 | 
			
		||||
        this.group = groupId;
 | 
			
		||||
 | 
			
		||||
        await this.gotoSubmissionsPage(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to set current phase information.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async setPhaseInfo(): Promise<void> {
 | 
			
		||||
        this.submission = undefined;
 | 
			
		||||
        this.canAssess = false;
 | 
			
		||||
        this.assessments = [];
 | 
			
		||||
        this.userGrades = undefined;
 | 
			
		||||
        this.publishedSubmissions = [];
 | 
			
		||||
 | 
			
		||||
        this.canSubmit = AddonModWorkshopHelper.canSubmit(
 | 
			
		||||
            this.workshop!,
 | 
			
		||||
            this.access!,
 | 
			
		||||
            this.phases![AddonModWorkshopPhase.PHASE_SUBMISSION].tasks,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.showSubmit = this.workshop!.phase == AddonModWorkshopPhase.PHASE_SUBMISSION && this.canSubmit &&
 | 
			
		||||
            ((this.access!.creatingsubmissionallowed && !this.submission) ||
 | 
			
		||||
                (this.access!.modifyingsubmissionallowed && !!this.submission));
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        if (this.canSubmit) {
 | 
			
		||||
            promises.push(AddonModWorkshopHelper.getUserSubmission(this.workshop!.id, { cmId: this.module.id })
 | 
			
		||||
                .then(async (submission) => {
 | 
			
		||||
                    this.submission = await AddonModWorkshopHelper.applyOfflineData(submission, this.offlineSubmissions);
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.access!.canviewallsubmissions && this.workshop!.phase >= AddonModWorkshopPhase.PHASE_SUBMISSION) {
 | 
			
		||||
            promises.push(this.gotoSubmissionsPage(this.page));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let assessPromise = Promise.resolve();
 | 
			
		||||
 | 
			
		||||
        if (this.workshop!.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT) {
 | 
			
		||||
            this.canAssess = AddonModWorkshopHelper.canAssess(this.workshop!, this.access!);
 | 
			
		||||
 | 
			
		||||
            if (this.canAssess) {
 | 
			
		||||
                assessPromise = AddonModWorkshopHelper.getReviewerAssessments(this.workshop!.id, {
 | 
			
		||||
                    cmId: this.module.id,
 | 
			
		||||
                }).then(async (assessments) => {
 | 
			
		||||
                    await Promise.all(assessments.map(async (assessment) => {
 | 
			
		||||
                        assessment.strategy = this.workshop!.strategy;
 | 
			
		||||
                        if (!this.hasOffline) {
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        try {
 | 
			
		||||
                            const offlineAssessment = await AddonModWorkshopOffline.getAssessment(this.workshop!.id, assessment.id);
 | 
			
		||||
 | 
			
		||||
                            assessment.offline = true;
 | 
			
		||||
                            assessment.timemodified = Math.floor(offlineAssessment.timemodified / 1000);
 | 
			
		||||
                        } catch {
 | 
			
		||||
                            // Ignore errors.
 | 
			
		||||
                        }
 | 
			
		||||
                    }));
 | 
			
		||||
 | 
			
		||||
                    this.assessments = assessments;
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.workshop!.phase == AddonModWorkshopPhase.PHASE_CLOSED) {
 | 
			
		||||
            promises.push(AddonModWorkshop.getGrades(this.workshop!.id, { cmId: this.module.id }).then((grades) => {
 | 
			
		||||
                this.userGrades = grades.submissionlongstrgrade || grades.assessmentlongstrgrade ? grades : undefined;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            if (this.access!.canviewpublishedsubmissions) {
 | 
			
		||||
                promises.push(assessPromise.then(async () => {
 | 
			
		||||
                    const submissions: AddonModWorkshopSubmissionDataWithOfflineData[] =
 | 
			
		||||
                        await AddonModWorkshop.getSubmissions(this.workshop!.id, { cmId: this.module.id });
 | 
			
		||||
 | 
			
		||||
                    this.publishedSubmissions = submissions.filter((submission) => {
 | 
			
		||||
                        if (submission.published) {
 | 
			
		||||
                            submission.reviewedby = [];
 | 
			
		||||
 | 
			
		||||
                            this.assessments.forEach((assessment) => {
 | 
			
		||||
                                if (assessment.submissionid == submission.id) {
 | 
			
		||||
                                    submission.reviewedby!.push(AddonModWorkshopHelper.realGradeValue(this.workshop!, assessment));
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        return false;
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs the sync of the activity.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected sync(): Promise<AddonModWorkshopSyncResult> {
 | 
			
		||||
        return AddonModWorkshopSync.syncWorkshop(this.workshop!.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if sync has succeed from result sync data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param result Data returned on the sync function.
 | 
			
		||||
     * @return If suceed or not.
 | 
			
		||||
     */
 | 
			
		||||
    protected hasSyncSucceed(result: AddonModWorkshopSyncResult): boolean {
 | 
			
		||||
        return result.updated;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        super.ngOnDestroy();
 | 
			
		||||
        this.obsSubmissionChanged?.off();
 | 
			
		||||
        this.obsAssessmentSaved?.off();
 | 
			
		||||
        this.appResumeSubscription?.unsubscribe();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								src/addons/mod/workshop/components/phase/phase.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/addons/mod/workshop/components/phase/phase.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>{{ 'addon.mod_workshop.userplan' | translate }}</ion-title>
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times" slot="icon-only"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-list>
 | 
			
		||||
        <ng-container *ngFor="let phase of phases">
 | 
			
		||||
            <ion-item-divider [class.core-selected-item]="workshopPhase == phase.code">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ phase.title }}</h2>
 | 
			
		||||
                    <p class="ion-text-wrap" *ngIf="workshopPhase == phase.code">
 | 
			
		||||
                        {{ 'addon.mod_workshop.userplancurrentphase' | translate }}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item-divider>
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngIf="phase.switchUrl" [href]="phase.switchUrl" detail="false">
 | 
			
		||||
                <ion-icon slot="start" name="fas-exchange-alt"></ion-icon>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <p>{{ 'addon.mod_workshop.switchphase' + phase.code | translate }}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-icon slot="end" name="fas-external-link-alt"></ion-icon>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
            <ion-item class="ion-text-wrap" *ngFor="let task of phase.tasks"
 | 
			
		||||
                [class.item-dimmed]="phase.code != workshopPhase || (task.code == 'submit' && !showSubmit)"
 | 
			
		||||
                (click)="runTask(task)" detail="false">
 | 
			
		||||
                <ion-icon slot="start" name="far-circle" *ngIf="task.completed == null"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-times-circle" color="danger" *ngIf="task.completed == ''"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-info-circle" color="info" *ngIf="task.completed == 'info'"></ion-icon>
 | 
			
		||||
                <ion-icon slot="start" name="fas-check-circle" color="success" *ngIf="task.completed == '1'"></ion-icon>
 | 
			
		||||
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2 class="ion-text-wrap">{{task.title}}</h2>
 | 
			
		||||
                    <p *ngIf="task.details" [innerHTML]="task.details"></p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-icon slot="end" *ngIf="task.link && task.code != 'submit'" name="fas-external-link-alt"></ion-icon>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </ion-list>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										73
									
								
								src/addons/mod/workshop/components/phase/phase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/addons/mod/workshop/components/phase/phase.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
// (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, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { ModalController } from '@singletons';
 | 
			
		||||
import { AddonModWorkshopPhaseData, AddonModWorkshopPhase, AddonModWorkshopPhaseTaskData } from '../../services/workshop';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the phase info modal.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    templateUrl: 'phase.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopPhaseInfoComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() phases!: AddonModWorkshopPhaseDataWithSwitch[];
 | 
			
		||||
    @Input() workshopPhase!: AddonModWorkshopPhase;
 | 
			
		||||
    @Input() showSubmit = false;
 | 
			
		||||
    @Input() protected externalUrl!: string;
 | 
			
		||||
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
 | 
			
		||||
        // Treat phases.
 | 
			
		||||
        for (const x in this.phases) {
 | 
			
		||||
            this.phases[x].tasks.forEach((task) => {
 | 
			
		||||
                if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) {
 | 
			
		||||
                    // Add links to manage examples.
 | 
			
		||||
                    task.link = this.externalUrl;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            const action = this.phases[x].actions.find((action) => action.url && action.type == 'switchphase');
 | 
			
		||||
            this.phases[x].switchUrl = action ? action.url : '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
     */
 | 
			
		||||
    closeModal(): void {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open task.
 | 
			
		||||
     *
 | 
			
		||||
     * @param task Task to be done.
 | 
			
		||||
     */
 | 
			
		||||
    runTask(task: AddonModWorkshopPhaseTaskData): void {
 | 
			
		||||
        if (task.code == 'submit') {
 | 
			
		||||
            // This will close the modal and go to the submit.
 | 
			
		||||
            ModalController.dismiss(true);
 | 
			
		||||
        } else if (task.link) {
 | 
			
		||||
            CoreUtils.openInBrowser(task.link);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddonModWorkshopPhaseDataWithSwitch = AddonModWorkshopPhaseData & {
 | 
			
		||||
    switchUrl?: string;
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,108 @@
 | 
			
		||||
<core-loading [hideUntil]="loaded">
 | 
			
		||||
    <div *ngIf="!summary">
 | 
			
		||||
        <ion-item class="ion-text-wrap addon-workshop-submission-title">
 | 
			
		||||
            <core-user-avatar [user]="profile" [courseId]="courseId" [userId]="profile?.id" slot="start">
 | 
			
		||||
            </core-user-avatar>
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2>
 | 
			
		||||
                    <core-format-text [text]="submission.title" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                        [courseId]="courseId">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </h2>
 | 
			
		||||
                <p *ngIf="profile && profile?.fullname">{{profile.fullname}}</p>
 | 
			
		||||
                <p *ngIf="showGrade(submission.grade)"
 | 
			
		||||
                    [class.addon-has-overriden-grade]="showGrade(submission.gradeover)">
 | 
			
		||||
                    {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.grade}}
 | 
			
		||||
                </p>
 | 
			
		||||
                <p *ngIf="showGrade(submission.gradeover)" class="addon-overriden-grade">
 | 
			
		||||
                    {{ 'addon.mod_workshop.gradeover' | translate }}: {{submission.gradeover}}
 | 
			
		||||
                </p>
 | 
			
		||||
                <p *ngIf="access.canviewallsubmissions && showGrade(submission.gradinggrade)">
 | 
			
		||||
                    {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{submission.gradinggrade}}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
            <ion-note slot="end" *ngIf="!submission.timemodified">
 | 
			
		||||
                <ion-icon name="fas-clock"></ion-icon> {{ 'core.notsent' | translate }}
 | 
			
		||||
            </ion-note>
 | 
			
		||||
            <ion-note slot="end" *ngIf="submission.timemodified">
 | 
			
		||||
                {{submission.timemodified | coreDateDayOrTime}}
 | 
			
		||||
                <ng-container *ngIf="submission.offline">
 | 
			
		||||
                    <ion-icon name="fas-clock"></ion-icon> {{ 'core.notsent' | translate }}
 | 
			
		||||
                </ng-container>
 | 
			
		||||
                <ng-container *ngIf="submission.deleted">
 | 
			
		||||
                    <ion-icon name="fas-trash"></ion-icon> {{ 'core.deletedoffline' | translate }}
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ion-note>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="submission.content">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <core-format-text [component]="component" [componentId]="componentId" [text]="submission.content"
 | 
			
		||||
                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <core-files [files]="submission.attachmentfiles" [component]="component" [componentId]="componentId"></core-files>
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="viewDetails && submission.feedbackauthor">
 | 
			
		||||
            <core-user-avatar *ngIf="evaluateByProfile" [user]="evaluateByProfile" slot="start" [courseId]="courseId"
 | 
			
		||||
                [userId]="evaluateByProfile.id"></core-user-avatar>
 | 
			
		||||
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2 *ngIf="evaluateByProfile && evaluateByProfile.fullname">
 | 
			
		||||
                    {{ 'addon.mod_workshop.feedbackby' | translate : {$a: evaluateByProfile.fullname} }}
 | 
			
		||||
                </h2>
 | 
			
		||||
                <core-format-text [text]="submission.feedbackauthor" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                    [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <ion-item *ngIf="viewDetails">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <ion-button expand="block" (click)="gotoSubmission()">
 | 
			
		||||
                    {{ 'core.showmore' | translate }}
 | 
			
		||||
                    <ion-icon name="fas-chevron-right" slot="end"></ion-icon>
 | 
			
		||||
                </ion-button>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <ion-item class="ion-text-wrap" *ngIf="summary" [detail]="submission.timemodified" (click)="gotoSubmission()">
 | 
			
		||||
        <core-user-avatar [user]="profile" slot="start" [courseId]="courseId" [userId]="profile?.id">
 | 
			
		||||
        </core-user-avatar>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <h2>
 | 
			
		||||
                <core-format-text [text]="submission.title" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                    [courseId]="courseId">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </h2>
 | 
			
		||||
            <p *ngIf="profile && profile.fullname">{{profile.fullname}}</p>
 | 
			
		||||
            <p *ngIf="submission.reviewedbydone">
 | 
			
		||||
                {{ 'addon.mod_workshop.receivedgrades' | translate }}: {{submission.reviewedbydone}} / {{submission.reviewedbycount}}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p *ngIf="submission.reviewerofdone">
 | 
			
		||||
                {{ 'addon.mod_workshop.givengrades' | translate }}: {{submission.reviewerofdone}} / {{submission.reviewerofcount}}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p *ngIf="!showGrade(submission.gradeover) && showGrade(submission.grade)">
 | 
			
		||||
                {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.grade}}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p *ngIf="showGrade(submission.gradeover)" class="addon-overriden-grade">
 | 
			
		||||
                {{ 'addon.mod_workshop.submissiongradeof' | translate:{$a: workshop.grade } }}: {{submission.gradeover}}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p *ngIf="access.canviewallsubmissions && showGrade(submission.gradinggrade)">
 | 
			
		||||
                {{ 'addon.mod_workshop.gradinggradeof' | translate:{$a: workshop.gradinggrade } }}: {{submission.gradinggrade}}
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
            <ion-badge *ngIf="assessment && (showGrade(assessment.grade) || assessment.offline)" color="success">
 | 
			
		||||
                {{ 'addon.mod_workshop.assessedsubmission' | translate }}
 | 
			
		||||
            </ion-badge>
 | 
			
		||||
            <ion-badge *ngIf="assessment && !showGrade(assessment.grade) && !assessment.offline" color="danger">
 | 
			
		||||
                {{ 'addon.mod_workshop.notassessed' | translate }}
 | 
			
		||||
            </ion-badge>
 | 
			
		||||
 | 
			
		||||
        </ion-label>
 | 
			
		||||
        <ion-note slot="end" *ngIf="submission.timemodified">
 | 
			
		||||
            {{submission.timemodified | coreDateDayOrTime}}
 | 
			
		||||
            <div *ngIf="offline"><ion-icon name="fas-clock"></ion-icon> {{ 'core.notsent' | translate }}</div>
 | 
			
		||||
            <div *ngIf="submission.deleted"><ion-icon name="fas-trash"></ion-icon> {{ 'core.deletedoffline' | translate }}</div>
 | 
			
		||||
        </ion-note>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
</core-loading>
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
:host {
 | 
			
		||||
  p.addon-overriden-grade {
 | 
			
		||||
    color: var(--ion-color-success);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  p.addon-has-overriden-grade {
 | 
			
		||||
    color: var(--ion-color-danger);
 | 
			
		||||
    text-decoration: line-through;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								src/addons/mod/workshop/components/submission/submission.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/addons/mod/workshop/components/submission/submission.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,138 @@
 | 
			
		||||
// (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, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { Params } from '@angular/router';
 | 
			
		||||
import { CoreCourseModule } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { AddonModWorkshopSubmissionPage } from '../../pages/submission/submission';
 | 
			
		||||
import { AddonModWorkshopModuleHandlerService } from '../../services/handlers/module';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshopPhase,
 | 
			
		||||
    AddonModWorkshopData,
 | 
			
		||||
    AddonModWorkshopGetWorkshopAccessInformationWSResponse,
 | 
			
		||||
} from '../../services/workshop';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopHelper,
 | 
			
		||||
    AddonModWorkshopSubmissionAssessmentWithFormData,
 | 
			
		||||
    AddonModWorkshopSubmissionDataWithOfflineData,
 | 
			
		||||
} from '../../services/workshop-helper';
 | 
			
		||||
import { AddonModWorkshopOffline } from '../../services/workshop-offline';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays workshop submission.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'addon-mod-workshop-submission',
 | 
			
		||||
    templateUrl: 'addon-mod-workshop-submission.html',
 | 
			
		||||
    styleUrls: ['submission.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopSubmissionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() submission!: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    @Input() module!: CoreCourseModule;
 | 
			
		||||
    @Input() workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input() access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input() assessment?: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input() summary = false;
 | 
			
		||||
 | 
			
		||||
    component = AddonModWorkshopProvider.COMPONENT;
 | 
			
		||||
    componentId?: number;
 | 
			
		||||
    userId: number;
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    offline = false;
 | 
			
		||||
    viewDetails = false;
 | 
			
		||||
    profile?: CoreUserProfile;
 | 
			
		||||
    showGrade: (grade?: number|string) => boolean;
 | 
			
		||||
    evaluateByProfile?: CoreUserProfile;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.userId = CoreSites.getCurrentSiteUserId();
 | 
			
		||||
        this.showGrade = AddonModWorkshopHelper.showGrade;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.componentId = this.module.instance;
 | 
			
		||||
        this.userId = this.submission.authorid || this.userId;
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        this.offline = !!this.submission?.offline || !!this.assessment?.offline;
 | 
			
		||||
 | 
			
		||||
        if (this.submission.id) {
 | 
			
		||||
            promises.push(AddonModWorkshopOffline.getEvaluateSubmission(this.workshop.id, this.submission.id)
 | 
			
		||||
                .then((offlineSubmission) => {
 | 
			
		||||
                    this.submission.gradeover = parseInt(offlineSubmission.gradeover, 10);
 | 
			
		||||
                    this.offline = true;
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }).catch(() => {
 | 
			
		||||
                    // Ignore errors.
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.userId) {
 | 
			
		||||
            promises.push(CoreUser.getProfile(this.userId, this.courseId, true).then((profile) => {
 | 
			
		||||
                this.profile = profile;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.viewDetails = !this.summary && this.workshop.phase == AddonModWorkshopPhase.PHASE_CLOSED &&
 | 
			
		||||
            CoreNavigator.getCurrentRoute().component != AddonModWorkshopSubmissionPage;
 | 
			
		||||
 | 
			
		||||
        if (this.viewDetails && this.submission.gradeoverby) {
 | 
			
		||||
            promises.push(CoreUser.getProfile(this.submission.gradeoverby, this.courseId, true).then((profile) => {
 | 
			
		||||
                this.evaluateByProfile = profile;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Promise.all(promises).finally(() => {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Navigate to the submission.
 | 
			
		||||
     */
 | 
			
		||||
    gotoSubmission(): void {
 | 
			
		||||
        if (this.submission.timemodified) {
 | 
			
		||||
            const params: Params = {
 | 
			
		||||
                module: this.module,
 | 
			
		||||
                workshop: this.workshop,
 | 
			
		||||
                access: this.access,
 | 
			
		||||
                profile: this.profile,
 | 
			
		||||
                submission: this.submission,
 | 
			
		||||
                assessment: this.assessment,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            CoreNavigator.navigateToSitePath(
 | 
			
		||||
                AddonModWorkshopModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.module.id}/${this.submission.id}`,
 | 
			
		||||
                { params },
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								src/addons/mod/workshop/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/addons/mod/workshop/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
{
 | 
			
		||||
    "alreadygraded": "Already graded",
 | 
			
		||||
    "areainstructauthors": "Instructions for submission",
 | 
			
		||||
    "areainstructreviewers": "Instructions for assessment",
 | 
			
		||||
    "assess": "Assess",
 | 
			
		||||
    "assessedsubmission": "Assessed submission",
 | 
			
		||||
    "assessmentform": "Assessment form",
 | 
			
		||||
    "assessmentsettings": "Assessment settings",
 | 
			
		||||
    "assessmentstrategynotsupported": "Assessment strategy {{$a}} not supported",
 | 
			
		||||
    "assessmentweight": "Assessment weight",
 | 
			
		||||
    "assignedassessments": "Assigned submissions to assess",
 | 
			
		||||
    "assignedassessmentsnone": "You have no assigned submission to assess",
 | 
			
		||||
    "conclusion": "Conclusion",
 | 
			
		||||
    "createsubmission": "Add submission",
 | 
			
		||||
    "deletesubmission": "Delete submission",
 | 
			
		||||
    "editsubmission": "Edit submission",
 | 
			
		||||
    "feedbackauthor": "Feedback for the author",
 | 
			
		||||
    "feedbackby": "Feedback by {{$a}}",
 | 
			
		||||
    "feedbackreviewer": "Feedback for the reviewer",
 | 
			
		||||
    "givengrades": "Grades given",
 | 
			
		||||
    "gradecalculated": "Calculated grade for submission",
 | 
			
		||||
    "gradeinfo": "Grade: {{$a.received}} of {{$a.max}}",
 | 
			
		||||
    "gradeover": "Override grade for submission",
 | 
			
		||||
    "gradesreport": "Workshop grades report",
 | 
			
		||||
    "gradinggrade": "Grade for assessment",
 | 
			
		||||
    "gradinggradecalculated": "Calculated grade for assessment",
 | 
			
		||||
    "gradinggradeof": "Grade for assessment (of {{$a}})",
 | 
			
		||||
    "gradinggradeover": "Override grade for assessment",
 | 
			
		||||
    "modulenameplural": "Workshops",
 | 
			
		||||
    "nogradeyet": "No grade yet",
 | 
			
		||||
    "notassessed": "Not assessed yet",
 | 
			
		||||
    "notoverridden": "Not overridden",
 | 
			
		||||
    "noyoursubmission": "You have not submitted your work yet",
 | 
			
		||||
    "overallfeedback": "Overall feedback",
 | 
			
		||||
    "publishedsubmissions": "Published submissions",
 | 
			
		||||
    "publishsubmission": "Publish submission",
 | 
			
		||||
    "publishsubmission_help": "Published submissions are available to the others when the workshop is closed.",
 | 
			
		||||
    "reassess": "Re-assess",
 | 
			
		||||
    "receivedgrades": "Grades received",
 | 
			
		||||
    "submissionattachment": "Attachment",
 | 
			
		||||
    "submissioncontent": "Submission content",
 | 
			
		||||
    "submissiondeleteconfirm": "Are you sure you want to delete the following submission?",
 | 
			
		||||
    "submissiongrade": "Grade for submission",
 | 
			
		||||
    "submissiongradeof": "Grade for submission (of {{$a}})",
 | 
			
		||||
    "submissionrequiredcontent": "You need to enter some text or add a file.",
 | 
			
		||||
    "submissionrequiredtitle": "You need to enter a title.",
 | 
			
		||||
    "submissionsreport": "Workshop submissions report",
 | 
			
		||||
    "submissiontitle": "Title",
 | 
			
		||||
    "switchphase10": "Switch to the setup phase",
 | 
			
		||||
    "switchphase20": "Switch to the submission phase",
 | 
			
		||||
    "switchphase30": "Switch to the assessment phase",
 | 
			
		||||
    "switchphase40": "Switch to the evaluation phase",
 | 
			
		||||
    "switchphase50": "Close workshop",
 | 
			
		||||
    "userplan": "Workshop planner",
 | 
			
		||||
    "userplancurrentphase": "Current phase",
 | 
			
		||||
    "warningassessmentmodified": "The submission was modified on the site.",
 | 
			
		||||
    "warningsubmissionmodified": "The assessment was modified on the site.",
 | 
			
		||||
    "weightinfo": "Weight: {{$a}}",
 | 
			
		||||
    "yourassessment": "Your assessment",
 | 
			
		||||
    "yourassessmentfor": "Your assessment for {{$a}}",
 | 
			
		||||
    "yourgrades": "Your grades",
 | 
			
		||||
    "yoursubmission": "Your submission"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/addons/mod/workshop/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/addons/mod/workshop/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]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
 | 
			
		||||
    <addon-mod-workshop-index [module]="module" [courseId]="courseId" [group]="selectedGroup" (dataRetrieved)="updateData($event)">
 | 
			
		||||
    </addon-mod-workshop-index>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										41
									
								
								src/addons/mod/workshop/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/addons/mod/workshop/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
// (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 { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { AddonModWorkshopIndexComponent } from '../../components/index/index';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays a workshop.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-mod-workshop-index',
 | 
			
		||||
    templateUrl: 'index.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopIndexPage extends CoreCourseModuleMainActivityPage<AddonModWorkshopIndexComponent> implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(AddonModWorkshopIndexComponent) activityComponent?: AddonModWorkshopIndexComponent;
 | 
			
		||||
 | 
			
		||||
    selectedGroup = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        super.ngOnInit();
 | 
			
		||||
        this.selectedGroup = CoreNavigator.getRouteNumberParam('group') || 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								src/addons/mod/workshop/workshop-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/addons/mod/workshop/workshop-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
// (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 { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { CanLeaveGuard } from '@guards/can-leave';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonModWorkshopIndexPage } from './pages/index/index';
 | 
			
		||||
import { AddonModWorkshopComponentsModule } from './components/components.module';
 | 
			
		||||
import { AddonModWorkshopSubmissionPage } from './pages/submission/submission';
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
import { AddonModWorkshopAssessmentPage } from './pages/assessment/assessment';
 | 
			
		||||
import { AddonModWorkshopEditSubmissionPage } from './pages/edit-submission/edit-submission';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId',
 | 
			
		||||
        component: AddonModWorkshopIndexPage,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/:submissionId',
 | 
			
		||||
        component: AddonModWorkshopSubmissionPage,
 | 
			
		||||
        canDeactivate: [CanLeaveGuard],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/:submissionId/edit', // @todo
 | 
			
		||||
        component: AddonModWorkshopEditSubmissionPage,
 | 
			
		||||
        canDeactivate: [CanLeaveGuard],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: ':courseId/:cmId/:submissionId/:assessmentId',
 | 
			
		||||
        component: AddonModWorkshopAssessmentPage,
 | 
			
		||||
        canDeactivate: [CanLeaveGuard],
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        AddonModWorkshopComponentsModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonModWorkshopIndexPage,
 | 
			
		||||
        AddonModWorkshopSubmissionPage,
 | 
			
		||||
        AddonModWorkshopAssessmentPage,
 | 
			
		||||
        AddonModWorkshopEditSubmissionPage,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopLazyModule {}
 | 
			
		||||
@ -67,12 +67,12 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
    prefetchStatus?: string; // Used when calling fillContextMenu.
 | 
			
		||||
    prefetchText?: string; // Used when calling fillContextMenu.
 | 
			
		||||
    size?: string; // Used when calling fillContextMenu.
 | 
			
		||||
    isDestroyed?: boolean; // Whether the component is destroyed, used when calling fillContextMenu.
 | 
			
		||||
    isDestroyed = false; // Whether the component is destroyed, used when calling fillContextMenu.
 | 
			
		||||
    contextMenuStatusObserver?: CoreEventObserver; // Observer of package status, used when calling fillContextMenu.
 | 
			
		||||
    contextFileStatusObserver?: CoreEventObserver; // Observer of file status, used when calling fillContextMenu.
 | 
			
		||||
 | 
			
		||||
    protected fetchContentDefaultError = 'core.course.errorgetmodule'; // Default error to show when loading contents.
 | 
			
		||||
    protected isCurrentView?: boolean; // Whether the component is in the current view.
 | 
			
		||||
    protected isCurrentView = false; // Whether the component is in the current view.
 | 
			
		||||
    protected siteId?: string; // Current Site ID.
 | 
			
		||||
    protected statusObserver?: CoreEventObserver; // Observer of package status. Only if setStatusListener is called.
 | 
			
		||||
    protected currentStatus?: string; // The current status of the module. Only if setStatusListener is called.
 | 
			
		||||
 | 
			
		||||
@ -306,20 +306,16 @@ export class CoreGradesHelperProvider {
 | 
			
		||||
     * @param selectedGrade Selected grade value.
 | 
			
		||||
     * @return Selected grade label.
 | 
			
		||||
     */
 | 
			
		||||
    getGradeLabelFromValue(grades: CoreGradesMenuItem[], selectedGrade: number): string {
 | 
			
		||||
    getGradeLabelFromValue(grades: CoreGradesMenuItem[], selectedGrade?: number): string {
 | 
			
		||||
        selectedGrade = Number(selectedGrade);
 | 
			
		||||
 | 
			
		||||
        if (!grades || !selectedGrade || selectedGrade <= 0) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (const x in grades) {
 | 
			
		||||
            if (grades[x].value == selectedGrade) {
 | 
			
		||||
                return grades[x].label;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const grade = grades.find((grade) => grade.value == selectedGrade);
 | 
			
		||||
 | 
			
		||||
        return '';
 | 
			
		||||
        return grade ? grade.label : '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -633,31 +629,35 @@ export class CoreGradesHelperProvider {
 | 
			
		||||
     * @param scale Scale csv list String. If not provided, it will take it from the module grade info.
 | 
			
		||||
     * @return Array with objects with value and label to create a propper HTML select.
 | 
			
		||||
     */
 | 
			
		||||
    makeGradesMenu(
 | 
			
		||||
        gradingType: number,
 | 
			
		||||
    async makeGradesMenu(
 | 
			
		||||
        gradingType?: number,
 | 
			
		||||
        moduleId?: number,
 | 
			
		||||
        defaultLabel: string = '',
 | 
			
		||||
        defaultValue: string | number = '',
 | 
			
		||||
        scale?: string,
 | 
			
		||||
    ): Promise<CoreGradesMenuItem[]> {
 | 
			
		||||
        if (typeof gradingType == 'undefined') {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (gradingType < 0) {
 | 
			
		||||
            if (scale) {
 | 
			
		||||
                return Promise.resolve(CoreUtils.makeMenuFromList(scale, defaultLabel, undefined, defaultValue));
 | 
			
		||||
            } else if (moduleId) {
 | 
			
		||||
                return CoreCourse.getModuleBasicGradeInfo(moduleId).then((gradeInfo) => {
 | 
			
		||||
                    if (gradeInfo && gradeInfo.scale) {
 | 
			
		||||
                        return CoreUtils.makeMenuFromList(gradeInfo.scale, defaultLabel, undefined,  defaultValue);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return [];
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                return Promise.resolve([]);
 | 
			
		||||
                return CoreUtils.makeMenuFromList(scale, defaultLabel, undefined, defaultValue);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (moduleId) {
 | 
			
		||||
                const gradeInfo = await CoreCourse.getModuleBasicGradeInfo(moduleId);
 | 
			
		||||
                if (gradeInfo && gradeInfo.scale) {
 | 
			
		||||
                    return CoreUtils.makeMenuFromList(gradeInfo.scale, defaultLabel, undefined, defaultValue);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (gradingType > 0) {
 | 
			
		||||
            const grades: CoreGradesMenuItem[] = [];
 | 
			
		||||
 | 
			
		||||
            if (defaultLabel) {
 | 
			
		||||
                // Key as string to avoid resorting of the object.
 | 
			
		||||
                grades.push({
 | 
			
		||||
@ -665,6 +665,7 @@ export class CoreGradesHelperProvider {
 | 
			
		||||
                    value: defaultValue,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (let i = gradingType; i >= 0; i--) {
 | 
			
		||||
                grades.push({
 | 
			
		||||
                    label: i + ' / ' + gradingType,
 | 
			
		||||
@ -672,10 +673,10 @@ export class CoreGradesHelperProvider {
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Promise.resolve(grades);
 | 
			
		||||
            return grades;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Promise.resolve([]);
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ export class CoreForms {
 | 
			
		||||
     * @param form Form element.
 | 
			
		||||
     * @param siteId The site affected. If not provided, no site affected.
 | 
			
		||||
     */
 | 
			
		||||
    static triggerFormCancelledEvent(formRef: ElementRef | HTMLFormElement | undefined, siteId?: string): void {
 | 
			
		||||
    static triggerFormCancelledEvent(formRef?: ElementRef | HTMLFormElement | undefined, siteId?: string): void {
 | 
			
		||||
        if (!formRef) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -81,7 +81,7 @@ export class CoreForms {
 | 
			
		||||
     * @param online Whether the action was done in offline or not.
 | 
			
		||||
     * @param siteId The site affected. If not provided, no site affected.
 | 
			
		||||
     */
 | 
			
		||||
    static triggerFormSubmittedEvent(formRef: ElementRef | HTMLFormElement | undefined, online?: boolean, siteId?: string): void {
 | 
			
		||||
    static triggerFormSubmittedEvent(formRef?: ElementRef | HTMLFormElement | undefined, online?: boolean, siteId?: string): void {
 | 
			
		||||
        if (!formRef) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -275,6 +275,11 @@ ion-toolbar {
 | 
			
		||||
            color: $base;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ion-icon.ion-color-#{$color-name} {
 | 
			
		||||
        color: $base;
 | 
			
		||||
        --ion-color-base: #{$base};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Avatar
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user