MOBILE-3594 course: Implement some course services
parent
4c7545fb46
commit
cc5378aee7
|
@ -71,6 +71,12 @@ export class CoreConstants {
|
|||
static readonly OUTDATED = 'outdated';
|
||||
static readonly NOT_DOWNLOADABLE = 'notdownloadable';
|
||||
|
||||
static readonly DOWNLOADED_ICON = 'cloud-done';
|
||||
static readonly DOWNLOADING_ICON = 'spinner';
|
||||
static readonly NOT_DOWNLOADED_ICON = 'cloud-download';
|
||||
static readonly OUTDATED_ICON = 'fas-redo-alt';
|
||||
static readonly NOT_DOWNLOADABLE_ICON = '';
|
||||
|
||||
// Constants from Moodle's resourcelib.
|
||||
static readonly RESOURCELIB_DISPLAY_AUTO = 0; // Try the best way.
|
||||
static readonly RESOURCELIB_DISPLAY_EMBED = 1; // Display using object tag.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CORE_SITE_SCHEMAS } from '@/core/services/sites';
|
||||
|
||||
import {
|
||||
SITE_SCHEMA as COURSE_SITE_SCHEMA,
|
||||
OFFLINE_SITE_SCHEMA as COURSE_OFFLINE_SITE_SCHEMA,
|
||||
} from './services/course-db';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: CORE_SITE_SCHEMAS,
|
||||
useValue: [
|
||||
COURSE_SITE_SCHEMA,
|
||||
COURSE_OFFLINE_SITE_SCHEMA,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CoreCourseModule {
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// (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 { CoreSiteSchema } from '@services/sites';
|
||||
|
||||
/**
|
||||
* Database variables for CoreCourse service.
|
||||
*/
|
||||
export const COURSE_STATUS_TABLE = 'course_status';
|
||||
export const SITE_SCHEMA: CoreSiteSchema = {
|
||||
name: 'CoreCourseProvider',
|
||||
version: 1,
|
||||
tables: [
|
||||
{
|
||||
name: COURSE_STATUS_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'INTEGER',
|
||||
primaryKey: true,
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'TEXT',
|
||||
notNull: true,
|
||||
},
|
||||
{
|
||||
name: 'previous',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'updated',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'downloadTime',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'previousDownloadTime',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Database variables for CoreCourseOffline service.
|
||||
*/
|
||||
export const MANUAL_COMPLETION_TABLE = 'course_manual_completion';
|
||||
export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
|
||||
name: 'CoreCourseOfflineProvider',
|
||||
version: 1,
|
||||
tables: [
|
||||
{
|
||||
name: MANUAL_COMPLETION_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'cmid',
|
||||
type: 'INTEGER',
|
||||
primaryKey: true,
|
||||
},
|
||||
{
|
||||
name: 'completed',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'courseid',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'coursename',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'timecompleted',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export type CoreCourseStatusDBRecord = {
|
||||
id: number;
|
||||
status: string;
|
||||
previous: string;
|
||||
updated: number;
|
||||
downloadTime: number;
|
||||
previousDownloadTime: number;
|
||||
};
|
||||
|
||||
export type CoreCourseManualCompletionDBRecord = {
|
||||
cmid: number;
|
||||
completed: number;
|
||||
courseid: number;
|
||||
coursename: string;
|
||||
timecompleted: number;
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
// (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 { makeSingleton } from '@singletons/core.singletons';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreCourseManualCompletionDBRecord, MANUAL_COMPLETION_TABLE } from './course-db';
|
||||
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
||||
|
||||
/**
|
||||
* Service to handle offline data for courses.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreCourseOfflineProvider {
|
||||
|
||||
/**
|
||||
* Delete a manual completion stored.
|
||||
*
|
||||
* @param cmId The module ID to remove the completion.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async deleteManualCompletion(cmId: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
await site.getDb().deleteRecords(MANUAL_COMPLETION_TABLE, { cmid: cmId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all offline manual completions for a certain course.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the list of completions.
|
||||
*/
|
||||
async getAllManualCompletions(siteId?: string): Promise<CoreCourseManualCompletionDBRecord[]> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
return await site.getDb().getRecords(MANUAL_COMPLETION_TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all offline manual completions for a certain course.
|
||||
*
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the list of completions.
|
||||
*/
|
||||
async getCourseManualCompletions(courseId: number, siteId?: string): Promise<CoreCourseManualCompletionDBRecord[]> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
return await site.getDb().getRecords(MANUAL_COMPLETION_TABLE, { courseid: courseId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offline manual completion for a certain module.
|
||||
*
|
||||
* @param cmId The module ID to remove the completion.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the completion, rejected if failure or not found.
|
||||
*/
|
||||
async getManualCompletion(cmId: number, siteId?: string): Promise<CoreCourseManualCompletionDBRecord> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
return await site.getDb().getRecord(MANUAL_COMPLETION_TABLE, { cmid: cmId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Offline version for manually marking a module as completed.
|
||||
*
|
||||
* @param cmId The module ID to store the completion.
|
||||
* @param completed Whether the module is completed or not.
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @param courseName Course name. Recommended, it is used to display a better warning message.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when completion is successfully stored.
|
||||
*/
|
||||
async markCompletedManually(
|
||||
cmId: number,
|
||||
completed: boolean,
|
||||
courseId: number,
|
||||
courseName?: string,
|
||||
siteId?: string,
|
||||
): Promise<CoreStatusWithWarningsWSResponse> {
|
||||
|
||||
// Store the offline data.
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
const entry: CoreCourseManualCompletionDBRecord = {
|
||||
cmid: cmId,
|
||||
completed: completed ? 1 : 0,
|
||||
courseid: courseId,
|
||||
coursename: courseName || '',
|
||||
timecompleted: Date.now(),
|
||||
};
|
||||
await site.getDb().insertRecord(MANUAL_COMPLETION_TABLE, entry);
|
||||
|
||||
return ({
|
||||
status: true,
|
||||
offline: true,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreCourseOffline extends makeSingleton(CoreCourseOfflineProvider) { }
|
|
@ -0,0 +1,990 @@
|
|||
// (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 { CoreSites } from '@services/sites';
|
||||
import { CoreCourse, CoreCourseSection } from './course';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { makeSingleton, Translate } from '@singletons/core.singletons';
|
||||
import { CoreFilepool } from '@services/filepool';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import {
|
||||
CoreCourseBasicData,
|
||||
CoreCourseGetCoursesData,
|
||||
CoreCourses,
|
||||
CoreCourseSearchedData,
|
||||
CoreEnrolledCourseBasicData,
|
||||
CoreEnrolledCourseData,
|
||||
} from '@features/courses/services/courses';
|
||||
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses.helper';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreLoginHelper, CoreLoginHelperProvider } from '@features/login/services/login.helper';
|
||||
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
||||
import { CoreCourseOffline } from './course-offline';
|
||||
|
||||
/**
|
||||
* Prefetch info of a module.
|
||||
*/
|
||||
export type CoreCourseModulePrefetchInfo = {
|
||||
/**
|
||||
* Downloaded size.
|
||||
*/
|
||||
size?: number;
|
||||
|
||||
/**
|
||||
* Downloadable size in a readable format.
|
||||
*/
|
||||
sizeReadable?: string;
|
||||
|
||||
/**
|
||||
* Module status.
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* Icon's name of the module status.
|
||||
*/
|
||||
statusIcon?: string;
|
||||
|
||||
/**
|
||||
* Time when the module was last downloaded.
|
||||
*/
|
||||
downloadTime?: number;
|
||||
|
||||
/**
|
||||
* Download time in a readable format.
|
||||
*/
|
||||
downloadTimeReadable?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Progress of downloading a list of courses.
|
||||
*/
|
||||
export type CoreCourseCoursesProgress = {
|
||||
/**
|
||||
* Number of courses downloaded so far.
|
||||
*/
|
||||
count: number;
|
||||
|
||||
/**
|
||||
* Toal of courses to download.
|
||||
*/
|
||||
total: number;
|
||||
|
||||
/**
|
||||
* Whether the download has been successful so far.
|
||||
*/
|
||||
success: boolean;
|
||||
|
||||
/**
|
||||
* Last downloaded course.
|
||||
*/
|
||||
courseId?: number;
|
||||
};
|
||||
|
||||
export type CorePrefetchStatusInfo = {
|
||||
status: string; // Status of the prefetch.
|
||||
statusTranslatable: string; // Status translatable string.
|
||||
icon: string; // Icon based on the status.
|
||||
loading: boolean; // If it's a loading status.
|
||||
badge?: string; // Progress badge string if any.
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to gather some common course functions.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreCourseHelperProvider {
|
||||
|
||||
protected courseDwnPromises: { [s: string]: { [id: number]: Promise<void> } } = {};
|
||||
protected logger: CoreLogger;
|
||||
|
||||
constructor() {
|
||||
|
||||
this.logger = CoreLogger.getInstance('CoreCourseHelperProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function treats every module on the sections provided to load the handler data, treat completion
|
||||
* and navigate to a module page if required. It also returns if sections has content.
|
||||
*
|
||||
* @param sections List of sections to treat modules.
|
||||
* @param courseId Course ID of the modules.
|
||||
* @param completionStatus List of completion status.
|
||||
* @param courseName Course name. Recommended if completionStatus is supplied.
|
||||
* @param forCoursePage Whether the data will be used to render the course page.
|
||||
* @return Whether the sections have content.
|
||||
*/
|
||||
addHandlerDataForModules(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the status of a section.
|
||||
*
|
||||
* @param section Section to calculate its status. It can't be "All sections".
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @param refresh True if it shouldn't use module status cache (slower).
|
||||
* @param checkUpdates Whether to use the WS to check updates. Defaults to true.
|
||||
* @return Promise resolved when the status is calculated.
|
||||
*/
|
||||
calculateSectionStatus(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the status of a list of sections, setting attributes to determine the icons/data to be shown.
|
||||
*
|
||||
* @param sections Sections to calculate their status.
|
||||
* @param courseId Course ID the sections belong to.
|
||||
* @param refresh True if it shouldn't use module status cache (slower).
|
||||
* @param checkUpdates Whether to use the WS to check updates. Defaults to true.
|
||||
* @return Promise resolved when the states are calculated.
|
||||
*/
|
||||
calculateSectionsStatus(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a confirm and prefetch a course. It will retrieve the sections and the course options if not provided.
|
||||
* This function will set the icon to "spinner" when starting and it will also set it back to the initial icon if the
|
||||
* user cancels. All the other updates of the icon should be made when CoreEvents.COURSE_STATUS_CHANGED is received.
|
||||
*
|
||||
* @param data An object where to store the course icon and title: "prefetchCourseIcon", "title" and "downloadSucceeded".
|
||||
* @param course Course to prefetch.
|
||||
* @param sections List of course sections.
|
||||
* @param courseHandlers List of course handlers.
|
||||
* @param menuHandlers List of course menu handlers.
|
||||
* @return Promise resolved when the download finishes, rejected if an error occurs or the user cancels.
|
||||
*/
|
||||
confirmAndPrefetchCourse(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm and prefetches a list of courses.
|
||||
*
|
||||
* @param courses List of courses to download.
|
||||
* @param onProgress Function to call everytime a course is downloaded.
|
||||
* @return Resolved when downloaded, rejected if error or canceled.
|
||||
*/
|
||||
async confirmAndPrefetchCourses(
|
||||
courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||
onProgress?: (data: CoreCourseCoursesProgress) => void,
|
||||
): Promise<void> {
|
||||
const siteId = CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
// Confirm the download without checking size because it could take a while.
|
||||
await CoreDomUtils.instance.showConfirm(Translate.instance.instant('core.areyousure'));
|
||||
|
||||
const total = courses.length;
|
||||
let count = 0;
|
||||
|
||||
const promises = courses.map((course) => {
|
||||
const subPromises: Promise<void>[] = [];
|
||||
let sections: CoreCourseSection[];
|
||||
let handlers: any;
|
||||
let menuHandlers: any;
|
||||
let success = true;
|
||||
|
||||
// Get the sections and the handlers.
|
||||
subPromises.push(CoreCourse.instance.getSections(course.id, false, true).then((courseSections) => {
|
||||
sections = courseSections;
|
||||
|
||||
return;
|
||||
}));
|
||||
|
||||
/**
|
||||
* @todo
|
||||
subPromises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, course).then((cHandlers: any) => {
|
||||
handlers = cHandlers;
|
||||
}));
|
||||
subPromises.push(this.courseOptionsDelegate.getMenuHandlersToDisplay(this.injector, course).then((mHandlers: any) => {
|
||||
menuHandlers = mHandlers;
|
||||
}));
|
||||
*/
|
||||
|
||||
return Promise.all(subPromises).then(() => this.prefetchCourse(course, sections, handlers, menuHandlers, siteId))
|
||||
.catch((error) => {
|
||||
success = false;
|
||||
|
||||
throw error;
|
||||
}).finally(() => {
|
||||
// Course downloaded or failed, notify the progress.
|
||||
count++;
|
||||
if (onProgress) {
|
||||
onProgress({ count: count, total: total, courseId: course.id, success: success });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (onProgress) {
|
||||
// Notify the start of the download.
|
||||
onProgress({ count: 0, total: total, success: true });
|
||||
}
|
||||
|
||||
return CoreUtils.instance.allPromises(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show confirmation dialog and then remove a module files.
|
||||
*
|
||||
* @param module Module to remove the files.
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @param done Function to call when done. It will close the context menu.
|
||||
* @return Promise resolved when done.
|
||||
* @todo module type.
|
||||
*/
|
||||
async confirmAndRemoveFiles(module: any, courseId: number, done?: () => void): Promise<void> {
|
||||
let modal: CoreIonLoadingElement | undefined;
|
||||
|
||||
try {
|
||||
|
||||
await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletestoreddata');
|
||||
|
||||
modal = await CoreDomUtils.instance.showModalLoading();
|
||||
|
||||
await this.removeModuleStoredData(module, courseId);
|
||||
|
||||
done && done();
|
||||
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
CoreDomUtils.instance.showErrorModal(error);
|
||||
}
|
||||
} finally {
|
||||
modal?.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size to download a section and show a confirm modal if needed.
|
||||
*
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @param section Section. If not provided, all sections.
|
||||
* @param sections List of sections. Used when downloading all the sections.
|
||||
* @param alwaysConfirm True to show a confirm even if the size isn't high, false otherwise.
|
||||
* @return Promise resolved if the user confirms or there's no need to confirm.
|
||||
*/
|
||||
confirmDownloadSizeSection(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prefetch a module, showing a confirmation modal if the size is big.
|
||||
* This function is meant to be called from a context menu option. It will also modify some data like the prefetch icon.
|
||||
*
|
||||
* @param instance The component instance that has the context menu. It should have prefetchStatusIcon and isDestroyed.
|
||||
* @param module Module to be prefetched
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @param done Function to call when done. It will close the context menu.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
contextMenuPrefetch(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the status of a list of courses.
|
||||
*
|
||||
* @param courses Courses
|
||||
* @return Promise resolved with the status.
|
||||
*/
|
||||
async determineCoursesStatus(courses: CoreCourseBasicData[]): Promise<string> {
|
||||
// Get the status of each course.
|
||||
const promises: Promise<string>[] = [];
|
||||
const siteId = CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
courses.forEach((course) => {
|
||||
promises.push(CoreCourse.instance.getCourseStatus(course.id, siteId));
|
||||
});
|
||||
|
||||
const statuses = await Promise.all(promises);
|
||||
|
||||
// Now determine the status of the whole list.
|
||||
let status = statuses[0];
|
||||
const filepool = CoreFilepool.instance;
|
||||
for (let i = 1; i < statuses.length; i++) {
|
||||
status = filepool.determinePackagesStatus(status, statuses[i]);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to open a module main file, downloading the package if needed.
|
||||
* This is meant for modules like mod_resource.
|
||||
*
|
||||
* @param module The module to download.
|
||||
* @param courseId The course ID of the module.
|
||||
* @param component The component to link the files to.
|
||||
* @param componentId An ID to use in conjunction with the component.
|
||||
* @param files List of files of the module. If not provided, use module.contents.
|
||||
* @param siteId The site ID. If not defined, current site.
|
||||
* @return Resolved on success.
|
||||
*/
|
||||
downloadModuleAndOpenFile(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to download a module that has a main file and return the local file's path and other info.
|
||||
* This is meant for modules like mod_resource.
|
||||
*
|
||||
* @param module The module to download.
|
||||
* @param courseId The course ID of the module.
|
||||
* @param component The component to link the files to.
|
||||
* @param componentId An ID to use in conjunction with the component.
|
||||
* @param files List of files of the module. If not provided, use module.contents.
|
||||
* @param siteId The site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
downloadModuleWithMainFileIfNeeded(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to download a module that has a main file and return the local file's path and other info.
|
||||
* This is meant for modules like mod_resource.
|
||||
*
|
||||
* @param module The module to download.
|
||||
* @param courseId The course ID of the module.
|
||||
* @param fixedUrl Main file's fixed URL.
|
||||
* @param files List of files of the module.
|
||||
* @param status The package status.
|
||||
* @param component The component to link the files to.
|
||||
* @param componentId An ID to use in conjunction with the component.
|
||||
* @param siteId The site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected downloadModuleWithMainFile(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to download a module.
|
||||
*
|
||||
* @param module The module to download.
|
||||
* @param courseId The course ID of the module.
|
||||
* @param component The component to link the files to.
|
||||
* @param componentId An ID to use in conjunction with the component.
|
||||
* @param files List of files of the module. If not provided, use module.contents.
|
||||
* @param siteId The site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
downloadModule(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the Context Menu for a certain module.
|
||||
*
|
||||
* @param instance The component instance that has the context menu.
|
||||
* @param module Module to be prefetched
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @param invalidateCache Invalidates the cache first.
|
||||
* @param component Component of the module.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
fillContextMenu(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course. It will first check the user courses, and fallback to another WS if not enrolled.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the course.
|
||||
*/
|
||||
async getCourse(
|
||||
courseId: number,
|
||||
siteId?: string,
|
||||
): Promise<{ enrolled: boolean; course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData }> {
|
||||
siteId = siteId || CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData;
|
||||
|
||||
// Try with enrolled courses first.
|
||||
try {
|
||||
course = await CoreCourses.instance.getUserCourse(courseId, false, siteId);
|
||||
|
||||
return ({ enrolled: true, course: course });
|
||||
} catch {
|
||||
// Not enrolled or an error happened. Try to use another WebService.
|
||||
}
|
||||
|
||||
const available = await CoreCourses.instance.isGetCoursesByFieldAvailableInSite(siteId);
|
||||
|
||||
if (available) {
|
||||
course = await CoreCourses.instance.getCourseByField('id', courseId, siteId);
|
||||
} else {
|
||||
course = await CoreCourses.instance.getCourse(courseId, siteId);
|
||||
}
|
||||
|
||||
return ({ enrolled: false, course: course });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course, wait for any course format plugin to load, and open the course page. It basically chains the functions
|
||||
* getCourse and openCourse.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param params Other params to pass to the course page.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
*/
|
||||
async getAndOpenCourse(courseId: number, params?: Params, siteId?: string): Promise<any> {
|
||||
const modal = await CoreDomUtils.instance.showModalLoading();
|
||||
|
||||
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData | { id: number };
|
||||
|
||||
try {
|
||||
const data = await this.getCourse(courseId, siteId);
|
||||
|
||||
course = data.course;
|
||||
} catch {
|
||||
// Cannot get course, return a "fake".
|
||||
course = { id: courseId };
|
||||
}
|
||||
|
||||
modal?.dismiss();
|
||||
|
||||
return this.openCourse(course, params, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the course has a block with that name.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param name Block name to search.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with true if the block exists or false otherwise.
|
||||
* @since 3.3
|
||||
*/
|
||||
async hasABlockNamed(courseId: number, name: string, siteId?: string): Promise<boolean> {
|
||||
try {
|
||||
const blocks = await CoreCourse.instance.getCourseBlocks(courseId, siteId);
|
||||
|
||||
return blocks.some((block) => block.name == name);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the prefetch icon for selected courses.
|
||||
*
|
||||
* @param courses Courses array to get info from.
|
||||
* @param prefetch Prefetch information.
|
||||
* @param minCourses Min course to show icon.
|
||||
* @return Resolved with the prefetch information updated when done.
|
||||
*/
|
||||
async initPrefetchCoursesIcons(
|
||||
courses: CoreCourseBasicData[],
|
||||
prefetch: CorePrefetchStatusInfo,
|
||||
minCourses: number = 2,
|
||||
): Promise<CorePrefetchStatusInfo> {
|
||||
if (!courses || courses.length < minCourses) {
|
||||
// Not enough courses.
|
||||
prefetch.icon = '';
|
||||
|
||||
return prefetch;
|
||||
}
|
||||
|
||||
const status = await this.determineCoursesStatus(courses);
|
||||
|
||||
prefetch = this.getCourseStatusIconAndTitleFromStatus(status);
|
||||
|
||||
if (prefetch.loading) {
|
||||
// It seems all courses are being downloaded, show a download button instead.
|
||||
prefetch.icon = CoreConstants.NOT_DOWNLOADED_ICON;
|
||||
}
|
||||
|
||||
return prefetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load offline completion into a list of sections.
|
||||
* This should be used in 3.6 sites or higher, where the course contents already include the completion.
|
||||
*
|
||||
* @param courseId The course to get the completion.
|
||||
* @param sections List of sections of the course.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async loadOfflineCompletion(courseId: number, sections: any[], siteId?: string): Promise<void> {
|
||||
const offlineCompletions = await CoreCourseOffline.instance.getCourseManualCompletions(courseId, siteId);
|
||||
|
||||
if (!offlineCompletions || !offlineCompletions.length) {
|
||||
// No offline completion.
|
||||
return;
|
||||
}
|
||||
|
||||
const totalOffline = offlineCompletions.length;
|
||||
let loaded = 0;
|
||||
const offlineCompletionsMap = CoreUtils.instance.arrayToObject(offlineCompletions, 'cmid');
|
||||
// Load the offline data in the modules.
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
const section = sections[i];
|
||||
if (!section.modules || !section.modules.length) {
|
||||
// Section has no modules, ignore it.
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < section.modules.length; j++) {
|
||||
const module = section.modules[j];
|
||||
const offlineCompletion = offlineCompletionsMap[module.id];
|
||||
|
||||
if (offlineCompletion && typeof module.completiondata != 'undefined' &&
|
||||
offlineCompletion.timecompleted >= module.completiondata.timecompleted * 1000) {
|
||||
// The module has offline completion. Load it.
|
||||
module.completiondata.state = offlineCompletion.completed;
|
||||
module.completiondata.offline = true;
|
||||
|
||||
// If all completions have been loaded, stop.
|
||||
loaded++;
|
||||
if (loaded == totalOffline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the courses in the array.
|
||||
*
|
||||
* @param courses Courses array to prefetch.
|
||||
* @param prefetch Prefetch information to be updated.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async prefetchCourses(
|
||||
courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||
prefetch: CorePrefetchStatusInfo,
|
||||
): Promise<void> {
|
||||
prefetch.loading = true;
|
||||
prefetch.icon = CoreConstants.DOWNLOADING_ICON;
|
||||
prefetch.badge = '';
|
||||
|
||||
try {
|
||||
await this.confirmAndPrefetchCourses(courses, (progress) => {
|
||||
prefetch.badge = progress.count + ' / ' + progress.total;
|
||||
});
|
||||
prefetch.icon = CoreConstants.OUTDATED_ICON;
|
||||
} finally {
|
||||
prefetch.loading = false;
|
||||
prefetch.badge = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course download promise (if any).
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Download promise, undefined if not found.
|
||||
*/
|
||||
getCourseDownloadPromise(courseId: number, siteId?: string): Promise<void> {
|
||||
siteId = siteId || CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
return this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][courseId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course status icon and the langkey to use as a title.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the icon name and the title key.
|
||||
*/
|
||||
async getCourseStatusIconAndTitle(courseId: number, siteId?: string): Promise<CorePrefetchStatusInfo> {
|
||||
const status = await CoreCourse.instance.getCourseStatus(courseId, siteId);
|
||||
|
||||
return this.getCourseStatusIconAndTitleFromStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course status icon and the langkey to use as a title from status.
|
||||
*
|
||||
* @param status Course status.
|
||||
* @return Title and icon name.
|
||||
*/
|
||||
getCourseStatusIconAndTitleFromStatus(status: string): CorePrefetchStatusInfo {
|
||||
const prefetchStatus: CorePrefetchStatusInfo = {
|
||||
status: status,
|
||||
icon: this.getPrefetchStatusIcon(status, false),
|
||||
statusTranslatable: '',
|
||||
loading: false,
|
||||
};
|
||||
|
||||
if (status == CoreConstants.DOWNLOADED) {
|
||||
// Always show refresh icon, we cannot know if there's anything new in course options.
|
||||
prefetchStatus.statusTranslatable = 'core.course.refreshcourse';
|
||||
} else if (status == CoreConstants.DOWNLOADING) {
|
||||
prefetchStatus.statusTranslatable = 'core.downloading';
|
||||
prefetchStatus.loading = true;
|
||||
} else {
|
||||
prefetchStatus.statusTranslatable = 'core.course.downloadcourse';
|
||||
}
|
||||
|
||||
return prefetchStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon given the status and if trust the download status.
|
||||
*
|
||||
* @param status Status constant.
|
||||
* @param trustDownload True to show download success, false to show an outdated status when downloaded.
|
||||
* @return Icon name.
|
||||
*/
|
||||
getPrefetchStatusIcon(status: string, trustDownload: boolean = false): string {
|
||||
if (status == CoreConstants.NOT_DOWNLOADED) {
|
||||
return CoreConstants.NOT_DOWNLOADED_ICON;
|
||||
}
|
||||
if (status == CoreConstants.OUTDATED || (status == CoreConstants.DOWNLOADED && !trustDownload)) {
|
||||
return CoreConstants.OUTDATED_ICON;
|
||||
}
|
||||
if (status == CoreConstants.DOWNLOADED && trustDownload) {
|
||||
return CoreConstants.DOWNLOADED_ICON;
|
||||
}
|
||||
if (status == CoreConstants.DOWNLOADING) {
|
||||
return CoreConstants.DOWNLOADING_ICON;
|
||||
}
|
||||
|
||||
return CoreConstants.DOWNLOADING_ICON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course ID from a module instance ID, showing an error message if it can't be retrieved.
|
||||
*
|
||||
* @param id Instance ID.
|
||||
* @param module Name of the module. E.g. 'glossary'.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the module's course ID.
|
||||
* @todo module type.
|
||||
*/
|
||||
async getModuleCourseIdByInstance(id: number, module: any, siteId?: string): Promise<number> {
|
||||
try {
|
||||
const cm = await CoreCourse.instance.getModuleBasicInfoByInstance(id, module, siteId);
|
||||
|
||||
return cm.course;
|
||||
} catch (error) {
|
||||
CoreDomUtils.instance.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prefetch info for a module.
|
||||
*
|
||||
* @param module Module to get the info from.
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @param invalidateCache Invalidates the cache first.
|
||||
* @param component Component of the module.
|
||||
* @return Promise resolved with the info.
|
||||
*/
|
||||
getModulePrefetchInfo(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download ID of a section. It's used to interact with CoreCourseModulePrefetchDelegate.
|
||||
*
|
||||
* @param section Section.
|
||||
* @return Section download ID.
|
||||
* @todo section type.
|
||||
*/
|
||||
getSectionDownloadId(section: any): string {
|
||||
return 'Section-' + section.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a module using instance ID and module name.
|
||||
*
|
||||
* @param instanceId Activity instance ID.
|
||||
* @param modName Module name of the activity.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @param courseId Course ID. If not defined we'll try to retrieve it from the site.
|
||||
* @param sectionId Section the module belongs to. If not defined we'll try to retrieve it from the site.
|
||||
* @param useModNameToGetModule If true, the app will retrieve all modules of this type with a single WS call. This reduces the
|
||||
* number of WS calls, but it isn't recommended for modules that can return a lot of contents.
|
||||
* @param modParams Params to pass to the module
|
||||
* @param navCtrl NavController for adding new pages to the current history. Optional for legacy support, but
|
||||
* generates a warning if omitted.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
navigateToModuleByInstance(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a module.
|
||||
*
|
||||
* @param moduleId Module's ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @param courseId Course ID. If not defined we'll try to retrieve it from the site.
|
||||
* @param sectionId Section the module belongs to. If not defined we'll try to retrieve it from the site.
|
||||
* @param modName If set, the app will retrieve all modules of this type with a single WS call. This reduces the
|
||||
* number of WS calls, but it isn't recommended for modules that can return a lot of contents.
|
||||
* @param modParams Params to pass to the module
|
||||
* @param navCtrl NavController for adding new pages to the current history. Optional for legacy support, but
|
||||
* generates a warning if omitted.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
navigateToModule(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a module.
|
||||
*
|
||||
* @param navCtrl The NavController to use.
|
||||
* @param module The module to open.
|
||||
* @param courseId The course ID of the module.
|
||||
* @param sectionId The section ID of the module.
|
||||
* @param modParams Params to pass to the module
|
||||
* @param True if module can be opened, false otherwise.
|
||||
*/
|
||||
openModule(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the activities in a course and also the course addons.
|
||||
*
|
||||
* @param course The course to prefetch.
|
||||
* @param sections List of course sections.
|
||||
* @param courseHandlers List of course options handlers.
|
||||
* @param courseMenuHandlers List of course menu handlers.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the download finishes.
|
||||
*/
|
||||
async prefetchCourse(
|
||||
course: CoreEnrolledCourseDataWithExtraInfoAndOptions,
|
||||
sections: CoreCourseSection[],
|
||||
courseHandlers: any[], // @todo CoreCourseOptionsHandlerToDisplay[],
|
||||
courseMenuHandlers: any[], // @todo CoreCourseOptionsMenuHandlerToDisplay[],
|
||||
siteId?: string,
|
||||
): Promise<void> {
|
||||
siteId = siteId || CoreSites.instance.getCurrentSiteId();
|
||||
|
||||
if (this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][course.id]) {
|
||||
// There's already a download ongoing for this course, return the promise.
|
||||
return this.courseDwnPromises[siteId][course.id];
|
||||
} else if (!this.courseDwnPromises[siteId]) {
|
||||
this.courseDwnPromises[siteId] = {};
|
||||
}
|
||||
|
||||
// First of all, mark the course as being downloaded.
|
||||
this.courseDwnPromises[siteId][course.id] = CoreCourse.instance.setCourseStatus(
|
||||
course.id,
|
||||
CoreConstants.DOWNLOADING,
|
||||
siteId,
|
||||
).then(async () => {
|
||||
|
||||
const promises: Promise<any>[] = [];
|
||||
|
||||
// Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections".
|
||||
/*
|
||||
* @todo
|
||||
let allSectionsSection = sections[0];
|
||||
if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
||||
allSectionsSection = { id: CoreCourseProvider.ALL_SECTIONS_ID };
|
||||
}
|
||||
promises.push(this.prefetchSection(allSectionsSection, course.id, sections));
|
||||
|
||||
// Prefetch course options.
|
||||
courseHandlers.forEach((handler) => {
|
||||
if (handler.prefetch) {
|
||||
promises.push(handler.prefetch(course));
|
||||
}
|
||||
});
|
||||
courseMenuHandlers.forEach((handler) => {
|
||||
if (handler.prefetch) {
|
||||
promises.push(handler.prefetch(course));
|
||||
}
|
||||
});*/
|
||||
|
||||
// Prefetch other data needed to render the course.
|
||||
if (CoreCourses.instance.isGetCoursesByFieldAvailable()) {
|
||||
promises.push(CoreCourses.instance.getCoursesByField('id', course.id));
|
||||
}
|
||||
|
||||
const sectionWithModules = sections.find((section) => section.modules && section.modules.length > 0);
|
||||
if (!sectionWithModules || typeof sectionWithModules.modules[0].completion == 'undefined') {
|
||||
promises.push(CoreCourse.instance.getActivitiesCompletionStatus(course.id));
|
||||
}
|
||||
|
||||
// @todo promises.push(this.filterHelper.getFilters('course', course.id));
|
||||
|
||||
return CoreUtils.instance.allPromises(promises);
|
||||
}).then(() =>
|
||||
// Download success, mark the course as downloaded.
|
||||
CoreCourse.instance.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId)).catch(async (error) => {
|
||||
// Error, restore previous status.
|
||||
await CoreCourse.instance.setCoursePreviousStatus(course.id, siteId);
|
||||
|
||||
throw error;
|
||||
}).finally(() => {
|
||||
delete this.courseDwnPromises[siteId!][course.id];
|
||||
});
|
||||
|
||||
return this.courseDwnPromises[siteId][course.id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prefetch a module, showing a confirmation modal if the size is big
|
||||
* and invalidating contents if refreshing.
|
||||
*
|
||||
* @param handler Prefetch handler to use. Must implement 'prefetch' and 'invalidateContent'.
|
||||
* @param module Module to download.
|
||||
* @param size Object containing size to download (in bytes) and a boolean to indicate if its totally calculated.
|
||||
* @param courseId Course ID of the module.
|
||||
* @param refresh True if refreshing, false otherwise.
|
||||
* @return Promise resolved when downloaded.
|
||||
*/
|
||||
prefetchModule(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch one section or all the sections.
|
||||
* If the section is "All sections" it will prefetch all the sections.
|
||||
*
|
||||
* @param section Section.
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @param sections List of sections. Used when downloading all the sections.
|
||||
* @return Promise resolved when the prefetch is finished.
|
||||
*/
|
||||
async prefetchSection(): Promise<void> {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch a certain section if it needs to be prefetched.
|
||||
* If the section is "All sections" it will be ignored.
|
||||
*
|
||||
* @param section Section to prefetch.
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @return Promise resolved when the section is prefetched.
|
||||
*/
|
||||
protected prefetchSingleSectionIfNeeded(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or restore the prefetch of a section.
|
||||
* If the section is "All sections" it will be ignored.
|
||||
*
|
||||
* @param section Section to download.
|
||||
* @param result Result of CoreCourseModulePrefetchDelegate.getModulesStatus for this section.
|
||||
* @param courseId Course ID the section belongs to.
|
||||
* @return Promise resolved when the section has been prefetched.
|
||||
*/
|
||||
protected prefetchSingleSection(): void {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a section has content.
|
||||
*
|
||||
* @param section Section to check.
|
||||
* @return Whether the section has content.
|
||||
* @todo section type.
|
||||
*/
|
||||
sectionHasContent(section: any): boolean {
|
||||
if (section.hiddenbynumsections) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (typeof section.availabilityinfo != 'undefined' && section.availabilityinfo != '') ||
|
||||
section.summary != '' || (section.modules && section.modules.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for any course format plugin to load, and open the course page.
|
||||
*
|
||||
* If the plugin's promise is resolved, the course page will be opened. If it is rejected, they will see an error.
|
||||
* If the promise for the plugin is still in progress when the user tries to open the course, a loader
|
||||
* will be displayed until it is complete, before the course page is opened. If the promise is already complete,
|
||||
* they will see the result immediately.
|
||||
*
|
||||
* @param course Course to open
|
||||
* @param params Params to pass to the course page.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
openCourse(course: CoreEnrolledCourseBasicData | { 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);
|
||||
} else {
|
||||
// We need to load the site first.
|
||||
params = params || {};
|
||||
Object.assign(params, { course: course });
|
||||
|
||||
return CoreLoginHelper.instance.redirect(CoreLoginHelperProvider.OPEN_COURSE, params, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete course files.
|
||||
*
|
||||
* @param courseId Course id.
|
||||
* @return Promise to be resolved once the course files are deleted.
|
||||
*/
|
||||
async deleteCourseFiles(courseId: number): Promise<void> {
|
||||
const sections = await CoreCourse.instance.getSections(courseId);
|
||||
const modules = CoreArray.flatten(sections.map((section) => section.modules));
|
||||
|
||||
await Promise.all(
|
||||
modules.map((module) => this.removeModuleStoredData(module, courseId)),
|
||||
);
|
||||
|
||||
await CoreCourse.instance.setCourseStatus(courseId, CoreConstants.NOT_DOWNLOADED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove module stored data.
|
||||
*
|
||||
* @param module Module to remove the files.
|
||||
* @param courseId Course ID the module belongs to.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
// @todo remove when done.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async removeModuleStoredData(module: any, courseId: number): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
// @todo
|
||||
// promises.push(this.prefetchDelegate.removeModuleFiles(module, courseId));
|
||||
|
||||
// @todo
|
||||
// const handler = this.prefetchDelegate.getPrefetchHandlerFor(module);
|
||||
// if (handler) {
|
||||
// promises.push(CoreSites.instance.getCurrentSite().deleteComponentFromCache(handler.component, module.id));
|
||||
// }
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreCourseHelper extends makeSingleton(CoreCourseHelperProvider) {}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,181 @@
|
|||
// (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 { PopoverController } from '@ionic/angular';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreCourses, CoreCourseSearchedData, CoreCourseUserAdminOrNavOptionIndexed, CoreEnrolledCourseData } from './courses';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
// import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion';
|
||||
// import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover';
|
||||
|
||||
/**
|
||||
* Helper to gather some common courses functions.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreCoursesHelperProvider {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return Promise resolved with the list of courses and the category.
|
||||
*/
|
||||
async getCoursesForPopover(): Promise<void> {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
loadCourseExtraInfo(
|
||||
course: CoreEnrolledCourseDataWithExtraInfo,
|
||||
courseByField: CoreCourseSearchedData,
|
||||
addCategoryName: boolean = false,
|
||||
): void {
|
||||
if (courseByField) {
|
||||
course.displayname = courseByField.displayname;
|
||||
course.categoryname = addCategoryName ? courseByField.categoryname : undefined;
|
||||
|
||||
if (courseByField.overviewfiles && courseByField.overviewfiles[0]) {
|
||||
course.courseImage = courseByField.overviewfiles[0].fileurl;
|
||||
} else {
|
||||
delete course.courseImage;
|
||||
}
|
||||
} else {
|
||||
delete course.displayname;
|
||||
delete course.courseImage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async loadCoursesExtraInfo(courses: CoreEnrolledCourseDataWithExtraInfo[], loadCategoryNames: boolean = false): Promise<void> {
|
||||
if (!courses.length ) {
|
||||
// No courses or cannot get the data, stop.
|
||||
return;
|
||||
}
|
||||
|
||||
let coursesInfo = {};
|
||||
let courseInfoAvailable = false;
|
||||
|
||||
const site = CoreSites.instance.getCurrentSite();
|
||||
const promises: Promise<void>[] = [];
|
||||
const colors: (string | undefined)[] = [];
|
||||
|
||||
if (site?.isVersionGreaterEqualThan('3.8')) {
|
||||
promises.push(site.getConfig().then((configs) => {
|
||||
for (let x = 0; x < 10; x++) {
|
||||
colors[x] = configs['core_admin_coursecolor' + (x + 1)] || undefined;
|
||||
}
|
||||
|
||||
return;
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
}));
|
||||
}
|
||||
|
||||
if (CoreCourses.instance.isGetCoursesByFieldAvailable() && (loadCategoryNames ||
|
||||
(typeof courses[0].overviewfiles == 'undefined' && typeof courses[0].displayname == 'undefined'))) {
|
||||
const courseIds = courses.map((course) => course.id).join(',');
|
||||
|
||||
courseInfoAvailable = true;
|
||||
|
||||
// Get the extra data for the courses.
|
||||
promises.push(CoreCourses.instance.getCoursesByField('ids', courseIds).then((coursesInfos) => {
|
||||
coursesInfo = CoreUtils.instance.arrayToObject(coursesInfos, 'id');
|
||||
|
||||
return;
|
||||
}));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
courses.forEach((course) => {
|
||||
this.loadCourseExtraInfo(course, courseInfoAvailable ? coursesInfo[course.id] : course, loadCategoryNames);
|
||||
|
||||
if (!course.courseImage) {
|
||||
course.colorNumber = course.id % 10;
|
||||
course.color = colors.length ? colors[course.colorNumber] : undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return Courses filled with options.
|
||||
*/
|
||||
async getUserCoursesWithOptions(): Promise<void> {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a context menu to select a course, and return the courseId and categoryId of the selected course (-1 for all courses).
|
||||
* Returns an empty object if popover closed without picking a course.
|
||||
*
|
||||
* @param event Click event.
|
||||
* @param courses List of courses, from CoreCoursesHelperProvider.getCoursesForPopover.
|
||||
* @param courseId The course to select at start.
|
||||
* @return Promise resolved with the course ID and category ID.
|
||||
*/
|
||||
async selectCourse(): Promise<void> {
|
||||
// @todo params and logic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreCoursesHelper extends makeSingleton(CoreCoursesHelperProvider) { }
|
||||
|
||||
/**
|
||||
* Enrolled course data with extra rendering info.
|
||||
*/
|
||||
export type CoreEnrolledCourseDataWithExtraInfo = CoreEnrolledCourseData & {
|
||||
colorNumber?: number; // Color index number.
|
||||
color?: string; // Color RGB.
|
||||
courseImage?: string; // Course thumbnail.
|
||||
categoryname?: string; // Category name,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enrolled course data with admin and navigation option availability.
|
||||
*/
|
||||
export type CoreEnrolledCourseDataWithOptions = CoreEnrolledCourseData & {
|
||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enrolled course data with admin and navigation option availability and extra rendering info.
|
||||
*/
|
||||
export type CoreEnrolledCourseDataWithExtraInfoAndOptions = CoreEnrolledCourseDataWithExtraInfo & CoreEnrolledCourseDataWithOptions;
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CoreCourseModule } from './course/course.module';
|
||||
import { CoreCoursesModule } from './courses/courses.module';
|
||||
import { CoreEmulatorModule } from './emulator/emulator.module';
|
||||
import { CoreFileUploaderInitModule } from './fileuploader/fileuploader-init.module';
|
||||
|
@ -24,6 +25,7 @@ import { CoreSettingsInitModule } from './settings/settings-init.module';
|
|||
imports: [
|
||||
CoreEmulatorModule,
|
||||
CoreLoginModule,
|
||||
CoreCourseModule,
|
||||
CoreCoursesModule,
|
||||
CoreSettingsInitModule,
|
||||
CoreFileUploaderInitModule,
|
||||
|
|
|
@ -24,7 +24,7 @@ import { CoreConstants } from '@/core/constants';
|
|||
import { CoreConfig } from '@services/config';
|
||||
// import { CoreFilterProvider } from '@features/filter/providers/filter';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
// import { CoreCourseProvider } from '@features/course/providers/course';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { makeSingleton, Translate } from '@singletons/core.singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
|
||||
|
@ -58,7 +58,6 @@ export class CoreSettingsHelperProvider {
|
|||
|
||||
constructor() {
|
||||
// protected filterProvider: CoreFilterProvider,
|
||||
// protected courseProvider: CoreCourseProvider,
|
||||
|
||||
if (!CoreConstants.CONFIG.forceColorScheme) {
|
||||
// Update color scheme when a user enters or leaves a site, or when the site info is updated.
|
||||
|
@ -116,7 +115,7 @@ export class CoreSettingsHelperProvider {
|
|||
promises.push(site.deleteFolder().then(() => {
|
||||
filepoolService.clearAllPackagesStatus(siteId);
|
||||
filepoolService.clearFilepool(siteId);
|
||||
// this.courseProvider.clearAllCoursesStatus(siteId);
|
||||
CoreCourse.instance.clearAllCoursesStatus(siteId);
|
||||
|
||||
siteInfo.spaceUsage = 0;
|
||||
|
||||
|
|
|
@ -990,6 +990,13 @@ export type CoreStatusWithWarningsWSResponse = {
|
|||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Special response structure of many webservices that contains only warnings.
|
||||
*/
|
||||
export type CoreWarningsWSResponse = {
|
||||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure of files returned by WS.
|
||||
*/
|
||||
|
|
|
@ -228,3 +228,11 @@ export type CoreEventLoadPageMainMenuData = {
|
|||
redirectPage: string;
|
||||
redirectParams?: Params;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to COURSE_STATUS_CHANGED event.
|
||||
*/
|
||||
export type CoreEventCourseStatusChanged = {
|
||||
courseId: number; // Course Id.
|
||||
status: string;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue