commit
8ae7153ad8
|
@ -22,7 +22,8 @@
|
|||
"addon.block_myoverview.past": "block_myoverview",
|
||||
"addon.block_myoverview.pluginname": "block_myoverview",
|
||||
"addon.block_myoverview.title": "block_myoverview",
|
||||
"addon.block_recentcourses.nocourses": "moodle",
|
||||
"addon.block_recentlyaccessedcourses.nocourses": "block_recentlyaccessedcourses",
|
||||
"addon.block_recentlyaccessedcourses.pluginname": "block_recentlyaccessedcourses",
|
||||
"addon.block_sitemainmenu.pluginname": "block_site_main_menu",
|
||||
"addon.block_timeline.duedate": "block_timeline",
|
||||
"addon.block_timeline.next30days": "block_timeline",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<!-- "Sort" selector. -->
|
||||
<ion-col [hidden]="!showSortFilter">
|
||||
<ion-select text-start [(ngModel)]="sort" (ngModelChange)="switchSort()" interface="popover" class="core-button-select">
|
||||
<ion-option value="title">{{ 'addon.block_myoverview.title' | translate }}</ion-option>
|
||||
<ion-option value="fullname">{{ 'addon.block_myoverview.title' | translate }}</ion-option>
|
||||
<ion-option value="lastaccess">{{ 'addon.block_myoverview.lastaccessed' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
</ion-col>
|
||||
|
|
|
@ -45,7 +45,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
hidden: []
|
||||
};
|
||||
selectedFilter = 'inprogress';
|
||||
sort = 'title';
|
||||
sort = 'fullname';
|
||||
currentSite: any;
|
||||
downloadAllCoursesEnabled: boolean;
|
||||
filteredCourses: any[];
|
||||
|
@ -103,7 +103,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
this.sort = value;
|
||||
}));
|
||||
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter).then((value) => {
|
||||
this.selectedFilter = value;
|
||||
this.selectedFilter = typeof this.courses[value] == 'undefined' ? 'inprogress' : value;
|
||||
}));
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
|
@ -145,26 +145,10 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchContent(): Promise<any> {
|
||||
return this.coursesHelper.getUserCoursesWithOptions().then((courses) => {
|
||||
// Fetch course completion status.
|
||||
return Promise.all(courses.map((course) => {
|
||||
if (typeof course.enablecompletion != 'undefined' && course.enablecompletion == 0) {
|
||||
// Completion is disabled for this course, there is no need to fetch the completion status.
|
||||
return Promise.resolve(course);
|
||||
}
|
||||
|
||||
return this.courseCompletionProvider.getCompletion(course.id).catch(() => {
|
||||
// Ignore error, maybe course compleiton is disabled or user ha no permission.
|
||||
}).then((completion) => {
|
||||
course.completed = completion && completion.completed;
|
||||
|
||||
return course;
|
||||
});
|
||||
}));
|
||||
}).then((courses) => {
|
||||
return this.coursesHelper.getUserCoursesWithOptions(this.sort).then((courses) => {
|
||||
this.showSortFilter = courses.length > 0 && typeof courses[0].lastaccess != 'undefined';
|
||||
|
||||
this.sortCourses(courses);
|
||||
this.initCourseFilters(courses);
|
||||
|
||||
this.courses.filter = '';
|
||||
this.showFilter = false;
|
||||
|
@ -210,22 +194,9 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
this.prefetchIconsInitialized = true;
|
||||
|
||||
Object.keys(this.prefetchCoursesData).forEach((filter) => {
|
||||
if (!this.courses[filter] || this.courses[filter].length < 2) {
|
||||
// Not enough courses.
|
||||
this.prefetchCoursesData[filter].icon = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.courseHelper.determineCoursesStatus(this.courses[filter]).then((status) => {
|
||||
let icon = this.courseHelper.getCourseStatusIconAndTitleFromStatus(status).icon;
|
||||
if (icon == 'spinner') {
|
||||
// It seems all courses are being downloaded, show a download button instead.
|
||||
icon = 'cloud-download';
|
||||
}
|
||||
this.prefetchCoursesData[filter].icon = icon;
|
||||
this.courseHelper.initPrefetchCoursesIcons(this.courses[filter], this.prefetchCoursesData[filter]).then((prefetch) => {
|
||||
this.prefetchCoursesData[filter] = prefetch;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -236,23 +207,13 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
*/
|
||||
prefetchCourses(): Promise<any> {
|
||||
const selected = this.selectedFilter,
|
||||
selectedData = this.prefetchCoursesData[selected],
|
||||
initialIcon = selectedData.icon;
|
||||
initialIcon = this.prefetchCoursesData[selected].icon;
|
||||
|
||||
selectedData.icon = 'spinner';
|
||||
selectedData.badge = '';
|
||||
|
||||
return this.courseHelper.confirmAndPrefetchCourses(this.courses[this.selectedFilter], (progress) => {
|
||||
selectedData.badge = progress.count + ' / ' + progress.total;
|
||||
}).then(() => {
|
||||
selectedData.icon = 'refresh';
|
||||
}).catch((error) => {
|
||||
return this.courseHelper.prefetchCourses(this.courses[selected], this.prefetchCoursesData[selected]).catch((error) => {
|
||||
if (!this.isDestroyed) {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||
selectedData.icon = initialIcon;
|
||||
this.prefetchCoursesData[selected].icon = initialIcon;
|
||||
}
|
||||
}).finally(() => {
|
||||
selectedData.badge = '';
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -265,25 +226,11 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
}
|
||||
|
||||
/**
|
||||
* Sort and init courses filters.
|
||||
* @param {any[]} courses Courses to sort.
|
||||
* Init courses filters.
|
||||
*
|
||||
* @param {any[]} courses Courses to filter.
|
||||
*/
|
||||
sortCourses(courses: any[]): void {
|
||||
if (this.showSortFilter) {
|
||||
if (this.sort == 'lastaccess') {
|
||||
courses.sort((a, b) => {
|
||||
return b.lastaccess - a.lastaccess;
|
||||
});
|
||||
} else if (this.sort == 'title') {
|
||||
courses.sort((a, b) => {
|
||||
const compareA = a.fullname.toLowerCase(),
|
||||
compareB = b.fullname.toLowerCase();
|
||||
|
||||
return compareA.localeCompare(compareB);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initCourseFilters(courses: any[]): void {
|
||||
this.courses.all = [];
|
||||
this.courses.past = [];
|
||||
this.courses.inprogress = [];
|
||||
|
@ -314,6 +261,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.filteredCourses = this.courses[this.selectedFilter];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -321,7 +270,24 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
*/
|
||||
switchSort(): void {
|
||||
this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewSort', this.sort);
|
||||
this.sortCourses(this.courses.all);
|
||||
const courses = this.courses.all.concat(this.courses.hidden);
|
||||
|
||||
if (this.showSortFilter) {
|
||||
if (this.sort == 'lastaccess') {
|
||||
courses.sort((a, b) => {
|
||||
return b.lastaccess - a.lastaccess;
|
||||
});
|
||||
} else if (this.sort == 'fullname') {
|
||||
courses.sort((a, b) => {
|
||||
const compareA = a.fullname.toLowerCase(),
|
||||
compareB = b.fullname.toLowerCase();
|
||||
|
||||
return compareA.localeCompare(compareB);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.initCourseFilters(courses);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AddonBlockRecentlyAccessedCoursesComponent } from './recentlyaccessedcourses/recentlyaccessedcourses';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CoreCoursesComponentsModule } from '@core/courses/components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonBlockRecentlyAccessedCoursesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CoreCoursesComponentsModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
AddonBlockRecentlyAccessedCoursesComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonBlockRecentlyAccessedCoursesComponent
|
||||
]
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedCoursesComponentsModule {}
|
|
@ -0,0 +1,21 @@
|
|||
<ion-item-divider color="light">
|
||||
<h2>{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}</h2>
|
||||
<div *ngIf="downloadAllCoursesEnabled && courses && courses.length > 1" class="core-button-spinner" item-end>
|
||||
<button *ngIf="prefetchCoursesData.icon && prefetchCoursesData.icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
|
||||
<core-icon [name]="prefetchCoursesData.icon"></core-icon>
|
||||
</button>
|
||||
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData.badge">{{prefetchCoursesData.badge}}</ion-badge>
|
||||
<ion-spinner *ngIf="!prefetchCoursesData.icon || prefetchCoursesData.icon == 'spinner'"></ion-spinner>
|
||||
</div>
|
||||
</ion-item-divider>
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<core-empty-box *ngIf="courses.length == 0" image="assets/img/icons/courses.svg" [message]="'addon.block_recentlyaccessedcourses.nocourses' | translate"></core-empty-box>
|
||||
<!-- List of courses. -->
|
||||
<ion-grid no-padding>
|
||||
<ion-row no-padding>
|
||||
<ion-col *ngFor="let course of courses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
||||
<core-courses-course-progress [course]="course" class="core-recentlyaccessedcourses"></core-courses-course-progress>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</core-loading>
|
|
@ -0,0 +1,152 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, Injector } from '@angular/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreCoursesHelperProvider } from '@core/courses/providers/helper';
|
||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion';
|
||||
import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
||||
|
||||
/**
|
||||
* Component to render a recent courses block.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-block-recentlyaccessedcourses',
|
||||
templateUrl: 'addon-block-recentlyaccessedcourses.html'
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnDestroy {
|
||||
courses = [];
|
||||
prefetchCoursesData = {
|
||||
icon: '',
|
||||
badge: ''
|
||||
};
|
||||
downloadAllCoursesEnabled: boolean;
|
||||
|
||||
protected prefetchIconsInitialized = false;
|
||||
protected isDestroyed;
|
||||
protected updateSiteObserver;
|
||||
protected courseIds = [];
|
||||
protected fetchContentDefaultError = 'Error getting recent courses data.';
|
||||
|
||||
constructor(injector: Injector, private coursesProvider: CoreCoursesProvider,
|
||||
private courseCompletionProvider: AddonCourseCompletionProvider, private eventsProvider: CoreEventsProvider,
|
||||
private courseHelper: CoreCourseHelperProvider, private utils: CoreUtilsProvider,
|
||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider) {
|
||||
|
||||
super(injector, 'AddonBlockRecentlyAccessedCoursesComponent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
// Refresh the enabled flags if site is updated.
|
||||
this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
|
||||
const wasEnabled = this.downloadAllCoursesEnabled;
|
||||
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
if (!wasEnabled && this.downloadAllCoursesEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
});
|
||||
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the invalidate content function.
|
||||
*
|
||||
* @return {Promise<any>} Resolved when done.
|
||||
*/
|
||||
protected invalidateContent(): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses().finally(() => {
|
||||
// Invalidate course completion data.
|
||||
return this.utils.allPromises(this.courseIds.map((courseId) => {
|
||||
return this.courseCompletionProvider.invalidateCourseCompletion(courseId);
|
||||
}));
|
||||
}));
|
||||
|
||||
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
|
||||
if (this.courseIds.length > 0) {
|
||||
promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds.join(',')));
|
||||
}
|
||||
|
||||
return this.utils.allPromises(promises).finally(() => {
|
||||
this.prefetchIconsInitialized = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the courses for recent courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchContent(): Promise<any> {
|
||||
return this.coursesHelper.getUserCoursesWithOptions('lastaccess', 10).then((courses) => {
|
||||
this.courses = courses;
|
||||
|
||||
this.initPrefetchCoursesIcons();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the prefetch icon for selected courses.
|
||||
*/
|
||||
protected initPrefetchCoursesIcons(): void {
|
||||
if (this.prefetchIconsInitialized || !this.downloadAllCoursesEnabled) {
|
||||
// Already initialized.
|
||||
return;
|
||||
}
|
||||
|
||||
this.prefetchIconsInitialized = true;
|
||||
|
||||
this.courseHelper.initPrefetchCoursesIcons(this.courses, this.prefetchCoursesData).then((prefetch) => {
|
||||
this.prefetchCoursesData = prefetch;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the shown courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetchCourses(): Promise<any> {
|
||||
const initialIcon = this.prefetchCoursesData.icon;
|
||||
|
||||
return this.courseHelper.prefetchCourses(this.courses, this.prefetchCoursesData).catch((error) => {
|
||||
if (!this.isDestroyed) {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||
this.prefetchCoursesData.icon = initialIcon;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.updateSiteObserver && this.updateSiteObserver.off();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"nocourses": "No recent courses",
|
||||
"pluginname": "Recently accessed courses"
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { CoreBlockHandlerData } from '@core/block/providers/delegate';
|
||||
import { AddonBlockRecentlyAccessedCoursesComponent } from '../components/recentlyaccessedcourses/recentlyaccessedcourses';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockRecentlyAccessedCoursesHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockRecentlyAccessedCourses';
|
||||
blockName = 'recentlyaccessedcourses';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the block.
|
||||
*
|
||||
* @param {Injector} injector Injector.
|
||||
* @param {any} block The block to render.
|
||||
* @param {string} contextLevel The context where the block will be used.
|
||||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_recentlyaccessedcourses.pluginname',
|
||||
class: 'addon-block-recentlyaccessedcourses',
|
||||
component: AddonBlockRecentlyAccessedCoursesComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockRecentlyAccessedCoursesComponentsModule } from './components/components.module';
|
||||
import { AddonBlockRecentlyAccessedCoursesHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
CoreComponentsModule,
|
||||
AddonBlockRecentlyAccessedCoursesComponentsModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
providers: [
|
||||
AddonBlockRecentlyAccessedCoursesHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedCoursesModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRecentlyAccessedCoursesHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -88,6 +88,7 @@ import { AddonBlockActivityModulesModule } from '@addon/block/activitymodules/ac
|
|||
import { AddonBlockMyOverviewModule } from '@addon/block/myoverview/myoverview.module';
|
||||
import { AddonBlockSiteMainMenuModule } from '@addon/block/sitemainmenu/sitemainmenu.module';
|
||||
import { AddonBlockTimelineModule } from '@addon/block/timeline/timeline.module';
|
||||
import { AddonBlockRecentlyAccessedCoursesModule } from '@addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module';
|
||||
import { AddonModAssignModule } from '@addon/mod/assign/assign.module';
|
||||
import { AddonModBookModule } from '@addon/mod/book/book.module';
|
||||
import { AddonModChatModule } from '@addon/mod/chat/chat.module';
|
||||
|
@ -202,6 +203,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonBlockMyOverviewModule,
|
||||
AddonBlockSiteMainMenuModule,
|
||||
AddonBlockTimelineModule,
|
||||
AddonBlockRecentlyAccessedCoursesModule,
|
||||
AddonModAssignModule,
|
||||
AddonModBookModule,
|
||||
AddonModChatModule,
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
"addon.block_myoverview.past": "Past",
|
||||
"addon.block_myoverview.pluginname": "Course Overview",
|
||||
"addon.block_myoverview.title": "Title",
|
||||
"addon.block_recentlyaccessedcourses.nocourses": "No recent courses",
|
||||
"addon.block_recentlyaccessedcourses.pluginname": "Recently accessed courses",
|
||||
"addon.block_sitemainmenu.pluginname": "Main menu",
|
||||
"addon.block_timeline.duedate": "Due date",
|
||||
"addon.block_timeline.next30days": "Next 30 days",
|
||||
|
|
|
@ -738,6 +738,53 @@ export class CoreCourseHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the prefetch icon for selected courses.
|
||||
*
|
||||
* @param {any[]} courses Courses array to get info from.
|
||||
* @param {any} prefetch Prefetch information.
|
||||
* @return {Promise<any>} Resolved with the prefetch information updated when done.
|
||||
*/
|
||||
initPrefetchCoursesIcons(courses: any[], prefetch: any): Promise<any> {
|
||||
if (!courses || courses.length < 2) {
|
||||
// Not enough courses.
|
||||
prefetch.icon = '';
|
||||
|
||||
return Promise.resolve(prefetch);
|
||||
}
|
||||
|
||||
return this.determineCoursesStatus(courses).then((status) => {
|
||||
let icon = this.getCourseStatusIconAndTitleFromStatus(status).icon;
|
||||
if (icon == 'spinner') {
|
||||
// It seems all courses are being downloaded, show a download button instead.
|
||||
icon = 'cloud-download';
|
||||
}
|
||||
prefetch.icon = icon;
|
||||
|
||||
return prefetch;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the courses in the array.
|
||||
*
|
||||
* @param {any[]} courses Courses array to prefetch.
|
||||
* @param {any} prefetch Prefetch information to be updated.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetchCourses(courses: any[], prefetch: any): Promise<any> {
|
||||
prefetch.icon = 'spinner';
|
||||
prefetch.badge = '';
|
||||
|
||||
return this.confirmAndPrefetchCourses(courses, (progress) => {
|
||||
prefetch.badge = progress.count + ' / ' + progress.total;
|
||||
}).then(() => {
|
||||
prefetch.icon = 'refresh';
|
||||
}).finally(() => {
|
||||
prefetch.badge = '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course download promise (if any).
|
||||
*
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCoursesProvider } from './courses';
|
||||
import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion';
|
||||
|
||||
/**
|
||||
* Helper to gather some common courses functions.
|
||||
|
@ -22,7 +23,8 @@ import { CoreCoursesProvider } from './courses';
|
|||
@Injectable()
|
||||
export class CoreCoursesHelperProvider {
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider) { }
|
||||
constructor(private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider,
|
||||
private courseCompletionProvider: AddonCourseCompletionProvider) { }
|
||||
|
||||
/**
|
||||
* Given a course object returned by core_enrol_get_users_courses and another one returned by core_course_get_courses_by_field,
|
||||
|
@ -85,9 +87,11 @@ export class CoreCoursesHelperProvider {
|
|||
/**
|
||||
* Get user courses with admin and nav options.
|
||||
*
|
||||
* @return {Promise<any[]>} Promise resolved when done.
|
||||
* @param {string} [sort=fullname] Sort courses after get them. If sort is not defined it won't be sorted.
|
||||
* @param {number} [slice=0] Slice results to get the X first one. If slice > 0 it will be done after sorting.
|
||||
* @return {Promise<any[]>} Courses filled with options.
|
||||
*/
|
||||
getUserCoursesWithOptions(): Promise<any[]> {
|
||||
getUserCoursesWithOptions(sort: string = 'fullname', slice: number = 0): Promise<any[]> {
|
||||
return this.coursesProvider.getUserCourses().then((courses) => {
|
||||
const promises = [],
|
||||
courseIds = courses.map((course) => {
|
||||
|
@ -107,12 +111,43 @@ export class CoreCoursesHelperProvider {
|
|||
promises.push(this.loadCoursesExtraInfo(courses));
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return courses.sort((a, b) => {
|
||||
if (courses.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
switch (sort) {
|
||||
case 'fullname':
|
||||
courses.sort((a, b) => {
|
||||
const compareA = a.fullname.toLowerCase(),
|
||||
compareB = b.fullname.toLowerCase();
|
||||
|
||||
return compareA.localeCompare(compareB);
|
||||
});
|
||||
break;
|
||||
case 'lastaccess':
|
||||
courses.sort((a, b) => {
|
||||
return b.lastaccess - b.lastaccess;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// Sort not implemented. Do not sort.
|
||||
}
|
||||
courses = slice > 0 ? courses.slice(0, slice) : courses;
|
||||
|
||||
// Fetch course completion status.
|
||||
return Promise.all(courses.map((course) => {
|
||||
if (typeof course.enablecompletion != 'undefined' && course.enablecompletion == 0) {
|
||||
// Completion is disabled for this course, there is no need to fetch the completion status.
|
||||
return Promise.resolve(course);
|
||||
}
|
||||
|
||||
return this.courseCompletionProvider.getCompletion(course.id).catch(() => {
|
||||
// Ignore error, maybe course completion is disabled or user has no permission.
|
||||
}).then((completion) => {
|
||||
course.completed = completion && completion.completed;
|
||||
|
||||
return course;
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue