commit
9ac9a5f5c2
|
@ -21,11 +21,16 @@ import {
|
||||||
CoreCourses,
|
CoreCourses,
|
||||||
CoreCourseSummaryData,
|
CoreCourseSummaryData,
|
||||||
} from '@features/courses/services/courses';
|
} from '@features/courses/services/courses';
|
||||||
import { CoreCourseSearchedDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
|
import {
|
||||||
|
CoreCourseSearchedDataWithExtraInfoAndOptions,
|
||||||
|
CoreCoursesHelper,
|
||||||
|
CoreEnrolledCourseDataWithOptions,
|
||||||
|
} from '@features/courses/services/courses-helper';
|
||||||
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
||||||
import { AddonCourseCompletion } from '@/addons/coursecompletion/services/coursecompletion';
|
import { AddonCourseCompletion } from '@/addons/coursecompletion/services/coursecompletion';
|
||||||
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to render a recent courses block.
|
* Component to render a recent courses block.
|
||||||
|
@ -38,11 +43,12 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
|
|
||||||
@Input() downloadEnabled = false;
|
@Input() downloadEnabled = false;
|
||||||
|
|
||||||
courses: (Omit<CoreCourseSummaryData, 'visible'> & CoreCourseSearchedDataWithExtraInfoAndOptions)[] = [];
|
courses: AddonBlockRecentlyAccessedCourse[] = [];
|
||||||
|
|
||||||
downloadCourseEnabled = false;
|
downloadCourseEnabled = false;
|
||||||
scrollElementId!: string;
|
scrollElementId!: string;
|
||||||
|
|
||||||
|
protected site!: CoreSite;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected coursesObserver?: CoreEventObserver;
|
protected coursesObserver?: CoreEventObserver;
|
||||||
protected updateSiteObserver?: CoreEventObserver;
|
protected updateSiteObserver?: CoreEventObserver;
|
||||||
|
@ -50,6 +56,8 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('AddonBlockRecentlyAccessedCoursesComponent');
|
super('AddonBlockRecentlyAccessedCoursesComponent');
|
||||||
|
|
||||||
|
this.site = CoreSites.getRequiredCurrentSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,14 +76,14 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
|
this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
|
||||||
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
|
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
|
||||||
|
|
||||||
}, CoreSites.getCurrentSiteId());
|
}, this.site.getId());
|
||||||
|
|
||||||
this.coursesObserver = CoreEvents.on(
|
this.coursesObserver = CoreEvents.on(
|
||||||
CoreCoursesProvider.EVENT_MY_COURSES_UPDATED,
|
CoreCoursesProvider.EVENT_MY_COURSES_UPDATED,
|
||||||
(data) => {
|
(data) => {
|
||||||
this.refreshCourseList(data);
|
this.refreshCourseList(data);
|
||||||
},
|
},
|
||||||
CoreSites.getCurrentSiteId(),
|
this.site.getId(),
|
||||||
);
|
);
|
||||||
|
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
@ -99,8 +107,12 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
protected async invalidateCourses(courseIds: number[]): Promise<void> {
|
protected async invalidateCourses(courseIds: number[]): Promise<void> {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
const invalidateCoursePromise = this.site.isVersionGreaterEqualThan('3.8')
|
||||||
|
? CoreCourses.invalidateRecentCourses()
|
||||||
|
: CoreCourses.invalidateUserCourses();
|
||||||
|
|
||||||
// Invalidate course completion data.
|
// Invalidate course completion data.
|
||||||
promises.push(CoreCourses.invalidateRecentCourses().finally(() =>
|
promises.push(invalidateCoursePromise.finally(() =>
|
||||||
CoreUtils.allPromises(courseIds.map((courseId) =>
|
CoreUtils.allPromises(courseIds.map((courseId) =>
|
||||||
AddonCourseCompletion.invalidateCourseCompletion(courseId)))));
|
AddonCourseCompletion.invalidateCourseCompletion(courseId)))));
|
||||||
|
|
||||||
|
@ -123,6 +135,13 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
const showCategories = this.block.configsRecord && this.block.configsRecord.displaycategories &&
|
const showCategories = this.block.configsRecord && this.block.configsRecord.displaycategories &&
|
||||||
this.block.configsRecord.displaycategories.value == '1';
|
this.block.configsRecord.displaycategories.value == '1';
|
||||||
|
|
||||||
|
// WS is failing on 3.7 and 3.6, use a fallback.
|
||||||
|
if (!this.site.isVersionGreaterEqualThan('3.8')) {
|
||||||
|
this.courses = await CoreCoursesHelper.getUserCoursesWithOptions('lastaccess', 10, undefined, showCategories);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const recentCourses = await CoreCourses.getRecentCourses();
|
const recentCourses = await CoreCourses.getRecentCourses();
|
||||||
const courseIds = recentCourses.map((course) => course.id);
|
const courseIds = recentCourses.map((course) => course.id);
|
||||||
|
|
||||||
|
@ -191,3 +210,9 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddonBlockRecentlyAccessedCourse =
|
||||||
|
(Omit<CoreCourseSummaryData, 'visible'> & CoreCourseSearchedDataWithExtraInfoAndOptions) |
|
||||||
|
(CoreEnrolledCourseDataWithOptions & {
|
||||||
|
categoryname?: string; // Category name,
|
||||||
|
});
|
||||||
|
|
|
@ -16,11 +16,17 @@ import { Component, OnInit, OnDestroy, Input } from '@angular/core';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData, CoreCourses } from '@features/courses/services/courses';
|
import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData, CoreCourses } from '@features/courses/services/courses';
|
||||||
import { CoreCoursesHelper, CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper';
|
import {
|
||||||
|
CoreCourseSearchedDataWithExtraInfoAndOptions,
|
||||||
|
CoreCoursesHelper,
|
||||||
|
CoreEnrolledCourseDataWithOptions,
|
||||||
|
} from '@features/courses/services/courses-helper';
|
||||||
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
|
||||||
import { AddonCourseCompletion } from '@/addons/coursecompletion/services/coursecompletion';
|
import { AddonCourseCompletion } from '@/addons/coursecompletion/services/coursecompletion';
|
||||||
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { AddonBlockStarredCourse, AddonBlockStarredCourses } from '../../services/starredcourses';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to render a starred courses block.
|
* Component to render a starred courses block.
|
||||||
|
@ -33,11 +39,12 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
|
|
||||||
@Input() downloadEnabled = false;
|
@Input() downloadEnabled = false;
|
||||||
|
|
||||||
courses: CoreEnrolledCourseDataWithOptions [] = [];
|
courses: AddonBlockStarredCoursesCourse[] = [];
|
||||||
|
|
||||||
downloadCourseEnabled = false;
|
downloadCourseEnabled = false;
|
||||||
scrollElementId!: string;
|
scrollElementId!: string;
|
||||||
|
|
||||||
|
protected site!: CoreSite;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected coursesObserver?: CoreEventObserver;
|
protected coursesObserver?: CoreEventObserver;
|
||||||
protected updateSiteObserver?: CoreEventObserver;
|
protected updateSiteObserver?: CoreEventObserver;
|
||||||
|
@ -45,6 +52,8 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('AddonBlockStarredCoursesComponent');
|
super('AddonBlockStarredCoursesComponent');
|
||||||
|
|
||||||
|
this.site = CoreSites.getRequiredCurrentSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,8 +103,12 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
protected async invalidateCourses(courseIds: number[]): Promise<void> {
|
protected async invalidateCourses(courseIds: number[]): Promise<void> {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
const invalidateCoursePromise = this.site.isVersionGreaterEqualThan('4.0')
|
||||||
|
? CoreCourses.invalidateUserCourses()
|
||||||
|
: AddonBlockStarredCourses.invalidateStarredCourses();
|
||||||
|
|
||||||
// Invalidate course completion data.
|
// Invalidate course completion data.
|
||||||
promises.push(CoreCourses.invalidateUserCourses().finally(() =>
|
promises.push(invalidateCoursePromise.finally(() =>
|
||||||
CoreUtils.allPromises(courseIds.map((courseId) =>
|
CoreUtils.allPromises(courseIds.map((courseId) =>
|
||||||
AddonCourseCompletion.invalidateCourseCompletion(courseId)))));
|
AddonCourseCompletion.invalidateCourseCompletion(courseId)))));
|
||||||
|
|
||||||
|
@ -118,8 +131,36 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
const showCategories = this.block.configsRecord && this.block.configsRecord.displaycategories &&
|
const showCategories = this.block.configsRecord && this.block.configsRecord.displaycategories &&
|
||||||
this.block.configsRecord.displaycategories.value == '1';
|
this.block.configsRecord.displaycategories.value == '1';
|
||||||
|
|
||||||
// @TODO: Sort won't coincide with website because timemodified is not informed.
|
if (this.site.isVersionGreaterEqualThan('4.0')) {
|
||||||
this.courses = await CoreCoursesHelper.getUserCoursesWithOptions('timemodified', 0, 'isfavourite', showCategories);
|
this.courses = await CoreCoursesHelper.getUserCoursesWithOptions('timemodified', 0, 'isfavourite', showCategories);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timemodified not present, use the block WS to retrieve the info.
|
||||||
|
const starredCourses = await AddonBlockStarredCourses.getStarredCourses();
|
||||||
|
|
||||||
|
const courseIds = starredCourses.map((course) => course.id);
|
||||||
|
|
||||||
|
// Get the courses using getCoursesByField to get more info about each course.
|
||||||
|
const courses = await CoreCourses.getCoursesByField('ids', courseIds.join(','));
|
||||||
|
|
||||||
|
this.courses = starredCourses.map((recentCourse) => {
|
||||||
|
const course = courses.find((course) => recentCourse.id == course.id);
|
||||||
|
|
||||||
|
return Object.assign(recentCourse, course);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get course options and extra info.
|
||||||
|
const options = await CoreCourses.getCoursesAdminAndNavOptions(courseIds);
|
||||||
|
this.courses.forEach((course) => {
|
||||||
|
course.navOptions = options.navOptions[course.id];
|
||||||
|
course.admOptions = options.admOptions[course.id];
|
||||||
|
|
||||||
|
if (!showCategories) {
|
||||||
|
course.categoryname = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,7 +196,6 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.invalidateCourses([course.id]);
|
await this.invalidateCourses([course.id]);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,3 +209,9 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddonBlockStarredCoursesCourse =
|
||||||
|
(AddonBlockStarredCourse & CoreCourseSearchedDataWithExtraInfoAndOptions) |
|
||||||
|
(CoreEnrolledCourseDataWithOptions & {
|
||||||
|
categoryname?: string; // Category name,
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
// (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 { CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
const ROOT_CACHE_KEY = 'AddonBlockStarredCourses:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features regarding starred courses.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonBlockStarredCoursesProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for get starred courrses value WS call.
|
||||||
|
*
|
||||||
|
* @return Cache key.
|
||||||
|
*/
|
||||||
|
protected getStarredCoursesCacheKey(): string {
|
||||||
|
return ROOT_CACHE_KEY + ':starredcourses';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get starred courrses.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @return Promise resolved when the info is retrieved.
|
||||||
|
*/
|
||||||
|
async getStarredCourses(siteId?: string): Promise<AddonBlockStarredCourse[]> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getStarredCoursesCacheKey(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return await site.read<AddonBlockStarredCourse[]>('block_starredcourses_get_starred_courses', undefined, preSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates get starred courrses WS call.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID to invalidate. If not defined, use current site.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateStarredCourses(siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await site.invalidateWsCacheForKey(this.getStarredCoursesCacheKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonBlockStarredCourses = makeSingleton(AddonBlockStarredCoursesProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of block_starredcourses_get_starred_courses WS.
|
||||||
|
*/
|
||||||
|
export type AddonBlockStarredCoursesGetStarredCoursesWSParams = {
|
||||||
|
limit?: number; // Limit.
|
||||||
|
offset?: number; // Offset.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by block_starredcourses_get_starred_courses WS.
|
||||||
|
*/
|
||||||
|
export type AddonBlockStarredCourse = {
|
||||||
|
id: number; // Id.
|
||||||
|
fullname: string; // Fullname.
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
idnumber: string; // Idnumber.
|
||||||
|
summary: string; // Summary.
|
||||||
|
summaryformat: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
startdate: number; // Startdate.
|
||||||
|
enddate: number; // Enddate.
|
||||||
|
visible: boolean; // Visible.
|
||||||
|
showactivitydates: boolean; // Showactivitydates.
|
||||||
|
showcompletionconditions: boolean; // Showcompletionconditions.
|
||||||
|
fullnamedisplay: string; // Fullnamedisplay.
|
||||||
|
viewurl: string; // Viewurl.
|
||||||
|
courseimage: string; // Courseimage.
|
||||||
|
progress?: number; // Progress.
|
||||||
|
hasprogress: boolean; // Hasprogress.
|
||||||
|
isfavourite: boolean; // Isfavourite.
|
||||||
|
hidden: boolean; // Hidden.
|
||||||
|
timeaccess?: number; // Timeaccess.
|
||||||
|
showshortname: boolean; // Showshortname.
|
||||||
|
coursecategory: string; // Coursecategory.
|
||||||
|
};
|
|
@ -1308,6 +1308,7 @@ export type CoreCourseSummary = {
|
||||||
coursecategory: string; // @since 3.7. Coursecategory.
|
coursecategory: string; // @since 3.7. Coursecategory.
|
||||||
showactivitydates: boolean | null; // @since 3.11. Whether the activity dates are shown or not.
|
showactivitydates: boolean | null; // @since 3.11. Whether the activity dates are shown or not.
|
||||||
showcompletionconditions: boolean | null; // @since 3.11. Whether the activity completion conditions are shown or not.
|
showcompletionconditions: boolean | null; // @since 3.11. Whether the activity completion conditions are shown or not.
|
||||||
|
timemodified?: number; // @since 4.0. Last time course settings were updated (timestamp).
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -236,7 +236,7 @@ export class CoreCoursesHelperProvider {
|
||||||
courses = courses.filter((course) => !!course.isfavourite);
|
courses = courses.filter((course) => !!course.isfavourite);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Filter not implemented.
|
// Filter not implemented.
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
|
@ -251,10 +251,10 @@ export class CoreCoursesHelperProvider {
|
||||||
case 'lastaccess':
|
case 'lastaccess':
|
||||||
courses.sort((a, b) => (b.lastaccess || 0) - (a.lastaccess || 0));
|
courses.sort((a, b) => (b.lastaccess || 0) - (a.lastaccess || 0));
|
||||||
break;
|
break;
|
||||||
// @todo Time modified property is not defined in CoreEnrolledCourseDataWithOptions, so it Won't do anything.
|
// Time modified property is defined on Moodle 4.0.
|
||||||
// case 'timemodified':
|
case 'timemodified':
|
||||||
// courses.sort((a, b) => b.timemodified - a.timemodified);
|
courses.sort((a, b) => (b.timemodified || 0) - (a.timemodified || 0));
|
||||||
// break;
|
break;
|
||||||
case 'shortname':
|
case 'shortname':
|
||||||
courses.sort((a, b) => {
|
courses.sort((a, b) => {
|
||||||
const compareA = a.shortname.toLowerCase();
|
const compareA = a.shortname.toLowerCase();
|
||||||
|
|
|
@ -1328,6 +1328,7 @@ export type CoreEnrolledCourseData = CoreEnrolledCourseBasicData & {
|
||||||
overviewfiles?: CoreWSExternalFile[];
|
overviewfiles?: CoreWSExternalFile[];
|
||||||
showactivitydates?: boolean; // @since 3.11. Whether the activity dates are shown or not.
|
showactivitydates?: boolean; // @since 3.11. Whether the activity dates are shown or not.
|
||||||
showcompletionconditions?: boolean; // @since 3.11. Whether the activity completion conditions are shown or not.
|
showcompletionconditions?: boolean; // @since 3.11. Whether the activity completion conditions are shown or not.
|
||||||
|
timemodified?: number; // @since 4.0. Last time course settings were updated (timestamp).
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue