MOBILE-3628 competency: Add competencies and learning plans functionality
parent
717583dc9b
commit
e8830eedaf
|
@ -30,6 +30,7 @@ import { AddonQtypeModule } from './qtype/qtype.module';
|
||||||
import { AddonBlogModule } from './blog/blog.module';
|
import { AddonBlogModule } from './blog/blog.module';
|
||||||
import { AddonRemoteThemesModule } from './remotethemes/remotethemes.module';
|
import { AddonRemoteThemesModule } from './remotethemes/remotethemes.module';
|
||||||
import { AddonNotesModule } from './notes/notes.module';
|
import { AddonNotesModule } from './notes/notes.module';
|
||||||
|
import { AddonCompetencyModule } from './competency/competency.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -37,6 +38,7 @@ import { AddonNotesModule } from './notes/notes.module';
|
||||||
AddonBadgesModule,
|
AddonBadgesModule,
|
||||||
AddonBlogModule,
|
AddonBlogModule,
|
||||||
AddonCalendarModule,
|
AddonCalendarModule,
|
||||||
|
AddonCompetencyModule,
|
||||||
AddonCourseCompletionModule,
|
AddonCourseCompletionModule,
|
||||||
AddonMessagesModule,
|
AddonMessagesModule,
|
||||||
AddonPrivateFilesModule,
|
AddonPrivateFilesModule,
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { CoreBlockHandlerData } from '@features/block/services/block-delegate';
|
||||||
import { CoreBlockOnlyTitleComponent } from '@features/block/components/only-title-block/only-title-block';
|
import { CoreBlockOnlyTitleComponent } from '@features/block/components/only-title-block/only-title-block';
|
||||||
import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler';
|
import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from '@addons/competency/services/handlers/mainmenu';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block handler.
|
* Block handler.
|
||||||
|
@ -39,7 +40,7 @@ export class AddonBlockLearningPlansHandlerService extends CoreBlockBaseHandler
|
||||||
title: 'addon.block_learningplans.pluginname',
|
title: 'addon.block_learningplans.pluginname',
|
||||||
class: 'addon-block-learning-plans',
|
class: 'addon-block-learning-plans',
|
||||||
component: CoreBlockOnlyTitleComponent,
|
component: CoreBlockOnlyTitleComponent,
|
||||||
link: 'AddonCompetencyPlanListPage',
|
link: AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { AddonCompetencyCourseCompetenciesPageModule } from './pages/coursecompetencies/coursecompetencies.module';
|
||||||
|
import { AddonCompetencyCourseCompetenciesPage } from './pages/coursecompetencies/coursecompetencies.page';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AddonCompetencyCourseCompetenciesPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
AddonCompetencyCourseCompetenciesPageModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseLazyModule {}
|
|
@ -0,0 +1,108 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { conditionalRoutes } from '@/app/app-routing.module';
|
||||||
|
import { CoreScreen } from '@services/screen';
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonCompetencyPlanPage } from './pages/plan/plan.page';
|
||||||
|
import { AddonCompetencyPlanListPage } from './pages/planlist/planlist.page';
|
||||||
|
import { AddonCompetencyCompetenciesPage } from './pages/competencies/competencies.page';
|
||||||
|
import { AddonCompetencyCompetencyPage } from './pages/competency/competency.page';
|
||||||
|
import { AddonCompetencyCompetencySummaryPage } from './pages/competencysummary/competencysummary.page';
|
||||||
|
import { AddonCompetencyCourseCompetenciesPage } from './pages/coursecompetencies/coursecompetencies.page';
|
||||||
|
import { AddonCompetencyCourseCompetenciesPageModule } from './pages/coursecompetencies/coursecompetencies.module';
|
||||||
|
|
||||||
|
const mobileRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: AddonCompetencyPlanListPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'competencies/',
|
||||||
|
component: AddonCompetencyCompetenciesPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'competencies/:competencyId',
|
||||||
|
component: AddonCompetencyCompetencyPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'course/:courseId',
|
||||||
|
component: AddonCompetencyCourseCompetenciesPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'summary/:competencyId',
|
||||||
|
component: AddonCompetencyCompetencySummaryPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':planId',
|
||||||
|
component: AddonCompetencyPlanPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tabletRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'summary/:competencyId',
|
||||||
|
component: AddonCompetencyCompetencySummaryPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'competencies',
|
||||||
|
component: AddonCompetencyCompetenciesPage,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: ':competencyId',
|
||||||
|
component: AddonCompetencyCompetencyPage,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'course/:courseId',
|
||||||
|
component: AddonCompetencyCourseCompetenciesPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AddonCompetencyPlanListPage,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: ':planId',
|
||||||
|
component: AddonCompetencyPlanPage,
|
||||||
|
},
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
...conditionalRoutes(mobileRoutes, () => CoreScreen.isMobile),
|
||||||
|
...conditionalRoutes(tabletRoutes, () => CoreScreen.isTablet),
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CoreSharedModule,
|
||||||
|
AddonCompetencyCourseCompetenciesPageModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyPlanPage,
|
||||||
|
AddonCompetencyPlanListPage,
|
||||||
|
AddonCompetencyCompetenciesPage,
|
||||||
|
AddonCompetencyCompetencyPage,
|
||||||
|
AddonCompetencyCompetencySummaryPage,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyLazyModule {}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||||
|
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
||||||
|
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
|
||||||
|
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||||
|
import { CoreUserDelegate } from '@features/user/services/user-delegate';
|
||||||
|
import { AddonCompetencyProvider } from './services/competency';
|
||||||
|
import { AddonCompetencyHelperProvider } from './services/competency-helper';
|
||||||
|
import { AddonCompetencyCompetencyLinkHandler } from './services/handlers/competency-link';
|
||||||
|
import { AddonCompetencyCourseOptionHandler } from './services/handlers/course-option';
|
||||||
|
import { AddonCompetencyMainMenuHandler, AddonCompetencyMainMenuHandlerService } from './services/handlers/mainmenu';
|
||||||
|
import { AddonCompetencyPlanLinkHandler } from './services/handlers/plan-link';
|
||||||
|
import { AddonCompetencyPlansLinkHandler } from './services/handlers/plans-link';
|
||||||
|
import { AddonCompetencyPushClickHandler } from './services/handlers/push-click';
|
||||||
|
import { AddonCompetencyUserCompetencyLinkHandler } from './services/handlers/user-competency-link';
|
||||||
|
import { AddonCompetencyUserHandler } from './services/handlers/user';
|
||||||
|
import { Routes } from '@angular/router';
|
||||||
|
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
|
||||||
|
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||||
|
import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index-routing.module';
|
||||||
|
|
||||||
|
// List of providers (without handlers).
|
||||||
|
export const ADDON_COMPETENCY_SERVICES: Type<unknown>[] = [
|
||||||
|
AddonCompetencyProvider,
|
||||||
|
AddonCompetencyHelperProvider,
|
||||||
|
];
|
||||||
|
|
||||||
|
const mainMenuChildrenRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
|
loadChildren: () => import('./competency-lazy.module').then(m => m.AddonCompetencyLazyModule),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const courseIndexRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
|
loadChildren: () => import('@addons/competency/competency-course-lazy.module').then(m => m.AddonCompetencyCourseLazyModule),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CoreMainMenuTabRoutingModule.forChild(mainMenuChildrenRoutes),
|
||||||
|
CoreMainMenuRoutingModule.forChild({ children: mainMenuChildrenRoutes }),
|
||||||
|
CoreCourseIndexRoutingModule.forChild({ children: courseIndexRoutes }),
|
||||||
|
],
|
||||||
|
exports: [CoreMainMenuRoutingModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => async () => {
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonCompetencyCompetencyLinkHandler.instance);
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonCompetencyPlanLinkHandler.instance);
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonCompetencyPlansLinkHandler.instance);
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonCompetencyUserCompetencyLinkHandler.instance);
|
||||||
|
CoreMainMenuDelegate.registerHandler(AddonCompetencyMainMenuHandler.instance);
|
||||||
|
CoreUserDelegate.registerHandler(AddonCompetencyUserHandler.instance);
|
||||||
|
CoreCourseOptionsDelegate.registerHandler(AddonCompetencyCourseOptionHandler.instance);
|
||||||
|
CorePushNotificationsDelegate.registerClickHandler(AddonCompetencyPushClickHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyModule {}
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"nocompetenciesincourse": "No competencies have been linked to this course.",
|
||||||
|
"nocrossreferencedcompetencies": "No other competencies have been cross-referenced to this competency.",
|
||||||
|
"noevidence": "No evidence",
|
||||||
|
"noplanswerecreated": "No learning plans were created.",
|
||||||
|
"nouserplanswithcompetency": "No learning plans contain this competency.",
|
||||||
|
"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",
|
||||||
|
"uponcoursecompletion": "Upon course completion:",
|
||||||
|
"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,34 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>{{ title }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!competencies.loaded" (ionRefresh)="refreshCompetencies($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competencies.loaded">
|
||||||
|
<ion-list>
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let competency of competencies.items"
|
||||||
|
[title]="competency.competency.shortname" (click)="competencies.select(competency)"
|
||||||
|
[class.core-selected-item]="competencies.isSelected(competency)">
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ competency.competency.shortname }} <em>{{competency.competency.idnumber}}</em></h2>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge slot="end" *ngIf="competency.usercompetency"
|
||||||
|
[color]="competency.usercompetency.proficiency ? 'success' : 'danger'">
|
||||||
|
{{ competency.usercompetency.gradename }}
|
||||||
|
</ion-badge>
|
||||||
|
<ion-badge slot="end" *ngIf="competency.usercompetencycourse"
|
||||||
|
[color]="competency.usercompetencycourse.proficiency ? 'success' : 'danger'">
|
||||||
|
{{ competency.usercompetencycourse.gradename }}
|
||||||
|
</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</core-split-view>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,172 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import {
|
||||||
|
AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForCourseCompetenciesPageCompetency, AddonCompetency,
|
||||||
|
} from '../../services/competency';
|
||||||
|
import { Params, ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
|
||||||
|
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
|
||||||
|
import { Translate } from '@singletons';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of competencies of a learning plan.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competencies',
|
||||||
|
templateUrl: 'competencies.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetenciesPage implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
protected planId?: number;
|
||||||
|
protected courseId?: number;
|
||||||
|
protected userId?: number;
|
||||||
|
|
||||||
|
competenciesLoaded = false;
|
||||||
|
competencies: AddonCompetencyListManager;
|
||||||
|
title = '';
|
||||||
|
|
||||||
|
constructor(protected route: ActivatedRoute) {
|
||||||
|
this.planId = CoreNavigator.getRouteNumberParam('planId', { route });
|
||||||
|
if (!this.planId) {
|
||||||
|
this.courseId = CoreNavigator.getRouteNumberParam('courseId', { route });
|
||||||
|
this.userId = CoreNavigator.getRouteNumberParam('userId', { route });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.competencies =
|
||||||
|
new AddonCompetencyListManager(AddonCompetencyCompetenciesPage, this.planId, this.courseId, this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngAfterViewInit(): Promise<void> {
|
||||||
|
await this.fetchCompetencies();
|
||||||
|
|
||||||
|
this.competencies.start(this.splitView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competencies and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchCompetencies(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (this.planId) {
|
||||||
|
|
||||||
|
const response = await AddonCompetency.getLearningPlan(this.planId);
|
||||||
|
|
||||||
|
if (response.competencycount <= 0) {
|
||||||
|
throw new CoreError(Translate.instant('addon.competency.errornocompetenciesfound'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.title = response.plan.name;
|
||||||
|
this.userId = response.plan.userid;
|
||||||
|
|
||||||
|
this.competencies.setItems(response.competencies);
|
||||||
|
} else if (this.courseId) {
|
||||||
|
const response = await AddonCompetency.getCourseCompetencies(this.courseId, this.userId);
|
||||||
|
this.title = Translate.instant('addon.competency.coursecompetencies');
|
||||||
|
|
||||||
|
this.competencies.setItems(response.competencies);
|
||||||
|
} else {
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
} catch (message) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(message, 'Error getting competencies data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competencies.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
async refreshCompetencies(refresher?: IonRefresher): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (this.planId) {
|
||||||
|
await AddonCompetency.invalidateLearningPlan(this.planId);
|
||||||
|
} else {
|
||||||
|
await AddonCompetency.invalidateCourseCompetencies(this.courseId!, this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
this.fetchCompetencies().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.competencies.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddonCompetencyDataForPlanPageCompetencyFormatted =
|
||||||
|
AddonCompetencyDataForPlanPageCompetency | AddonCompetencyDataForCourseCompetenciesPageCompetency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to manage competencies list.
|
||||||
|
*/
|
||||||
|
class AddonCompetencyListManager extends CorePageItemsListManager<AddonCompetencyDataForPlanPageCompetencyFormatted> {
|
||||||
|
|
||||||
|
planId?: number;
|
||||||
|
courseId?: number;
|
||||||
|
userId?: number;
|
||||||
|
|
||||||
|
constructor(pageComponent: unknown, planId?: number, courseId?: number, userId?: number) {
|
||||||
|
super(pageComponent);
|
||||||
|
this.planId = planId;
|
||||||
|
this.courseId = courseId;
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getItemPath(competency: AddonCompetencyDataForPlanPageCompetencyFormatted): string {
|
||||||
|
return String(competency.competency.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getItemQueryParams(): Params {
|
||||||
|
if (this.planId) {
|
||||||
|
return { planId: this.planId };
|
||||||
|
} else {
|
||||||
|
return { courseId: this.courseId, userId: this.userId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||||
|
return route.params.competencyId ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title *ngIf="competency">
|
||||||
|
{{ competency!.competency.competency.shortname }} <small>{{ competency.competency.competency.idnumber }}</small>
|
||||||
|
</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!competencyLoaded" (ionRefresh)="refreshCompetency($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competencyLoaded">
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
|
||||||
|
<ion-label><h2>{{ user!.fullname }}</h2></ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<ion-card *ngIf="competency">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="competency!.competency.competency.description">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="competency!.competency.competency.description" [contextLevel]="contextLevel"
|
||||||
|
[contextInstanceId]="contextInstanceId">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.path' | translate }}</strong>
|
||||||
|
<a *ngIf="competency!.competency.comppath.showlinks"
|
||||||
|
[href]="competency!.competency.comppath.pluginbaseurl + '/competencies.php?competencyframeworkid=' +
|
||||||
|
competency!.competency.comppath.framework.id + '&pagecontextid=' +
|
||||||
|
competency!.competency.comppath.pagecontextid"
|
||||||
|
core-link [title]="competency!.competency.comppath.framework.name">
|
||||||
|
{{ competency!.competency.comppath.framework.name }}
|
||||||
|
</a>
|
||||||
|
<ng-container *ngIf="!competency!.competency.comppath.showlinks">
|
||||||
|
{{ competency!.competency.comppath.framework.name }}
|
||||||
|
</ng-container>
|
||||||
|
/
|
||||||
|
<span *ngFor="let ancestor of competency!.competency.comppath.ancestors">
|
||||||
|
<a *ngIf="competency!.competency.comppath.showlinks" (click)="openCompetencySummary(ancestor.id)"
|
||||||
|
class="core-clickable">
|
||||||
|
{{ ancestor.name }}
|
||||||
|
</a>
|
||||||
|
<ng-container *ngIf="!competency!.competency.comppath.showlinks">{{ ancestor.name }}</ng-container>
|
||||||
|
<ng-container *ngIf="!ancestor.last"> / </ng-container>
|
||||||
|
</span>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<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)" class="core-clickable">
|
||||||
|
{{ relatedcomp.shortname }} - {{ relatedcomp.idnumber }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="coursemodules">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.activities' | translate }}</strong>
|
||||||
|
<p *ngIf="coursemodules!.length == 0">
|
||||||
|
{{ 'addon.competency.noactivities' | translate }}
|
||||||
|
</p>
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url" [title]="activity.name"
|
||||||
|
core-link capture="true">
|
||||||
|
<img slot="start" core-external-content [src]="activity.iconurl" alt="" role="presentation" *ngIf="activity.iconurl"
|
||||||
|
class="core-module-icon">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
||||||
|
[courseId]="courseId">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ng-container *ngIf="userCompetency">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="competency!.usercompetency && competency!.usercompetency!.status">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.reviewstatus' | translate }}</strong>
|
||||||
|
{{ competency!.usercompetency!.statusname }}
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.proficient' | translate }}</strong>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge slot="end" color="success" *ngIf="userCompetency!.proficiency">
|
||||||
|
{{ 'core.yes' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
<ion-badge slot="end" color="danger" *ngIf="!userCompetency!.proficiency">
|
||||||
|
{{ 'core.no' | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.rating' | translate }}</strong>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge color="dark" slot="end">{{ userCompetency!.gradename }}</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<div *ngIf="competency">
|
||||||
|
<h3 class="ion-margin-horizontal">{{ 'addon.competency.evidence' | translate }}</h3>
|
||||||
|
<p class="ion-margin-horizontal" *ngIf="competency!.evidence.length == 0">
|
||||||
|
{{ 'addon.competency.noevidence' | translate }}
|
||||||
|
</p>
|
||||||
|
<ion-card *ngFor="let evidence of competency!.evidence">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="evidence.actionuser" core-user-link [userId]="evidence.actionuser!.id"
|
||||||
|
[courseId]="courseId">
|
||||||
|
<core-user-avatar [user]="evidence.actionuser" slot="start"></core-user-avatar>
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ evidence.actionuser!.fullname }}</h2>
|
||||||
|
<p>{{ evidence.timemodified * 1000 | coreFormatDate }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<p><ion-badge color="dark">{{ evidence.gradename }}</ion-badge></p>
|
||||||
|
<p class="ion-margin-top" *ngIf="evidence.description">{{ evidence.description }}</p>
|
||||||
|
<blockquote *ngIf="evidence.note">{{ evidence.note }}</blockquote>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</div>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,186 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { AddonCompetencyHelper } from '@addons/competency/services/competency-helper';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CoreCourseModuleSummary } from '@features/course/services/course';
|
||||||
|
import { CoreUserSummary } from '@features/user/services/user';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { Translate } from '@singletons';
|
||||||
|
import {
|
||||||
|
AddonCompetencyDataForUserCompetencySummaryWSResponse,
|
||||||
|
AddonCompetencyUserCompetencyPlan,
|
||||||
|
AddonCompetencyUserCompetency,
|
||||||
|
AddonCompetencyUserCompetencyCourse,
|
||||||
|
AddonCompetency,
|
||||||
|
AddonCompetencyDataForUserCompetencySummaryInPlanWSResponse,
|
||||||
|
AddonCompetencyDataForUserCompetencySummaryInCourseWSResponse,
|
||||||
|
} from '@addons/competency/services/competency';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { ContextLevel } from '@/core/constants';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from '@addons/competency/services/handlers/mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competency',
|
||||||
|
templateUrl: 'competency.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencyPage implements OnInit {
|
||||||
|
|
||||||
|
competencyLoaded = false;
|
||||||
|
competencyId!: number;
|
||||||
|
planId?: number;
|
||||||
|
courseId?: number;
|
||||||
|
userId?: number;
|
||||||
|
planStatus?: number;
|
||||||
|
coursemodules?: CoreCourseModuleSummary[];
|
||||||
|
user?: CoreUserSummary;
|
||||||
|
competency?: AddonCompetencyDataForUserCompetencySummaryWSResponse;
|
||||||
|
userCompetency?: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse;
|
||||||
|
contextLevel?: string;
|
||||||
|
contextInstanceId?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.competencyId = CoreNavigator.getRouteNumberParam('competencyId')!;
|
||||||
|
this.planId = CoreNavigator.getRouteNumberParam('planId');
|
||||||
|
if (!this.planId) {
|
||||||
|
this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
|
||||||
|
this.userId = CoreNavigator.getRouteNumberParam('userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.fetchCompetency();
|
||||||
|
|
||||||
|
const name = this.competency && this.competency.competency && this.competency.competency.competency &&
|
||||||
|
this.competency.competency.competency.shortname;
|
||||||
|
|
||||||
|
if (this.planId) {
|
||||||
|
CoreUtils.ignoreErrors(AddonCompetency.logCompetencyInPlanView(
|
||||||
|
this.planId,
|
||||||
|
this.competencyId,
|
||||||
|
this.planStatus!,
|
||||||
|
name,
|
||||||
|
this.userId,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
CoreUtils.ignoreErrors(
|
||||||
|
AddonCompetency.logCompetencyInCourseView(this.courseId!, this.competencyId, name, this.userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.competencyLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competency and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchCompetency(): Promise<void> {
|
||||||
|
|
||||||
|
try {
|
||||||
|
let competency: AddonCompetencyDataForUserCompetencySummaryInPlanWSResponse |
|
||||||
|
AddonCompetencyDataForUserCompetencySummaryInCourseWSResponse;
|
||||||
|
|
||||||
|
if (this.planId) {
|
||||||
|
this.planStatus = undefined;
|
||||||
|
|
||||||
|
competency = await AddonCompetency.getCompetencyInPlan(this.planId, this.competencyId);
|
||||||
|
} else if (this.courseId) {
|
||||||
|
competency = await AddonCompetency.getCompetencyInCourse(this.courseId, this.competencyId, this.userId);
|
||||||
|
} else {
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the context.
|
||||||
|
if (this.courseId) {
|
||||||
|
this.contextLevel = ContextLevel.COURSE;
|
||||||
|
this.contextInstanceId = this.courseId;
|
||||||
|
} else {
|
||||||
|
this.contextLevel = ContextLevel.USER;
|
||||||
|
this.contextInstanceId = this.userId || competency.usercompetencysummary.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.competency = competency.usercompetencysummary;
|
||||||
|
this.userCompetency = this.competency.usercompetencyplan || this.competency.usercompetency;
|
||||||
|
|
||||||
|
if ('plan' in competency) {
|
||||||
|
this.planStatus = competency.plan.status;
|
||||||
|
this.competency.usercompetency!.statusname =
|
||||||
|
AddonCompetencyHelper.getCompetencyStatusName(this.competency.usercompetency!.status);
|
||||||
|
} else {
|
||||||
|
this.userCompetency = this.competency.usercompetencycourse;
|
||||||
|
this.coursemodules = competency.coursemodules;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.competency.user.id != CoreSites.getCurrentSiteUserId()) {
|
||||||
|
// Get the user profile 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 = Translate.instant(key, { $a: evidence.desca });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting competency data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competency.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
async refreshCompetency(refresher: IonRefresher): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (this.planId) {
|
||||||
|
await AddonCompetency.invalidateCompetencyInPlan(this.planId, this.competencyId);
|
||||||
|
} else {
|
||||||
|
await AddonCompetency.invalidateCompetencyInCourse(this.courseId!, this.competencyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
this.fetchCompetency().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/summary/' + competencyId,
|
||||||
|
{
|
||||||
|
params: { contextLevel: this.contextLevel, contextInstanceId: this.contextInstanceId },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title *ngIf="competency">
|
||||||
|
{{ competency!.competency.shortname }} <small>{{ competency!.competency.idnumber }}</small>
|
||||||
|
</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!competencyLoaded" (ionRefresh)="refreshCompetency($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="competencyLoaded">
|
||||||
|
<ion-card *ngIf="competency">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="competency!.competency.description">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="competency!.competency.description" [contextLevel]="contextLevel"
|
||||||
|
[contextInstanceId]="contextInstanceId">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<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-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,104 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ContextLevel } from '@/core/constants';
|
||||||
|
import { AddonCompetencySummary, AddonCompetency } from '@addons/competency/services/competency';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from '@addons/competency/services/handlers/mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-competency-summary',
|
||||||
|
templateUrl: 'competencysummary.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCompetencySummaryPage implements OnInit {
|
||||||
|
|
||||||
|
competencyLoaded = false;
|
||||||
|
competencyId!: number;
|
||||||
|
competency?: AddonCompetencySummary;
|
||||||
|
contextLevel?: ContextLevel;
|
||||||
|
contextInstanceId?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.competencyId = CoreNavigator.getRouteNumberParam('competencyId')!;
|
||||||
|
this.contextLevel = CoreNavigator.getRouteParam<ContextLevel>('contextLevel');
|
||||||
|
this.contextInstanceId = CoreNavigator.getRouteNumberParam('contextInstanceId');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.fetchCompetency();
|
||||||
|
const name = this.competency!.competency && this.competency!.competency.shortname;
|
||||||
|
|
||||||
|
CoreUtils.ignoreErrors(AddonCompetency.logCompetencyView(this.competencyId, name));
|
||||||
|
} finally {
|
||||||
|
this.competencyLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competency summary and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchCompetency(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const result = await AddonCompetency.getCompetencySummary(this.competencyId);
|
||||||
|
if (!this.contextLevel || typeof this.contextInstanceId == 'undefined') {
|
||||||
|
// Context not specified, use user context.
|
||||||
|
this.contextLevel = ContextLevel.USER;
|
||||||
|
this.contextInstanceId = result.usercompetency!.userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.competency = result.competency;
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting competency summary data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competency summary.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCompetency(refresher: IonRefresher): void {
|
||||||
|
AddonCompetency.invalidateCompetencySummary(this.competencyId).finally(() => {
|
||||||
|
this.fetchCompetency().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/summary/' + competencyId,
|
||||||
|
{
|
||||||
|
params: { contextLevel: this.contextLevel, contextInstanceId: this.contextInstanceId },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>{{ 'addon.competency.coursecompetencies' | translate }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!competenciesLoaded" (ionRefresh)="refreshCourseCompetencies($event.target)">
|
||||||
|
<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">
|
||||||
|
<ng-container *ngIf="competencies!.cangradecompetencies">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="competencies!.settings.pushratingstouserplans">
|
||||||
|
<ion-label>{{ 'addon.competency.coursecompetencyratingsarepushedtouserplans' | translate }}</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="!competencies!.settings.pushratingstouserplans" color="danger">
|
||||||
|
<ion-label>{{ 'addon.competency.coursecompetencyratingsarenotpushedtouserplans' | translate }}</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="competencies!.statistics.canbegradedincourse">
|
||||||
|
<ion-label>
|
||||||
|
{{ 'addon.competency.xcompetenciesproficientoutofyincourse' | translate: {$a:
|
||||||
|
{x: competencies!.statistics.proficientcompetencycount, y: competencies!.statistics.competencycount} } }}
|
||||||
|
<core-progress-bar [progress]="competencies!.statistics.proficientcompetencypercentage"></core-progress-bar>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap"
|
||||||
|
*ngIf="competencies!.statistics.canmanagecoursecompetencies && competencies!.statistics.leastproficientcount > 0">
|
||||||
|
<ion-label>
|
||||||
|
<strong>{{ 'addon.competency.competenciesmostoftennotproficientincourse' | translate }}</strong>:
|
||||||
|
<p *ngFor="let comp of competencies!.statistics.leastproficient">
|
||||||
|
<a (click)="openCompetencySummary(comp.id)">
|
||||||
|
{{ comp.shortname }} - {{ comp.idnumber }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<h3 class="ion-margin-horizontal" *ngIf="competencies && competencies!.statistics.competencycount > 0">
|
||||||
|
{{ 'addon.competency.coursecompetencies' | translate }}
|
||||||
|
</h3>
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
|
||||||
|
<ion-label><h2>{{ user!.fullname }}</h2></ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
<core-empty-box *ngIf="competencies && competencies!.statistics.competencycount == 0"
|
||||||
|
icon="fas-award" message="{{ 'addon.competency.nocompetenciesincourse' | translate }}">
|
||||||
|
</core-empty-box>
|
||||||
|
|
||||||
|
<div *ngIf="competencies">
|
||||||
|
<ion-card *ngFor="let competency of competencies!.competencies">
|
||||||
|
<ion-item class="ion-text-wrap" (click)="openCompetency(competency.competency.id)"
|
||||||
|
[title]="competency.competency.shortname" detail="true">
|
||||||
|
<ion-label>
|
||||||
|
<h2><strong>{{competency.competency.shortname}} <em>{{competency.competency.idnumber}}</em></strong></h2>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge slot="end" *ngIf="competency.usercompetencycourse && competency.usercompetencycourse!.gradename"
|
||||||
|
[color]="competency.usercompetencycourse!.proficiency ? 'success' : 'danger'">
|
||||||
|
{{ competency.usercompetencycourse!.gradename }}
|
||||||
|
</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<p *ngIf="competency.competency.description">
|
||||||
|
<core-format-text [text]="competency.competency.description" contextLevel="course"
|
||||||
|
[contextInstanceId]="courseId">
|
||||||
|
</core-format-text>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<strong>{{ 'addon.competency.path' | translate }}</strong>
|
||||||
|
<a *ngIf="competency.comppath.showlinks"
|
||||||
|
[href]="competency.comppath.pluginbaseurl + '/competencies.php?competencyframeworkid=' +
|
||||||
|
competency.comppath.framework.id + '&pagecontextid=' + competency.comppath.pagecontextid"
|
||||||
|
core-link [title]="competency.comppath.framework.name">
|
||||||
|
{{ competency.comppath.framework.name }}
|
||||||
|
</a>
|
||||||
|
<ng-container *ngIf="!competency.comppath.showlinks">
|
||||||
|
{{ competency.comppath.framework.name }}
|
||||||
|
</ng-container>
|
||||||
|
/
|
||||||
|
<span *ngFor="let ancestor of competency.comppath.ancestors">
|
||||||
|
<a *ngIf="competency.comppath.showlinks" (click)="openCompetencySummary(ancestor.id)">
|
||||||
|
{{ ancestor.name }}
|
||||||
|
</a>
|
||||||
|
<ng-container *ngIf="!competency.comppath.showlinks">{{ ancestor.name }}</ng-container>
|
||||||
|
<ng-container *ngIf="!ancestor.last"> / </ng-container>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="competencies!.statistics.canmanagecoursecompetencies">
|
||||||
|
<strong>{{ 'addon.competency.uponcoursecompletion' | translate }}</strong>
|
||||||
|
<ng-container *ngFor="let ruleoutcome of competency.ruleoutcomeoptions">
|
||||||
|
<span *ngIf="ruleoutcome.selected">{{ ruleoutcome.text }}</span>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ 'addon.competency.activities' | translate }}</strong>
|
||||||
|
<p *ngIf="competency.coursemodules.length == 0">
|
||||||
|
{{ 'addon.competency.noactivities' | translate }}
|
||||||
|
</p>
|
||||||
|
<ion-item class="ion-text-wrap core-course-module-handler item-media" [title]="activity.name" core-link
|
||||||
|
*ngFor="let activity of competency.coursemodules" [href]="activity.url" capture="true">
|
||||||
|
<img slot="start" [src]="activity.iconurl" core-external-content alt="" role="presentation"
|
||||||
|
*ngIf="activity.iconurl" class="core-module-icon">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
||||||
|
[courseId]="courseId">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="competency.plans">
|
||||||
|
<strong>{{ 'addon.competency.userplans' | translate }}</strong>
|
||||||
|
<p *ngIf="competency.plans.length == 0">
|
||||||
|
{{ 'addon.competency.nouserplanswithcompetency' | translate }}
|
||||||
|
</p>
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let plan of competency.plans" [href]="plan.url"
|
||||||
|
[title]="plan.name" core-link capture="true">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="plan.name" contextLevel="user" [contextInstanceId]="plan.userid">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
</div>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,28 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonCompetencyCourseCompetenciesPage } from './coursecompetencies.page';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AddonCompetencyCourseCompetenciesPage,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseCompetenciesPageModule {}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AddonCompetencyDataForCourseCompetenciesPageWSResponse, AddonCompetency } from '@addons/competency/services/competency';
|
||||||
|
import { AddonCompetencyHelper } from '@addons/competency/services/competency-helper';
|
||||||
|
import { CoreUserProfile } from '@features/user/services/user';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from '@addons/competency/services/handlers/mainmenu';
|
||||||
|
import { ContextLevel } from '@/core/constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of competencies of a course.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-coursecompetencies',
|
||||||
|
templateUrl: 'coursecompetencies.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyCourseCompetenciesPage implements OnInit {
|
||||||
|
|
||||||
|
competenciesLoaded = false;
|
||||||
|
competencies?: AddonCompetencyDataForCourseCompetenciesPageWSResponse;
|
||||||
|
user?: CoreUserProfile;
|
||||||
|
courseId!: number;
|
||||||
|
|
||||||
|
protected userId!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.courseId = CoreNavigator.getRouteNumberParam('courseId')!;
|
||||||
|
this.userId = CoreNavigator.getRouteNumberParam('userId')!;
|
||||||
|
|
||||||
|
this.fetchCourseCompetencies().finally(() => {
|
||||||
|
this.competenciesLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the competencies and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchCourseCompetencies(): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.competencies = await AddonCompetency.getCourseCompetencies(this.courseId, this.userId);
|
||||||
|
|
||||||
|
// Get the user profile image.
|
||||||
|
this.user = await AddonCompetencyHelper.getProfile(this.userId);
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting course competencies data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a competency.
|
||||||
|
*
|
||||||
|
* @param competencyId
|
||||||
|
*/
|
||||||
|
openCompetency(competencyId: number): void {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/competencies/' + competencyId,
|
||||||
|
{
|
||||||
|
params: { courseId: this.courseId, userId: this.userId },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the summary of a competency.
|
||||||
|
*
|
||||||
|
* @param competencyId
|
||||||
|
*/
|
||||||
|
openCompetencySummary(competencyId: number): void {
|
||||||
|
CoreNavigator.navigateToSitePath('/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/summary/' + competencyId, {
|
||||||
|
params: {
|
||||||
|
contextLevel: ContextLevel.COURSE,
|
||||||
|
contextInstanceId: this.courseId,
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the competencies.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshCourseCompetencies(refresher?: IonRefresher): void {
|
||||||
|
AddonCompetency.invalidateCourseCompetencies(this.courseId, this.userId).finally(() => {
|
||||||
|
this.fetchCourseCompetencies().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title *ngIf="plan">{{plan!.plan.name}}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshLearningPlan($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ion-card *ngIf="user">
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<core-user-avatar [user]="user" slot="start"></core-user-avatar>
|
||||||
|
<h2>{{ user!.fullname }}</h2>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card *ngIf="plan">
|
||||||
|
<ion-list>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="plan!.plan.description" lines="none">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="plan!.plan.description" contextLevel="user"
|
||||||
|
[contextInstanceId]="plan!.plan.userid">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" lines="none">
|
||||||
|
<ion-label>
|
||||||
|
<p>
|
||||||
|
<strong>{{ 'addon.competency.status' | translate }}</strong>: {{ plan!.plan.statusname }}
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="plan!.plan.duedate > 0" lines="none">
|
||||||
|
<ion-label>
|
||||||
|
<p>
|
||||||
|
<strong>{{ 'addon.competency.duedate' | translate }}</strong>:
|
||||||
|
{{ plan.plan.duedate * 1000 | coreFormatDate }}
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="plan!.plan.template" lines="none">
|
||||||
|
<ion-label>
|
||||||
|
<p>
|
||||||
|
<strong>{{ 'addon.competency.template' | translate }}</strong>: {{ plan!.plan.template!.shortname }}
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" lines="none">
|
||||||
|
<ion-label>
|
||||||
|
<p>
|
||||||
|
<strong>{{ 'addon.competency.progress' | translate }}</strong>:
|
||||||
|
{{ 'addon.competency.xcompetenciesproficientoutofy' | translate:
|
||||||
|
{$a: {x: plan.proficientcompetencycount, y: plan.competencycount} } }}
|
||||||
|
</p>
|
||||||
|
<core-progress-bar [progress]="plan.proficientcompetencypercentage"
|
||||||
|
[text]="plan.proficientcompetencypercentageformatted"></core-progress-bar>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card *ngIf="plan">
|
||||||
|
<ion-card-header class="ion-text-wrap">
|
||||||
|
<h2>{{ 'addon.competency.learningplancompetencies' | translate }}</h2>
|
||||||
|
</ion-card-header>
|
||||||
|
<ion-list>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="plan.competencycount == 0">
|
||||||
|
<ion-label>
|
||||||
|
<p>{{ 'addon.competency.nocompetencies' | translate }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let competency of plan.competencies"
|
||||||
|
(click)="openCompetency(competency.competency.id)"
|
||||||
|
[title]="competency.competency.shortname" detail="true">
|
||||||
|
<ion-label><h2>{{competency.competency.shortname}} <em>{{competency.competency.idnumber}}</em></h2></ion-label>
|
||||||
|
<ion-badge *ngIf="competency.usercompetencyplan" slot="end"
|
||||||
|
[color]="competency.usercompetencyplan.proficiency ? 'success' : 'danger'">
|
||||||
|
{{ competency.usercompetencyplan.gradename }}
|
||||||
|
</ion-badge>
|
||||||
|
<ion-badge *ngIf="!competency.usercompetencyplan" slot="end"
|
||||||
|
[color]="competency.usercompetency.proficiency ? 'success' : 'danger'">
|
||||||
|
{{ competency.usercompetency.gradename }}
|
||||||
|
</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</ion-card>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,93 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { AddonCompetencyDataForPlanPageWSResponse, AddonCompetency } from '../../services/competency';
|
||||||
|
import { AddonCompetencyHelper } from '../../services/competency-helper';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreUserProfile } from '@features/user/services/user';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from '@addons/competency/services/handlers/mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a learning plan.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-plan',
|
||||||
|
templateUrl: 'plan.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanPage implements OnInit {
|
||||||
|
|
||||||
|
protected planId!: number;
|
||||||
|
loaded = false;
|
||||||
|
plan?: AddonCompetencyDataForPlanPageWSResponse;
|
||||||
|
user?: CoreUserProfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.planId = CoreNavigator.getRouteNumberParam('planId')!;
|
||||||
|
|
||||||
|
this.fetchLearningPlan().finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the learning plan and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchLearningPlan(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const plan = await AddonCompetency.getLearningPlan(this.planId);
|
||||||
|
plan.plan.statusname = AddonCompetencyHelper.getPlanStatusName(plan.plan.status);
|
||||||
|
|
||||||
|
// Get the user profile image.
|
||||||
|
this.user = await AddonCompetencyHelper.getProfile(plan.plan.userid);
|
||||||
|
|
||||||
|
this.plan = plan;
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting learning plan data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to a particular competency.
|
||||||
|
*
|
||||||
|
* @param competencyId
|
||||||
|
*/
|
||||||
|
openCompetency(competencyId: number): void {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/competencies/' + competencyId,
|
||||||
|
{ params: { planId: this.planId } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the learning plan.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshLearningPlan(refresher: IonRefresher): void {
|
||||||
|
AddonCompetency.invalidateLearningPlan(this.planId).finally(() => {
|
||||||
|
this.fetchLearningPlan().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>{{ 'addon.competency.userplans' | translate }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!plans.loaded" (ionRefresh)="refreshLearningPlans($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="plans.loaded">
|
||||||
|
<core-empty-box *ngIf="plans.empty" icon="fas-route" [message]="'addon.competency.noplanswerecreated' | translate">
|
||||||
|
|
||||||
|
</core-empty-box>
|
||||||
|
<ion-list *ngIf="!plans.empty" class="ion-no-margin">
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let plan of plans.items" [title]="plan.name" (click)="plans.select(plan)"
|
||||||
|
[class.core-selected-item]="plans.isSelected(plan)">
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ plan.name }}</h2>
|
||||||
|
<p *ngIf="plan.duedate > 0">
|
||||||
|
{{ 'addon.competency.duedate' | translate }}:
|
||||||
|
{{ plan.duedate * 1000 | coreFormatDate :'strftimedatetimeshort' }}
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
<ion-badge slot="end" class="ion-text-wrap" [color]="plan.statuscolor">{{ plan.statusname }}</ion-badge>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</core-split-view>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,140 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { AddonCompetencyProvider, AddonCompetencyPlan, AddonCompetency } from '../../services/competency';
|
||||||
|
import { AddonCompetencyHelper } from '../../services/competency-helper';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
|
||||||
|
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the list of learning plans.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-competency-planlist',
|
||||||
|
templateUrl: 'planlist.html',
|
||||||
|
})
|
||||||
|
export class AddonCompetencyPlanListPage implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
protected userId?: number;
|
||||||
|
plans: AddonCompetencyPlanListManager;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.plans = new AddonCompetencyPlanListManager(AddonCompetencyPlanListPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.userId = CoreNavigator.getRouteNumberParam('userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngAfterViewInit(): Promise<void> {
|
||||||
|
await this.fetchLearningPlans();
|
||||||
|
|
||||||
|
this.plans.start(this.splitView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the learning plans and updates the view.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchLearningPlans(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const plans = await AddonCompetency.getLearningPlans(this.userId);
|
||||||
|
plans.forEach((plan: AddonCompetencyPlanFormatted) => {
|
||||||
|
plan.statusname = AddonCompetencyHelper.getPlanStatusName(plan.status);
|
||||||
|
switch (plan.status) {
|
||||||
|
case AddonCompetencyProvider.STATUS_ACTIVE:
|
||||||
|
plan.statuscolor = 'success';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_COMPLETE:
|
||||||
|
plan.statuscolor = 'danger';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
plan.statuscolor = 'warning';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.plans.setItems(plans);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting learning plans data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the learning plans.
|
||||||
|
*
|
||||||
|
* @param refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshLearningPlans(refresher: IonRefresher): void {
|
||||||
|
AddonCompetency.invalidateLearningPlans(this.userId).finally(() => {
|
||||||
|
this.fetchLearningPlans().finally(() => {
|
||||||
|
refresher?.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.plans.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Competency plan with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonCompetencyPlanFormatted = AddonCompetencyPlan & {
|
||||||
|
statuscolor?: string; // Calculated in the app. Color of the plan's status.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to manage plan list.
|
||||||
|
*/
|
||||||
|
class AddonCompetencyPlanListManager extends CorePageItemsListManager<AddonCompetencyPlanFormatted> {
|
||||||
|
|
||||||
|
constructor(pageComponent: unknown) {
|
||||||
|
super(pageComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getItemPath(plan: AddonCompetencyPlanFormatted): string {
|
||||||
|
return String(plan.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||||
|
return route.params.planId ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { AddonCompetencyProvider } from './competency';
|
||||||
|
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
|
||||||
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features regarding learning plans.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient helper to get the user profile image.
|
||||||
|
*
|
||||||
|
* @param userId User Id
|
||||||
|
* @return User profile Image URL or true if default icon.
|
||||||
|
*/
|
||||||
|
async getProfile(userId?: number): Promise<CoreUserProfile | undefined> {
|
||||||
|
if (!userId || userId == CoreSites.getCurrentSiteUserId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user profile to retrieve the user image.
|
||||||
|
return CoreUser.getProfile(userId, undefined, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the review status name translated.
|
||||||
|
*
|
||||||
|
* @param status
|
||||||
|
*/
|
||||||
|
getCompetencyStatusName(status: number): string {
|
||||||
|
let statusTranslateName: string;
|
||||||
|
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 String(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Translate.instant('addon.competency.usercompetencystatus_' + statusTranslateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status name translated.
|
||||||
|
*
|
||||||
|
* @param status
|
||||||
|
*/
|
||||||
|
getPlanStatusName(status: number): string {
|
||||||
|
let statusTranslateName: string;
|
||||||
|
switch (status) {
|
||||||
|
case AddonCompetencyProvider.STATUS_DRAFT:
|
||||||
|
statusTranslateName = 'draft';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_ACTIVE:
|
||||||
|
statusTranslateName = 'active';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_COMPLETE:
|
||||||
|
statusTranslateName = 'complete';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_WAITING_FOR_REVIEW:
|
||||||
|
statusTranslateName = 'waitingforreview';
|
||||||
|
break;
|
||||||
|
case AddonCompetencyProvider.STATUS_IN_REVIEW:
|
||||||
|
statusTranslateName = 'inreview';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We can use the current status name.
|
||||||
|
return String(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Translate.instant('addon.competency.planstatus' + statusTranslateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyHelper = makeSingleton(AddonCompetencyHelperProvider);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,66 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to a competency in a plan or in a course.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyCompetencyLinkHandlerService extends CoreContentLinksHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonCompetencyCompetencyLinkHandler';
|
||||||
|
pattern = /\/admin\/tool\/lp\/(user_competency_in_course|user_competency_in_plan)\.php/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: Record<string, string>, courseId?: number): CoreContentLinksAction[] {
|
||||||
|
courseId = courseId || parseInt(params.courseid || params.cid, 10);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
action: (siteId: string): void => {
|
||||||
|
const pageParams = {
|
||||||
|
planId: params.planid,
|
||||||
|
courseId: courseId,
|
||||||
|
userId: params.userid,
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/competencies/' + params.competencyid,
|
||||||
|
{ params: pageParams, siteId },
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
// Handler is disabled if all competency features are disabled.
|
||||||
|
const disabled = await AddonCompetency.allCompetenciesDisabled(siteId);
|
||||||
|
|
||||||
|
return !disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyCompetencyLinkHandler = makeSingleton(AddonCompetencyCompetencyLinkHandlerService);
|
|
@ -0,0 +1,139 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreCourseProvider } from '@features/course/services/course';
|
||||||
|
import {
|
||||||
|
CoreCourseAccess,
|
||||||
|
CoreCourseOptionsHandler,
|
||||||
|
CoreCourseOptionsHandlerData,
|
||||||
|
} from '@features/course/services/course-options-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses';
|
||||||
|
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
|
||||||
|
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
||||||
|
import { ContextLevel } from '@/core/constants';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course nav handler.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyCourseOptionHandlerService implements CoreCourseOptionsHandler {
|
||||||
|
|
||||||
|
name = 'AddonCompetency';
|
||||||
|
priority = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabledForCourse(
|
||||||
|
courseId: number,
|
||||||
|
accessData: CoreCourseAccess,
|
||||||
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||||
|
return false; // Not enabled for guests.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navOptions && typeof navOptions.competencies != 'undefined') {
|
||||||
|
return navOptions.competencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const competencies = await AddonCompetency.getCourseCompetencies(courseId);
|
||||||
|
|
||||||
|
return competencies ? !competencies.canmanagecoursecompetencies : false;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getDisplayData(): CoreCourseOptionsHandlerData {
|
||||||
|
return {
|
||||||
|
title: 'addon.competency.competencies',
|
||||||
|
class: 'addon-competency-course-handler',
|
||||||
|
page: AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async invalidateEnabledForCourse(courseId: number, navOptions?: CoreCourseUserAdminOrNavOptionIndexed): Promise<void> {
|
||||||
|
if (navOptions && typeof navOptions.competencies != 'undefined') {
|
||||||
|
// No need to invalidate anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddonCompetency.invalidateCourseCompetencies(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async prefetch(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void> {
|
||||||
|
// Get the competencies in the course.
|
||||||
|
const competencies = await AddonCompetency.getCourseCompetencies(course.id, undefined, undefined, true);
|
||||||
|
|
||||||
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
|
// Prefetch all the competencies.
|
||||||
|
if (competencies && competencies.competencies) {
|
||||||
|
competencies.competencies.forEach((competency) => {
|
||||||
|
promises.push(AddonCompetency.getCompetencyInCourse(
|
||||||
|
course.id,
|
||||||
|
competency.competency.id,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
|
||||||
|
promises.push(AddonCompetency.getCompetencySummary(
|
||||||
|
competency.competency.id,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (competency.coursemodules) {
|
||||||
|
competency.coursemodules.forEach((module) => {
|
||||||
|
promises.push(CoreFilterHelper.getFilters(ContextLevel.MODULE, module.id, { courseId: course.id }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (competency.plans) {
|
||||||
|
competency.plans.forEach((plan) => {
|
||||||
|
promises.push(CoreFilterHelper.getFilters(ContextLevel.USER, plan.userid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyCourseOptionHandler = makeSingleton(AddonCompetencyCourseOptionHandlerService);
|
|
@ -0,0 +1,54 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to inject an option into main menu.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyMainMenuHandlerService implements CoreMainMenuHandler {
|
||||||
|
|
||||||
|
static readonly PAGE_NAME = 'competency';
|
||||||
|
|
||||||
|
name = 'AddonCompetency';
|
||||||
|
priority = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
// Check the user has at least one learn plan available.
|
||||||
|
const plans = await AddonCompetency.getLearningPlans();
|
||||||
|
|
||||||
|
return plans.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getDisplayData(): CoreMainMenuHandlerData {
|
||||||
|
return {
|
||||||
|
icon: 'fas-route',
|
||||||
|
title: 'addon.competency.myplans',
|
||||||
|
page: AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
|
class: 'addon-competency-handler',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyMainMenuHandler = makeSingleton(AddonCompetencyMainMenuHandlerService);
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to a plan.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyPlanLinkHandlerService extends CoreContentLinksHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonCompetencyPlanLinkHandler';
|
||||||
|
pattern = /\/admin\/tool\/lp\/plan\.php.*([?&]id=\d+)/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
|
||||||
|
return [{
|
||||||
|
action: (siteId: string): void => {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/' + params.id,
|
||||||
|
{ siteId },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
// Handler is disabled if all competency features are disabled.
|
||||||
|
const disabled = await AddonCompetency.allCompetenciesDisabled(siteId);
|
||||||
|
|
||||||
|
return !disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyPlanLinkHandler = makeSingleton(AddonCompetencyPlanLinkHandlerService);
|
|
@ -0,0 +1,58 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to user plans.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyPlansLinkHandlerService extends CoreContentLinksHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonCompetencyPlansLinkHandler';
|
||||||
|
pattern = /\/admin\/tool\/lp\/plans\.php/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
|
||||||
|
return [{
|
||||||
|
action: (siteId: string): void => {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME,
|
||||||
|
{ params: { userId: params.userid }, siteId },
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
// Handler is disabled if all competency features are disabled.
|
||||||
|
const disabled = await AddonCompetency.allCompetenciesDisabled(siteId);
|
||||||
|
|
||||||
|
return !disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyPlansLinkHandler = makeSingleton(AddonCompetencyPlansLinkHandlerService);
|
|
@ -0,0 +1,102 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate';
|
||||||
|
import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreUrlUtils } from '@services/utils/url';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for competencies push notifications clicks.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyPushClickHandlerService implements CorePushNotificationsClickHandler {
|
||||||
|
|
||||||
|
name = 'AddonCompetencyPushClickHandler';
|
||||||
|
priority = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async handles(notification: AddonCompetencyPushNotificationData): Promise<boolean> {
|
||||||
|
if (CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'moodle' &&
|
||||||
|
(notification.name == 'competencyplancomment' || notification.name == 'competencyusercompcomment')) {
|
||||||
|
// If all competency features are disabled, don't handle the click.
|
||||||
|
return AddonCompetency.allCompetenciesDisabled(notification.site).then((disabled) => !disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async handleClick(notification: AddonCompetencyPushNotificationData): Promise<void> {
|
||||||
|
const contextUrlParams = CoreUrlUtils.extractUrlParams(notification.contexturl);
|
||||||
|
|
||||||
|
if (notification.name == 'competencyplancomment') {
|
||||||
|
// Open the learning plan.
|
||||||
|
const planId = Number(contextUrlParams.id);
|
||||||
|
|
||||||
|
await CoreUtils.ignoreErrors(AddonCompetency.invalidateLearningPlan(planId, notification.site));
|
||||||
|
|
||||||
|
await CoreNavigator.navigateToSitePath('/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/' + planId, {
|
||||||
|
siteId: notification.site,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.contexturl && notification.contexturl.indexOf('user_competency_in_plan.php') != -1) {
|
||||||
|
// Open the competency.
|
||||||
|
const courseId = Number(notification.course);
|
||||||
|
const competencyId = Number(contextUrlParams.competencyid);
|
||||||
|
const planId = Number(contextUrlParams.planid);
|
||||||
|
const userId = Number(contextUrlParams.userid);
|
||||||
|
|
||||||
|
await CoreUtils.ignoreErrors(AddonCompetency.invalidateCompetencyInPlan(planId, competencyId, notification.site));
|
||||||
|
await CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/competencies/' + competencyId,
|
||||||
|
{
|
||||||
|
params: { planId, courseId, userId },
|
||||||
|
siteId: notification.site,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the list of plans.
|
||||||
|
const userId = Number(contextUrlParams.userid);
|
||||||
|
|
||||||
|
await CoreUtils.ignoreErrors(AddonCompetency.invalidateLearningPlans(userId, notification.site));
|
||||||
|
|
||||||
|
await CoreNavigator.navigateToSitePath('/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME, {
|
||||||
|
params: { userId },
|
||||||
|
siteId: notification.site,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyPushClickHandler = makeSingleton(AddonCompetencyPushClickHandlerService);
|
||||||
|
|
||||||
|
type AddonCompetencyPushNotificationData = CorePushNotificationsNotificationBasicData & {
|
||||||
|
contexturl: string;
|
||||||
|
course: number;
|
||||||
|
};
|
|
@ -0,0 +1,59 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to a usr competency.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyUserCompetencyLinkHandlerService extends CoreContentLinksHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonCompetencyUserCompetencyLinkHandler';
|
||||||
|
pattern = /\/admin\/tool\/lp\/user_competency\.php.*([?&]id=\d+)/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
|
||||||
|
|
||||||
|
return [{
|
||||||
|
action: (siteId: string): void => {
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/summary/' + params.id,
|
||||||
|
{ siteId },
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(siteId: string): Promise<boolean> {
|
||||||
|
// Handler is disabled if all competency features are disabled.
|
||||||
|
const disabled = await AddonCompetency.allCompetenciesDisabled(siteId);
|
||||||
|
|
||||||
|
return !disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyUserCompetencyLinkHandler = makeSingleton(AddonCompetencyUserCompetencyLinkHandlerService);
|
|
@ -0,0 +1,99 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreUserProfile } from '@features/user/services/user';
|
||||||
|
import { CoreUserProfileHandler, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonCompetency } from '../competency';
|
||||||
|
import { AddonCompetencyMainMenuHandlerService } from './mainmenu';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profile competencies handler.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler {
|
||||||
|
|
||||||
|
name = 'AddonCompetency:learningPlan';
|
||||||
|
priority = 900;
|
||||||
|
type = CoreUserDelegateService.TYPE_NEW_PAGE;
|
||||||
|
cacheEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
if (courseId) {
|
||||||
|
const response = await AddonCompetency.getCourseCompetencies(courseId, user.id);
|
||||||
|
|
||||||
|
return response.competencies.length > 0;
|
||||||
|
} else {
|
||||||
|
const plans = await AddonCompetency.getLearningPlans(user.id);
|
||||||
|
|
||||||
|
// Check the user has at least one learn plan available.
|
||||||
|
return plans.length > 0;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getDisplayData(user: CoreUserProfile, courseId: number): CoreUserProfileHandlerData {
|
||||||
|
if (courseId) {
|
||||||
|
return {
|
||||||
|
icon: 'fas-award',
|
||||||
|
title: 'addon.competency.competencies',
|
||||||
|
class: 'addon-competency-handler',
|
||||||
|
action: (event, user, courseId): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
CoreNavigator.navigateToSitePath(
|
||||||
|
'/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME + '/course/' + courseId,
|
||||||
|
{
|
||||||
|
params: { userId: user.id },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
icon: 'fas-route',
|
||||||
|
title: 'addon.competency.learningplans',
|
||||||
|
class: 'addon-competency-handler',
|
||||||
|
action: (event, user): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
CoreNavigator.navigateToSitePath('/' + AddonCompetencyMainMenuHandlerService.PAGE_NAME, {
|
||||||
|
params: { userId: user.id },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonCompetencyUserHandler = makeSingleton(AddonCompetencyUserHandlerService);
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
|
import { Component, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, ActivatedRouteSnapshot, Params } from '@angular/router';
|
import { ActivatedRouteSnapshot, Params } from '@angular/router';
|
||||||
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
|
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { IonRefresher } from '@ionic/angular';
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
@ -73,9 +73,7 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
|
||||||
canviewsubmissions: false,
|
canviewsubmissions: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
protected route: ActivatedRoute,
|
|
||||||
) {
|
|
||||||
this.submissions = new AddonModAssignSubmissionListManager(AddonModAssignSubmissionListPage);
|
this.submissions = new AddonModAssignSubmissionListManager(AddonModAssignSubmissionListPage);
|
||||||
|
|
||||||
// Update data if some grade changes.
|
// Update data if some grade changes.
|
||||||
|
|
|
@ -118,7 +118,7 @@ import { CoreSitePluginsAssignSubmissionComponent } from '@features/siteplugins/
|
||||||
import { ADDON_BADGES_SERVICES } from '@addons/badges/badges.module';
|
import { ADDON_BADGES_SERVICES } from '@addons/badges/badges.module';
|
||||||
import { ADDON_CALENDAR_SERVICES } from '@addons/calendar/calendar.module';
|
import { ADDON_CALENDAR_SERVICES } from '@addons/calendar/calendar.module';
|
||||||
import { ADDON_COURSECOMPLETION_SERVICES } from '@addons/coursecompletion/coursecompletion.module';
|
import { ADDON_COURSECOMPLETION_SERVICES } from '@addons/coursecompletion/coursecompletion.module';
|
||||||
// @todo import { ADDON_COMPETENCY_SERVICES } from '@addons/competency/competency.module';
|
import { ADDON_COMPETENCY_SERVICES } from '@addons/competency/competency.module';
|
||||||
import { ADDON_MESSAGEOUTPUT_SERVICES } from '@addons/messageoutput/messageoutput.module';
|
import { ADDON_MESSAGEOUTPUT_SERVICES } from '@addons/messageoutput/messageoutput.module';
|
||||||
import { ADDON_MESSAGES_SERVICES } from '@addons/messages/messages.module';
|
import { ADDON_MESSAGES_SERVICES } from '@addons/messages/messages.module';
|
||||||
import { ADDON_MOD_ASSIGN_SERVICES } from '@addons/mod/assign/assign.module';
|
import { ADDON_MOD_ASSIGN_SERVICES } from '@addons/mod/assign/assign.module';
|
||||||
|
@ -283,7 +283,7 @@ export class CoreCompileProvider {
|
||||||
...ADDON_BADGES_SERVICES,
|
...ADDON_BADGES_SERVICES,
|
||||||
...ADDON_CALENDAR_SERVICES,
|
...ADDON_CALENDAR_SERVICES,
|
||||||
...ADDON_COURSECOMPLETION_SERVICES,
|
...ADDON_COURSECOMPLETION_SERVICES,
|
||||||
// @todo ...ADDON_COMPETENCY_SERVICES,
|
...ADDON_COMPETENCY_SERVICES,
|
||||||
...ADDON_MESSAGEOUTPUT_SERVICES,
|
...ADDON_MESSAGEOUTPUT_SERVICES,
|
||||||
...ADDON_MESSAGES_SERVICES,
|
...ADDON_MESSAGES_SERVICES,
|
||||||
...ADDON_MOD_ASSIGN_SERVICES,
|
...ADDON_MOD_ASSIGN_SERVICES,
|
||||||
|
|
Loading…
Reference in New Issue