MOBILE-2354 workshop: Index page
This commit is contained in:
		
							parent
							
								
									3652d0591d
								
							
						
					
					
						commit
						0591c35a0b
					
				
							
								
								
									
										45
									
								
								src/addon/mod/workshop/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/addon/mod/workshop/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { IonicModule } from 'ionic-angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { CoreCourseComponentsModule } from '@core/course/components/components.module'; | ||||
| import { AddonModWorkshopIndexComponent } from './index/index'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         AddonModWorkshopIndexComponent | ||||
|     ], | ||||
|     imports: [ | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|         CoreCourseComponentsModule | ||||
|     ], | ||||
|     providers: [ | ||||
|     ], | ||||
|     exports: [ | ||||
|         AddonModWorkshopIndexComponent | ||||
|     ], | ||||
|     entryComponents: [ | ||||
|         AddonModWorkshopIndexComponent | ||||
|     ] | ||||
| }) | ||||
| export class AddonModWorkshopComponentsModule {} | ||||
							
								
								
									
										176
									
								
								src/addon/mod/workshop/components/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/addon/mod/workshop/components/index/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| <!-- Buttons to add to the header. --> | ||||
| <core-navbar-buttons end> | ||||
|     <core-context-menu> | ||||
|         <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></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()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="size" [priority]="400" [content]="size" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| 
 | ||||
| <!-- Content. --> | ||||
| <core-loading [hideUntil]="loaded" class="core-loading-center"> | ||||
|     <core-course-module-description *ngIf="description && selectedPhase == workshopPhases.PHASE_SETUP" [description]="description" [component]="component" [componentId]="componentId"></core-course-module-description> | ||||
| 
 | ||||
|     <ion-card *ngIf="phases"> | ||||
|         <ion-item (click)="selectPhase()"> | ||||
|             <h2 stacked text-wrap>{{ phases[selectedPhase].title }}</h2> | ||||
|             <p text-wrap *ngIf="phases[selectedPhase].code == workshop.phase">{{ 'addon.mod_workshop.userplancurrentphase' | translate }}</p> | ||||
|             <ion-icon item-end name="arrow-dropdown"></ion-icon> | ||||
|         </ion-item> | ||||
|         <a ion-item text-wrap *ngIf="phases[selectedPhase].switchUrl" [href]="phases[selectedPhase].switchUrl" detail-none> | ||||
|             <ion-icon item-start name="swap"></ion-icon> | ||||
|             {{ 'addon.mod_workshop.switchphase' + selectedPhase | translate }} | ||||
|             <ion-icon item-end name="open"></ion-icon> | ||||
|         </a> | ||||
|     </ion-card> | ||||
| 
 | ||||
|     <ion-card *ngIf="phases && phases[selectedPhase] && phases[selectedPhase].tasks.length"> | ||||
|         <ion-item text-wrap *ngFor="let task of phases[selectedPhase].tasks" [ngClass]="{'item-dimmed': selectedPhase != workshop.phase}" (click)="runTask(task)" detail-none> | ||||
|             <ion-icon item-start name="radio-button-off" *ngIf="task.completed == null"></ion-icon> | ||||
|             <ion-icon item-start name="close-circle" color="danger" *ngIf="task.completed == ''"></ion-icon> | ||||
|             <ion-icon item-start name="information-circle" color="info" *ngIf="task.completed == 'info'"></ion-icon> | ||||
|             <ion-icon item-start name="checkmark-circle" color="success" *ngIf="task.completed == '1'"></ion-icon> | ||||
| 
 | ||||
|             <h2>{{task.title}}</h2> | ||||
|             <p *ngIf="task.details"><core-format-text [text]="task.details"></core-format-text></p> | ||||
|             <ion-icon item-end *ngIf="task.link && !task.support" name="open"></ion-icon> | ||||
|         </ion-item> | ||||
|     </ion-card> | ||||
| 
 | ||||
|     <!-- Has something offline. --> | ||||
|     <div class="core-warning-card" icon-start *ngIf="hasOffline"> | ||||
|         <ion-icon name="warning"></ion-icon> | ||||
|         {{ 'core.hasdatatosync' | translate: {$a: moduleName} }} | ||||
|     </div> | ||||
| 
 | ||||
|     <div *ngIf="access && workshop && workshop.phase >= selectedPhase"> | ||||
|         <!-- SUBMISSION PHASE --> | ||||
|         <ng-container *ngIf="selectedPhase == workshopPhases.PHASE_SUBMISSION"> | ||||
|             <ion-card *ngIf="workshop.instructauthors"> | ||||
|                 <ion-item text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h2> | ||||
|                     <core-format-text fullOnClick="true" [component]="component" [componentId]="workshop.cmid" [text]="workshop.instructauthors"></core-format-text> | ||||
|                 </ion-item> | ||||
|             </ion-card> | ||||
| 
 | ||||
|             <ion-card *ngIf="canSubmit"> | ||||
|                 <ion-item text-wrap *ngIf="!submission"> | ||||
|                     <h2>{{ 'addon.mod_workshop.yoursubmission' | translate }}</h2> | ||||
|                     <p>{{ 'addon.mod_workshop.noyoursubmission' | translate }}</p> | ||||
|                 </ion-item> | ||||
| 
 | ||||
|                 <!-- <addon-mod-workshop-submission *ngIf="submission" [submission]="submission" [courseId]="workshop.course" [module]="module" [workshop]="workshop" [access]="access"></addon-mod-workshop-submission> --> | ||||
|             </ion-card> | ||||
| 
 | ||||
|             <!-- Show only on current phase --> | ||||
|             <ng-container *ngIf="workshop.phase == selectedPhase"> | ||||
|                 <ion-item text-wrap *ngIf="canSubmit && ((access.creatingsubmissionallowed && !submission) || (access.modifyingsubmissionallowed && submission))"> | ||||
|                     <button ion-button icon-start block *ngIf="access.creatingsubmissionallowed && !submission" (click)="runTaskByCode('submit')"> | ||||
|                         <ion-icon name="add"></ion-icon> | ||||
|                         {{ 'addon.mod_workshop.createsubmission' | translate }} | ||||
|                     </button> | ||||
|                     <button ion-button icon-start block *ngIf="access.modifyingsubmissionallowed && submission" (click)="runTaskByCode('submit')"> | ||||
|                         <ion-icon name="create"></ion-icon> | ||||
|                         {{ 'addon.mod_workshop.editsubmission' | translate }} | ||||
|                     </button> | ||||
|                 </ion-item> | ||||
|             </ng-container> | ||||
|         </ng-container> | ||||
| 
 | ||||
