MOBILE-3659 course: Implement course format delegate
parent
94b8707ce6
commit
c617ec2dce
|
@ -23,12 +23,9 @@ import { CoreFilepool } from '@services/filepool';
|
|||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import {
|
||||
CoreCourseAnyCourseData,
|
||||
CoreCourseBasicData,
|
||||
CoreCourseGetCoursesData,
|
||||
CoreCourses,
|
||||
CoreCourseSearchedData,
|
||||
CoreEnrolledCourseBasicData,
|
||||
CoreEnrolledCourseData,
|
||||
} from '@features/courses/services/courses';
|
||||
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
|
@ -463,10 +460,10 @@ export class CoreCourseHelperProvider {
|
|||
async getCourse(
|
||||
courseId: number,
|
||||
siteId?: string,
|
||||
): Promise<{ enrolled: boolean; course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData }> {
|
||||
): Promise<{ enrolled: boolean; course: CoreCourseAnyCourseData }> {
|
||||
siteId = siteId || CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData;
|
||||
let course: CoreCourseAnyCourseData;
|
||||
|
||||
// Try with enrolled courses first.
|
||||
try {
|
||||
|
@ -495,11 +492,12 @@ export class CoreCourseHelperProvider {
|
|||
* @param courseId Course ID.
|
||||
* @param params Other params to pass to the course page.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async getAndOpenCourse(courseId: number, params?: Params, siteId?: string): Promise<any> {
|
||||
async getAndOpenCourse(courseId: number, params?: Params, siteId?: string): Promise<void> {
|
||||
const modal = await CoreDomUtils.instance.showModalLoading();
|
||||
|
||||
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData | { id: number };
|
||||
let course: CoreCourseAnyCourseData | { id: number };
|
||||
|
||||
try {
|
||||
const data = await this.getCourse(courseId, siteId);
|
||||
|
@ -575,7 +573,7 @@ export class CoreCourseHelperProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async loadOfflineCompletion(courseId: number, sections: any[], siteId?: string): Promise<void> {
|
||||
async loadOfflineCompletion(courseId: number, sections: CoreCourseSection[], siteId?: string): Promise<void> {
|
||||
const offlineCompletions = await CoreCourseOffline.instance.getCourseManualCompletions(courseId, siteId);
|
||||
|
||||
if (!offlineCompletions || !offlineCompletions.length) {
|
||||
|
@ -602,7 +600,7 @@ export class CoreCourseHelperProvider {
|
|||
offlineCompletion.timecompleted >= module.completiondata.timecompleted * 1000) {
|
||||
// The module has offline completion. Load it.
|
||||
module.completiondata.state = offlineCompletion.completed;
|
||||
module.completiondata.offline = true;
|
||||
// @todo module.completiondata.offline = true;
|
||||
|
||||
// If all completions have been loaded, stop.
|
||||
loaded++;
|
||||
|
@ -758,7 +756,7 @@ export class CoreCourseHelperProvider {
|
|||
* @return Section download ID.
|
||||
* @todo section type.
|
||||
*/
|
||||
getSectionDownloadId(section: any): string {
|
||||
getSectionDownloadId(section: CoreCourseSection): string {
|
||||
return 'Section-' + section.id;
|
||||
}
|
||||
|
||||
|
@ -978,7 +976,7 @@ export class CoreCourseHelperProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async openCourse(course: CoreEnrolledCourseBasicData | { id: number }, params?: Params, siteId?: string): Promise<void> {
|
||||
async openCourse(course: CoreCourseAnyCourseData | { id: number }, params?: Params, siteId?: string): Promise<void> {
|
||||
if (!siteId || siteId == CoreSites.instance.getCurrentSiteId()) {
|
||||
// Current site, we can open the course.
|
||||
return CoreCourse.instance.openCourse(course, params);
|
||||
|
|
|
@ -30,12 +30,14 @@ import { CoreCourseStatusDBRecord, COURSE_STATUS_TABLE } from './database/course
|
|||
import { CoreCourseOffline } from './course-offline';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import {
|
||||
CoreCourses,
|
||||
CoreCourseAnyCourseData,
|
||||
CoreCoursesProvider,
|
||||
} from '../../courses/services/courses';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreWSError } from '@classes/errors/wserror';
|
||||
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
|
||||
import { CoreCourseHelper } from './course-helper';
|
||||
import { CoreCourseFormatDelegate } from './format-delegate';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmCourse:';
|
||||
|
||||
|
@ -71,9 +73,6 @@ export class CoreCourseProvider {
|
|||
protected logger: CoreLogger;
|
||||
|
||||
constructor() {
|
||||
// @todo
|
||||
// protected courseFormatDelegate: CoreCourseFormatDelegate,
|
||||
// protected sitePluginsProvider: CoreSitePluginsProvider,
|
||||
this.logger = CoreLogger.getInstance('CoreCourseProvider');
|
||||
}
|
||||
|
||||
|
@ -981,39 +980,22 @@ export class CoreCourseProvider {
|
|||
* @param params Other params to pass to the course page.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async openCourse(
|
||||
course: { id: number ; format?: string },
|
||||
params?: Params, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
): Promise<void> {
|
||||
async openCourse(course: CoreCourseAnyCourseData | { id: number }, params?: Params): Promise<void> {
|
||||
// @todo const loading = await CoreDomUtils.instance.showModalLoading();
|
||||
|
||||
// Wait for site plugins to be fetched.
|
||||
// @todo await this.sitePluginsProvider.waitFetchPlugins();
|
||||
|
||||
if (typeof course.format == 'undefined') {
|
||||
// This block can be replaced by a call to CourseHelper.getCourse(), but it is circular dependant.
|
||||
const coursesProvider = CoreCourses.instance;
|
||||
try {
|
||||
course = await coursesProvider.getUserCourse(course.id, true);
|
||||
} catch (error) {
|
||||
// Not enrolled or an error happened. Try to use another WebService.
|
||||
const available = coursesProvider.isGetCoursesByFieldAvailableInSite();
|
||||
try {
|
||||
if (available) {
|
||||
course = await coursesProvider.getCourseByField('id', course.id);
|
||||
} else {
|
||||
course = await coursesProvider.getCourse(course.id);
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
if (!('format' in course) || typeof course.format == 'undefined') {
|
||||
const result = await CoreCourseHelper.instance.getCourse(course.id);
|
||||
|
||||
course = result.course;
|
||||
}
|
||||
|
||||
/* @todo
|
||||
if (!this.sitePluginsProvider.sitePluginPromiseExists('format_' + course.format)) {
|
||||
// No custom format plugin. We don't need to wait for anything.
|
||||
await this.courseFormatDelegate.openCourse(course, params);
|
||||
await CoreCourseFormatDelegate.instance.openCourse(course, params);
|
||||
loading.dismiss();
|
||||
|
||||
return;
|
||||
|
@ -1024,20 +1006,17 @@ export class CoreCourseProvider {
|
|||
/* @todo await this.sitePluginsProvider.sitePluginLoaded('format_' + course.format);
|
||||
// The format loaded successfully, but the handlers wont be registered until all site plugins have loaded.
|
||||
if (this.sitePluginsProvider.sitePluginsFinishedLoading) {
|
||||
return this.courseFormatDelegate.openCourse(course, params);
|
||||
return CoreCourseFormatDelegate.instance.openCourse(course, params);
|
||||
}*/
|
||||
|
||||
// Wait for plugins to be loaded.
|
||||
const deferred = CoreUtils.instance.promiseDefer<void>();
|
||||
|
||||
const observer = CoreEvents.on(CoreEvents.SITE_PLUGINS_LOADED, () => {
|
||||
observer && observer.off();
|
||||
observer?.off();
|
||||
|
||||
/* @todo this.courseFormatDelegate.openCourse(course, params).then((response) => {
|
||||
deferred.resolve(response);
|
||||
}).catch((error) => {
|
||||
deferred.reject(error);
|
||||
});*/
|
||||
CoreCourseFormatDelegate.instance.openCourse(<CoreCourseAnyCourseData> course, params)
|
||||
.then(deferred.resolve).catch(deferred.reject);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
|
|
@ -0,0 +1,377 @@
|
|||
// (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, Type } from '@angular/core';
|
||||
import { Params } from '@angular/router';
|
||||
|
||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreCourseSection } from './course';
|
||||
import { CoreCourseFormatDefaultHandler } from './handlers/default-format';
|
||||
|
||||
/**
|
||||
* Interface that all course format handlers must implement.
|
||||
*/
|
||||
export interface CoreCourseFormatHandler extends CoreDelegateHandler {
|
||||
/**
|
||||
* Name of the format the handler supports. E.g. 'singleactivity'.
|
||||
*/
|
||||
format: string;
|
||||
|
||||
/**
|
||||
* Get the title to use in course page. If not defined, course fullname.
|
||||
* This function will be called without sections first, and then call it again when the sections are retrieved.
|
||||
*
|
||||
* @param course The course.
|
||||
* @param sections List of sections.
|
||||
* @return Title.
|
||||
*/
|
||||
getCourseTitle?(course: CoreCourseAnyCourseData, sections?: CoreCourseSection[]): string;
|
||||
|
||||
/**
|
||||
* Whether it allows seeing all sections at the same time. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it can view all sections.
|
||||
*/
|
||||
canViewAllSections?(course: CoreCourseAnyCourseData): boolean;
|
||||
|
||||
/**
|
||||
* Whether the option blocks should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it can display blocks.
|
||||
*/
|
||||
displayBlocks?(course: CoreCourseAnyCourseData): boolean;
|
||||
|
||||
/**
|
||||
* Whether the option to enable section/module download should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the option to enable section/module download should be displayed.
|
||||
*/
|
||||
displayEnableDownload?(course: CoreCourseAnyCourseData): boolean;
|
||||
|
||||
/**
|
||||
* Whether the default section selector should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the default section selector should be displayed.
|
||||
*/
|
||||
displaySectionSelector?(course: CoreCourseAnyCourseData): boolean;
|
||||
|
||||
/**
|
||||
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
|
||||
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @param sections List of course sections.
|
||||
* @return Whether the refresher should be displayed.
|
||||
*/
|
||||
displayRefresher?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean;
|
||||
|
||||
/**
|
||||
* Given a list of sections, get the "current" section that should be displayed first. Defaults to first section.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Promise resolved with current section.
|
||||
*/
|
||||
getCurrentSection?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection>;
|
||||
|
||||
/**
|
||||
* Open the page to display a course. If not defined, the page CoreCourseSectionPage will be opened.
|
||||
* Implement it only if you want to create your own page to display the course. In general it's better to use the method
|
||||
* getCourseFormatComponent because it will display the course handlers at the top.
|
||||
* Your page should include the course handlers using CoreCoursesDelegate.
|
||||
*
|
||||
* @param course The course to open. It should contain a "format" attribute.
|
||||
* @param params Params to pass to the course page.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
openCourse?(course: CoreCourseAnyCourseData, params?: Params): Promise<void>;
|
||||
|
||||
/**
|
||||
* Return the Component to use to display the course format instead of using the default one.
|
||||
* Use it if you want to display a format completely different from the default one.
|
||||
* If you want to customize the default format there are several methods to customize parts of it.
|
||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
getCourseFormatComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
|
||||
|
||||
/**
|
||||
* Return the Component to use to display the course summary inside the default course format.
|
||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
getCourseSummaryComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
|
||||
|
||||
/**
|
||||
* Return the Component to use to display the section selector inside the default course format.
|
||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
getSectionSelectorComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
|
||||
|
||||
/**
|
||||
* Return the Component to use to display a single section. This component will only be used if the user is viewing a
|
||||
* single section. If all the sections are displayed at once then it won't be used.
|
||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
getSingleSectionComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
|
||||
|
||||
/**
|
||||
* Return the Component to use to display all sections in a course.
|
||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
getAllSectionsComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
|
||||
|
||||
/**
|
||||
* Invalidate the data required to load the course format.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateData?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Whether the view should be refreshed when completion changes. If your course format doesn't display
|
||||
* activity completion then you should return false.
|
||||
*
|
||||
* @param course The course.
|
||||
* @return Whether course view should be refreshed when an activity completion changes.
|
||||
*/
|
||||
shouldRefreshWhenCompletionChanges?(course: CoreCourseAnyCourseData): Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to interact with course formats.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseFormatHandler> {
|
||||
|
||||
protected featurePrefix = 'CoreCourseFormatDelegate_';
|
||||
protected handlerNameProperty = 'format';
|
||||
|
||||
constructor(protected defaultHandler: CoreCourseFormatDefaultHandler) {
|
||||
super('CoreCoursesCourseFormatDelegate', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether it allows seeing all sections at the same time. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it allows seeing all sections at the same time.
|
||||
*/
|
||||
canViewAllSections(course: CoreCourseAnyCourseData): boolean {
|
||||
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'canViewAllSections', [course]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the option blocks should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it can display blocks.
|
||||
*/
|
||||
displayBlocks(course: CoreCourseAnyCourseData): boolean {
|
||||
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayBlocks', [course]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the option to enable section/module download should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the option to enable section/module download should be displayed
|
||||
*/
|
||||
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
|
||||
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayEnableDownload', [course]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
|
||||
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @param sections List of course sections.
|
||||
* @return Whether the refresher should be displayed.
|
||||
*/
|
||||
displayRefresher(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean {
|
||||
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayRefresher', [course, sections]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default section selector should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the section selector should be displayed.
|
||||
*/
|
||||
displaySectionSelector(course: CoreCourseAnyCourseData): boolean {
|
||||
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displaySectionSelector', [course]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display all sections in a course.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
async getAllSectionsComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
|
||||
try {
|
||||
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getAllSectionsComponent', [course]);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting all sections component', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display a course format.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
async getCourseFormatComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
|
||||
try {
|
||||
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getCourseFormatComponent', [course]);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting course format component', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display the course summary in the default course format.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
async getCourseSummaryComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
|
||||
try {
|
||||
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getCourseSummaryComponent', [course]);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting course summary component', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a course, return the title to use in the course page.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Course title.
|
||||
*/
|
||||
getCourseTitle(course: CoreCourseAnyCourseData, sections?: CoreCourseSection[]): string | undefined {
|
||||
return this.executeFunctionOnEnabled(course.format || '', 'getCourseTitle', [course, sections]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a course and a list of sections, return the current section that should be displayed first.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Promise resolved with current section.
|
||||
*/
|
||||
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
|
||||
try {
|
||||
const section = await this.executeFunctionOnEnabled<CoreCourseSection>(
|
||||
course.format || '',
|
||||
'getCurrentSection',
|
||||
[course, sections],
|
||||
);
|
||||
|
||||
return section || sections[0];
|
||||
} catch {
|
||||
// This function should never fail. Just return the first section (usually, "All sections").
|
||||
return sections[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display the section selector inside the default course format.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
async getSectionSelectorComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
|
||||
try {
|
||||
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getSectionSelectorComponent', [course]);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting section selector component', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display a single section. This component will only be used if the user is viewing
|
||||
* a single section. If all the sections are displayed at once then it won't be used.
|
||||
*
|
||||
* @param course The course to render.
|
||||
* @return Promise resolved with component to use, undefined if not found.
|
||||
*/
|
||||
async getSingleSectionComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
|
||||
try {
|
||||
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getSingleSectionComponent', [course]);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting single section component', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the data required to load the course format.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
async invalidateData(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void> {
|
||||
await this.executeFunctionOnEnabled(course.format || '', 'invalidateData', [course, sections]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a course. Should not be called directly. Call CoreCourseHelper.openCourse instead.
|
||||
*
|
||||
* @param course The course to open. It should contain a "format" attribute.
|
||||
* @param params Params to pass to the course page.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async openCourse(course: CoreCourseAnyCourseData, params?: Params): Promise<void> {
|
||||
await this.executeFunctionOnEnabled(course.format || '', 'openCourse', [course, params]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the view should be refreshed when completion changes. If your course format doesn't display
|
||||
* activity completion then you should return false.
|
||||
*
|
||||
* @param course The course.
|
||||
* @return Whether course view should be refreshed when an activity completion changes.
|
||||
*/
|
||||
async shouldRefreshWhenCompletionChanges(course: CoreCourseAnyCourseData): Promise<boolean | undefined> {
|
||||
return await this.executeFunctionOnEnabled(course.format || '', 'shouldRefreshWhenCompletionChanges', [course]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreCourseFormatDelegate extends makeSingleton(CoreCourseFormatDelegateService) {}
|
|
@ -0,0 +1,193 @@
|
|||
// (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 { Params } from '@angular/router';
|
||||
|
||||
import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreCourseSection } from '../course';
|
||||
import { CoreCourseFormatHandler } from '../format-delegate';
|
||||
|
||||
/**
|
||||
* Default handler used when the course format doesn't have a specific implementation.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
|
||||
|
||||
name = 'CoreCourseFormatDefault';
|
||||
format = 'default';
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return Promise resolved with true if enabled.
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title to use in course page.
|
||||
*
|
||||
* @param course The course.
|
||||
* @return Title.
|
||||
*/
|
||||
getCourseTitle(course: CoreCourseAnyCourseData): string {
|
||||
if (course.displayname) {
|
||||
return course.displayname;
|
||||
} else if (course.fullname) {
|
||||
return course.fullname;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether it allows seeing all sections at the same time. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it can view all sections.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
canViewAllSections(course: CoreCourseAnyCourseData): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the option blocks should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether it can display blocks.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
displayBlocks(course: CoreCourseAnyCourseData): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the option to enable section/module download should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the option to enable section/module download should be displayed
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default section selector should be displayed. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @return Whether the default section selector should be displayed.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
displaySectionSelector(course: CoreCourseAnyCourseData): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
|
||||
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
|
||||
*
|
||||
* @param course The course to check.
|
||||
* @param sections List of course sections.
|
||||
* @return Whether the refresher should be displayed.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
displayRefresher?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of sections, get the "current" section that should be displayed first.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Current section (or promise resolved with current section).
|
||||
*/
|
||||
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
|
||||
let marker: number | undefined;
|
||||
|
||||
// We need the "marker" to determine the current section.
|
||||
if ('marker' in course) {
|
||||
// We already have it.
|
||||
marker = course.marker;
|
||||
} else if (!CoreCourses.instance.isGetCoursesByFieldAvailable()) {
|
||||
// Cannot get the current section, return all of them.
|
||||
return sections[0];
|
||||
} else {
|
||||
// Try to retrieve the marker.
|
||||
const courseData = await CoreUtils.instance.ignoreErrors(CoreCourses.instance.getCourseByField('id', course.id));
|
||||
|
||||
marker = courseData?.marker;
|
||||
}
|
||||
|
||||
if (marker && marker > 0) {
|
||||
// Find the marked section.
|
||||
const section = sections.find((sect) => sect.section == marker);
|
||||
|
||||
if (section) {
|
||||
return section;
|
||||
}
|
||||
}
|
||||
|
||||
// Marked section not found or we couldn't retrieve the marker. Return all sections.
|
||||
return sections[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the data required to load the course format.
|
||||
*
|
||||
* @param course The course to get the title.
|
||||
* @param sections List of sections.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async invalidateData(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void> {
|
||||
await CoreCourses.instance.invalidateCoursesByField('id', course.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the page to display a course. If not defined, the page CoreCourseSectionPage will be opened.
|
||||
* Implement it only if you want to create your own page to display the course. In general it's better to use the method
|
||||
* getCourseFormatComponent because it will display the course handlers at the top.
|
||||
* Your page should include the course handlers using CoreCoursesDelegate.
|
||||
*
|
||||
* @param course The course to open. It should contain a "format" attribute.
|
||||
* @param params Params to pass to the course page.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async openCourse(course: CoreCourseAnyCourseData, params?: Params): Promise<void> {
|
||||
params = params || {};
|
||||
Object.assign(params, { course: course });
|
||||
|
||||
// Don't return the .push promise, we don't want to display a loading modal during the page transition.
|
||||
// @todo navCtrl.push('CoreCourseSectionPage', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the view should be refreshed when completion changes. If your course format doesn't display
|
||||
* activity completion then you should return false.
|
||||
*
|
||||
* @param course The course.
|
||||
* @return Whether course view should be refreshed when an activity completion changes.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async shouldRefreshWhenCompletionChanges(course: CoreCourseAnyCourseData): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
||||
import { NavController } from '@ionic/angular';
|
||||
import { CoreCourses, CoreCourseSearchedData } from '../../services/courses';
|
||||
import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '../../services/courses-helper';
|
||||
|
@ -95,13 +96,11 @@ export class CoreCoursesCourseListItemComponent implements OnInit {
|
|||
* @param course The course to open.
|
||||
*/
|
||||
openCourse(): void {
|
||||
/* if (this.isEnrolled) {
|
||||
if (this.isEnrolled) {
|
||||
CoreCourseHelper.instance.openCourse(this.course);
|
||||
} else {
|
||||
this.navCtrl.navigateForward('/main/home/courses/preview', { queryParams: { course: this.course } });
|
||||
} */
|
||||
// @todo while opencourse function is not completed, open preview page.
|
||||
this.navCtrl.navigateForward('/main/home/courses/preview', { queryParams: { course: this.course } });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1588,3 +1588,5 @@ type CoreCourseSetFavouriteCoursesWSParams = {
|
|||
favourite: boolean; // Favourite status.
|
||||
}[];
|
||||
};
|
||||
|
||||
export type CoreCourseAnyCourseData = CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData;
|
||||
|
|
Loading…
Reference in New Issue