2020-11-20 11:08:30 +00:00
|
|
|
// (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 { CoreUtils } from '@services/utils/utils';
|
2020-12-24 10:47:03 +00:00
|
|
|
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
|
2021-09-20 07:47:27 +00:00
|
|
|
import {
|
|
|
|
CoreCourseAnyCourseDataWithOptions,
|
|
|
|
CoreCourses,
|
|
|
|
CoreCourseSearchedData,
|
|
|
|
CoreCourseUserAdminOrNavOptionIndexed,
|
|
|
|
CoreEnrolledCourseData,
|
|
|
|
} from './courses';
|
2020-12-14 13:50:37 +00:00
|
|
|
import { makeSingleton, Translate } from '@singletons';
|
2020-11-19 15:37:46 +00:00
|
|
|
import { CoreWSExternalFile } from '@services/ws';
|
2022-02-01 15:50:03 +00:00
|
|
|
import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion';
|
2022-07-27 06:45:51 +00:00
|
|
|
import moment from 'moment-timezone';
|
2022-09-08 06:34:26 +00:00
|
|
|
import { of } from 'rxjs';
|
2022-07-18 10:22:38 +00:00
|
|
|
import { firstValueFrom, zipIncludingComplete } from '@/core/utils/rxjs';
|
2022-07-01 12:48:55 +00:00
|
|
|
import { catchError, map } from 'rxjs/operators';
|
2023-11-22 09:20:12 +00:00
|
|
|
import { chainRequests, WSObservable } from '@classes/sites/authenticated-site';
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2023-06-01 13:31:46 +00:00
|
|
|
// Id for a course item representing all courses (for example, for course filters).
|
|
|
|
export const ALL_COURSES_ID = -1;
|
|
|
|
|
2020-11-20 11:08:30 +00:00
|
|
|
/**
|
|
|
|
* Helper to gather some common courses functions.
|
|
|
|
*/
|
2020-12-09 13:38:53 +00:00
|
|
|
@Injectable({ providedIn: 'root' })
|
2020-11-20 11:08:30 +00:00
|
|
|
export class CoreCoursesHelperProvider {
|
|
|
|
|
2021-11-17 16:26:22 +00:00
|
|
|
protected courseSiteColors: Record<string, (string | undefined)[]> = {};
|
|
|
|
|
2020-11-20 11:08:30 +00:00
|
|
|
/**
|
|
|
|
* Get the courses to display the course picker popover. If a courseId is specified, it will also return its categoryId.
|
|
|
|
*
|
|
|
|
* @param courseId Course ID to get the category.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Promise resolved with the list of courses and the category.
|
2020-11-20 11:08:30 +00:00
|
|
|
*/
|
2023-06-01 13:31:46 +00:00
|
|
|
async getCoursesForPopover(courseId?: number): Promise<{courses: CoreEnrolledCourseData[]; categoryId?: number}> {
|
|
|
|
const courses: CoreEnrolledCourseData[] = await CoreCourses.getUserCourses(false);
|
2020-12-14 13:50:37 +00:00
|
|
|
|
|
|
|
// Add "All courses".
|
|
|
|
courses.unshift({
|
2023-06-01 13:31:46 +00:00
|
|
|
id: ALL_COURSES_ID,
|
2021-03-02 10:41:04 +00:00
|
|
|
fullname: Translate.instant('core.fulllistofcourses'),
|
2023-06-01 13:31:46 +00:00
|
|
|
shortname: Translate.instant('core.fulllistofcourses'),
|
2020-12-14 13:50:37 +00:00
|
|
|
categoryid: -1,
|
2023-06-01 13:31:46 +00:00
|
|
|
summary: '',
|
|
|
|
summaryformat: 1,
|
2020-12-14 13:50:37 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let categoryId: number | undefined;
|
|
|
|
if (courseId) {
|
|
|
|
// Search the course to get the category.
|
|
|
|
const course = courses.find((course) => course.id == courseId);
|
|
|
|
|
|
|
|
if (course) {
|
|
|
|
categoryId = course.categoryid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
courses: courses,
|
|
|
|
categoryId: categoryId,
|
|
|
|
};
|
2020-11-20 11:08:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a course object returned by core_enrol_get_users_courses and another one returned by core_course_get_courses_by_field,
|
|
|
|
* load some extra data to the first one.
|
|
|
|
*
|
|
|
|
* @param course Course returned by core_enrol_get_users_courses.
|
|
|
|
* @param courseByField Course returned by core_course_get_courses_by_field.
|
|
|
|
* @param addCategoryName Whether add category name or not.
|
|
|
|
*/
|
2021-11-17 16:26:22 +00:00
|
|
|
protected loadCourseExtraInfo(
|
2020-11-20 11:08:30 +00:00
|
|
|
course: CoreEnrolledCourseDataWithExtraInfo,
|
|
|
|
courseByField: CoreCourseSearchedData,
|
|
|
|
addCategoryName: boolean = false,
|
|
|
|
): void {
|
|
|
|
if (courseByField) {
|
|
|
|
course.displayname = courseByField.displayname;
|
|
|
|
course.categoryname = addCategoryName ? courseByField.categoryname : undefined;
|
2020-11-19 15:37:46 +00:00
|
|
|
course.overviewfiles = course.overviewfiles || courseByField.overviewfiles;
|
2020-11-20 11:08:30 +00:00
|
|
|
} else {
|
|
|
|
delete course.displayname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-20 07:47:27 +00:00
|
|
|
/**
|
|
|
|
* Loads the color of courses or the thumb image.
|
|
|
|
*
|
|
|
|
* @param courses List of courses.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Promise resolved when done.
|
2021-09-20 07:47:27 +00:00
|
|
|
*/
|
|
|
|
async loadCoursesColorAndImage(courses: CoreCourseSearchedData[]): Promise<void> {
|
|
|
|
if (!courses.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-17 16:26:22 +00:00
|
|
|
await Promise.all(courses.map((course) => this.loadCourseColorAndImage(course)));
|
2021-09-20 07:47:27 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 11:08:30 +00:00
|
|
|
/**
|
|
|
|
* Given a list of courses returned by core_enrol_get_users_courses, load some extra data using the WebService
|
|
|
|
* core_course_get_courses_by_field if available.
|
|
|
|
*
|
|
|
|
* @param courses List of courses.
|
|
|
|
* @param loadCategoryNames Whether load category names or not.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Promise resolved when done.
|
2020-11-20 11:08:30 +00:00
|
|
|
*/
|
2022-07-01 12:48:55 +00:00
|
|
|
loadCoursesExtraInfo(
|
|
|
|
courses: CoreEnrolledCourseDataWithExtraInfo[],
|
|
|
|
loadCategoryNames: boolean = false,
|
|
|
|
): Promise<CoreEnrolledCourseDataWithExtraInfo[]> {
|
|
|
|
return firstValueFrom(this.loadCoursesExtraInfoObservable(courses, loadCategoryNames));
|
|
|
|
}
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
/**
|
|
|
|
* Given a list of courses returned by core_enrol_get_users_courses, load some extra data using the WebService
|
|
|
|
* core_course_get_courses_by_field if available.
|
|
|
|
*
|
|
|
|
* @param courses List of courses.
|
|
|
|
* @param loadCategoryNames Whether load category names or not.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Promise resolved when done.
|
2022-07-01 12:48:55 +00:00
|
|
|
*/
|
|
|
|
loadCoursesExtraInfoObservable(
|
|
|
|
courses: CoreEnrolledCourseDataWithExtraInfo[],
|
|
|
|
loadCategoryNames: boolean = false,
|
|
|
|
options: CoreSitesCommonWSOptions = {},
|
2022-09-08 06:34:26 +00:00
|
|
|
): WSObservable<CoreEnrolledCourseDataWithExtraInfo[]> {
|
2022-07-01 12:48:55 +00:00
|
|
|
if (!courses.length) {
|
|
|
|
return of([]);
|
|
|
|
}
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
if (!loadCategoryNames && (courses[0].overviewfiles !== undefined || courses[0].displayname !== undefined)) {
|
|
|
|
// No need to load more data.
|
|
|
|
return of(courses);
|
|
|
|
}
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
const courseIds = courses.map((course) => course.id).join(',');
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
// Get the extra data for the courses.
|
|
|
|
return CoreCourses.getCoursesByFieldObservable('ids', courseIds, options).pipe(map(coursesInfosArray => {
|
|
|
|
const coursesInfo = CoreUtils.arrayToObject(coursesInfosArray, 'id');
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
courses.forEach((course) => {
|
|
|
|
this.loadCourseExtraInfo(course, coursesInfo[course.id], loadCategoryNames);
|
|
|
|
});
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return courses;
|
|
|
|
}));
|
2020-11-19 15:37:46 +00:00
|
|
|
}
|
2020-11-20 11:08:30 +00:00
|
|
|
|
2020-11-19 15:37:46 +00:00
|
|
|
/**
|
|
|
|
* Load course colors from site config.
|
|
|
|
*
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns course colors RGB.
|
2020-11-19 15:37:46 +00:00
|
|
|
*/
|
|
|
|
protected async loadCourseSiteColors(): Promise<(string | undefined)[]> {
|
2021-11-17 16:26:22 +00:00
|
|
|
const site = CoreSites.getRequiredCurrentSite();
|
|
|
|
const siteId = site.getId();
|
|
|
|
|
|
|
|
if (this.courseSiteColors[siteId] !== undefined) {
|
|
|
|
return this.courseSiteColors[siteId];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!site.isVersionGreaterEqualThan('3.8')) {
|
|
|
|
this.courseSiteColors[siteId] = [];
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2020-11-19 15:37:46 +00:00
|
|
|
const colors: (string | undefined)[] = [];
|
|
|
|
|
2021-11-17 16:26:22 +00:00
|
|
|
try {
|
|
|
|
const configs = await site.getConfig();
|
|
|
|
for (let x = 0; x < 10; x++) {
|
|
|
|
colors[x] = configs['core_admin_coursecolor' + (x + 1)] || undefined;
|
2020-11-20 11:08:30 +00:00
|
|
|
}
|
2021-11-17 16:26:22 +00:00
|
|
|
|
|
|
|
this.courseSiteColors[siteId] = colors;
|
|
|
|
} catch {
|
|
|
|
// Ignore errors.
|
2020-11-19 15:37:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return colors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the color of the course or the thumb image.
|
|
|
|
*
|
|
|
|
* @param course Course data.
|
|
|
|
*/
|
2021-11-17 16:26:22 +00:00
|
|
|
async loadCourseColorAndImage(course: CoreCourseWithImageAndColor): Promise<void> {
|
2023-06-08 12:52:31 +00:00
|
|
|
// Moodle 4.1 downwards geopatterns are embedded in b64 in only some WS, remote them to keep it coherent.
|
|
|
|
if (course.courseimage?.startsWith('data')) {
|
|
|
|
course.courseimage = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (course.courseimage !== undefined) {
|
2023-10-18 13:01:57 +00:00
|
|
|
course.courseImage = course.courseimage; // @deprecated since 4.3 use courseimage instead.
|
2023-06-08 12:52:31 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-19 15:37:46 +00:00
|
|
|
if (course.overviewfiles && course.overviewfiles[0]) {
|
2023-06-08 12:52:31 +00:00
|
|
|
course.courseimage = course.overviewfiles[0].fileurl;
|
2023-10-18 13:01:57 +00:00
|
|
|
course.courseImage = course.courseimage; // @deprecated since 4.3 use courseimage instead.
|
2021-11-17 16:26:22 +00:00
|
|
|
|
|
|
|
return;
|
2020-11-19 15:37:46 +00:00
|
|
|
}
|
2021-11-17 16:26:22 +00:00
|
|
|
|
|
|
|
const colors = await this.loadCourseSiteColors();
|
|
|
|
|
|
|
|
course.colorNumber = course.id % 10;
|
|
|
|
course.color = colors.length ? colors[course.colorNumber] : undefined;
|
2020-11-20 11:08:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get user courses with admin and nav options.
|
|
|
|
*
|
|
|
|
* @param sort Sort courses after get them. If sort is not defined it won't be sorted.
|
|
|
|
* @param slice Slice results to get the X first one. If slice > 0 it will be done after sorting.
|
|
|
|
* @param filter Filter using some field.
|
|
|
|
* @param loadCategoryNames Whether load category names or not.
|
2022-07-01 12:48:55 +00:00
|
|
|
* @param options Options.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Courses filled with options.
|
2020-11-20 11:08:30 +00:00
|
|
|
*/
|
2022-07-01 12:48:55 +00:00
|
|
|
getUserCoursesWithOptions(
|
2020-12-10 10:45:27 +00:00
|
|
|
sort: string = 'fullname',
|
|
|
|
slice: number = 0,
|
|
|
|
filter?: string,
|
|
|
|
loadCategoryNames: boolean = false,
|
2020-12-24 10:47:03 +00:00
|
|
|
options: CoreSitesCommonWSOptions = {},
|
2022-07-18 12:07:05 +00:00
|
|
|
): Promise<CoreEnrolledCourseDataWithExtraInfoAndOptions[]> {
|
2022-07-01 12:48:55 +00:00
|
|
|
return firstValueFrom(this.getUserCoursesWithOptionsObservable({
|
|
|
|
sort,
|
|
|
|
slice,
|
|
|
|
filter,
|
|
|
|
loadCategoryNames,
|
|
|
|
...options,
|
2021-09-03 09:51:26 +00:00
|
|
|
}));
|
2022-07-01 12:48:55 +00:00
|
|
|
}
|
2020-12-10 10:45:27 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
/**
|
|
|
|
* Get user courses with admin and nav options.
|
|
|
|
*
|
|
|
|
* @param options Options.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Courses filled with options.
|
2022-07-01 12:48:55 +00:00
|
|
|
*/
|
|
|
|
getUserCoursesWithOptionsObservable(
|
|
|
|
options: CoreCoursesGetWithOptionsOptions = {},
|
2022-09-08 06:34:26 +00:00
|
|
|
): WSObservable<CoreEnrolledCourseDataWithExtraInfoAndOptions[]> {
|
2022-07-18 12:07:05 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return CoreCourses.getUserCoursesObservable(options).pipe(
|
|
|
|
chainRequests(options.readingStrategy, (courses, newReadingStrategy) => {
|
|
|
|
if (courses.length <= 0) {
|
|
|
|
return of([]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const courseIds = courses.map((course) => course.id); // Use all courses to get options, to use cache.
|
|
|
|
const newOptions = {
|
|
|
|
...options,
|
|
|
|
readingStrategy: newReadingStrategy,
|
|
|
|
};
|
|
|
|
courses = this.filterAndSortCoursesWithOptions(courses, options);
|
|
|
|
|
2022-07-18 10:22:38 +00:00
|
|
|
return zipIncludingComplete(
|
2022-07-01 12:48:55 +00:00
|
|
|
this.loadCoursesExtraInfoObservable(courses, options.loadCategoryNames, newOptions),
|
|
|
|
CoreCourses.getCoursesAdminAndNavOptionsObservable(courseIds, newOptions).pipe(map(courseOptions => {
|
|
|
|
courses.forEach((course: CoreEnrolledCourseDataWithOptions) => {
|
|
|
|
course.navOptions = courseOptions.navOptions[course.id];
|
|
|
|
course.admOptions = courseOptions.admOptions[course.id];
|
|
|
|
});
|
|
|
|
})),
|
|
|
|
...courses.map(course => this.loadCourseCompletedStatus(course, newOptions)),
|
|
|
|
).pipe(map(() => courses));
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
2020-12-10 10:45:27 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
/**
|
|
|
|
* Filter and sort some courses.
|
|
|
|
*
|
|
|
|
* @param courses Courses.
|
|
|
|
* @param options Options
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Courses filtered and sorted.
|
2022-07-01 12:48:55 +00:00
|
|
|
*/
|
|
|
|
protected filterAndSortCoursesWithOptions(
|
|
|
|
courses: CoreEnrolledCourseData[],
|
|
|
|
options: CoreCoursesGetWithOptionsOptions = {},
|
|
|
|
): CoreEnrolledCourseData[] {
|
|
|
|
const sort = options.sort ?? 'fullname';
|
|
|
|
const slice = options.slice ?? -1;
|
|
|
|
|
|
|
|
switch (options.filter) {
|
2020-12-10 10:45:27 +00:00
|
|
|
case 'isfavourite':
|
|
|
|
courses = courses.filter((course) => !!course.isfavourite);
|
|
|
|
break;
|
|
|
|
default:
|
2021-12-03 10:10:44 +00:00
|
|
|
// Filter not implemented.
|
2020-12-10 10:45:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (sort) {
|
|
|
|
case 'fullname':
|
|
|
|
courses.sort((a, b) => {
|
|
|
|
const compareA = a.fullname.toLowerCase();
|
|
|
|
const compareB = b.fullname.toLowerCase();
|
|
|
|
|
|
|
|
return compareA.localeCompare(compareB);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'lastaccess':
|
|
|
|
courses.sort((a, b) => (b.lastaccess || 0) - (a.lastaccess || 0));
|
|
|
|
break;
|
2021-12-03 10:10:44 +00:00
|
|
|
// Time modified property is defined on Moodle 4.0.
|
|
|
|
case 'timemodified':
|
|
|
|
courses.sort((a, b) => (b.timemodified || 0) - (a.timemodified || 0));
|
|
|
|
break;
|
2020-12-10 10:45:27 +00:00
|
|
|
case 'shortname':
|
|
|
|
courses.sort((a, b) => {
|
|
|
|
const compareA = a.shortname.toLowerCase();
|
|
|
|
const compareB = b.shortname.toLowerCase();
|
|
|
|
|
|
|
|
return compareA.localeCompare(compareB);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Sort not implemented. Do not sort.
|
|
|
|
}
|
|
|
|
|
|
|
|
courses = slice > 0 ? courses.slice(0, slice) : courses;
|
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return courses;
|
|
|
|
}
|
2020-12-10 10:45:27 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
/**
|
|
|
|
* Given a course object, fetch and set its completed status if not present already.
|
|
|
|
*
|
|
|
|
* @param course Course.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Observable.
|
2022-07-01 12:48:55 +00:00
|
|
|
*/
|
|
|
|
protected loadCourseCompletedStatus(
|
|
|
|
course: CoreEnrolledCourseDataWithExtraInfo,
|
|
|
|
options: CoreSitesCommonWSOptions = {},
|
2022-09-08 06:34:26 +00:00
|
|
|
): WSObservable<CoreEnrolledCourseDataWithExtraInfo> {
|
2022-07-01 12:48:55 +00:00
|
|
|
if (course.completed !== undefined) {
|
|
|
|
// The WebService already returns the completed status, no need to fetch it.
|
|
|
|
return of(course);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (course.enablecompletion !== undefined && !course.enablecompletion) {
|
|
|
|
// Completion is disabled for this course, there is no need to fetch the completion status.
|
|
|
|
return of(course);
|
|
|
|
}
|
2020-12-10 10:45:27 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return AddonCourseCompletion.getCompletionObservable(course.id, options).pipe(
|
|
|
|
map(completion => {
|
|
|
|
course.completed = completion.completed;
|
2020-12-10 10:45:27 +00:00
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return course;
|
|
|
|
}),
|
|
|
|
catchError(() => {
|
2020-12-10 10:45:27 +00:00
|
|
|
// Ignore error, maybe course completion is disabled or user has no permission.
|
|
|
|
course.completed = false;
|
|
|
|
|
2022-07-01 12:48:55 +00:00
|
|
|
return of(course);
|
|
|
|
}),
|
|
|
|
);
|
2020-11-20 11:08:30 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 10:12:41 +00:00
|
|
|
/**
|
|
|
|
* Calculates if course date is past.
|
|
|
|
*
|
|
|
|
* @param course Course Object.
|
|
|
|
* @param gradePeriodAfter Classify past courses as in progress for these many days after the course end date.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Wether the course is past.
|
2022-04-22 10:12:41 +00:00
|
|
|
*/
|
|
|
|
isPastCourse(course: CoreEnrolledCourseDataWithOptions, gradePeriodAfter = 0): boolean {
|
|
|
|
if (course.completed) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!course.enddate) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the end date to use for display classification purposes, incorporating the grace period, if any.
|
|
|
|
const endDate = moment(course.enddate * 1000).add(gradePeriodAfter, 'days').valueOf();
|
|
|
|
|
|
|
|
return endDate < Date.now();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates if course date is future.
|
|
|
|
*
|
|
|
|
* @param course Course Object.
|
|
|
|
* @param gradePeriodAfter Classify past courses as in progress for these many days after the course end date.
|
|
|
|
* @param gradePeriodBefore Classify future courses as in progress for these many days prior to the course start date.
|
2022-12-01 11:31:00 +00:00
|
|
|
* @returns Wether the course is future.
|
2022-04-22 10:12:41 +00:00
|
|
|
*/
|
|
|
|
isFutureCourse(
|
|
|
|
course: CoreEnrolledCourseDataWithOptions,
|
|
|
|
gradePeriodAfter = 0,
|
|
|
|
gradePeriodBefore = 0,
|
|
|
|
): boolean {
|
|
|
|
if (this.isPastCourse(course, gradePeriodAfter) || !course.startdate) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the start date to use for display classification purposes, incorporating the grace period, if any.
|
|
|
|
const startDate = moment(course.startdate * 1000).subtract(gradePeriodBefore, 'days').valueOf();
|
|
|
|
|
|
|
|
return startDate > Date.now();
|
|
|
|
}
|
|
|
|
|
2022-07-14 12:23:51 +00:00
|
|
|
/**
|
|
|
|
* Retrieves my courses page module.
|
|
|
|
*
|
|
|
|
* @returns My courses page module.
|
|
|
|
*/
|
|
|
|
async getMyRouteModule(): Promise<unknown> {
|
2023-01-25 10:08:36 +00:00
|
|
|
return import('../courses-my-lazy.module').then(m => m.CoreCoursesMyLazyModule);
|
2022-07-14 12:23:51 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 11:08:30 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 10:41:04 +00:00
|
|
|
export const CoreCoursesHelper = makeSingleton(CoreCoursesHelperProvider);
|
2020-11-20 11:08:30 +00:00
|
|
|
|
|
|
|
/**
|
2020-11-19 15:37:46 +00:00
|
|
|
* Course with colors info and course image.
|
2020-11-20 11:08:30 +00:00
|
|
|
*/
|
2020-11-19 15:37:46 +00:00
|
|
|
export type CoreCourseWithImageAndColor = {
|
|
|
|
id: number; // Course id.
|
|
|
|
overviewfiles?: CoreWSExternalFile[];
|
2020-11-20 11:08:30 +00:00
|
|
|
colorNumber?: number; // Color index number.
|
|
|
|
color?: string; // Color RGB.
|
2023-06-08 12:52:31 +00:00
|
|
|
courseImage?: string; // Course thumbnail. @deprecated since 4.3, use courseimage instead.
|
|
|
|
courseimage?: string; // Course thumbnail.
|
2020-11-19 15:37:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enrolled course data with extra rendering info.
|
|
|
|
*/
|
|
|
|
export type CoreEnrolledCourseDataWithExtraInfo = CoreCourseWithImageAndColor & CoreEnrolledCourseData & {
|
2020-11-20 11:08:30 +00:00
|
|
|
categoryname?: string; // Category name,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enrolled course data with admin and navigation option availability.
|
|
|
|
*/
|
|
|
|
export type CoreEnrolledCourseDataWithOptions = CoreEnrolledCourseData & {
|
|
|
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
|
|
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
|
|
|
};
|
|
|
|
|
2021-09-20 07:47:27 +00:00
|
|
|
/**
|
|
|
|
* Course summary data with admin and navigation option availability.
|
|
|
|
*/
|
|
|
|
export type CoreCourseSearchedDataWithOptions = CoreCourseSearchedData & {
|
|
|
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
|
|
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
|
|
|
};
|
|
|
|
|
2020-11-20 11:08:30 +00:00
|
|
|
/**
|
|
|
|
* Enrolled course data with admin and navigation option availability and extra rendering info.
|
|
|
|
*/
|
|
|
|
export type CoreEnrolledCourseDataWithExtraInfoAndOptions = CoreEnrolledCourseDataWithExtraInfo & CoreEnrolledCourseDataWithOptions;
|
2021-09-20 07:47:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Searched course data with admin and navigation option availability and extra rendering info.
|
|
|
|
*/
|
|
|
|
export type CoreCourseSearchedDataWithExtraInfoAndOptions = CoreCourseWithImageAndColor & CoreCourseSearchedDataWithOptions;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Any course data with admin and navigation option availability and extra rendering info.
|
|
|
|
*/
|
|
|
|
export type CoreCourseAnyCourseDataWithExtraInfoAndOptions = CoreCourseWithImageAndColor & CoreCourseAnyCourseDataWithOptions & {
|
|
|
|
categoryname?: string; // Category name,
|
|
|
|
};
|
2022-07-01 12:48:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Options for getUserCoursesWithOptionsObservable.
|
|
|
|
*/
|
|
|
|
export type CoreCoursesGetWithOptionsOptions = CoreSitesCommonWSOptions & {
|
|
|
|
sort?: string; // Sort courses after get them. Defaults to 'fullname'.
|
|
|
|
slice?: number; // Slice results to get the X first one. If slice > 0 it will be done after sorting.
|
|
|
|
filter?: string; // Filter using some field.
|
|
|
|
loadCategoryNames?: boolean; // Whether load category names or not.
|
|
|
|
};
|