|         <!-- ASSESSMENT PHASE --> | ||||
|         <ng-container *ngIf="selectedPhase == workshopPhases.PHASE_ASSESSMENT"> | ||||
|             <ion-card *ngIf="workshop.instructreviewers"> | ||||
|                 <ion-item text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h2> | ||||
|                     <core-format-text fullOnClick="true" [component]="component" [componentId]="workshop.cmid" [text]="workshop.instructreviewers"></core-format-text> | ||||
|                 </ion-item> | ||||
|             </ion-card> | ||||
| 
 | ||||
|             <ion-card *ngIf="canAssess && assessments.length"> | ||||
|                 <ion-item text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.assignedassessments' | translate }}</h2> | ||||
|                 </ion-item> | ||||
|                 <!-- <addon-mod-workshop-submission *ngFor="let assessment of assessments" [submission]="assessment.submission" [assessment]="assessment" [courseId]="workshop.course" [module]="module" [workshop]="workshop" [access]="access" summary="true"></addon-mod-workshop-submission> --> | ||||
|             </ion-card > | ||||
|         </ng-container> | ||||
| 
 | ||||
|         <ion-card *ngIf="!access.canviewallsubmissions && selectedPhase == workshop.phase && (canSubmit || canAssess) && selectedPhase == workshopPhases.PHASE_EVALUATION"> | ||||
|             <ion-item text-wrap *ngIf="submission" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push> | ||||
|                 <h2>{{ 'addon.mod_workshop.yoursubmission' | translate }}</h2> | ||||
|             </ion-item> | ||||
|             <ion-item text-wrap *ngIf="canAssess" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push> | ||||
|                 <h2>{{ 'addon.mod_workshop.assignedassessments' | translate }}</h2> | ||||
|             </ion-item> | ||||
|         </ion-card> | ||||
| 
 | ||||
|         <!-- CLOSED PHASE --> | ||||
|         <ng-container *ngIf="selectedPhase == workshopPhases.PHASE_CLOSED"> | ||||
|             <ion-card *ngIf="workshop.conclusion"> | ||||
|                 <ion-item text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.conclusion' | translate }}</h2> | ||||
|                     <core-format-text fullOnClick="true" [component]="component" [componentId]="workshop.cmid" [text]="workshop.conclusion"></core-format-text> | ||||
|                 </ion-item> | ||||
|             </ion-card> | ||||
| 
 | ||||
|             <ion-card *ngIf="userGrades"> | ||||
|                 <ion-item-divider color="light" text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.yourgrades' | translate }}</h2> | ||||
|                 </ion-item-divider> | ||||
|                 <ion-item text-wrap *ngIf="userGrades.submissionlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push> | ||||
|                     <h2>{{ 'addon.mod_workshop.submissiongrade' | translate }}</h2> | ||||
|                     <core-format-text [text]="userGrades.submissionlongstrgrade"></core-format-text> | ||||
|                 </ion-item> | ||||
|                 <ion-item text-wrap *ngIf="userGrades.assessmentlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push> | ||||
|                     <h2>{{ 'addon.mod_workshop.gradinggrade' | translate }}</h2> | ||||
|                     <core-format-text [text]="userGrades.assessmentlongstrgrade"></core-format-text> | ||||
|                 </ion-item> | ||||
|             </ion-card> | ||||
| 
 | ||||
|             <ion-card *ngIf="publishedSubmissions.length"> | ||||
|                 <ion-item text-wrap> | ||||
|                     <h2>{{ 'addon.mod_workshop.publishedsubmissions' | translate }}</h2> | ||||
|                 </ion-item> | ||||
|                 <!-- <addon-mod-workshop-submission *ngFor="let submission of publishedSubmissions" [submission]="submission" [courseId]="workshop.course" [module]="module" [workshop]="workshop" [access]="access" summary="true"></addon-mod-workshop-submission> --> | ||||
|             </ion-card> | ||||
|         </ng-container> | ||||
| 
 | ||||
|         <!-- MULTIPLE PHASES SUBMISSION OR GREATER only teachers --> | ||||
|         <ion-card *ngIf="workshop.phase == selectedPhase && access.canviewallsubmissions && selectedPhase >= workshopPhases.PHASE_SUBMISSION && grades.length"> | ||||
|             <ion-item text-wrap *ngIf="selectedPhase == workshopPhases.PHASE_SUBMISSION"> | ||||
|                 <h2>{{ 'addon.mod_workshop.submissionsreport' | translate }}</h2> | ||||
|             </ion-item> | ||||
|             <ion-item text-wrap *ngIf="selectedPhase > workshopPhases.PHASE_SUBMISSION"> | ||||
|                 <h2>{{ 'addon.mod_workshop.gradesreport' | translate }}</h2> | ||||
|             </ion-item> | ||||
|             <ion-item 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)]="selectedGroup" (ionChange)="setGroup(selectedGroup)" aria-labelledby="addon-workshop-groupslabel"> | ||||
|                     <ion-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">{{groupOpt.name}}</ion-option> | ||||
|                 </ion-select> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <!--  <addon-mod-workshop-submission *ngFor="submission of grades" [submission]="submission" [courseId]="workshop.course" [module]="module" [workshop]="workshop" [access]="access" summary="true"></addon-mod-workshop-submission>--> | ||||
| 
 | ||||
|             <ion-grid ngIf="page > 0 || hasNextPage"> | ||||
|                 <ion-row align-items-center> | ||||
|                     <ion-col *ngIf="page > 0"> | ||||
|                         <button ion-button block outline icon-start (click)="gotoSubmissionsPage(page - 1)">> | ||||
|                             <ion-icon name="arrow-back"></ion-icon> | ||||
|                             {{ 'core.previous' | translate }} | ||||
|                         </button> | ||||
|                     </ion-col> | ||||
|                     <ion-col *ngIf="hasNextPage"> | ||||
|                         <button ion-button block icon-end click)="gotoSubmissionsPage(page + 1)"> | ||||
|                             {{ 'core.next' | translate }} | ||||
|                             <ion-icon name="arrow-forward"></ion-icon> | ||||
|                         </button> | ||||
|                     </ion-col> | ||||
|                 </ion-row> | ||||
|             </ion-grid> | ||||
|         </ion-card> | ||||
|     </div> | ||||
| </core-loading> | ||||
							
								
								
									
										485
									
								
								src/addon/mod/workshop/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										485
									
								
								src/addon/mod/workshop/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,485 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // 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, Optional, Injector } from '@angular/core'; | ||||
