forked from EVOgeek/Vmeda.Online
		
	MOBILE-3594 course: Implement some course services
This commit is contained in:
		
							parent
							
								
									4c7545fb46
								
							
						
					
					
						commit
						cc5378aee7
					
				| @ -71,6 +71,12 @@ export class CoreConstants { | |||||||
|     static readonly OUTDATED = 'outdated'; |     static readonly OUTDATED = 'outdated'; | ||||||
|     static readonly NOT_DOWNLOADABLE = 'notdownloadable'; |     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.
 |     // Constants from Moodle's resourcelib.
 | ||||||
|     static readonly RESOURCELIB_DISPLAY_AUTO = 0; // Try the best way.
 |     static readonly RESOURCELIB_DISPLAY_AUTO = 0; // Try the best way.
 | ||||||
|     static readonly RESOURCELIB_DISPLAY_EMBED = 1; // Display using object tag.
 |     static readonly RESOURCELIB_DISPLAY_EMBED = 1; // Display using object tag.
 | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								src/core/features/course/course.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/core/features/course/course.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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 { | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								src/core/features/course/services/course-db.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/core/features/course/services/course-db.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | }; | ||||||
							
								
								
									
										117
									
								
								src/core/features/course/services/course-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/core/features/course/services/course-offline.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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) { } | ||||||
							
								
								
									
										990
									
								
								src/core/features/course/services/course.helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										990
									
								
								src/core/features/course/services/course.helper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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) {} | ||||||
							
								
								
									
										1505
									
								
								src/core/features/course/services/course.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1505
									
								
								src/core/features/course/services/course.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										181
									
								
								src/core/features/courses/services/courses.helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/core/features/courses/services/courses.helper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | 
 | ||||||
							
								
								
									
										1592
									
								
								src/core/features/courses/services/courses.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1592
									
								
								src/core/features/courses/services/courses.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -14,6 +14,7 @@ | |||||||
| 
 | 
 | ||||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
|  | import { CoreCourseModule } from './course/course.module'; | ||||||
| import { CoreCoursesModule } from './courses/courses.module'; | import { CoreCoursesModule } from './courses/courses.module'; | ||||||
| import { CoreEmulatorModule } from './emulator/emulator.module'; | import { CoreEmulatorModule } from './emulator/emulator.module'; | ||||||
| import { CoreFileUploaderInitModule } from './fileuploader/fileuploader-init.module'; | import { CoreFileUploaderInitModule } from './fileuploader/fileuploader-init.module'; | ||||||
| @ -24,6 +25,7 @@ import { CoreSettingsInitModule } from './settings/settings-init.module'; | |||||||
|     imports: [ |     imports: [ | ||||||
|         CoreEmulatorModule, |         CoreEmulatorModule, | ||||||
|         CoreLoginModule, |         CoreLoginModule, | ||||||
|  |         CoreCourseModule, | ||||||
|         CoreCoursesModule, |         CoreCoursesModule, | ||||||
|         CoreSettingsInitModule, |         CoreSettingsInitModule, | ||||||
|         CoreFileUploaderInitModule, |         CoreFileUploaderInitModule, | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ import { CoreConstants } from '@/core/constants'; | |||||||
| import { CoreConfig } from '@services/config'; | import { CoreConfig } from '@services/config'; | ||||||
| // import { CoreFilterProvider } from '@features/filter/providers/filter';
 | // import { CoreFilterProvider } from '@features/filter/providers/filter';
 | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | 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 { makeSingleton, Translate } from '@singletons/core.singletons'; | ||||||
| import { CoreError } from '@classes/errors/error'; | import { CoreError } from '@classes/errors/error'; | ||||||
| 
 | 
 | ||||||
| @ -58,7 +58,6 @@ export class CoreSettingsHelperProvider { | |||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         // protected filterProvider: CoreFilterProvider,
 |         // protected filterProvider: CoreFilterProvider,
 | ||||||
|         // protected courseProvider: CoreCourseProvider,
 |  | ||||||
| 
 | 
 | ||||||
|         if (!CoreConstants.CONFIG.forceColorScheme) { |         if (!CoreConstants.CONFIG.forceColorScheme) { | ||||||
|             // Update color scheme when a user enters or leaves a site, or when the site info is updated.
 |             // 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(() => { |         promises.push(site.deleteFolder().then(() => { | ||||||
|             filepoolService.clearAllPackagesStatus(siteId); |             filepoolService.clearAllPackagesStatus(siteId); | ||||||
|             filepoolService.clearFilepool(siteId); |             filepoolService.clearFilepool(siteId); | ||||||
|             // this.courseProvider.clearAllCoursesStatus(siteId);
 |             CoreCourse.instance.clearAllCoursesStatus(siteId); | ||||||
| 
 | 
 | ||||||
|             siteInfo.spaceUsage = 0; |             siteInfo.spaceUsage = 0; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -990,6 +990,13 @@ export type CoreStatusWithWarningsWSResponse = { | |||||||
|     warnings?: CoreWSExternalWarning[]; |     warnings?: CoreWSExternalWarning[]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Special response structure of many webservices that contains only warnings. | ||||||
|  |  */ | ||||||
|  | export type CoreWarningsWSResponse = { | ||||||
|  |     warnings?: CoreWSExternalWarning[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Structure of files returned by WS. |  * Structure of files returned by WS. | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -228,3 +228,11 @@ export type CoreEventLoadPageMainMenuData = { | |||||||
|     redirectPage: string; |     redirectPage: string; | ||||||
|     redirectParams?: Params; |     redirectParams?: Params; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Data passed to COURSE_STATUS_CHANGED event. | ||||||
|  |  */ | ||||||
|  | export type CoreEventCourseStatusChanged = { | ||||||
|  |     courseId: number; // Course Id.
 | ||||||
|  |     status: string; | ||||||
|  | }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user