MOBILE-2323 learning plans: Migrate learning plans
parent
bb524a384d
commit
61ab280b2d
|
@ -0,0 +1,68 @@
|
||||||
|
// (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 { AddonCompetencyProvider } from './providers/competency';
|
||||||
|
import { AddonCompetencyHelperProvider } from './providers/helper';
|
||||||
|
import { AddonCompetencyCourseOptionHandler } from './providers/course-option-handler';
|
||||||
|
import { AddonCompetencyMainMenuHandler } from './providers/mainmenu-handler';
|
||||||
|
import { AddonCompetencyUserHandler } from './providers/user-handler';
|
||||||
|
import { AddonCompetencyComponentsModule } from './components/components.module';
|
||||||
|
import { CoreCourseProvider } from '../../core/course/providers/course';
|
||||||
|
import { CoreCourseOptionsDelegate } from '../../core/course/providers/options-delegate';
|
||||||
|
import { CoreMainMenuDelegate } from '../../core/mainmenu/providers/delegate';
|
||||||
|
import { CoreUserDelegate } from '../../core/user/providers/user-delegate';
|
||||||
|
import { CoreUserProvider } from '../../core/user/providers/user';
|
||||||
|
import { CoreEventsProvider } from '../../providers/events';
|
||||||
|
import { CoreSitesProvider } from '../../providers/sites';
|
||||||
|
import { CoreCoursesProvider } from '../../core/courses/providers/courses';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
AddonCompetencyComponentsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AddonCompetencyProvider,
|
||||||
|
AddonCompetencyHelperProvider,
|
||||||
|
AddonCompetencyCourseOptionHandler,
|
||||||
|
AddonCompetencyMainMenuHandler,
|
||||||
|
AddonCompetencyUserHandler
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AddonCompetencyModule {
|
||||||
|
constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: AddonCompetencyMainMenuHandler,
|
||||||
|
courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonCompetencyCourseOptionHandler,
|
||||||
|
userDelegate: CoreUserDelegate, userHandler: AddonCompetencyUserHandler,
|
||||||
|
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
|
||||||
|
|
||||||
|
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||||
|
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||||
|
userDelegate.registerHandler(userHandler);
|
||||||
|
|
||||||
|
eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
|
||||||
|
courseOptionHandler.clearCoursesNavCache();
|
||||||
|
userHandler.clearUsersNavCache();
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED, () => {
|
||||||
|
courseOptionHandler.clearCoursesNavCache();
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, () => {
|
||||||
|
userHandler.clearUsersNavCache();
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 { CorePipesModule } from '../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyCourseComponent } from './course/course';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCourseComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonCompetencyCourseComponent
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
AddonCompetencyCourseComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AddonCompetencyComponentsModule {}
|
|
@ -0,0 +1,73 @@
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="competenciesLoaded" (ionRefresh)="refreshCourseCompetencies($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competenciesLoaded">
|
||||||
|
<ion-card *ngIf="!user && competencies && competencies.statistics.competencycount > 0">
|
||||||
|
<ion-item text-wrap *ngIf="competencies.settings.pushratingstouserplans">
|
||||||
|
{{ 'addon.competency.coursecompetencyratingsarepushedtouserplans' | translate }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="!competencies.settings.pushratingstouserplans">
|
||||||
|
{{ 'addon.competency.coursecompetencyratingsarenotpushedtouserplans' | translate }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.progress' | translate }}</strong>:
|
||||||
|
{{ 'addon.competency.xcompetenciesproficientoutofyincourse' | translate:{$a: {x: competencies.statistics.proficientcompetencycount, y: competencies.statistics.competencycount} } }} ({{ competencies.statistics.proficientcompetencypercentageformatted }}%)
|
||||||
|
<core-progress-bar [progress]="competencies.statistics.proficientcompetencypercentage"></core-progress-bar>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="competencies.statistics.leastproficientcount > 0">
|
||||||
|
<strong>{{ 'addon.competency.competenciesmostoftennotproficientincourse' | translate }}</strong>:
|
||||||
|
<p *ngFor="let comp of competencies.statistics.leastproficient">
|
||||||
|
<a ui-sref="openCompetencySummary(comp.id)">
|
||||||
|
{{ comp.shortname }} - {{ comp.idnumber }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<h3 margin-horizontal *ngIf="competencies && competencies.statistics.competencycount > 0">{{ 'addon.competency.competencies' | translate }}</h3>
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-avatar *ngIf="user.profileimageurl && user.profileimageurl !== true" item-start>
|
||||||
|
<img [src]="user.profileimageurl" [alt]="'core.pictureof' | translate:{$a: user.fullname}" core-external-content>
|
||||||
|
</ion-avatar>
|
||||||
|
<span *ngIf="user.profileimageurl === true" item-start>
|
||||||
|
<ion-icon name="person"></ion-icon>
|
||||||
|
</span>
|
||||||
|
<h2><core-format-text [text]="user.fullname"></core-format-text></h2>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
<core-empty-box *ngIf="competencies && competencies.statistics.competencycount == 0" icon="ribbon" message="{{ 'addon.competency.nocompetencies' | translate }}"></core-empty-box>
|
||||||
|
|
||||||
|
<div *ngIf="competencies">
|
||||||
|
<ion-card *ngFor="let competency of competencies.competencies">
|
||||||
|
<a ion-item text-wrap (click)="openCompetency(competency.competency.id)" [title]="competency.competency.shortname">
|
||||||
|
{{competency.competency.shortname}} <small>{{competency.competency.idnumber}}</small>
|
||||||
|
<ion-badge item-end *ngIf="competency.usercompetencycourse && competency.usercompetencycourse.gradename" [color]="competency.usercompetencycourse.proficiency ? 'success' : 'danger'">{{ competency.usercompetencycourse.gradename }}</ion-badge><br>
|
||||||
|
</a>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<div *ngIf="competency.competency.description">
|
||||||
|
<core-format-text [text]=" competency.competency.description "></core-format-text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ 'addon.competency.path' | translate }}</strong>:
|
||||||
|
{{ competency.comppath.framework.name }}
|
||||||
|
<span *ngFor="let ancestor of competency.comppath.ancestors">
|
||||||
|
/ <a (click)="openCompetencySummary(ancestor.id)">{{ ancestor.name }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ 'addon.competency.activities' | translate }}</strong>:
|
||||||
|
<span *ngIf="competency.coursemodules.length == 0">
|
||||||
|
{{ 'addon.competency.noactivities' | translate }}
|
||||||
|
</span>
|
||||||
|
<a ion-item text-wrap *ngFor="let activity of competency.coursemodules" [href]="activity.url" [title]="activity.name">
|
||||||
|
<img item-start [src]="activity.iconurl" core-external-content alt="" role="presentation" *ngIf="activity.iconurl" class="core-module-icon">
|
||||||
|
<core-format-text [text]="activity.name"></core-format-text>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</div>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,112 @@
|
||||||
|
// (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, Input, Optional } from '@angular/core';
|
||||||
|
import { Content, NavController } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreAppProvider } from '../../../../providers/app';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays the competencies of a course.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-competency-course',
|
||||||
|
templateUrl: 'course.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseComponent {
|
||||||
|
@ViewChild(Content) content: Content;
|
||||||
|
|
||||||
|
@Input() courseId: number;
|
||||||
|
@Input() userId: number;
|
||||||
|
|
||||||
|
competenciesLoaded = false;
|
||||||
|
competencies: any;
|
||||||
|
user: any;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, private translate: TranslateService,
|
||||||
|
private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private competencyProvider: AddonCompetencyProvider, private helperProvider: AddonCompetencyHelperProvider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fetchCourseCompetencies().finally(() => {
|
||||||
|
this.competenciesLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competencies and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchCourseCompetencies(): Promise<void> {
|
||||||
|
return this.competencyProvider.getCourseCompetencies(this.courseId, this.userId).then((competencies) => {
|
||||||
|
this.competencies = competencies;
|
||||||
|
|
||||||
|
// Get the user profile image.
|
||||||
|
this.helperProvider.getProfile(this.userId).then((user) => {
|
||||||
|
this.user = user;
|
||||||
|
});
|
||||||
|
}, (message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting course competencies data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetency(competencyId: number): void {
|
||||||
|
if (this.appProvider.isWide()) {
|
||||||
|
this.navCtrl.push('AddonCompetencyCompetenciesPage', {competencyId, courseId: this.courseId, userId: this.userId});
|
||||||
|
} else {
|
||||||
|
this.navCtrl.push('AddonCompetencyCompetencyPage', {competencyId, courseId: this.courseId, userId: this.userId});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
this.navCtrl.push('AddonCompetencyCompetencySummaryPage', {competencyId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competencies.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCourseCompetencies(refresher: any): void {
|
||||||
|
this.competencyProvider.invalidateCourseCompetencies(this.courseId, this.userId).finally(() => {
|
||||||
|
this.fetchCourseCompetencies().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"activities": "Activities",
|
||||||
|
"competencies": "Competencies",
|
||||||
|
"competenciesmostoftennotproficientincourse": "Competencies most often not proficient in this course",
|
||||||
|
"coursecompetencies": "Course competencies",
|
||||||
|
"coursecompetencyratingsarenotpushedtouserplans": "Competency ratings in this course do not affect learning plans.",
|
||||||
|
"coursecompetencyratingsarepushedtouserplans": "Competency ratings in this course are updated immediately in learning plans.",
|
||||||
|
"crossreferencedcompetencies": "Cross-referenced competencies",
|
||||||
|
"duedate": "Due date",
|
||||||
|
"errornocompetenciesfound": "No competencies found",
|
||||||
|
"evidence": "Evidence",
|
||||||
|
"evidence_competencyrule": "The rule of the competency was met.",
|
||||||
|
"evidence_coursecompleted": "The course '{{$a}}' was completed.",
|
||||||
|
"evidence_coursemodulecompleted": "The activity '{{$a}}' was completed.",
|
||||||
|
"evidence_courserestored": "The rating was restored along with the course '{{$a}}'.",
|
||||||
|
"evidence_evidenceofpriorlearninglinked": "The evidence of prior learning '{{$a}}' was linked.",
|
||||||
|
"evidence_evidenceofpriorlearningunlinked": "The evidence of prior learning '{{$a}}' was unlinked.",
|
||||||
|
"evidence_manualoverride": "The competency rating was manually set.",
|
||||||
|
"evidence_manualoverrideincourse": "The competency rating was manually set in the course '{{$a}}'.",
|
||||||
|
"evidence_manualoverrideinplan": "The competency rating was manually set in the learning plan '{{$a}}'.",
|
||||||
|
"learningplancompetencies": "Learning plan competencies",
|
||||||
|
"learningplans": "Learning plans",
|
||||||
|
"myplans": "My learning plans",
|
||||||
|
"noactivities": "No activities",
|
||||||
|
"nocompetencies": "No competencies",
|
||||||
|
"nocrossreferencedcompetencies": "No other competencies have been cross-referenced to this competency.",
|
||||||
|
"noevidence": "No evidence",
|
||||||
|
"noplanswerecreated": "No learning plans were created.",
|
||||||
|
"path": "Path:",
|
||||||
|
"planstatusactive": "Active",
|
||||||
|
"planstatuscomplete": "Complete",
|
||||||
|
"planstatusdraft": "Draft",
|
||||||
|
"planstatusinreview": "In review",
|
||||||
|
"planstatuswaitingforreview": "Waiting for review",
|
||||||
|
"proficient": "Proficient",
|
||||||
|
"progress": "Progress",
|
||||||
|
"rating": "Rating",
|
||||||
|
"reviewstatus": "Review status",
|
||||||
|
"status": "Status",
|
||||||
|
"template": "Learning plan template",
|
||||||
|
"usercompetencystatus_idle": "Idle",
|
||||||
|
"usercompetencystatus_inreview": "In review",
|
||||||
|
"usercompetencystatus_waitingforreview": "Waiting for review",
|
||||||
|
"userplans": "Learning plans",
|
||||||
|
"xcompetenciesproficientoutofy": "{{$a.x}} out of {{$a.y}} competencies are proficient",
|
||||||
|
"xcompetenciesproficientoutofyincourse": "You are proficient in {{$a.x}} out of {{$a.y}} competencies in this course."
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title>{{ title }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="competenciesLoaded" (ionRefresh)="refreshCompetencies($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competenciesLoaded">
|
||||||
|
<ion-list>
|
||||||
|
<a ion-item text-wrap *ngFor="let competency of competencies" [title]="competency.competency.shortname" (click)="openCompetency(competency.competency.id)" [class.core-split-item-selected]="competency.competency.id == competencyId">
|
||||||
|
{{ competency.competency.shortname }} <small>{{competency.competency.idnumber}}</small>
|
||||||
|
<ion-badge item-end *ngIf="competency.usercompetency" [color]="competency.usercompetency.proficiency ? 'success' : 'danger'">{{ competency.usercompetency.gradename }}</ion-badge>
|
||||||
|
<ion-badge item-end *ngIf="competency.usercompetencycourse" [color]="competency.usercompetencycourse.proficiency ? 'success' : 'danger'">{{ competency.usercompetencycourse.gradename }}</ion-badge>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
||||||
|
</core-split-view>
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyCompetenciesPage } from './competencies';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCompetenciesPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyCompetenciesPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetenciesPageModule {}
|
|
@ -0,0 +1,143 @@
|
||||||
|
// (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, NavController, NavParams } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of competencies of a learning plan.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-competencies' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competencies',
|
||||||
|
templateUrl: 'competencies.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetenciesPage {
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
protected planId: number;
|
||||||
|
protected courseId: number;
|
||||||
|
protected competencyId: number;
|
||||||
|
protected userId: number;
|
||||||
|
|
||||||
|
competenciesLoaded = false;
|
||||||
|
competencies = [];
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
|
private domUtils: CoreDomUtilsProvider, private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
this.planId = navParams.get('planId');
|
||||||
|
this.courseId = navParams.get('courseId');
|
||||||
|
this.competencyId = navParams.get('competencyId');
|
||||||
|
this.userId = navParams.get('userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
if (this.competencyId) {
|
||||||
|
// There is a competency to load.
|
||||||
|
this.openCompetency(this.competencyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetchCompetencies().then(() => {
|
||||||
|
if (!this.competencyId && this.splitviewCtrl.isOn() && this.competencies.length > 0) {
|
||||||
|
// Take first and load it.
|
||||||
|
this.openCompetency(this.competencies[0].id);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.competenciesLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competencies and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchCompetencies(): Promise<void> {
|
||||||
|
let promise;
|
||||||
|
|
||||||
|
if (this.planId) {
|
||||||
|
promise = this.competencyProvider.getLearningPlan(this.planId);
|
||||||
|
} else if (this.courseId) {
|
||||||
|
promise = this.competencyProvider.getCourseCompetencies(this.courseId, this.userId);
|
||||||
|
} else {
|
||||||
|
promise = Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((response) => {
|
||||||
|
if (response.competencycount <= 0) {
|
||||||
|
return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.planId) {
|
||||||
|
this.title = response.plan.name;
|
||||||
|
this.userId = response.plan.userid;
|
||||||
|
} else {
|
||||||
|
this.title = this.translate.instant('addon.competency.coursecompetencies');
|
||||||
|
}
|
||||||
|
this.competencies = response.competencies;
|
||||||
|
}).catch((message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting competencies data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetency(competencyId: number): void {
|
||||||
|
this.competencyId = competencyId;
|
||||||
|
let params;
|
||||||
|
if (this.planId) {
|
||||||
|
params = {competencyId, planId: this.planId};
|
||||||
|
} else {
|
||||||
|
params = {competencyId, courseId: this.courseId, userId: this.userId};
|
||||||
|
}
|
||||||
|
this.splitviewCtrl.push('AddonCompetencyCompetencyPage', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competencies.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCompetencies(refresher: any): void {
|
||||||
|
let promise;
|
||||||
|
if (this.planId) {
|
||||||
|
promise = this.competencyProvider.invalidateLearningPlan(this.planId);
|
||||||
|
} else {
|
||||||
|
promise = this.competencyProvider.invalidateCourseCompetencies(this.courseId, this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.finally(() => {
|
||||||
|
this.fetchCompetencies().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title *ngIf="competency">{{ competency.competency.competency.shortname }} <small>{{ competency.competency.competency.idnumber }}</small></ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="competencyLoaded" (ionRefresh)="refreshCompetency($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competencyLoaded">
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-avatar *ngIf="user.profileimageurl && user.profileimageurl !== true" item-start>
|
||||||
|
<img [src]="user.profileimageurl" [alt]="'core.pictureof' | translate:{$a: user.fullname}" core-external-content>
|
||||||
|
</ion-avatar>
|
||||||
|
<span *ngIf="user.profileimageurl === true" item-start>
|
||||||
|
<ion-icon name="person"></ion-icon>
|
||||||
|
</span>
|
||||||
|
<h2><core-format-text [text]="user.fullname"></core-format-text></h2>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<ion-card *ngIf="competency">
|
||||||
|
<ion-item text-wrap *ngIf="competency.competency.competency.description">
|
||||||
|
<core-format-text [text]="competency.competency.competency.description"></core-format-text>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.path' | translate }}</strong>:
|
||||||
|
{{ competency.competency.comppath.framework.name }}
|
||||||
|
<span *ngFor="let ancestor of competency.competency.comppath.ancestors">
|
||||||
|
/ <a (click)="openCompetencySummary(ancestor.id)">{{ ancestor.name }}</a>
|
||||||
|
</span>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.crossreferencedcompetencies' | translate }}</strong>:
|
||||||
|
<div *ngIf="!competency.competency.hasrelatedcompetencies">{{ 'addon.competency.nocrossreferencedcompetencies' | translate }}</div>
|
||||||
|
<div *ngIf="competency.competency.hasrelatedcompetencies">
|
||||||
|
<p *ngFor="let relatedcomp of competency.competency.relatedcompetencies">
|
||||||
|
<a (click)="openCompetencySummary(relatedcomp.id)">
|
||||||
|
{{ relatedcomp.shortname }} - {{ relatedcomp.idnumber }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="coursemodules">
|
||||||
|
<strong>{{ 'addon.competency.activities' | translate }}</strong>:
|
||||||
|
<span *ngIf="coursemodules.length == 0">
|
||||||
|
{{ 'addon.competency.noactivities' | translate }}
|
||||||
|
</span>
|
||||||
|
<a ion-item text-wrap *ngFor="let activity of coursemodules" [href]="activity.url" [title]="activity.name">
|
||||||
|
<img item-start core-external-content [src]="activity.iconurl" alt="" role="presentation" *ngIf="activity.iconurl" class="core-module-icon">
|
||||||
|
<core-format-text [text]="activity.name"></core-format-text>
|
||||||
|
</a>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="competency.usercompetency.status">
|
||||||
|
<strong>{{ 'addon.competency.reviewstatus' | translate }}</strong>:
|
||||||
|
{{ competency.usercompetency.statusname }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.proficient' | translate }}</strong>:
|
||||||
|
<ion-badge color="success" *ngIf="competency.usercompetency.proficiency">
|
||||||
|
{{ 'core.yes' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
<ion-badge color="danger" *ngIf="!competency.usercompetency.proficiency">
|
||||||
|
{{ 'core.no' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.rating' | translate }}</strong>:
|
||||||
|
<ion-badge color="dark">{{ competency.usercompetency.gradename }}</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<div *ngIf="competency">
|
||||||
|
<h3 margin-horizontal>{{ 'addon.competency.evidence' | translate }}</h3>
|
||||||
|
<p margin-horizontal *ngIf="competency.evidence.length == 0">
|
||||||
|
{{ 'addon.competency.noevidence' | translate }}
|
||||||
|
</p>
|
||||||
|
<ion-card *ngFor="let evidence of competency.evidence">
|
||||||
|
<a ion-item text-wrap *ngIf="evidence.actionuser" (click)="openUserProfile(evidence.actionuser.id)">
|
||||||
|
<ion-avatar item-start>
|
||||||
|
<img core-external-content [src]="evidence.actionuser.profileimageurlsmall" [alt]="'core.pictureof' | translate:{$a: evidence.actionuser.fullname}" role="presentation">
|
||||||
|
</ion-avatar>
|
||||||
|
<h2>{{ evidence.actionuser.fullname }}</h2>
|
||||||
|
<p>{{ evidence.timemodified | coreToLocaleString }}</p>
|
||||||
|
</a>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<p><ion-badge color="dark">{{ evidence.gradename }}</ion-badge></p>
|
||||||
|
<p margin-top *ngIf="evidence.description">{{ evidence.description }}</p>
|
||||||
|
<blockquote *ngIf="evidence.note"><core-format-text [text]="evidence.note"></core-format-text></blockquote>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</div>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyCompetencyPage } from './competency';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCompetencyPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyCompetencyPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencyPageModule {}
|
|
@ -0,0 +1,184 @@
|
||||||
|
// (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, Optional } from '@angular/core';
|
||||||
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-competency' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competency',
|
||||||
|
templateUrl: 'competency.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencyPage {
|
||||||
|
competencyLoaded = false;
|
||||||
|
competencyId: number;
|
||||||
|
planId: number;
|
||||||
|
courseId: number;
|
||||||
|
userId: number;
|
||||||
|
planStatus: number;
|
||||||
|
coursemodules: any;
|
||||||
|
user: any;
|
||||||
|
competency: any;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
|
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
@Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
this.competencyId = navParams.get('competencyId');
|
||||||
|
this.planId = navParams.get('planId');
|
||||||
|
this.courseId = navParams.get('courseId');
|
||||||
|
this.userId = navParams.get('userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchCompetency().then(() => {
|
||||||
|
if (this.planId) {
|
||||||
|
this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, this.userId);
|
||||||
|
} else {
|
||||||
|
this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, this.userId);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.competencyLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competency and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchCompetency(): Promise<void> {
|
||||||
|
let promise;
|
||||||
|
if (this.planId) {
|
||||||
|
this.planStatus = null;
|
||||||
|
promise = this.competencyProvider.getCompetencyInPlan(this.planId, this.competencyId);
|
||||||
|
} else if (this.courseId) {
|
||||||
|
promise = this.competencyProvider.getCompetencyInCourse(this.courseId, this.competencyId, this.userId);
|
||||||
|
} else {
|
||||||
|
promise = Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((competency) => {
|
||||||
|
this.competency = competency.usercompetencysummary;
|
||||||
|
|
||||||
|
if (this.planId) {
|
||||||
|
this.planStatus = competency.plan.status;
|
||||||
|
this.competency.usercompetency.statusname = this.getStatusName(this.competency.usercompetency.status);
|
||||||
|
} else {
|
||||||
|
this.competency.usercompetency = this.competency.usercompetencycourse;
|
||||||
|
this.coursemodules = competency.coursemodules;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.competency.user.id != this.sitesProvider.getCurrentSiteUserId()) {
|
||||||
|
this.competency.user.profileimageurl = this.competency.user.profileimageurl || true;
|
||||||
|
|
||||||
|
// Get the user profile image from the returned object.
|
||||||
|
this.user = this.competency.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.competency.evidence.forEach((evidence) => {
|
||||||
|
if (evidence.descidentifier) {
|
||||||
|
const key = 'addon.competency.' + evidence.descidentifier;
|
||||||
|
evidence.description = this.translate.instant(key, {$a: evidence.desca});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, (message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting competency data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to get the review status name translated.
|
||||||
|
*
|
||||||
|
* @param {number} status
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
protected getStatusName(status: number): any {
|
||||||
|
let statusTranslateName;
|
||||||
|
switch (status) {
|
||||||
|
case AddonCompetencyProvider.REVIEW_STATUS_IDLE:
|
||||||
|
statusTranslateName = 'idle';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.REVIEW_STATUS_IN_REVIEW:
|
||||||
|
statusTranslateName = 'inreview';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.REVIEW_STATUS_WAITING_FOR_REVIEW:
|
||||||
|
statusTranslateName = 'waitingforreview';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We can use the current status name.
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.translate.instant('addon.competency.usercompetencystatus_' + statusTranslateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competency.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCompetency(refresher: any): void {
|
||||||
|
let promise;
|
||||||
|
if (this.planId) {
|
||||||
|
promise = this.competencyProvider.invalidateCompetencyInPlan(this.planId, this.competencyId);
|
||||||
|
} else {
|
||||||
|
promise = this.competencyProvider.invalidateCompetencyInCourse(this.courseId, this.competencyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.finally(() => {
|
||||||
|
this.fetchCompetency().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
// Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
|
||||||
|
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||||
|
navCtrl.push('AddonCompetencyCompetencySummaryPage', {competencyId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the profile of a user.
|
||||||
|
*
|
||||||
|
* @param {number} userId
|
||||||
|
*/
|
||||||
|
openUserProfile(userId: number): void {
|
||||||
|
// Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
|
||||||
|
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||||
|
navCtrl.push('CoreUserProfilePage', {userId, courseId: this.courseId});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title *ngIf="competency">{{ competency.competency.shortname }} <small>{{ competency.competency.idnumber }}</small></ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="competencyLoaded" (ionRefresh)="refreshCompetency($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competencyLoaded">
|
||||||
|
<ion-card *ngIf="competency">
|
||||||
|
<ion-item text-wrap *ngIf="competency.competency.description">
|
||||||
|
<core-format-text [text]="competency.competency.description"></core-format-text>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.path' | translate }}</strong>:
|
||||||
|
{{ competency.comppath.framework.name }}
|
||||||
|
<span *ngFor="let ancestor of competency.comppath.ancestors">
|
||||||
|
/ <a (click)="openCompetencySummary(ancestor.id)">{{ ancestor.name }}</a>
|
||||||
|
</span>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyCompetencySummaryPage } from './competencysummary';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCompetencySummaryPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyCompetencySummaryPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencySummaryPageModule {}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// (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, Optional } from '@angular/core';
|
||||||
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-competency-summary' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competency-summary',
|
||||||
|
templateUrl: 'competencysummary.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencySummaryPage {
|
||||||
|
competencyLoaded = false;
|
||||||
|
competencyId: number;
|
||||||
|
competency: any;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
|
private domUtils: CoreDomUtilsProvider, @Optional() private svComponent: CoreSplitViewComponent,
|
||||||
|
private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
this.competencyId = navParams.get('competencyId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchCompetency().then(() => {
|
||||||
|
this.competencyProvider.logCompetencyView(this.competencyId);
|
||||||
|
}).finally(() => {
|
||||||
|
this.competencyLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competency summary and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchCompetency(): Promise<void> {
|
||||||
|
return this.competencyProvider.getCompetencySummary(this.competencyId).then((competency) => {
|
||||||
|
this.competency = competency;
|
||||||
|
}, (message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting competency summary data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competency summary.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCompetency(refresher: any): void {
|
||||||
|
this.competencyProvider.invalidateCompetencySummary(this.competencyId).finally(() => {
|
||||||
|
this.fetchCompetency().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
// Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
|
||||||
|
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||||
|
navCtrl.push('AddonCompetencyCompetencySummaryPage', {competencyId});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title>{{ 'addon.competency.coursecompetencies' | translate }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<addon-competency-course class="core-avoid-header" [courseId]="courseId" [userId]="userId"></addon-competency-course>
|
|
@ -0,0 +1,37 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyComponentsModule } from '../../components/components.module';
|
||||||
|
import { AddonCompetencyCourseCompetenciesPage } from './coursecompetencies';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCourseCompetenciesPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyCourseCompetenciesPage),
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
AddonCompetencyComponentsModule
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseCompetenciesPageModule {}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// (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, NavController, NavParams } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreAppProvider } from '../../../../providers/app';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of competencies of a course.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-coursecompetencies' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-coursecompetencies',
|
||||||
|
templateUrl: 'coursecompetencies.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseCompetenciesPage {
|
||||||
|
|
||||||
|
protected courseId: number;
|
||||||
|
protected userId: number;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
|
private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private competencyProvider: AddonCompetencyProvider, private helperProvider: AddonCompetencyHelperProvider) {
|
||||||
|
this.courseId = navParams.get('courseId');
|
||||||
|
this.userId = navParams.get('userId');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title *ngIf="plan">{{plan.plan.name}}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="planLoaded" (ionRefresh)="refreshLearningPlan($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="planLoaded">
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-avatar *ngIf="user.profileimageurl && user.profileimageurl !== true" item-start>
|
||||||
|
<img [src]="user.profileimageurl" [alt]="'core.pictureof' | translate:{$a: user.fullname}" core-external-content>
|
||||||
|
</ion-avatar>
|
||||||
|
<span *ngIf="user.profileimageurl === true" item-start>
|
||||||
|
<ion-icon name="person"></ion-icon>
|
||||||
|
</span>
|
||||||
|
<h2><core-format-text [text]="user.fullname"></core-format-text></h2>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card *ngIf="plan">
|
||||||
|
<ion-list>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.status' | translate }}</strong>:
|
||||||
|
{{ plan.plan.statusname }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="plan.plan.duedate > 0">
|
||||||
|
<strong>{{ 'addon.competency.duedate' | translate }}</strong>:
|
||||||
|
{{ plan.plan.duedate | coreToLocaleString }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap *ngIf="plan.plan.template">
|
||||||
|
<strong>{{ 'addon.competency.template' | translate }}</strong>:
|
||||||
|
{{ plan.plan.template.shortname }}
|
||||||
|
</ion-item>
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<strong>{{ 'addon.competency.progress' | translate }}</strong>:
|
||||||
|
{{ 'addon.competency.xcompetenciesproficientoutofy' | translate: {$a: {x: plan.proficientcompetencycount, y: plan.competencycount} } }}
|
||||||
|
<core-progress-bar [progress]="plan.proficientcompetencypercentage" [text]="plan.proficientcompetencypercentageformatted"></core-progress-bar>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card *ngIf="plan">
|
||||||
|
<ion-card-header text-wrap>{{ 'addon.competency.learningplancompetencies' | translate }}</ion-card-header>
|
||||||
|
<ion-list>
|
||||||
|
<ion-item text-wrap *ngIf="plan.competencycount == 0">
|
||||||
|
{{ 'addon.competency.nocompetencies' | translate }}
|
||||||
|
</ion-item>
|
||||||
|
<a ion-item text-wrap *ngFor="let competency of plan.competencies" (click)="openCompetency(competency.competency.id)" [title]="competency.competency.shortname">
|
||||||
|
{{competency.competency.shortname}} <small>{{competency.competency.idnumber}}</small>
|
||||||
|
<ion-badge item-end [color]="competency.usercompetency.proficiency ? 'success' : 'danger'">{{ competency.usercompetency.gradename }}</ion-badge>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
</ion-card>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyPlanPage } from './plan';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyPlanPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyPlanPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanPageModule {}
|
|
@ -0,0 +1,132 @@
|
||||||
|
// (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, Optional } from '@angular/core';
|
||||||
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreAppProvider } from '../../../../providers/app';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-plan' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-plan',
|
||||||
|
templateUrl: 'plan.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanPage {
|
||||||
|
protected planId: number;
|
||||||
|
planLoaded = false;
|
||||||
|
plan: any;
|
||||||
|
user: any;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
|
private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
@Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider,
|
||||||
|
private competencyHelperProvider: AddonCompetencyHelperProvider) {
|
||||||
|
this.planId = navParams.get('planId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchLearningPlan().finally(() => {
|
||||||
|
this.planLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the learning plan and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchLearningPlan(): Promise<void> {
|
||||||
|
return this.competencyProvider.getLearningPlan(this.planId).then((plan) => {
|
||||||
|
plan.plan.statusname = this.getStatusName(plan.plan.status);
|
||||||
|
// Get the user profile image.
|
||||||
|
this.competencyHelperProvider.getProfile(plan.plan.userid).then((user) => {
|
||||||
|
this.user = user;
|
||||||
|
});
|
||||||
|
this.plan = plan;
|
||||||
|
}, (message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting learning plan data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to a particular competency.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId
|
||||||
|
*/
|
||||||
|
openCompetency(competencyId: number): void {
|
||||||
|
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||||
|
navCtrl.push('AddonCompetencyCompetenciesPage', {competencyId, planId: this.planId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to get the status name translated.
|
||||||
|
*
|
||||||
|
* @param {number} status
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
protected getStatusName(status: number): any {
|
||||||
|
let statusTranslateName;
|
||||||
|
switch (status) {
|
||||||
|
case AddonCompetencyProvider.STATUS_DRAFT:
|
||||||
|
statusTranslateName = 'draft';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.REVIEW_STATUS_IN_REVIEW:
|
||||||
|
statusTranslateName = 'inreview';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.REVIEW_STATUS_WAITING_FOR_REVIEW:
|
||||||
|
statusTranslateName = 'waitingforreview';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_ACTIVE:
|
||||||
|
statusTranslateName = 'active';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_COMPLETE:
|
||||||
|
statusTranslateName = 'complete';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We can use the current status name.
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.translate.instant('addon.competency.planstatus' + statusTranslateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the learning plan.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshLearningPlan(refresher: any): void {
|
||||||
|
this.competencyProvider.invalidateLearningPlan(this.planId).finally(() => {
|
||||||
|
this.fetchLearningPlan().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title>{{ 'addon.competency.userplans' | translate }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="plansLoaded" (ionRefresh)="refreshLearningPlans($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="plansLoaded">
|
||||||
|
<core-empty-box *ngIf="plans.length == 0" icon="mapr" [message]="'addon.competency.noplanswerecreated' | translate">
|
||||||
|
</core-empty-box>
|
||||||
|
<ion-list *ngIf="plans.length > 0" no-margin>
|
||||||
|
<a ion-item text-wrap *ngFor="let plan of plans" [title]="plan.name" (click)="openPlan(plan.id)" [class.core-split-item-selected]="plan.id == planId">
|
||||||
|
<h2>{{ plan.name }}</h2>
|
||||||
|
<p *ngIf="plan.duedate > 0">{{ 'addon.competency.duedate' | translate }}: {{ plan.duedate | coreToLocaleString }}</p>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
||||||
|
</core-split-view>
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (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 { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||||
|
import { CorePipesModule } from '../../../../pipes/pipes.module';
|
||||||
|
import { AddonCompetencyPlanListPage } from './planlist';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyPlanListPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonCompetencyPlanListPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanListPageModule {}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// (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 { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '../../../../components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of learning plans.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-competency-planlist' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-planlist',
|
||||||
|
templateUrl: 'planlist.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanListPage {
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
protected userId: number;
|
||||||
|
protected planId: number;
|
||||||
|
plansLoaded = false;
|
||||||
|
plans = [];
|
||||||
|
|
||||||
|
constructor(navParams: NavParams, private translate: TranslateService, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
this.userId = navParams.get('userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
if (this.planId) {
|
||||||
|
// There is a learning plan to load.
|
||||||
|
this.openPlan(this.planId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetchLearningPlans().then(() => {
|
||||||
|
if (!this.planId && this.splitviewCtrl.isOn() && this.plans.length > 0) {
|
||||||
|
// Take first and load it.
|
||||||
|
this.openPlan(this.plans[0].id);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.plansLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the learning plans and updates the view.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchLearningPlans(): Promise<void> {
|
||||||
|
return this.competencyProvider.getLearningPlans(this.userId).then((plans) => {
|
||||||
|
this.plans = plans;
|
||||||
|
}).catch((message) => {
|
||||||
|
if (message) {
|
||||||
|
this.domUtils.showErrorModal(message);
|
||||||
|
} else {
|
||||||
|
this.domUtils.showErrorModal('Error getting learning plans data.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the learning plans.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshLearningPlans(refresher: any): void {
|
||||||
|
this.competencyProvider.invalidateLearningPlans(this.userId).finally(() => {
|
||||||
|
this.fetchLearningPlans().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a learning plan.
|
||||||
|
*
|
||||||
|
* @param {number} planId Learning plan to load.
|
||||||
|
*/
|
||||||
|
openPlan(planId: number): void {
|
||||||
|
this.planId = planId;
|
||||||
|
this.splitviewCtrl.push('AddonCompetencyPlanPage', { planId });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,572 @@
|
||||||
|
// (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 { CoreLoggerProvider } from '../../../providers/logger';
|
||||||
|
import { CoreSitesProvider } from '../../../providers/sites';
|
||||||
|
import { CoreUserProvider } from '../../../core/user/providers/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle caompetency learning plans.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonCompetencyProvider {
|
||||||
|
|
||||||
|
static STATUS_DRAFT = 0;
|
||||||
|
static STATUS_ACTIVE = 1;
|
||||||
|
static STATUS_COMPLETE = 2;
|
||||||
|
static STATUS_WAITIN_GFOR_REVIEW = 3;
|
||||||
|
static STATUS_IN_REVIEW = 4;
|
||||||
|
static REVIEW_STATUS_IDLE = 0;
|
||||||
|
static REVIEW_STATUS_WAITING_FOR_REVIEW = 1;
|
||||||
|
static REVIEW_STATUS_IN_REVIEW = 2;
|
||||||
|
protected ROOT_CACHE_KEY = 'mmaCompetency:';
|
||||||
|
|
||||||
|
protected logger;
|
||||||
|
|
||||||
|
constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) {
|
||||||
|
this.logger = loggerProvider.getInstance('AddonCompetencyProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for user learning plans data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} userId User ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getLearningPlansCacheKey(userId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'userplans:' + userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for learning plan data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} planId Plan ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getLearningPlanCacheKey(planId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'learningplan:' + planId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for competency in plan data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} planId Plan ID.
|
||||||
|
* @param {number} competencyId Competency ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCompetencyInPlanCacheKey(planId: number, competencyId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'plancompetency:' + planId + ':' + competencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for competency in course data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} courseId Course ID.
|
||||||
|
* @param {number} competencyId Competency ID.
|
||||||
|
* @param {number} userId User ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCompetencyInCourseCacheKey(courseId: number, competencyId: number, userId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'coursecompetency:' + userId + ':' + courseId + ':' + competencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for competency summary data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId Competency ID.
|
||||||
|
* @param {number} userId User ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCompetencySummaryCacheKey(competencyId: number, userId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'competencysummary:' + userId + ':' + competencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for course competencies data WS calls.
|
||||||
|
*
|
||||||
|
* @param {number} courseId Course ID.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getCourseCompetenciesCacheKey(courseId: number): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'coursecompetencies:' + courseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if competency learning plans WS is available.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<boolean>} True if competency learning plans WS is available, false otherwise.
|
||||||
|
*/
|
||||||
|
isPluginEnabled(siteId?: string): Promise<boolean> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
if (site.wsAvailable('core_competency_list_course_competencies') && site.wsAvailable('tool_lp_data_for_plans_page')) {
|
||||||
|
return this.getLearningPlans(0, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether competencies are enabled.
|
||||||
|
*
|
||||||
|
* @param {number} courseId Course ID.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} competencies if enabled for the given course, false otherwise.
|
||||||
|
*/
|
||||||
|
isPluginForCourseEnabled(courseId: number, siteId?: string): Promise<any> {
|
||||||
|
if (!this.sitesProvider.isLoggedIn()) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPluginEnabled(siteId)) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getCourseCompetencies(courseId, 0, siteId).catch(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plans for a certain user.
|
||||||
|
*
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the plans are retrieved.
|
||||||
|
*/
|
||||||
|
getLearningPlans(userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
this.logger.debug('Get plans for user ' + userId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
userid: userId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getLearningPlansCacheKey(userId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_plans_page', params, preSets).then((response) => {
|
||||||
|
if (response.plans) {
|
||||||
|
return response.plans;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a certain plan.
|
||||||
|
*
|
||||||
|
* @param {number} planId ID of the plan.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the plans are retrieved.
|
||||||
|
*/
|
||||||
|
getLearningPlan(planId: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
this.logger.debug('Get plan ' + planId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
planid: planId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getLearningPlanCacheKey(planId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_plan_page', params, preSets).then((response) => {
|
||||||
|
if (response.plan) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a certain competency in a plan.
|
||||||
|
*
|
||||||
|
* @param {number} planId ID of the plan.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the plans are retrieved.
|
||||||
|
*/
|
||||||
|
getCompetencyInPlan(planId: number, competencyId: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
this.logger.debug('Get competency ' + competencyId + ' in plan ' + planId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
planid: planId,
|
||||||
|
competencyid: competencyId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getCompetencyInPlanCacheKey(planId, competencyId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_user_competency_summary_in_plan', params, preSets).then((response) => {
|
||||||
|
if (response.usercompetencysummary) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a certain competency in a course.
|
||||||
|
*
|
||||||
|
* @param {number} courseId ID of the course.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the plans are retrieved.
|
||||||
|
*/
|
||||||
|
getCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
this.logger.debug('Get competency ' + competencyId + ' in course ' + courseId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
courseid: courseId,
|
||||||
|
competencyid: competencyId,
|
||||||
|
userid: userId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getCompetencyInCourseCacheKey(courseId, competencyId, userId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_user_competency_summary_in_course', params, preSets).then((response) => {
|
||||||
|
if (response.usercompetencysummary) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a certain competency summary.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the plans are retrieved.
|
||||||
|
*/
|
||||||
|
getCompetencySummary(competencyId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
this.logger.debug('Get competency ' + competencyId + ' summary for user' + userId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
competencyid: competencyId,
|
||||||
|
userid: userId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getCompetencySummaryCacheKey(competencyId, userId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_user_competency_summary', params, preSets).then((response) => {
|
||||||
|
if (response.competency) {
|
||||||
|
return response.competency;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an specific competency summary.
|
||||||
|
*
|
||||||
|
* @param {number} courseId ID of the course.
|
||||||
|
* @param {number} [userId] ID of the user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise to be resolved when the course competencies are retrieved.
|
||||||
|
*/
|
||||||
|
getCourseCompetencies(courseId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
this.logger.debug('Get course competencies for course ' + courseId);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
courseid: courseId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
cacheKey: this.getCourseCompetenciesCacheKey(courseId)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('tool_lp_data_for_course_competencies_page', params, preSets).then((response) => {
|
||||||
|
if (response.competencies) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
}).then((response) => {
|
||||||
|
|
||||||
|
if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = response.competencies.map((competency) =>
|
||||||
|
this.getCompetencyInCourse(courseId, competency.competency.id, userId, siteId)
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.all(promises).then((responses: any[]) => {
|
||||||
|
responses.forEach((resp, index) => {
|
||||||
|
response.competencies[index].usercompetencycourse = resp.usercompetencysummary.usercompetencycourse;
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates User Learning Plans data.
|
||||||
|
*
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateLearningPlans(userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(this.getLearningPlansCacheKey(userId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates Learning Plan data.
|
||||||
|
*
|
||||||
|
* @param {number} planId ID of the plan.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateLearningPlan(planId: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getLearningPlanCacheKey(planId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates Competency in Plan data.
|
||||||
|
*
|
||||||
|
* @param {number} planId ID of the plan.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateCompetencyInPlan(planId: number, competencyId: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getCompetencyInPlanCacheKey(planId, competencyId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates Competency in Course data.
|
||||||
|
*
|
||||||
|
* @param {number} courseId ID of the course.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(this.getCompetencyInCourseCacheKey(courseId, competencyId, userId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates Competency Summary data.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} [userId] ID of the user. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateCompetencySummary(competencyId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(this.getCompetencySummaryCacheKey(competencyId, userId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates Course Competencies data.
|
||||||
|
*
|
||||||
|
* @param {number} courseId ID of the course.
|
||||||
|
* @param {number} [userId] ID of the user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateCourseCompetencies(courseId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getCourseCompetenciesCacheKey(courseId));
|
||||||
|
}).then((response) => {
|
||||||
|
if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Competencies for other users are fetched with getCompetencyInCourse (and saved in their own cache).
|
||||||
|
We need to fecth the list of competencies to know which ones to invalidate. We can pass 0 as userId
|
||||||
|
to getCourseCompetencies, we just need the competency IDs and this way we avid extra WS calls. */
|
||||||
|
return this.getCourseCompetencies(courseId, 0, siteId).then((competencies) => {
|
||||||
|
const promises = competencies.competencies.map((competency) => {
|
||||||
|
return this.invalidateCompetencyInCourse(courseId, competency.competency.id, userId, siteId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report the competency as being viewed in plan.
|
||||||
|
*
|
||||||
|
* @param {number} planId ID of the plan.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} planStatus Current plan Status to decide what action should be logged.
|
||||||
|
* @param {number} [userId] User ID. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||||
|
*/
|
||||||
|
logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, userId?: number, siteId?: string)
|
||||||
|
: Promise<any> {
|
||||||
|
if (planId && competencyId) {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
planid: planId,
|
||||||
|
competencyid: competencyId,
|
||||||
|
userid: userId
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
typeExpected: 'boolean'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (planStatus == AddonCompetencyProvider.STATUS_COMPLETE) {
|
||||||
|
return site.write('core_competency_user_competency_plan_viewed', params, preSets);
|
||||||
|
} else {
|
||||||
|
return site.write('core_competency_user_competency_viewed_in_plan', params, preSets);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report the competency as being viewed in course.
|
||||||
|
*
|
||||||
|
* @param {number} courseId ID of the course.
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {number} [userId] User ID. If not defined, current user.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||||
|
*/
|
||||||
|
logCompetencyInCourseView(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
if (courseId && competencyId) {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
courseid: courseId,
|
||||||
|
competencyid: competencyId,
|
||||||
|
userid: userId
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
typeExpected: 'boolean'
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_competency_user_competency_viewed_in_course', params, preSets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report the competency as being viewed.
|
||||||
|
*
|
||||||
|
* @param {number} competencyId ID of the competency.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||||
|
*/
|
||||||
|
logCompetencyView(competencyId: number, siteId?: string): Promise<any> {
|
||||||
|
if (competencyId) {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
id: competencyId,
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
typeExpected: 'boolean'
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_competency_competency_viewed', params, preSets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// (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 } from 'ionic-angular';
|
||||||
|
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../../core/course/providers/options-delegate';
|
||||||
|
import { CoreCourseProvider } from '../../../core/course/providers/course';
|
||||||
|
import { AddonCompetencyCourseComponent } from '../components/course/course';
|
||||||
|
import { AddonCompetencyProvider } from '../providers/competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course nav handler.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonCompetencyCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||||
|
name = 'AddonCompetency';
|
||||||
|
priority = 700;
|
||||||
|
|
||||||
|
protected coursesNavEnabledCache = {};
|
||||||
|
|
||||||
|
constructor(private competencyProvider: AddonCompetencyProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear courses nav cache.
|
||||||
|
*/
|
||||||
|
clearCoursesNavCache(): void {
|
||||||
|
this.coursesNavEnabledCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled ona site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.competencyProvider.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled for a certain course.
|
||||||
|
*
|
||||||
|
* @param {number} courseId The course ID.
|
||||||
|
* @param {any} accessData Access type and data. Default, guest, ...
|
||||||
|
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||||
|
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
|
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||||
|
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||||
|
return false; // Not enabled for guests.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navOptions && typeof navOptions.competencies != 'undefined') {
|
||||||
|
return navOptions.competencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.coursesNavEnabledCache[courseId] != 'undefined') {
|
||||||
|
return this.coursesNavEnabledCache[courseId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.competencyProvider.isPluginForCourseEnabled(courseId).then((competencies) => {
|
||||||
|
const enabled = competencies ? !competencies.canmanagecoursecompetencies : false;
|
||||||
|
this.coursesNavEnabledCache[courseId] = enabled;
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the handler.
|
||||||
|
*
|
||||||
|
* @param {number} courseId The course ID.
|
||||||
|
* @return {CoreCourseOptionsHandlerData} Data.
|
||||||
|
*/
|
||||||
|
getDisplayData(courseId: number): CoreCourseOptionsHandlerData {
|
||||||
|
return {
|
||||||
|
title: 'addon.competency.competencies',
|
||||||
|
class: 'addon-competency-course-handler',
|
||||||
|
component: AddonCompetencyCourseComponent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should invalidate the data to determine if the handler is enabled for a certain course.
|
||||||
|
*
|
||||||
|
* @param {number} courseId The course ID.
|
||||||
|
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||||
|
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise<any> {
|
||||||
|
if (navOptions && typeof navOptions.competencies != 'undefined') {
|
||||||
|
// No need to invalidate anything.
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.competencyProvider.invalidateCourseCompetencies(courseId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// (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 { CoreSitesProvider } from '../../../providers/sites';
|
||||||
|
import { CoreUserProvider } from '../../../core/user/providers/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features regarding the user profile.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonCompetencyHelperProvider {
|
||||||
|
|
||||||
|
constructor(private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient helper to get the user profile image.
|
||||||
|
*
|
||||||
|
* @param {number} userId User Id
|
||||||
|
* @return {Promise<any>} User profile Image URL or true if default icon.
|
||||||
|
*/
|
||||||
|
getProfile(userId: number): Promise<any> {
|
||||||
|
if (!userId || userId == this.sitesProvider.getCurrentSiteUserId()) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user profile to retrieve the user image.
|
||||||
|
return this.userProvider.getProfile(userId, null, true).then((user) => {
|
||||||
|
user.profileimageurl = user.profileimageurl || true;
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// (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 { AddonCompetencyProvider } from './competency';
|
||||||
|
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../../core/mainmenu/providers/delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to inject an option into main menu.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonCompetencyMainMenuHandler implements CoreMainMenuHandler {
|
||||||
|
name = 'AddonCompetency';
|
||||||
|
priority = 900;
|
||||||
|
|
||||||
|
constructor(private competencyProvider: AddonCompetencyProvider) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.competencyProvider.isPluginEnabled().then((enabled) => {
|
||||||
|
if (!enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the user has at least one learn plan available.
|
||||||
|
return this.competencyProvider.getLearningPlans().then((plans) => {
|
||||||
|
return plans.length > 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the handler.
|
||||||
|
*
|
||||||
|
* @return {CoreMainMenuHandlerData} Data needed to render the handler.
|
||||||
|
*/
|
||||||
|
getDisplayData(): CoreMainMenuHandlerData {
|
||||||
|
return {
|
||||||
|
icon: 'map',
|
||||||
|
title: 'addon.competency.myplans',
|
||||||
|
page: 'AddonCompetencyPlanListPage',
|
||||||
|
class: 'addon-competency-handler'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
// (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 { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate';
|
||||||
|
import { CoreSitesProvider } from '../../../providers/sites';
|
||||||
|
import { CoreContentLinksHelperProvider } from '../../../core/contentlinks/providers/helper';
|
||||||
|
import { AddonCompetencyProvider } from './competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profile competencies handler.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonCompetencyUserHandler implements CoreUserProfileHandler {
|
||||||
|
name = 'AddonCompetency';
|
||||||
|
priority = 900;
|
||||||
|
type = CoreUserDelegate.TYPE_NEW_PAGE;
|
||||||
|
participantsNavEnabledCache = {};
|
||||||
|
usersNavEnabledCache = {};
|
||||||
|
|
||||||
|
constructor(private linkHelper: CoreContentLinksHelperProvider, protected sitesProvider: CoreSitesProvider,
|
||||||
|
private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear users nav cache.
|
||||||
|
*/
|
||||||
|
clearUsersNavCache(): void {
|
||||||
|
this.participantsNavEnabledCache = {};
|
||||||
|
this.usersNavEnabledCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.competencyProvider.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if handler is enabled for this user in this context.
|
||||||
|
*
|
||||||
|
* @param {any} user User to check.
|
||||||
|
* @param {number} courseId Course ID.
|
||||||
|
* @param {any} [navOptions] Course navigation options for current user. See $mmCourses#getUserNavigationOptions.
|
||||||
|
* @param {any} [admOptions] Course admin options for current user. See $mmCourses#getUserAdministrationOptions.
|
||||||
|
* @return {boolean|Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise.
|
||||||
|
*/
|
||||||
|
isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||||
|
if (courseId) {
|
||||||
|
const cacheKey = courseId + '.' + user.id;
|
||||||
|
|
||||||
|
// Link on a user course profile.
|
||||||
|
if (typeof this.participantsNavEnabledCache[cacheKey] != 'undefined') {
|
||||||
|
return this.participantsNavEnabledCache[cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.competencyProvider.getCourseCompetencies(courseId, user.id).then((response) => {
|
||||||
|
const enabled = response.competencies.length > 0;
|
||||||
|
this.participantsNavEnabledCache[cacheKey] = enabled;
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}).catch((message) => {
|
||||||
|
this.participantsNavEnabledCache[cacheKey] = false;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Link on a user site profile.
|
||||||
|
if (typeof this.usersNavEnabledCache[user.id] != 'undefined') {
|
||||||
|
return this.usersNavEnabledCache[user.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.competencyProvider.getLearningPlans(user.id).then((plans) => {
|
||||||
|
// Check the user has at least one learn plan available.
|
||||||
|
const enabled = plans.length > 0;
|
||||||
|
this.usersNavEnabledCache[user.id] = enabled;
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the handler.
|
||||||
|
*
|
||||||
|
* @return {CoreUserProfileHandlerData} Data needed to render the handler.
|
||||||
|
*/
|
||||||
|
getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData {
|
||||||
|
if (courseId) {
|
||||||
|
return {
|
||||||
|
icon: 'ribbon',
|
||||||
|
title: 'addon.competency.competencies',
|
||||||
|
class: 'addon-competency-handler',
|
||||||
|
action: ($event, navCtrl, user, courseId): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCourseCompetenciesPage', {courseId, userId: user.id});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
icon: 'map',
|
||||||
|
title: 'addon.competency.learningplans',
|
||||||
|
class: 'addon-competency-handler',
|
||||||
|
action: ($event, navCtrl, user, courseId): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', {userId: user.id});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,6 +70,7 @@ import { CoreCompileModule } from '@core/compile/compile.module';
|
||||||
|
|
||||||
// Addon modules.
|
// Addon modules.
|
||||||
import { AddonCalendarModule } from '@addon/calendar/calendar.module';
|
import { AddonCalendarModule } from '@addon/calendar/calendar.module';
|
||||||
|
import { AddonCompetencyModule } from '../addon/competency/competency.module';
|
||||||
import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
||||||
import { AddonFilesModule } from '@addon/files/files.module';
|
import { AddonFilesModule } from '@addon/files/files.module';
|
||||||
import { AddonModBookModule } from '@addon/mod/book/book.module';
|
import { AddonModBookModule } from '@addon/mod/book/book.module';
|
||||||
|
@ -147,6 +148,7 @@ export const CORE_PROVIDERS: any[] = [
|
||||||
CoreSitePluginsModule,
|
CoreSitePluginsModule,
|
||||||
CoreCompileModule,
|
CoreCompileModule,
|
||||||
AddonCalendarModule,
|
AddonCalendarModule,
|
||||||
|
AddonCompetencyModule,
|
||||||
AddonUserProfileFieldModule,
|
AddonUserProfileFieldModule,
|
||||||
AddonFilesModule,
|
AddonFilesModule,
|
||||||
AddonModBookModule,
|
AddonModBookModule,
|
||||||
|
|
Loading…
Reference in New Issue