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.
|
||||
import { AddonCalendarModule } from '@addon/calendar/calendar.module';
|
||||
import { AddonCompetencyModule } from '../addon/competency/competency.module';
|
||||
import { AddonUserProfileFieldModule } from '@addon/userprofilefield/userprofilefield.module';
|
||||
import { AddonFilesModule } from '@addon/files/files.module';
|
||||
import { AddonModBookModule } from '@addon/mod/book/book.module';
|
||||
|
@ -147,6 +148,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
CoreSitePluginsModule,
|
||||
CoreCompileModule,
|
||||
AddonCalendarModule,
|
||||
AddonCompetencyModule,
|
||||
AddonUserProfileFieldModule,
|
||||
AddonFilesModule,
|
||||
AddonModBookModule,
|
||||
|
|
Loading…
Reference in New Issue