| import { Content, ModalController, NavController, Platform } from 'ionic-angular'; | ||||
| import { CoreGroupInfo, CoreGroupsProvider } from '@providers/groups'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; | ||||
| import { AddonModWorkshopProvider } from '../../providers/workshop'; | ||||
| import { AddonModWorkshopHelperProvider } from '../../providers/helper'; | ||||
| import { AddonModWorkshopSyncProvider } from '../../providers/sync'; | ||||
| import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a workshop index page. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'addon-mod-workshop-index', | ||||
|     templateUrl: 'index.html', | ||||
| }) | ||||
| export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivityComponent { | ||||
|     @Input() group = 0; | ||||
| 
 | ||||
|     moduleName = 'workshop'; | ||||
|     workshop: any; | ||||
|     page = 0; | ||||
|     access: any; | ||||
|     phases: any; | ||||
|     grades: any; | ||||
|     assessments: any; | ||||
|     userGrades: any; | ||||
|     publishedSubmissions: any; | ||||
|     selectedPhase: number; | ||||
|     submission: any; | ||||
|     groupInfo: CoreGroupInfo = { | ||||
|         groups: [], | ||||
|         separateGroups: false, | ||||
|         visibleGroups: false | ||||
|     }; | ||||
|     canSubmit = false; | ||||
|     canAssess = false; | ||||
|     hasNextPage = false; | ||||
| 
 | ||||
|     workshopPhases = { | ||||
|         PHASE_SETUP: AddonModWorkshopProvider.PHASE_SETUP, | ||||
|         PHASE_SUBMISSION: AddonModWorkshopProvider.PHASE_SUBMISSION, | ||||
|         PHASE_ASSESSMENT: AddonModWorkshopProvider.PHASE_ASSESSMENT, | ||||
|         PHASE_EVALUATION: AddonModWorkshopProvider.PHASE_EVALUATION, | ||||
|         PHASE_CLOSED: AddonModWorkshopProvider.PHASE_CLOSED | ||||
|     }; | ||||
| 
 | ||||
|     protected offlineSubmissions = []; | ||||
|     protected supportedTasks = { // Add here native supported tasks.
 | ||||
|         submit: true | ||||
|     }; | ||||
|     protected obsSubmissionChanged: any; | ||||
|     protected obsAssessmentSaved: any; | ||||
|     protected appResumeSubscription: any; | ||||
| 
 | ||||
|     constructor(injector: Injector, private workshopProvider: AddonModWorkshopProvider, @Optional() content: Content, | ||||
|             private workshopOffline: AddonModWorkshopOfflineProvider, private groupsProvider: CoreGroupsProvider, | ||||
|             private navCtrl: NavController, private modalCtrl: ModalController, private utils: CoreUtilsProvider, | ||||
|             platform: Platform, private workshopHelper: AddonModWorkshopHelperProvider, | ||||
|             private workshopSync: AddonModWorkshopSyncProvider) { | ||||
|         super(injector, content); | ||||
| 
 | ||||
|         // Listen to submission and assessment changes.
 | ||||
|         this.obsSubmissionChanged = this.eventsProvider.on(AddonModWorkshopProvider.SUBMISSION_CHANGED, (data) => { | ||||
|             this.eventReceived(data); | ||||
|         }, this.siteId); | ||||
| 
 | ||||
|         // Listen to submission and assessment changes.
 | ||||
|         this.obsAssessmentSaved = this.eventsProvider.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.content && this.content.scrollToTop(); | ||||
| 
 | ||||
|             this.loaded = false; | ||||
|             this.refreshContent(true, false); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         super.ngOnInit(); | ||||
| 
 | ||||
|         this.loadContent(false, true).then(() => { | ||||
|             if (!this.workshop) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             this.workshopProvider.logView(this.workshop.id).then(() => { | ||||
|                 this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function called when we receive an event of submission changes. | ||||
|      * | ||||
|      * @param {any} data Data received by the event. | ||||
|      */ | ||||
|     protected eventReceived(data: any): void { | ||||
|         if ((this.workshop && this.workshop.id === data.workshopid) || data.cmid === module.id) { | ||||
|             this.content && this.content.scrollToTop(); | ||||
| 
 | ||||
|             this.loaded = false; | ||||
|             this.refreshContent(true, false); | ||||
|             // Check completion since it could be configured to complete once the user adds a new discussion or replies.
 | ||||
|             this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Perform the invalidate content function. | ||||
|      * | ||||
|      * @return {Promise<any>} Resolved when done. | ||||
|      */ | ||||
|     protected invalidateContent(): Promise<any> { | ||||
|         const promises = []; | ||||
| 
 | ||||
|         promises.push(this.workshopProvider.invalidateWorkshopData(this.courseId)); | ||||
|         if (this.workshop) { | ||||
|             promises.push(this.workshopProvider.invalidateWorkshopAccessInformationData(this.workshop.id)); | ||||
|             promises.push(this.workshopProvider.invalidateUserPlanPhasesData(this.workshop.id)); | ||||
|             if (this.canSubmit) { | ||||
|                 promises.push(this.workshopProvider.invalidateSubmissionsData(this.workshop.id)); | ||||
|             } | ||||
|             if (this.access.canviewallsubmissions) { | ||||
|                 promises.push(this.workshopProvider.invalidateGradeReportData(this.workshop.id)); | ||||
|                 promises.push(this.groupsProvider.invalidateActivityAllowedGroups(this.workshop.coursemodule)); | ||||
|                 promises.push(this.groupsProvider.invalidateActivityGroupMode(this.workshop.coursemodule)); | ||||
|             } | ||||
|             if (this.canAssess) { | ||||
|                 promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshop.id)); | ||||
|             } | ||||
|             promises.push(this.workshopProvider.invalidateGradesData(this.workshop.id)); | ||||
|         } | ||||
| 
 | ||||
|         return Promise.all(promises); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Compares sync event data with current data to check if refresh content is needed. | ||||
|      * | ||||
|      * @param {any} syncEventData Data receiven on sync observer. | ||||
|      * @return {boolean}          True if refresh is needed, false otherwise. | ||||
|      */ | ||||
|     protected isRefreshSyncNeeded(syncEventData: any): boolean { | ||||
|         if (this.workshop && syncEventData.workshopId == this.workshop.id) { | ||||
|             // Refresh the data.
 | ||||
|             this.content.scrollToTop(); | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Download feedback contents. | ||||
|      * | ||||
|      * @param  {boolean}      [refresh=false]    If it's refreshing content. | ||||
|      * @param  {boolean}      [sync=false]       If the refresh is needs syncing. | ||||
|      * @param  {boolean}      [showErrors=false] If show errors to the user of hide them. | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> { | ||||
|         return this.workshopProvider.getWorkshop(this.courseId, this.module.id).then((workshop) => { | ||||
|             this.workshop = workshop; | ||||
| 
 | ||||
|             this.selectedPhase = workshop.phase; | ||||
| 
 | ||||
|             this.description = workshop.intro || workshop.description; | ||||
|             this.dataRetrieved.emit(workshop); | ||||
| 
 | ||||
|             if (sync) { | ||||
|                 // Try to synchronize the feedback.
 | ||||
|                 return this.syncActivity(showErrors); | ||||
|             } | ||||
|         }).then(() => { | ||||
|             // Check if there are answers stored in offline.
 | ||||
|             return this.workshopProvider.getWorkshopAccessInformation(this.workshop.id); | ||||
|         }).then((accessData) => { | ||||
|             this.access = accessData; | ||||
| 
 | ||||
|             if (accessData.canviewallsubmissions) { | ||||
|                 return this.groupsProvider.getActivityGroupInfo(this.workshop.coursemodule, | ||||
|                         accessData.canviewallsubmissions).then((groupInfo) => { | ||||
|                     this.groupInfo = groupInfo; | ||||
| 
 | ||||
|                     // Check selected group is accessible.
 | ||||
|                     if (groupInfo && groupInfo.groups && groupInfo.groups.length > 0) { | ||||
|                         const found = groupInfo.groups.some((group) => { | ||||
|                             return group.id == this.group; | ||||
|                         }); | ||||
|                         if (!found) { | ||||
|                             this.group = groupInfo.groups[0].id; | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             return Promise.resolve(); | ||||
|         }).then(() => { | ||||
|             return this.workshopProvider.getUserPlanPhases(this.workshop.id); | ||||
|         }).then((phases) => { | ||||
|             this.phases = phases; | ||||
| 
 | ||||
|             // Treat phases.
 | ||||
|             for (const x in phases) { | ||||
|                 phases[x].tasks.forEach((task) => { | ||||
|                     if (!task.link && (task.code == 'examples' || task.code == 'prepareexamples')) { | ||||
|                         // Add links to manage examples.
 | ||||
|                         task.link = this.externalUrl; | ||||
|                     } else if (task.link && typeof this.supportedTasks[task.code] !== 'undefined') { | ||||
|                         task.support = true; | ||||
|                     } | ||||
|                 }); | ||||
|                 const action = phases[x].actions.find((action) => { | ||||
|                     return action.url && action.type == 'switchphase'; | ||||
|                 }); | ||||
|                 phases[x].switchUrl = action ? action.url : ''; | ||||
|             } | ||||
| 
 | ||||
|             // Check if there are info stored in offline.
 | ||||
|             return this.workshopOffline.hasWorkshopOfflineData(this.workshop.id).then((hasOffline) => { | ||||
|                 this.hasOffline = hasOffline; | ||||
|                 if (hasOffline) { | ||||
|                     return this.workshopOffline.getSubmissions(this.workshop.id).then((submissionsActions) => { | ||||
|                         this.offlineSubmissions = submissionsActions; | ||||
|                     }); | ||||
|                 } else { | ||||
|                     this.offlineSubmissions = []; | ||||
|                 } | ||||
|             }); | ||||
|         }).then(() => { | ||||
|             return this.setPhaseInfo(); | ||||
|         }).then(() => { | ||||
|             // All data obtained, now fill the context menu.
 | ||||
|             this.fillContextMenu(refresh); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves and shows submissions grade page. | ||||
|      * | ||||
|      * @param  {number}       page Page number to be retrieved. | ||||
|      * @return {Promise<any>}      Resolved when done. | ||||
|      */ | ||||
|     gotoSubmissionsPage(page: number): Promise<any> { | ||||
|         return this.workshopProvider.getGradesReport(this.workshop.id, this.group, page).then((report) => { | ||||
|             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; | ||||
| 
 | ||||
|             this.grades =  report.grades || []; | ||||
| 
 | ||||
|             this.grades.forEach((submission) => { | ||||
|                 const actions = this.workshopHelper.filterSubmissionActions(this.offlineSubmissions, submission.submissionid | ||||
|                     || false); | ||||
|                 submission = this.workshopHelper.applyOfflineData(submission, actions); | ||||
| 
 | ||||
|                 return this.workshopHelper.applyOfflineData(submission, actions).then((offlineSubmission) => { | ||||
|                     submission = offlineSubmission; | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Open task. | ||||
|      * | ||||
|      * @param {any} task Task to be done. | ||||
|      */ | ||||
|     runTask(task: any): void { | ||||
|         if (task.support) { | ||||
|             if (task.code == 'submit' && this.canSubmit && ((this.access.creatingsubmissionallowed && !this.submission) || | ||||
|                     (this.access.modifyingsubmissionallowed && this.submission))) { | ||||
|                 const params = { | ||||
|                     module: module, | ||||
|                     access: this.access, | ||||
|                     courseId: this.courseId, | ||||
|                     submission: this.submission | ||||
|                 }; | ||||
| 
 | ||||
|                 if (this.submission.id) { | ||||
|                     params['submissionId'] = this.submission.id; | ||||
|                 } | ||||
| 
 | ||||
|                 this.navCtrl.push('AddonModWorkshopEditSubmissionPage', params); | ||||
|             } | ||||
|         } else if (task.link) { | ||||
|             this.utils.openInBrowser(task.link); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Run task link on current phase. | ||||
|      * | ||||
|      * @param {string} taskCode Code related to the task to run. | ||||
|      */ | ||||
|     runTaskByCode(taskCode: string): void { | ||||
|         const task = this.workshopHelper.getTask(this.phases[this.workshop.phase].tasks, taskCode); | ||||
| 
 | ||||
|         return task ? this.runTask(task) : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Select Phase to be shown. | ||||
|      */ | ||||
|     selectPhase(): void { | ||||
|         if (this.phases) { | ||||
|             const modal = this.modalCtrl.create('AddonModWorkshopPhaseSelectorPage', { | ||||
|                     phases: this.utils.objectToArray(this.phases), | ||||
|                     selected: this.selectedPhase, | ||||
|                     workshopPhase: this.workshop.phase | ||||
|                 }); | ||||
|             modal.onDidDismiss((phase) => { | ||||
|                 // Add data to search object.
 | ||||
|                 typeof phase != 'undefined' && this.switchPhase(phase); | ||||
|             }); | ||||
|             modal.present(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set group to see the workshop. | ||||
|      * @param {number} groupId Group Id. | ||||
|      * @return {Promise<any>}  Promise resolved when done. | ||||
|      */ | ||||
|     setGroup(groupId: number): Promise<any> { | ||||
|         this.group = groupId; | ||||
| 
 | ||||
|         return this.gotoSubmissionsPage(0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Convenience function to set current phase information. | ||||
|      * | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     protected setPhaseInfo(): Promise<any> { | ||||
|         this.submission = false; | ||||
|         this.canAssess = false; | ||||
|         this.assessments = false; | ||||
|         this.userGrades = false; | ||||
|         this.publishedSubmissions = false; | ||||
| 
 | ||||
|         this.canSubmit = this.workshopHelper.canSubmit(this.workshop, this.access, | ||||
|             this.phases[AddonModWorkshopProvider.PHASE_SUBMISSION].tasks); | ||||
| 
 | ||||
|         const promises = []; | ||||
| 
 | ||||
|         if (this.canSubmit) { | ||||
|             promises.push(this.workshopHelper.getUserSubmission(this.workshop.id).then((submission) => { | ||||
|                 const actions = this.workshopHelper.filterSubmissionActions(this.offlineSubmissions, submission.id || false); | ||||
| 
 | ||||
|                 return this.workshopHelper.applyOfflineData(submission, actions).then((submission) => { | ||||
|                     this.submission = submission; | ||||
|                 }); | ||||
|             })); | ||||
|         } | ||||
| 
 | ||||
|         if (this.access.canviewallsubmissions && this.workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { | ||||
|             promises.push(this.gotoSubmissionsPage(this.page)); | ||||
|         } | ||||
| 
 | ||||
|         let assessPromise = Promise.resolve(); | ||||
| 
 | ||||
|         if (this.workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT) { | ||||
|             this.canAssess = this.workshopHelper.canAssess(this.workshop, this.access); | ||||
|             if (this.canAssess) { | ||||
|                 assessPromise = this.workshopHelper.getReviewerAssessments(this.workshop.id).then((assessments) => { | ||||
|                     const p2 = []; | ||||
| 
 | ||||
|                     assessments.forEach((assessment) => { | ||||
|                         assessment.strategy = this.workshop.strategy; | ||||
|                         if (this.hasOffline) { | ||||
|                             p2.push(this.workshopOffline.getAssessment(this.workshop.id, assessment.id) | ||||
|                                 .then((offlineAssessment) => { | ||||
|                                     assessment.offline = true; | ||||
|                                     assessment.timemodified = Math.floor(offlineAssessment.timemodified / 1000); | ||||
|                             }).catch(() => { | ||||
|                                 // Ignore errors.
 | ||||
|                             })); | ||||
|                         } | ||||
|                     }); | ||||
| 
 | ||||
|                     return Promise.all(p2).then(() => { | ||||
|                         this.assessments = assessments; | ||||
|                     }); | ||||
|                 }); | ||||
|                 promises.push(assessPromise); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { | ||||
|             promises.push(this.workshopProvider.getGrades(this.workshop.id).then((grades) => { | ||||
|                 this.userGrades = grades.submissionlongstrgrade || grades.assessmentlongstrgrade ? grades : false; | ||||
|             })); | ||||
| 
 | ||||
|             if (this.access.canviewpublishedsubmissions) { | ||||
|                 promises.push(assessPromise.then(() => { | ||||
|                     return this.workshopProvider.getSubmissions(this.workshop.id).then((submissions) => { | ||||
|                         this.publishedSubmissions = submissions.filter((submission) => { | ||||
|                             if (submission.published) { | ||||
|                                 this.assessments.forEach((assessment) => { | ||||
|                                     submission.reviewedby = []; | ||||
|                                     if (assessment.submissionid == submission.id) { | ||||
|                                         submission.reviewedby.push(this.workshopHelper.realGradeValue(this.workshop, assessment)); | ||||
|                                     } | ||||
|                                 }); | ||||
| 
 | ||||
|                                 return true; | ||||
|                             } | ||||
| 
 | ||||
|                             return false; | ||||
|                         }); | ||||
|                     }); | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return Promise.all(promises); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Switch shown phase. | ||||
|      * | ||||
|      * @param {number} phase Selected phase. | ||||
|      */ | ||||
|     switchPhase(phase: number): void { | ||||
|         this.selectedPhase = phase; | ||||
|         this.page = 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Performs the sync of the activity. | ||||
|      * | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     protected sync(): Promise<any> { | ||||
|         return this.workshopSync.syncWorkshop(this.workshop.id); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if sync has succeed from result sync data. | ||||
|      * | ||||
|      * @param  {any}     result Data returned on the sync function. | ||||
|      * @return {boolean}        If suceed or not. | ||||
|      */ | ||||
|     protected hasSyncSucceed(result: any): boolean { | ||||
|         return result.updated; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being destroyed. | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         super.ngOnDestroy(); | ||||
|         this.obsSubmissionChanged && this.obsSubmissionChanged.off(); | ||||
|         this.obsAssessmentSaved && this.obsAssessmentSaved.off(); | ||||
|         this.appResumeSubscription && this.appResumeSubscription.unsubscribe(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/addon/mod/workshop/lang/en.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/addon/mod/workshop/lang/en.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| { | ||||
|     "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", | ||||
|     "conclusion": "Conclusion", | ||||
|     "createsubmission": "Start preparing your 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", | ||||
|     "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", | ||||
|     "selectphase": "Select phase", | ||||
|     "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.", | ||||
|     "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", | ||||
|     "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" | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/addon/mod/workshop/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/addon/mod/workshop/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <ion-header> | ||||
|     <ion-navbar> | ||||
|         <ion-title><core-format-text [text]="title"></core-format-text></ion-title> | ||||
| 
 | ||||
|         <ion-buttons end> | ||||
|             <!-- The buttons defined by the component will be added in here. --> | ||||
|         </ion-buttons> | ||||
|     </ion-navbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher [enabled]="workshopComponent.loaded" (ionRefresh)="workshopComponent.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> | ||||
							
								
								
									
										33
									
								
								src/addon/mod/workshop/pages/index/index.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addon/mod/workshop/pages/index/index.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // 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 { IonicPageModule } from 'ionic-angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { AddonModWorkshopComponentsModule } from '../../components/components.module'; | ||||
| import { AddonModWorkshopIndexPage } from './index'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         AddonModWorkshopIndexPage, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreDirectivesModule, | ||||
|         AddonModWorkshopComponentsModule, | ||||
|         IonicPageModule.forChild(AddonModWorkshopIndexPage), | ||||
|         TranslateModule.forChild() | ||||
|     ], | ||||
| }) | ||||
| export class AddonModWorkshopIndexPageModule {} | ||||
							
								
								
									
										50
									
								
								src/addon/mod/workshop/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/addon/mod/workshop/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, ViewChild } from '@angular/core'; | ||||
| import { IonicPage, NavParams } from 'ionic-angular'; | ||||
| import { AddonModWorkshopIndexComponent } from '../../components/index/index'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a workshop. | ||||
|  */ | ||||
| @IonicPage({ segment: 'addon-mod-workshop-index' }) | ||||
| @Component({ | ||||
|     selector: 'page-addon-mod-workshop-index', | ||||
|     templateUrl: 'index.html', | ||||
| }) | ||||
| export class AddonModWorkshopIndexPage { | ||||
|     @ViewChild(AddonModWorkshopIndexComponent) workshopComponent: AddonModWorkshopIndexComponent; | ||||
| 
 | ||||
|     title: string; | ||||
|     module: any; | ||||
|     courseId: number; | ||||
|     selectedGroup: number; | ||||
| 
 | ||||
|     constructor(navParams: NavParams) { | ||||
|         this.module = navParams.get('module') || {}; | ||||
|         this.courseId = navParams.get('courseId'); | ||||
|         this.selectedGroup = navParams.get('group') || 0; | ||||
|         this.title = this.module.name; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Update some data based on the workshop instance. | ||||
|      * | ||||
|      * @param {any} workshop Workshop instance. | ||||
|      */ | ||||
|     updateData(workshop: any): void { | ||||
|         this.title = workshop.name || this.title; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/addon/mod/workshop/pages/phase/phase.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/addon/mod/workshop/pages/phase/phase.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| <ion-header> | ||||
|     <ion-navbar> | ||||
|         <ion-title>{{ 'addon.mod_workshop.selectphase' | translate }}</ion-title> | ||||
|         <ion-buttons end> | ||||
|             <button ion-button icon-only (click)="closeModal()" [attr.aria-label]="'core.close' | translate"> | ||||
|                 <ion-icon name="close"></ion-icon> | ||||
|             </button> | ||||
|         </ion-buttons> | ||||
|     </ion-navbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-list radio-group [(ngModel)]="selected" (ionChange)="switchPhase()"> | ||||
|         <ng-container *ngFor="let phase of phases"> | ||||
|             <ion-item *ngIf="workshopPhase >= phase.code || phase.tasks.length || phase.switchUrl"> | ||||
|                 <ion-label>{{ phase.title }} | ||||
|                     <p text-wrap *ngIf="workshopPhase == phase.code">{{ 'addon.mod_workshop.userplancurrentphase' | translate }}</p> | ||||
|                 </ion-label> | ||||
|                 <ion-radio [value]="phase.code"></ion-radio> | ||||
|             </ion-item> | ||||
|             <ion-item *ngIf="!(workshopPhase >= phase.code || phase.tasks.length || phase.switchUrl)"> | ||||
|                 {{ phase.title }} | ||||
|             </ion-item> | ||||
|         </ng-container> | ||||
|     </ion-list> | ||||
| </ion-content> | ||||
							
								
								
									
										33
									
								
								src/addon/mod/workshop/pages/phase/phase.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addon/mod/workshop/pages/phase/phase.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // 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 { IonicPageModule } from 'ionic-angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| import { AddonModWorkshopPhaseSelectorPage } from './phase'; | ||||
| import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         AddonModWorkshopPhaseSelectorPage, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreDirectivesModule, | ||||
|         CoreCompileHtmlComponentModule, | ||||
|         IonicPageModule.forChild(AddonModWorkshopPhaseSelectorPage), | ||||
|         TranslateModule.forChild() | ||||
|     ], | ||||
| }) | ||||
| export class AddonModWorkshopPhaseSelectorPageModule {} | ||||
							
								
								
									
										56
									
								
								src/addon/mod/workshop/pages/phase/phase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/addon/mod/workshop/pages/phase/phase.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // 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 } from '@angular/core'; | ||||
| import { IonicPage, NavParams, ViewController } from 'ionic-angular'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the phase selector modal. | ||||
|  */ | ||||
| @IonicPage({ segment: 'addon-mod-workshop-phase-selector' }) | ||||
| @Component({ | ||||
|     selector: 'page-addon-mod-workshop-phase-selector', | ||||
|     templateUrl: 'phase.html', | ||||
| }) | ||||
| export class AddonModWorkshopPhaseSelectorPage { | ||||
|     selected: number; | ||||
|     phases: any; | ||||
|     workshopPhase: number; | ||||
|     protected original: number; | ||||
| 
 | ||||
|     constructor(params: NavParams, private viewCtrl: ViewController) { | ||||
|         this.selected = params.get('selected'); | ||||
|         this.original = this.selected; | ||||
|         this.phases = params.get('phases'); | ||||
|         this.workshopPhase = params.get('workshopPhase'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Close modal. | ||||
|      */ | ||||
|     closeModal(): void { | ||||
|         this.viewCtrl.dismiss(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Select phase. | ||||
|      */ | ||||
|     switchPhase(): void { | ||||
|         // This is a quick hack to avoid the first switch phase call done just when opening the modal.
 | ||||
|         if (this.original != this.selected) { | ||||
|             this.viewCtrl.dismiss(this.selected); | ||||
|         } | ||||
|         this.original = null; | ||||
|     } | ||||
| } | ||||
| @ -268,7 +268,7 @@ export class AddonModWorkshopHelperProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a list of stored attachment files for a submission. See $mmaModWorkshopHelper#storeFiles. | ||||
|      * Get a list of stored attachment files for a submission. See AddonModWorkshopHelperProvider#storeFiles. | ||||
|      * | ||||
|      * @param  {number}  workshopId   Workshop ID. | ||||
|      * @param  {number}  submissionId If not editing, it will refer to timecreated. | ||||
| @ -286,7 +286,7 @@ export class AddonModWorkshopHelperProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a list of stored attachment files for a submission and online files also. See $mmaModWorkshopHelper#storeFiles. | ||||
|      * Get a list of stored attachment files for a submission and online files also. See AddonModWorkshopHelperProvider#storeFiles. | ||||
|      * | ||||
|      * @param  {any}     filesObject  Files object combining offline and online information. | ||||
|      * @param  {number}  workshopId   Workshop ID. | ||||
| @ -355,7 +355,7 @@ export class AddonModWorkshopHelperProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a list of stored attachment files for an assessment. See $mmaModWorkshopHelper#storeFiles. | ||||
|      * Get a list of stored attachment files for an assessment. See AddonModWorkshopHelperProvider#storeFiles. | ||||
|      * | ||||
|      * @param  {number} workshopId   Workshop ID. | ||||
|      * @param  {number} assessmentId Assessment ID. | ||||
| @ -372,7 +372,7 @@ export class AddonModWorkshopHelperProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a list of stored attachment files for an assessment and online files also. See $mmaModWorkshopHelper#storeFiles. | ||||
|      * Get a list of stored attachment files for an assessment and online files also. See AddonModWorkshopHelperProvider#storeFiles. | ||||
|      * | ||||
|      * @param  {object} filesObject  Files object combining offline and online information. | ||||
|      * @param  {number} workshopId   Workshop ID. | ||||
|  | ||||
							
								
								
									
										30
									
								
								src/addon/mod/workshop/providers/link-handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/addon/mod/workshop/providers/link-handler.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; | ||||
| import { CoreCourseHelperProvider } from '@core/course/providers/helper'; | ||||
| import { AddonModWorkshopProvider } from './workshop'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to treat links to workshop. | ||||
|  */ | ||||
| @Injectable() | ||||
| export class AddonModWorkshopLinkHandler extends CoreContentLinksModuleIndexHandler { | ||||
|     name = 'AddonModWorkshopLinkHandler'; | ||||
| 
 | ||||
|     constructor(courseHelper: CoreCourseHelperProvider) { | ||||
|         super(courseHelper, AddonModWorkshopProvider.COMPONENT, 'workshop'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								src/addon/mod/workshop/providers/module-handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/addon/mod/workshop/providers/module-handler.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { NavController, NavOptions } from 'ionic-angular'; | ||||
| import { AddonModWorkshopIndexComponent } from '../components/index/index'; | ||||
| import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; | ||||
| import { CoreCourseProvider } from '@core/course/providers/course'; | ||||
| import { AddonModWorkshopProvider } from './workshop'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to support workshop modules. | ||||
|  */ | ||||
| @Injectable() | ||||
| export class AddonModWorkshopModuleHandler implements CoreCourseModuleHandler { | ||||
|     name = 'AddonModWorkshop'; | ||||
|     modName = 'workshop'; | ||||
| 
 | ||||
|     constructor(private courseProvider: CoreCourseProvider, private workshopProvider: AddonModWorkshopProvider) { } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the handler is enabled on a site level. | ||||
|      * | ||||
|      * @return {Promise<boolean>} Whether or not the handler is enabled on a site level. | ||||
|      */ | ||||
|     isEnabled(): Promise<boolean> { | ||||
|         return this.workshopProvider.isPluginEnabled(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the data required to display the module in the course contents view. | ||||
|      * | ||||
|      * @param {any} module The module object. | ||||
|      * @param {number} courseId The course ID. | ||||
|      * @param {number} sectionId The section ID. | ||||
|      * @return {CoreCourseModuleHandlerData} Data to render the module. | ||||
|      */ | ||||
|     getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { | ||||
|         return { | ||||
|             icon: this.courseProvider.getModuleIconSrc('workshop'), | ||||
|             title: module.name, | ||||
|             class: 'addon-mod_workshop-handler', | ||||
|             showDownloadButton: true, | ||||
|             action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void { | ||||
|                 navCtrl.push('AddonModWorkshopIndexPage', {module: module, courseId: courseId}, options); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the component to render the module. This is needed to support singleactivity course format. | ||||
|      * The component returned must implement CoreCourseModuleMainComponent. | ||||
|      * | ||||
|      * @param {any} course The course object. | ||||
|      * @param {any} module The module object. | ||||
|      * @return {any} The component to use, undefined if not found. | ||||
|      */ | ||||
|     getMainComponent(course: any, module: any): any { | ||||
|         return AddonModWorkshopIndexComponent; | ||||
|     } | ||||
| } | ||||
| @ -418,7 +418,7 @@ export class AddonModWorkshopOfflineProvider { | ||||
|     getAllAssessments(siteId?: string): Promise<any[]> { | ||||
|         return this.sitesProvider.getSite(siteId).then((site) => { | ||||
|             return site.getDb().getRecords(this.ASSESSMENTS_TABLE).then((records) => { | ||||
|                 records.forEach(this.parseAssessnentRecord.bind(this)); | ||||
|                 records.forEach(this.parseAssessmentRecord.bind(this)); | ||||
| 
 | ||||
|                 return records; | ||||
|             }); | ||||
| @ -439,7 +439,7 @@ export class AddonModWorkshopOfflineProvider { | ||||
|             }; | ||||
| 
 | ||||
|             return site.getDb().getRecords(this.ASSESSMENTS_TABLE, conditions).then((records) => { | ||||
|                 records.forEach(this.parseAssessnentRecord.bind(this)); | ||||
|                 records.forEach(this.parseAssessmentRecord.bind(this)); | ||||
| 
 | ||||
|                 return records; | ||||
|             }); | ||||
| @ -462,7 +462,7 @@ export class AddonModWorkshopOfflineProvider { | ||||
|             }; | ||||
| 
 | ||||
|             return site.getDb().getRecord(this.ASSESSMENTS_TABLE, conditions).then((record) => { | ||||
|                 this.parseAssessnentRecord(record); | ||||
|                 this.parseAssessmentRecord(record); | ||||
| 
 | ||||
|                 return record; | ||||
|             }); | ||||
| @ -498,7 +498,7 @@ export class AddonModWorkshopOfflineProvider { | ||||
|      * | ||||
|      * @param {any} record Assessnent record, modified in place. | ||||
|      */ | ||||
|     protected parseAssessnentRecord(record: any): void { | ||||
|     protected parseAssessmentRecord(record: any): void { | ||||
|         record.inputdata = this.textUtils.parseJSON(record.inputdata); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -24,7 +24,7 @@ import { AddonModWorkshopOfflineProvider } from './offline'; | ||||
|  */ | ||||
| @Injectable() | ||||
| export class AddonModWorkshopProvider { | ||||
|     static COMPONENT = 'mmaWorkshopUrl'; | ||||
|     static COMPONENT = 'mmaModWorkshop'; | ||||
|     static PER_PAGE = 10; | ||||
|     static PHASE_SETUP = 10; | ||||
|     static PHASE_SUBMISSION = 20; | ||||
| @ -35,6 +35,9 @@ export class AddonModWorkshopProvider { | ||||
|     static EXAMPLES_BEFORE_SUBMISSION: 1; | ||||
|     static EXAMPLES_BEFORE_ASSESSMENT: 2; | ||||
| 
 | ||||
|     static SUBMISSION_CHANGED = 'addon_mod_workshop_submission_changed'; | ||||
|     static ASSESSMENT_SAVED = 'addon_mod_workshop_assessment_saved'; | ||||
| 
 | ||||
|     protected ROOT_CACHE_KEY = 'mmaModWorkshop:'; | ||||
| 
 | ||||
|     constructor( | ||||
| @ -346,12 +349,7 @@ export class AddonModWorkshopProvider { | ||||
| 
 | ||||
|             return site.read('mod_workshop_get_user_plan', params, preSets).then((response) => { | ||||
|                 if (response && response.userplan && response.userplan.phases) { | ||||
|                     const phases = {}; | ||||
|                     response.userplan.phases.forEach((phase) => { | ||||
|                         phases[phase.code] = phase; | ||||
|                     }); | ||||
| 
 | ||||
|                     return phases; | ||||
|                     return this.utils.arrayToObject(response.userplan.phases, 'code'); | ||||
|                 } | ||||
| 
 | ||||
|                 return Promise.reject(null); | ||||
| @ -1295,7 +1293,7 @@ export class AddonModWorkshopProvider { | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidate the prefetched content except files. | ||||
|      * To invalidate files, use $mmaModWorkshop#invalidateFiles. | ||||
|      * To invalidate files, use AddonModWorkshopProvider#invalidateFiles. | ||||
|      * | ||||
|      * @param  {number} moduleId The module ID. | ||||
|      * @param  {number} courseId Course ID. | ||||
|  | ||||
							
								
								
									
										49
									
								
								src/addon/mod/workshop/workshop.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/addon/mod/workshop/workshop.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // 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 { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; | ||||
| import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; | ||||
| import { AddonModWorkshopComponentsModule } from './components/components.module'; | ||||
| import { AddonModWorkshopModuleHandler } from './providers/module-handler'; | ||||
| import { AddonModWorkshopProvider } from './providers/workshop'; | ||||
| import { AddonModWorkshopLinkHandler } from './providers/link-handler'; | ||||
| import { AddonModWorkshopOfflineProvider } from './providers/offline'; | ||||
| import { AddonModWorkshopSyncProvider } from './providers/sync'; | ||||
| import { AddonModWorkshopHelperProvider } from './providers/helper'; | ||||
| import { AddonWorkshopAssessmentStrategyDelegate } from './providers/assessment-strategy-delegate'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|     ], | ||||
|     imports: [ | ||||
|         AddonModWorkshopComponentsModule | ||||
|     ], | ||||
|     providers: [ | ||||
|         AddonModWorkshopProvider, | ||||
|         AddonModWorkshopModuleHandler, | ||||
|         AddonModWorkshopLinkHandler, | ||||
|         AddonModWorkshopOfflineProvider, | ||||
|         AddonModWorkshopSyncProvider, | ||||
|         AddonModWorkshopHelperProvider, | ||||
|         AddonWorkshopAssessmentStrategyDelegate | ||||
|     ] | ||||
| }) | ||||
| export class AddonModWorkshopModule { | ||||
|     constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModWorkshopModuleHandler, | ||||
|             contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModWorkshopLinkHandler) { | ||||
|         moduleDelegate.registerHandler(moduleHandler); | ||||
|         contentLinksDelegate.registerHandler(linkHandler); | ||||
|     } | ||||
| } | ||||
| @ -96,6 +96,7 @@ import { AddonModQuizModule } from '@addon/mod/quiz/quiz.module'; | ||||
| import { AddonModScormModule } from '@addon/mod/scorm/scorm.module'; | ||||
| import { AddonModUrlModule } from '@addon/mod/url/url.module'; | ||||
| import { AddonModSurveyModule } from '@addon/mod/survey/survey.module'; | ||||
| import { AddonModWorkshopModule } from '@addon/mod/workshop/workshop.module'; | ||||
| import { AddonModImscpModule } from '@addon/mod/imscp/imscp.module'; | ||||
| import { AddonModWikiModule } from '@addon/mod/wiki/wiki.module'; | ||||
| import { AddonMessageOutputModule } from '@addon/messageoutput/messageoutput.module'; | ||||
| @ -202,6 +203,7 @@ export const CORE_PROVIDERS: any[] = [ | ||||
|         AddonModScormModule, | ||||
|         AddonModUrlModule, | ||||
|         AddonModSurveyModule, | ||||
|         AddonModWorkshopModule, | ||||
|         AddonModImscpModule, | ||||
|         AddonModWikiModule, | ||||
|         AddonMessageOutputModule, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user