MOBILE-4596 data: Decouple handlers
This commit is contained in:
		
							parent
							
								
									d6b4febe65
								
							
						
					
					
						commit
						f6bd83ae6c
					
				| @ -40,7 +40,7 @@ import { | |||||||
| } from '../../services/data'; | } from '../../services/data'; | ||||||
| import { AddonModDataHelper, AddonModDatDisplayFieldsOptions } from '../../services/data-helper'; | import { AddonModDataHelper, AddonModDatDisplayFieldsOptions } from '../../services/data-helper'; | ||||||
| import { AddonModDataAutoSyncData, AddonModDataSyncResult } from '../../services/data-sync'; | import { AddonModDataAutoSyncData, AddonModDataSyncResult } from '../../services/data-sync'; | ||||||
| import { AddonModDataPrefetchHandler } from '../../services/handlers/prefetch'; | import { AddonModDataPrefetchHandler } from '../../services/handlers/prefetch-lazy'; | ||||||
| import { AddonModDataComponentsCompileModule } from '../components-compile.module'; | import { AddonModDataComponentsCompileModule } from '../components-compile.module'; | ||||||
| import { AddonModDataSearchComponent } from '../search/search'; | import { AddonModDataSearchComponent } from '../search/search'; | ||||||
| import { CoreUrlUtils } from '@services/utils/url'; | import { CoreUrlUtils } from '@services/utils/url'; | ||||||
|  | |||||||
| @ -21,3 +21,8 @@ export const ADDON_MOD_DATA_ENTRIES_PER_PAGE = 25; | |||||||
| 
 | 
 | ||||||
| export const ADDON_MOD_DATA_ENTRY_CHANGED = 'addon_mod_data_entry_changed'; | export const ADDON_MOD_DATA_ENTRY_CHANGED = 'addon_mod_data_entry_changed'; | ||||||
| export const ADDON_MOD_DATA_AUTO_SYNCED = 'addon_mod_data_autom_synced'; | export const ADDON_MOD_DATA_AUTO_SYNCED = 'addon_mod_data_autom_synced'; | ||||||
|  | 
 | ||||||
|  | // Handlers.
 | ||||||
|  | export const ADDON_MOD_DATA_PREFETCH_NAME = 'AddonModData'; | ||||||
|  | export const ADDON_MOD_DATA_PREFETCH_MODNAME = 'data'; | ||||||
|  | export const ADDON_MOD_DATA_PREFETCH_COMPONENT = ADDON_MOD_DATA_COMPONENT; | ||||||
|  | |||||||
| @ -22,15 +22,15 @@ import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate'; | |||||||
| import { CoreCronDelegate } from '@services/cron'; | import { CoreCronDelegate } from '@services/cron'; | ||||||
| import { CORE_SITE_SCHEMAS } from '@services/sites'; | import { CORE_SITE_SCHEMAS } from '@services/sites'; | ||||||
| import { ADDON_MOD_DATA_OFFLINE_SITE_SCHEMA } from './services/database/data'; | import { ADDON_MOD_DATA_OFFLINE_SITE_SCHEMA } from './services/database/data'; | ||||||
| import { AddonModDataApproveLinkHandler } from './services/handlers/approve-link'; | import { getApproveLinkHandlerInstance } from './services/handlers/approve-link'; | ||||||
| import { AddonModDataDeleteLinkHandler } from './services/handlers/delete-link'; | import { getDeleteLinkHandlerInstance } from './services/handlers/delete-link'; | ||||||
| import { AddonModDataEditLinkHandler } from './services/handlers/edit-link'; | import { getEditLinkHandlerInstance } from './services/handlers/edit-link'; | ||||||
| import { AddonModDataIndexLinkHandler } from './services/handlers/index-link'; | import { AddonModDataIndexLinkHandler } from './services/handlers/index-link'; | ||||||
| import { AddonModDataListLinkHandler } from './services/handlers/list-link'; | import { AddonModDataListLinkHandler } from './services/handlers/list-link'; | ||||||
| import { AddonModDataModuleHandler } from './services/handlers/module'; | import { AddonModDataModuleHandler } from './services/handlers/module'; | ||||||
| import { AddonModDataPrefetchHandler } from './services/handlers/prefetch'; | import { getPrefetchHandlerInstance } from './services/handlers/prefetch'; | ||||||
| import { AddonModDataShowLinkHandler } from './services/handlers/show-link'; | import { getShowLinkHandlerInstance } from './services/handlers/show-link'; | ||||||
| import { AddonModDataSyncCronHandler } from './services/handlers/sync-cron'; | import { getCronHandlerInstance } from './services/handlers/sync-cron'; | ||||||
| import { AddonModDataTagAreaHandler } from './services/handlers/tag-area'; | import { AddonModDataTagAreaHandler } from './services/handlers/tag-area'; | ||||||
| import { AddonModDataFieldModule } from './fields/field.module'; | import { AddonModDataFieldModule } from './fields/field.module'; | ||||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||||
| @ -58,15 +58,16 @@ const routes: Routes = [ | |||||||
|             provide: APP_INITIALIZER, |             provide: APP_INITIALIZER, | ||||||
|             multi: true, |             multi: true, | ||||||
|             useValue: () => { |             useValue: () => { | ||||||
|  |                 CoreCourseModulePrefetchDelegate.registerHandler(getPrefetchHandlerInstance()); | ||||||
|  |                 CoreCronDelegate.register(getCronHandlerInstance()); | ||||||
|  |                 CoreContentLinksDelegate.registerHandler(getApproveLinkHandlerInstance()); | ||||||
|  |                 CoreContentLinksDelegate.registerHandler(getDeleteLinkHandlerInstance()); | ||||||
|  |                 CoreContentLinksDelegate.registerHandler(getShowLinkHandlerInstance()); | ||||||
|  |                 CoreContentLinksDelegate.registerHandler(getEditLinkHandlerInstance()); | ||||||
|  | 
 | ||||||
|                 CoreCourseModuleDelegate.registerHandler(AddonModDataModuleHandler.instance); |                 CoreCourseModuleDelegate.registerHandler(AddonModDataModuleHandler.instance); | ||||||
|                 CoreCourseModulePrefetchDelegate.registerHandler(AddonModDataPrefetchHandler.instance); |  | ||||||
|                 CoreCronDelegate.register(AddonModDataSyncCronHandler.instance); |  | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataIndexLinkHandler.instance); |                 CoreContentLinksDelegate.registerHandler(AddonModDataIndexLinkHandler.instance); | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataListLinkHandler.instance); |                 CoreContentLinksDelegate.registerHandler(AddonModDataListLinkHandler.instance); | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataApproveLinkHandler.instance); |  | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataDeleteLinkHandler.instance); |  | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataShowLinkHandler.instance); |  | ||||||
|                 CoreContentLinksDelegate.registerHandler(AddonModDataEditLinkHandler.instance); |  | ||||||
|                 CoreTagAreaDelegate.registerHandler(AddonModDataTagAreaHandler.instance); |                 CoreTagAreaDelegate.registerHandler(AddonModDataTagAreaHandler.instance); | ||||||
| 
 | 
 | ||||||
|                 CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_DATA_COMPONENT); |                 CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_DATA_COMPONENT); | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								src/addons/mod/data/services/handlers/approve-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/addons/mod/data/services/handlers/approve-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | // (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'; | ||||||
|  | import { AddonModDataHelper } from '../data-helper'; | ||||||
|  | import { AddonModDataApproveLinkHandlerService } from '@addons/mod/data/services/handlers/approve-link'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Content links handler for database approve/disapprove entry. | ||||||
|  |  * Match mod/data/view.php?d=6&approve=5 with a valid data id and entryid. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataApproveLinkHandlerLazyService extends AddonModDataApproveLinkHandlerService { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async handleAction(siteId: string, params: Record<string, string>, courseId?: number): Promise<void> { | ||||||
|  |         const dataId = parseInt(params.d, 10); | ||||||
|  |         const entryId = parseInt(params.approve, 10) || parseInt(params.disapprove, 10); | ||||||
|  |         const approve = parseInt(params.approve, 10) ? true : false; | ||||||
|  | 
 | ||||||
|  |         await AddonModDataHelper.approveOrDisapproveEntry(dataId, entryId, approve, courseId, siteId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { | ||||||
|  |         if (params.d === undefined || (params.approve === undefined && params.disapprove === undefined)) { | ||||||
|  |             // Required fields not defined. Cannot treat the URL.
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const AddonModDataApproveLinkHandler = makeSingleton(AddonModDataApproveLinkHandlerLazyService); | ||||||
| @ -12,18 +12,12 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
|  | import { ADDON_MOD_DATA_FEATURE_NAME } from '@addons/mod/data/constants'; | ||||||
| import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | ||||||
| import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; | import { CoreContentLinksAction, CoreContentLinksHandler } from '@features/contentlinks/services/contentlinks-delegate'; | ||||||
| import { makeSingleton } from '@singletons'; | import type { AddonModDataApproveLinkHandlerLazyService } from '@addons/mod/data/services/handlers/approve-link-lazy'; | ||||||
| import { AddonModDataHelper } from '../data-helper'; |  | ||||||
| import { ADDON_MOD_DATA_FEATURE_NAME } from '../../constants'; |  | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Content links handler for database approve/disapprove entry. |  | ||||||
|  * Match mod/data/view.php?d=6&approve=5 with a valid data id and entryid. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataApproveLinkHandlerService extends CoreContentLinksHandlerBase { | export class AddonModDataApproveLinkHandlerService extends CoreContentLinksHandlerBase { | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModDataApproveLinkHandler'; |     name = 'AddonModDataApproveLinkHandler'; | ||||||
| @ -36,27 +30,41 @@ export class AddonModDataApproveLinkHandlerService extends CoreContentLinksHandl | |||||||
|      */ |      */ | ||||||
|     getActions(siteIds: string[], url: string, params: Record<string, string>, courseId?: number): CoreContentLinksAction[] { |     getActions(siteIds: string[], url: string, params: Record<string, string>, courseId?: number): CoreContentLinksAction[] { | ||||||
|         return [{ |         return [{ | ||||||
|             action: async (siteId): Promise<void> => { |             action: (siteId) => this.handleAction(siteId, params, courseId), | ||||||
|                 const dataId = parseInt(params.d, 10); |  | ||||||
|                 const entryId = parseInt(params.approve, 10) || parseInt(params.disapprove, 10); |  | ||||||
|                 const approve = parseInt(params.approve, 10) ? true : false; |  | ||||||
| 
 |  | ||||||
|                 await AddonModDataHelper.approveOrDisapproveEntry(dataId, entryId, approve, courseId, siteId); |  | ||||||
|             }, |  | ||||||
|         }]; |         }]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * Handle link action. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site id. | ||||||
|  |      * @param params Params. | ||||||
|  |      * @param courseId Course id. | ||||||
|      */ |      */ | ||||||
|     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|         if (params.d === undefined || (params.approve === undefined && params.disapprove === undefined)) { |     async handleAction(siteId: string, params: Record<string, string>, courseId?: number): Promise<void> { | ||||||
|             // Required fields not defined. Cannot treat the URL.
 |         // Stub to override.
 | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| export const AddonModDataApproveLinkHandler = makeSingleton(AddonModDataApproveLinkHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get approve link handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Link handler. | ||||||
|  |  */ | ||||||
|  | export function getApproveLinkHandlerInstance(): CoreContentLinksHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataApproveLinkHandlerLazyService, | ||||||
|  |         AddonModDataApproveLinkHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataApproveLinkHandler } = await import('./approve-link-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataApproveLinkHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataApproveLinkHandlerService()); | ||||||
|  |     lazyHandler.setLazyOverrides(['isEnabled', 'handleAction']); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										50
									
								
								src/addons/mod/data/services/handlers/delete-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/addons/mod/data/services/handlers/delete-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | // (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'; | ||||||
|  | import { AddonModDataHelper } from '../data-helper'; | ||||||
|  | import { AddonModDataDeleteLinkHandlerService } from '@addons/mod/data/services/handlers/delete-link'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Content links handler for database delete entry. | ||||||
|  |  * Match mod/data/view.php?d=6&delete=5 with a valid data id and entryid. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataDeleteLinkHandlerLazyService extends AddonModDataDeleteLinkHandlerService { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async handleAction(siteId: string, params: Record<string, string>, courseId?: number): Promise<void> { | ||||||
|  |         const dataId = parseInt(params.d, 10); | ||||||
|  |         const entryId = parseInt(params.delete, 10); | ||||||
|  | 
 | ||||||
|  |         await AddonModDataHelper.showDeleteEntryModal(dataId, entryId, courseId, siteId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { | ||||||
|  |         if (params.d === undefined || params.delete === undefined) { | ||||||
|  |             // Required fields not defined. Cannot treat the URL.
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | export const AddonModDataDeleteLinkHandler = makeSingleton(AddonModDataDeleteLinkHandlerLazyService); | ||||||
| @ -12,18 +12,12 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
|  | import { ADDON_MOD_DATA_FEATURE_NAME } from '@addons/mod/data/constants'; | ||||||
| import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | ||||||
| import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; | import { CoreContentLinksAction, CoreContentLinksHandler } from '@features/contentlinks/services/contentlinks-delegate'; | ||||||
| import { makeSingleton } from '@singletons'; | import type { AddonModDataDeleteLinkHandlerLazyService } from '@addons/mod/data/services/handlers/delete-link-lazy'; | ||||||
| import { AddonModDataHelper } from '../data-helper'; |  | ||||||
| import { ADDON_MOD_DATA_FEATURE_NAME } from '../../constants'; |  | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Content links handler for database delete entry. |  | ||||||
|  * Match mod/data/view.php?d=6&delete=5 with a valid data id and entryid. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataDeleteLinkHandlerService extends CoreContentLinksHandlerBase { | export class AddonModDataDeleteLinkHandlerService extends CoreContentLinksHandlerBase { | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModDataDeleteLinkHandler'; |     name = 'AddonModDataDeleteLinkHandler'; | ||||||
| @ -35,26 +29,41 @@ export class AddonModDataDeleteLinkHandlerService extends CoreContentLinksHandle | |||||||
|      */ |      */ | ||||||
|     getActions(siteIds: string[], url: string, params: Record<string, string>, courseId?: number): CoreContentLinksAction[] { |     getActions(siteIds: string[], url: string, params: Record<string, string>, courseId?: number): CoreContentLinksAction[] { | ||||||
|         return [{ |         return [{ | ||||||
|             action: async (siteId): Promise<void> => { |             action: (siteId) => this.handleAction(siteId, params, courseId), | ||||||
|                 const dataId = parseInt(params.d, 10); |  | ||||||
|                 const entryId = parseInt(params.delete, 10); |  | ||||||
| 
 |  | ||||||
|                 await AddonModDataHelper.showDeleteEntryModal(dataId, entryId, courseId, siteId); |  | ||||||
|             }, |  | ||||||
|         }]; |         }]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * Handle link action. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site id. | ||||||
|  |      * @param params Params. | ||||||
|  |      * @param courseId Course id. | ||||||
|      */ |      */ | ||||||
|     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|         if (params.d === undefined || params.delete === undefined) { |     async handleAction(siteId: string, params: Record<string, string>, courseId?: number): Promise<void> { | ||||||
|             // Required fields not defined. Cannot treat the URL.
 |         // Stub to override.
 | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| export const AddonModDataDeleteLinkHandler = makeSingleton(AddonModDataDeleteLinkHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get delete link handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Link handler. | ||||||
|  |  */ | ||||||
|  | export function getDeleteLinkHandlerInstance(): CoreContentLinksHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataDeleteLinkHandlerLazyService, | ||||||
|  |         AddonModDataDeleteLinkHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataDeleteLinkHandler } = await import('./delete-link-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataDeleteLinkHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataDeleteLinkHandlerService()); | ||||||
|  |     lazyHandler.setLazyOverrides(['isEnabled', 'handleAction']); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										77
									
								
								src/addons/mod/data/services/handlers/edit-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/addons/mod/data/services/handlers/edit-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | // (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 { CoreCourse } from '@features/course/services/course'; | ||||||
|  | import { CoreNavigator } from '@services/navigator'; | ||||||
|  | import { CoreSitesReadingStrategy } from '@services/sites'; | ||||||
|  | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
|  | import { makeSingleton } from '@singletons'; | ||||||
|  | import { ADDON_MOD_DATA_FEATURE_NAME, ADDON_MOD_DATA_PAGE_NAME } from '../../constants'; | ||||||
|  | import { AddonModDataEditLinkHandlerService } from '@addons/mod/data/services/handlers/edit-link'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Content links handler for database add or edit entry. | ||||||
|  |  * Match mod/data/edit.php?d=6&rid=6 with a valid data and optional record id. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataEditLinkHandlerLazyService extends AddonModDataEditLinkHandlerService { | ||||||
|  | 
 | ||||||
|  |     name = 'AddonModDataEditLinkHandler'; | ||||||
|  |     featureName = ADDON_MOD_DATA_FEATURE_NAME; | ||||||
|  |     pattern = /\/mod\/data\/edit\.php.*([?&](d|rid)=\d+)/; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async handleAction(siteId: string, params: Record<string, string>): Promise<void> { | ||||||
|  |         const modal = await CoreDomUtils.showModalLoading(); | ||||||
|  |         const dataId = parseInt(params.d, 10); | ||||||
|  |         const rId = params.rid || ''; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             const module = await CoreCourse.getModuleBasicInfoByInstance( | ||||||
|  |                 dataId, | ||||||
|  |                 'data', | ||||||
|  |                 { siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE }, | ||||||
|  |             ); | ||||||
|  |             const pageParams: Params = { | ||||||
|  |                 title: module.name, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             await CoreNavigator.navigateToSitePath( | ||||||
|  |                 `${ADDON_MOD_DATA_PAGE_NAME}/${module.course}/${module.id}/edit/${rId}`, | ||||||
|  |                 { siteId, params: pageParams }, | ||||||
|  |             ); | ||||||
|  |         } finally { | ||||||
|  |             // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
 | ||||||
|  |             modal.dismiss(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { | ||||||
|  |         if (params.d === undefined) { | ||||||
|  |             // Id not defined. Cannot treat the URL.
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | export const AddonModDataEditLinkHandler = makeSingleton(AddonModDataEditLinkHandlerLazyService); | ||||||
| @ -12,22 +12,12 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
| import { Params } from '@angular/router'; | import { ADDON_MOD_DATA_FEATURE_NAME } from '@addons/mod/data/constants'; | ||||||
| import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | ||||||
| import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; | import { CoreContentLinksAction, CoreContentLinksHandler } from '@features/contentlinks/services/contentlinks-delegate'; | ||||||
| import { CoreCourse } from '@features/course/services/course'; | import type { AddonModDataEditLinkHandlerLazyService } from '@addons/mod/data/services/handlers/edit-link-lazy'; | ||||||
| import { CoreNavigator } from '@services/navigator'; |  | ||||||
| import { CoreSitesReadingStrategy } from '@services/sites'; |  | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; |  | ||||||
| import { makeSingleton } from '@singletons'; |  | ||||||
| import { ADDON_MOD_DATA_FEATURE_NAME, ADDON_MOD_DATA_PAGE_NAME } from '../../constants'; |  | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Content links handler for database add or edit entry. |  | ||||||
|  * Match mod/data/edit.php?d=6&rid=6 with a valid data and optional record id. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataEditLinkHandlerService extends CoreContentLinksHandlerBase { | export class AddonModDataEditLinkHandlerService extends CoreContentLinksHandlerBase { | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModDataEditLinkHandler'; |     name = 'AddonModDataEditLinkHandler'; | ||||||
| @ -39,44 +29,40 @@ export class AddonModDataEditLinkHandlerService extends CoreContentLinksHandlerB | |||||||
|      */ |      */ | ||||||
|     getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] { |     getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] { | ||||||
|         return [{ |         return [{ | ||||||
|             action: async (siteId): Promise<void> => { |             action: (siteId) => this.handleAction(siteId, params), | ||||||
|                 const modal = await CoreDomUtils.showModalLoading(); |  | ||||||
|                 const dataId = parseInt(params.d, 10); |  | ||||||
|                 const rId = params.rid || ''; |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     const module = await CoreCourse.getModuleBasicInfoByInstance( |  | ||||||
|                         dataId, |  | ||||||
|                         'data', |  | ||||||
|                         { siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE }, |  | ||||||
|                     ); |  | ||||||
|                     const pageParams: Params = { |  | ||||||
|                         title: module.name, |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     await CoreNavigator.navigateToSitePath( |  | ||||||
|                         `${ADDON_MOD_DATA_PAGE_NAME}/${module.course}/${module.id}/edit/${rId}`, |  | ||||||
|                         { siteId, params: pageParams }, |  | ||||||
|                     ); |  | ||||||
|                 } finally { |  | ||||||
|                     // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
 |  | ||||||
|                     modal.dismiss(); |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         }]; |         }]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * Handle link action. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site id. | ||||||
|  |      * @param params Params. | ||||||
|      */ |      */ | ||||||
|     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|         if (params.d === undefined) { |     async handleAction(siteId: string, params: Record<string, string>): Promise<void> { | ||||||
|             // Id not defined. Cannot treat the URL.
 |         // Stub to override.
 | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| export const AddonModDataEditLinkHandler = makeSingleton(AddonModDataEditLinkHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get edit link handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Link handler. | ||||||
|  |  */ | ||||||
|  | export function getEditLinkHandlerInstance(): CoreContentLinksHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataEditLinkHandlerLazyService, | ||||||
|  |         AddonModDataEditLinkHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataEditLinkHandler } = await import('./edit-link-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataEditLinkHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataEditLinkHandlerService()); | ||||||
|  |     lazyHandler.setLazyOverrides(['isEnabled', 'handleAction']); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										291
									
								
								src/addons/mod/data/services/handlers/prefetch-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								src/addons/mod/data/services/handlers/prefetch-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,291 @@ | |||||||
|  | // (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 { CoreComments } from '@features/comments/services/comments'; | ||||||
|  | import { CoreCourseCommonModWSOptions, CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; | ||||||
|  | import { CoreCourses } from '@features/courses/services/courses'; | ||||||
|  | import { CoreFilepool } from '@services/filepool'; | ||||||
|  | import { CoreGroup, CoreGroups } from '@services/groups'; | ||||||
|  | import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites'; | ||||||
|  | import { CoreTimeUtils } from '@services/utils/time'; | ||||||
|  | import { CoreUtils } from '@services/utils/utils'; | ||||||
|  | import { CoreWSFile } from '@services/ws'; | ||||||
|  | import { makeSingleton } from '@singletons'; | ||||||
|  | import { AddonModDataEntry, AddonModData, AddonModDataData } from '../data'; | ||||||
|  | import { AddonModDataSync, AddonModDataSyncResult } from '../data-sync'; | ||||||
|  | import { ContextLevel } from '@/core/constants'; | ||||||
|  | import { AddonModDataPrefetchHandlerService } from '@addons/mod/data/services/handlers/prefetch'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Handler to prefetch databases. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataPrefetchHandlerLazyService extends AddonModDataPrefetchHandlerService { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Retrieves all the entries for all the groups and then returns only unique entries. | ||||||
|  |      * | ||||||
|  |      * @param dataId Database Id. | ||||||
|  |      * @param groups Array of groups in the activity. | ||||||
|  |      * @param options Other options. | ||||||
|  |      * @returns All unique entries. | ||||||
|  |      */ | ||||||
|  |     protected async getAllUniqueEntries( | ||||||
|  |         dataId: number, | ||||||
|  |         groups: CoreGroup[], | ||||||
|  |         options: CoreSitesCommonWSOptions = {}, | ||||||
|  |     ): Promise<AddonModDataEntry[]> { | ||||||
|  | 
 | ||||||
|  |         const promises = groups.map((group) => AddonModData.fetchAllEntries(dataId, { | ||||||
|  |             groupId: group.id, | ||||||
|  |             ...options, // Include all options.
 | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         const responses = await Promise.all(promises); | ||||||
|  | 
 | ||||||
|  |         const uniqueEntries: Record<number, AddonModDataEntry> = {}; | ||||||
|  |         responses.forEach((groupEntries) => { | ||||||
|  |             groupEntries.forEach((entry) => { | ||||||
|  |                 uniqueEntries[entry.id] = entry; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         return CoreUtils.objectToArray(uniqueEntries); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Helper function to get all database info just once. | ||||||
|  |      * | ||||||
|  |      * @param module Module to get the files. | ||||||
|  |      * @param courseId Course ID the module belongs to. | ||||||
|  |      * @param omitFail True to always return even if fails. Default false. | ||||||
|  |      * @param options Other options. | ||||||
|  |      * @returns Promise resolved with the info fetched. | ||||||
|  |      */ | ||||||
|  |     protected async getDatabaseInfoHelper( | ||||||
|  |         module: CoreCourseAnyModuleData, | ||||||
|  |         courseId: number, | ||||||
|  |         omitFail: boolean, | ||||||
|  |         options: CoreCourseCommonModWSOptions = {}, | ||||||
|  |     ): Promise<{ database: AddonModDataData; groups: CoreGroup[]; entries: AddonModDataEntry[]; files: CoreWSFile[]}> { | ||||||
|  |         let groups: CoreGroup[] = []; | ||||||
|  |         let entries: AddonModDataEntry[] = []; | ||||||
|  |         let files: CoreWSFile[] = []; | ||||||
|  | 
 | ||||||
|  |         options.cmId = options.cmId || module.id; | ||||||
|  |         options.siteId = options.siteId || CoreSites.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         const database = await AddonModData.getDatabase(courseId, module.id, options); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             files = this.getIntroFilesFromInstance(module, database); | ||||||
|  | 
 | ||||||
|  |             const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); | ||||||
|  |             if (!groupInfo.groups || groupInfo.groups.length == 0) { | ||||||
|  |                 groupInfo.groups = [{ id: 0, name: '' }]; | ||||||
|  |             } | ||||||
|  |             groups = groupInfo.groups || []; | ||||||
|  | 
 | ||||||
|  |             entries = await this.getAllUniqueEntries(database.id, groups, options); | ||||||
|  |             files = files.concat(this.getEntriesFiles(entries)); | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 database, | ||||||
|  |                 groups, | ||||||
|  |                 entries, | ||||||
|  |                 files, | ||||||
|  |             }; | ||||||
|  |         } catch (error) { | ||||||
|  |             if (omitFail) { | ||||||
|  |                 // Any error, return the info we have.
 | ||||||
|  |                 return { | ||||||
|  |                     database, | ||||||
|  |                     groups, | ||||||
|  |                     entries, | ||||||
|  |                     files, | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             throw error; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the file contained in the entries. | ||||||
|  |      * | ||||||
|  |      * @param entries List of entries to get files from. | ||||||
|  |      * @returns List of files. | ||||||
|  |      */ | ||||||
|  |     protected getEntriesFiles(entries: AddonModDataEntry[]): CoreWSFile[] { | ||||||
|  |         let files: CoreWSFile[] = []; | ||||||
|  | 
 | ||||||
|  |         entries.forEach((entry) => { | ||||||
|  |             CoreUtils.objectToArray(entry.contents).forEach((content) => { | ||||||
|  |                 files = files.concat(<CoreWSFile[]>content.files); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         return files; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> { | ||||||
|  |         return this.getDatabaseInfoHelper(module, courseId, true).then((info) => info.files); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> { | ||||||
|  |         const data = await CoreUtils.ignoreErrors(AddonModData.getDatabase(courseId, module.id)); | ||||||
|  | 
 | ||||||
|  |         return this.getIntroFilesFromInstance(module, data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async invalidateContent(moduleId: number, courseId: number): Promise<void> { | ||||||
|  |         await AddonModData.invalidateContent(moduleId, courseId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> { | ||||||
|  |         const promises: Promise<void>[] = []; | ||||||
|  |         promises.push(AddonModData.invalidateDatabaseData(courseId)); | ||||||
|  |         promises.push(AddonModData.invalidateDatabaseAccessInformationData(module.instance)); | ||||||
|  | 
 | ||||||
|  |         await Promise.all(promises); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> { | ||||||
|  |         const database = await AddonModData.getDatabase(courseId, module.id, { | ||||||
|  |             readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE, | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         const accessData = await AddonModData.getDatabaseAccessInformation(database.id, { cmId: module.id }); | ||||||
|  |         // Check if database is restricted by time.
 | ||||||
|  |         if (!accessData.timeavailable) { | ||||||
|  |             const time = CoreTimeUtils.timestamp(); | ||||||
|  | 
 | ||||||
|  |             // It is restricted, checking times.
 | ||||||
|  |             if (database.timeavailablefrom && time < database.timeavailablefrom) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             if (database.timeavailableto && time > database.timeavailableto) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     prefetch(module: CoreCourseAnyModuleData, courseId: number): Promise<void> { | ||||||
|  |         return this.prefetchPackage(module, courseId, (siteId) => this.prefetchDatabase(module, courseId, siteId)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Prefetch a database. | ||||||
|  |      * | ||||||
|  |      * @param module Module. | ||||||
|  |      * @param courseId Course ID the module belongs to. | ||||||
|  |      * @param siteId Site ID. | ||||||
|  |      * @returns Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async prefetchDatabase(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise<void> { | ||||||
|  |         const options = { | ||||||
|  |             cmId: module.id, | ||||||
|  |             readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, | ||||||
|  |             siteId, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         const info = await this.getDatabaseInfoHelper(module, courseId, false, options); | ||||||
|  | 
 | ||||||
|  |         // Prefetch the database data.
 | ||||||
|  |         const database = info.database; | ||||||
|  | 
 | ||||||
|  |         const commentsEnabled = CoreComments.areCommentsEnabledInSite(); | ||||||
|  | 
 | ||||||
|  |         const promises: Promise<unknown>[] = []; | ||||||
|  | 
 | ||||||
|  |         promises.push(AddonModData.getFields(database.id, options)); | ||||||
|  |         promises.push(CoreFilepool.addFilesToQueue(siteId, info.files, this.component, module.id)); | ||||||
|  | 
 | ||||||
|  |         info.groups.forEach((group) => { | ||||||
|  |             promises.push(AddonModData.getDatabaseAccessInformation(database.id, { | ||||||
|  |                 groupId: group.id, | ||||||
|  |                 ...options, // Include all options.
 | ||||||
|  |             })); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         info.entries.forEach((entry) => { | ||||||
|  |             promises.push(AddonModData.getEntry(database.id, entry.id, options)); | ||||||
|  | 
 | ||||||
|  |             if (commentsEnabled && database.comments) { | ||||||
|  |                 promises.push(CoreComments.getComments( | ||||||
|  |                     ContextLevel.MODULE, | ||||||
|  |                     database.coursemodule, | ||||||
|  |                     'mod_data', | ||||||
|  |                     entry.id, | ||||||
|  |                     'database_entry', | ||||||
|  |                     0, | ||||||
|  |                     siteId, | ||||||
|  |                 )); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Add Basic Info to manage links.
 | ||||||
|  |         promises.push(CoreCourse.getModuleBasicInfoByInstance(database.id, 'data', { siteId })); | ||||||
|  | 
 | ||||||
|  |         // Get course data, needed to determine upload max size if it's configured to be course limit.
 | ||||||
|  |         promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); | ||||||
|  | 
 | ||||||
|  |         await Promise.all(promises); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sync a module. | ||||||
|  |      * | ||||||
|  |      * @param module Module. | ||||||
|  |      * @param courseId Course ID the module belongs to | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      * @returns Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModDataSyncResult> { | ||||||
|  |         const promises = [ | ||||||
|  |             AddonModDataSync.syncDatabase(module.instance, siteId), | ||||||
|  |             AddonModDataSync.syncRatings(module.id, true, siteId), | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         const results = await Promise.all(promises); | ||||||
|  | 
 | ||||||
|  |         return results.reduce((a, b) => ({ | ||||||
|  |             updated: a.updated || b.updated, | ||||||
|  |             warnings: (a.warnings || []).concat(b.warnings || []), | ||||||
|  |         }), { updated: false , warnings: [] }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | export const AddonModDataPrefetchHandler = makeSingleton(AddonModDataPrefetchHandlerLazyService); | ||||||
| @ -12,286 +12,50 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
| import { CoreComments } from '@features/comments/services/comments'; | import { | ||||||
|  |     ADDON_MOD_DATA_PREFETCH_COMPONENT, | ||||||
|  |     ADDON_MOD_DATA_PREFETCH_MODNAME, | ||||||
|  |     ADDON_MOD_DATA_PREFETCH_NAME, | ||||||
|  | } from '@addons/mod/data/constants'; | ||||||
| import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; | import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; | ||||||
| import { CoreCourseCommonModWSOptions, CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; | import { CoreCourseModulePrefetchHandler } from '@features/course/services/module-prefetch-delegate'; | ||||||
| import { CoreCourses } from '@features/courses/services/courses'; | import type { AddonModDataPrefetchHandlerLazyService } from '@addons/mod/data/services/handlers/prefetch-lazy'; | ||||||
| import { CoreFilepool } from '@services/filepool'; |  | ||||||
| import { CoreGroup, CoreGroups } from '@services/groups'; |  | ||||||
| import { CoreSitesCommonWSOptions, CoreSites, CoreSitesReadingStrategy } from '@services/sites'; |  | ||||||
| import { CoreTimeUtils } from '@services/utils/time'; |  | ||||||
| import { CoreUtils } from '@services/utils/utils'; |  | ||||||
| import { CoreWSFile } from '@services/ws'; |  | ||||||
| import { makeSingleton } from '@singletons'; |  | ||||||
| import { AddonModDataEntry, AddonModData, AddonModDataData } from '../data'; |  | ||||||
| import { AddonModDataSync, AddonModDataSyncResult } from '../data-sync'; |  | ||||||
| import { ContextLevel } from '@/core/constants'; |  | ||||||
| import { ADDON_MOD_DATA_COMPONENT } from '../../constants'; |  | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Handler to prefetch databases. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase { | export class AddonModDataPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase { | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModData'; |     name = ADDON_MOD_DATA_PREFETCH_NAME; | ||||||
|     modName = 'data'; |     modName = ADDON_MOD_DATA_PREFETCH_MODNAME; | ||||||
|     component = ADDON_MOD_DATA_COMPONENT; |     component = ADDON_MOD_DATA_PREFETCH_COMPONENT; | ||||||
|     updatesNames = /^configuration$|^.*files$|^entries$|^gradeitems$|^outcomes$|^comments$|^ratings/; |     updatesNames = /^configuration$|^.*files$|^entries$|^gradeitems$|^outcomes$|^comments$|^ratings/; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Retrieves all the entries for all the groups and then returns only unique entries. |  | ||||||
|      * |  | ||||||
|      * @param dataId Database Id. |  | ||||||
|      * @param groups Array of groups in the activity. |  | ||||||
|      * @param options Other options. |  | ||||||
|      * @returns All unique entries. |  | ||||||
|      */ |  | ||||||
|     protected async getAllUniqueEntries( |  | ||||||
|         dataId: number, |  | ||||||
|         groups: CoreGroup[], |  | ||||||
|         options: CoreSitesCommonWSOptions = {}, |  | ||||||
|     ): Promise<AddonModDataEntry[]> { |  | ||||||
| 
 |  | ||||||
|         const promises = groups.map((group) => AddonModData.fetchAllEntries(dataId, { |  | ||||||
|             groupId: group.id, |  | ||||||
|             ...options, // Include all options.
 |  | ||||||
|         })); |  | ||||||
| 
 |  | ||||||
|         const responses = await Promise.all(promises); |  | ||||||
| 
 |  | ||||||
|         const uniqueEntries: Record<number, AddonModDataEntry> = {}; |  | ||||||
|         responses.forEach((groupEntries) => { |  | ||||||
|             groupEntries.forEach((entry) => { |  | ||||||
|                 uniqueEntries[entry.id] = entry; |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         return CoreUtils.objectToArray(uniqueEntries); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Helper function to get all database info just once. |  | ||||||
|      * |  | ||||||
|      * @param module Module to get the files. |  | ||||||
|      * @param courseId Course ID the module belongs to. |  | ||||||
|      * @param omitFail True to always return even if fails. Default false. |  | ||||||
|      * @param options Other options. |  | ||||||
|      * @returns Promise resolved with the info fetched. |  | ||||||
|      */ |  | ||||||
|     protected async getDatabaseInfoHelper( |  | ||||||
|         module: CoreCourseAnyModuleData, |  | ||||||
|         courseId: number, |  | ||||||
|         omitFail: boolean, |  | ||||||
|         options: CoreCourseCommonModWSOptions = {}, |  | ||||||
|     ): Promise<{ database: AddonModDataData; groups: CoreGroup[]; entries: AddonModDataEntry[]; files: CoreWSFile[]}> { |  | ||||||
|         let groups: CoreGroup[] = []; |  | ||||||
|         let entries: AddonModDataEntry[] = []; |  | ||||||
|         let files: CoreWSFile[] = []; |  | ||||||
| 
 |  | ||||||
|         options.cmId = options.cmId || module.id; |  | ||||||
|         options.siteId = options.siteId || CoreSites.getCurrentSiteId(); |  | ||||||
| 
 |  | ||||||
|         const database = await AddonModData.getDatabase(courseId, module.id, options); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             files = this.getIntroFilesFromInstance(module, database); |  | ||||||
| 
 |  | ||||||
|             const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); |  | ||||||
|             if (!groupInfo.groups || groupInfo.groups.length == 0) { |  | ||||||
|                 groupInfo.groups = [{ id: 0, name: '' }]; |  | ||||||
|             } |  | ||||||
|             groups = groupInfo.groups || []; |  | ||||||
| 
 |  | ||||||
|             entries = await this.getAllUniqueEntries(database.id, groups, options); |  | ||||||
|             files = files.concat(this.getEntriesFiles(entries)); |  | ||||||
| 
 |  | ||||||
|             return { |  | ||||||
|                 database, |  | ||||||
|                 groups, |  | ||||||
|                 entries, |  | ||||||
|                 files, |  | ||||||
|             }; |  | ||||||
|         } catch (error) { |  | ||||||
|             if (omitFail) { |  | ||||||
|                 // Any error, return the info we have.
 |  | ||||||
|                 return { |  | ||||||
|                     database, |  | ||||||
|                     groups, |  | ||||||
|                     entries, |  | ||||||
|                     files, |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             throw error; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns the file contained in the entries. |  | ||||||
|      * |  | ||||||
|      * @param entries List of entries to get files from. |  | ||||||
|      * @returns List of files. |  | ||||||
|      */ |  | ||||||
|     protected getEntriesFiles(entries: AddonModDataEntry[]): CoreWSFile[] { |  | ||||||
|         let files: CoreWSFile[] = []; |  | ||||||
| 
 |  | ||||||
|         entries.forEach((entry) => { |  | ||||||
|             CoreUtils.objectToArray(entry.contents).forEach((content) => { |  | ||||||
|                 files = files.concat(<CoreWSFile[]>content.files); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         return files; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> { |  | ||||||
|         return this.getDatabaseInfoHelper(module, courseId, true).then((info) => info.files); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> { |  | ||||||
|         const data = await CoreUtils.ignoreErrors(AddonModData.getDatabase(courseId, module.id)); |  | ||||||
| 
 |  | ||||||
|         return this.getIntroFilesFromInstance(module, data); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     async invalidateContent(moduleId: number, courseId: number): Promise<void> { |  | ||||||
|         await AddonModData.invalidateContent(moduleId, courseId); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> { |  | ||||||
|         const promises: Promise<void>[] = []; |  | ||||||
|         promises.push(AddonModData.invalidateDatabaseData(courseId)); |  | ||||||
|         promises.push(AddonModData.invalidateDatabaseAccessInformationData(module.instance)); |  | ||||||
| 
 |  | ||||||
|         await Promise.all(promises); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> { |  | ||||||
|         const database = await AddonModData.getDatabase(courseId, module.id, { |  | ||||||
|             readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE, |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         const accessData = await AddonModData.getDatabaseAccessInformation(database.id, { cmId: module.id }); |  | ||||||
|         // Check if database is restricted by time.
 |  | ||||||
|         if (!accessData.timeavailable) { |  | ||||||
|             const time = CoreTimeUtils.timestamp(); |  | ||||||
| 
 |  | ||||||
|             // It is restricted, checking times.
 |  | ||||||
|             if (database.timeavailablefrom && time < database.timeavailablefrom) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|             if (database.timeavailableto && time > database.timeavailableto) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     prefetch(module: CoreCourseAnyModuleData, courseId: number): Promise<void> { |  | ||||||
|         return this.prefetchPackage(module, courseId, (siteId) => this.prefetchDatabase(module, courseId, siteId)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Prefetch a database. |  | ||||||
|      * |  | ||||||
|      * @param module Module. |  | ||||||
|      * @param courseId Course ID the module belongs to. |  | ||||||
|      * @param siteId Site ID. |  | ||||||
|      * @returns Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     protected async prefetchDatabase(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise<void> { |  | ||||||
|         const options = { |  | ||||||
|             cmId: module.id, |  | ||||||
|             readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, |  | ||||||
|             siteId, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const info = await this.getDatabaseInfoHelper(module, courseId, false, options); |  | ||||||
| 
 |  | ||||||
|         // Prefetch the database data.
 |  | ||||||
|         const database = info.database; |  | ||||||
| 
 |  | ||||||
|         const commentsEnabled = CoreComments.areCommentsEnabledInSite(); |  | ||||||
| 
 |  | ||||||
|         const promises: Promise<unknown>[] = []; |  | ||||||
| 
 |  | ||||||
|         promises.push(AddonModData.getFields(database.id, options)); |  | ||||||
|         promises.push(CoreFilepool.addFilesToQueue(siteId, info.files, this.component, module.id)); |  | ||||||
| 
 |  | ||||||
|         info.groups.forEach((group) => { |  | ||||||
|             promises.push(AddonModData.getDatabaseAccessInformation(database.id, { |  | ||||||
|                 groupId: group.id, |  | ||||||
|                 ...options, // Include all options.
 |  | ||||||
|             })); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         info.entries.forEach((entry) => { |  | ||||||
|             promises.push(AddonModData.getEntry(database.id, entry.id, options)); |  | ||||||
| 
 |  | ||||||
|             if (commentsEnabled && database.comments) { |  | ||||||
|                 promises.push(CoreComments.getComments( |  | ||||||
|                     ContextLevel.MODULE, |  | ||||||
|                     database.coursemodule, |  | ||||||
|                     'mod_data', |  | ||||||
|                     entry.id, |  | ||||||
|                     'database_entry', |  | ||||||
|                     0, |  | ||||||
|                     siteId, |  | ||||||
|                 )); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Add Basic Info to manage links.
 |  | ||||||
|         promises.push(CoreCourse.getModuleBasicInfoByInstance(database.id, 'data', { siteId })); |  | ||||||
| 
 |  | ||||||
|         // Get course data, needed to determine upload max size if it's configured to be course limit.
 |  | ||||||
|         promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); |  | ||||||
| 
 |  | ||||||
|         await Promise.all(promises); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Sync a module. |  | ||||||
|      * |  | ||||||
|      * @param module Module. |  | ||||||
|      * @param courseId Course ID the module belongs to |  | ||||||
|      * @param siteId Site ID. If not defined, current site. |  | ||||||
|      * @returns Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     async sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModDataSyncResult> { |  | ||||||
|         const promises = [ |  | ||||||
|             AddonModDataSync.syncDatabase(module.instance, siteId), |  | ||||||
|             AddonModDataSync.syncRatings(module.id, true, siteId), |  | ||||||
|         ]; |  | ||||||
| 
 |  | ||||||
|         const results = await Promise.all(promises); |  | ||||||
| 
 |  | ||||||
|         return results.reduce((a, b) => ({ |  | ||||||
|             updated: a.updated || b.updated, |  | ||||||
|             warnings: (a.warnings || []).concat(b.warnings || []), |  | ||||||
|         }), { updated: false , warnings: [] }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| export const AddonModDataPrefetchHandler = makeSingleton(AddonModDataPrefetchHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get prefetch handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Prefetch handler. | ||||||
|  |  */ | ||||||
|  | export function getPrefetchHandlerInstance(): CoreCourseModulePrefetchHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataPrefetchHandlerLazyService, | ||||||
|  |         AddonModDataPrefetchHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataPrefetchHandler } = await import('./prefetch-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataPrefetchHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataPrefetchHandlerService()); | ||||||
|  |     lazyHandler.setLazyMethods(['sync']); | ||||||
|  |     lazyHandler.setLazyOverrides([ | ||||||
|  |         'getFiles', | ||||||
|  |         'getIntroFiles', | ||||||
|  |         'invalidateContent', | ||||||
|  |         'invalidateModule', | ||||||
|  |         'isDownloadable', | ||||||
|  |         'prefetch', | ||||||
|  |     ]); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										87
									
								
								src/addons/mod/data/services/handlers/show-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/addons/mod/data/services/handlers/show-link-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | // (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 { CoreCourse } from '@features/course/services/course'; | ||||||
|  | import { CoreNavigator } from '@services/navigator'; | ||||||
|  | import { CoreSitesReadingStrategy } from '@services/sites'; | ||||||
|  | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
|  | import { makeSingleton } from '@singletons'; | ||||||
|  | import { ADDON_MOD_DATA_PAGE_NAME } from '../../constants'; | ||||||
|  | import { AddonModDataShowLinkHandlerService } from '@addons/mod/data/services/handlers/show-link'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Content links handler for database show entry. | ||||||
|  |  * Match mod/data/view.php?d=6&rid=5 with a valid data id and entryid. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataShowLinkHandlerLazyService extends AddonModDataShowLinkHandlerService { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async handleAction(siteId: string, params: Record<string, string>): Promise<void> { | ||||||
|  |         const modal = await CoreDomUtils.showModalLoading(); | ||||||
|  |         const dataId = parseInt(params.d, 10); | ||||||
|  |         const rId = params.rid || ''; | ||||||
|  |         const group = parseInt(params.group, 10) || false; | ||||||
|  |         const page = parseInt(params.page, 10) || false; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             const module = await CoreCourse.getModuleBasicInfoByInstance( | ||||||
|  |                 dataId, | ||||||
|  |                 'data', | ||||||
|  |                 { siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE }, | ||||||
|  |             ); | ||||||
|  |             const pageParams: Params = { | ||||||
|  |                 title: module.name, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             if (group) { | ||||||
|  |                 pageParams.group = group; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (params.mode && params.mode == 'single') { | ||||||
|  |                 pageParams.offset = page || 0; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             await CoreNavigator.navigateToSitePath( | ||||||
|  |                 `${ADDON_MOD_DATA_PAGE_NAME}/${module.course}/${module.id}/${rId}`, | ||||||
|  |                 { siteId, params: pageParams }, | ||||||
|  |             ); | ||||||
|  |         } finally { | ||||||
|  |             // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
 | ||||||
|  |             modal.dismiss(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { | ||||||
|  |         if (params.d === undefined) { | ||||||
|  |             // Id not defined. Cannot treat the URL.
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ((!params.mode || params.mode != 'single') && params.rid === undefined) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | export const AddonModDataShowLinkHandler = makeSingleton(AddonModDataShowLinkHandlerLazyService); | ||||||
| @ -12,22 +12,12 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
| import { Params } from '@angular/router'; | import { ADDON_MOD_DATA_FEATURE_NAME } from '@addons/mod/data/constants'; | ||||||
| import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; | ||||||
| import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; | import { CoreContentLinksAction, CoreContentLinksHandler } from '@features/contentlinks/services/contentlinks-delegate'; | ||||||
| import { CoreCourse } from '@features/course/services/course'; | import type { AddonModDataShowLinkHandlerLazyService } from '@addons/mod/data/services/handlers/show-link-lazy'; | ||||||
| import { CoreNavigator } from '@services/navigator'; |  | ||||||
| import { CoreSitesReadingStrategy } from '@services/sites'; |  | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; |  | ||||||
| import { makeSingleton } from '@singletons'; |  | ||||||
| import { ADDON_MOD_DATA_FEATURE_NAME, ADDON_MOD_DATA_PAGE_NAME } from '../../constants'; |  | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Content links handler for database show entry. |  | ||||||
|  * Match mod/data/view.php?d=6&rid=5 with a valid data id and entryid. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataShowLinkHandlerService extends CoreContentLinksHandlerBase { | export class AddonModDataShowLinkHandlerService extends CoreContentLinksHandlerBase { | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModDataShowLinkHandler'; |     name = 'AddonModDataShowLinkHandler'; | ||||||
| @ -40,58 +30,40 @@ export class AddonModDataShowLinkHandlerService extends CoreContentLinksHandlerB | |||||||
|      */ |      */ | ||||||
|     getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] { |     getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] { | ||||||
|         return [{ |         return [{ | ||||||
|             action: async (siteId): Promise<void> => { |             action: (siteId) => this.handleAction(siteId, params), | ||||||
|                 const modal = await CoreDomUtils.showModalLoading(); |  | ||||||
|                 const dataId = parseInt(params.d, 10); |  | ||||||
|                 const rId = params.rid || ''; |  | ||||||
|                 const group = parseInt(params.group, 10) || false; |  | ||||||
|                 const page = parseInt(params.page, 10) || false; |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     const module = await CoreCourse.getModuleBasicInfoByInstance( |  | ||||||
|                         dataId, |  | ||||||
|                         'data', |  | ||||||
|                         { siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE }, |  | ||||||
|                     ); |  | ||||||
|                     const pageParams: Params = { |  | ||||||
|                         title: module.name, |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     if (group) { |  | ||||||
|                         pageParams.group = group; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (params.mode && params.mode == 'single') { |  | ||||||
|                         pageParams.offset = page || 0; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     await CoreNavigator.navigateToSitePath( |  | ||||||
|                         `${ADDON_MOD_DATA_PAGE_NAME}/${module.course}/${module.id}/${rId}`, |  | ||||||
|                         { siteId, params: pageParams }, |  | ||||||
|                     ); |  | ||||||
|                 } finally { |  | ||||||
|                     // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
 |  | ||||||
|                     modal.dismiss(); |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         }]; |         }]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * Handle link action. | ||||||
|  |      * | ||||||
|  |      * @param siteId Site id. | ||||||
|  |      * @param params Params. | ||||||
|      */ |      */ | ||||||
|     async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> { |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|         if (params.d === undefined) { |     async handleAction(siteId: string, params: Record<string, string>): Promise<void> { | ||||||
|             // Id not defined. Cannot treat the URL.
 |         // Stub to override.
 | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ((!params.mode || params.mode != 'single') && params.rid === undefined) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| export const AddonModDataShowLinkHandler = makeSingleton(AddonModDataShowLinkHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get show link handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Link handler. | ||||||
|  |  */ | ||||||
|  | export function getShowLinkHandlerInstance(): CoreContentLinksHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataShowLinkHandlerLazyService, | ||||||
|  |         AddonModDataShowLinkHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataShowLinkHandler } = await import('./show-link-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataShowLinkHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataShowLinkHandlerService()); | ||||||
|  |     lazyHandler.setLazyOverrides(['isEnabled', 'handleAction']); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								src/addons/mod/data/services/handlers/sync-cron-lazy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/addons/mod/data/services/handlers/sync-cron-lazy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | // (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 { CoreCronHandler } from '@services/cron'; | ||||||
|  | import { makeSingleton } from '@singletons'; | ||||||
|  | import { AddonModDataSync } from '../data-sync'; | ||||||
|  | import { AddonModDataSyncCronHandlerService } from '@addons/mod/data/services/handlers/sync-cron'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Synchronization cron handler. | ||||||
|  |  */ | ||||||
|  | @Injectable({ providedIn: 'root' }) | ||||||
|  | export class AddonModDataSyncCronHandlerLazyService extends AddonModDataSyncCronHandlerService implements CoreCronHandler { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     execute(siteId?: string, force?: boolean): Promise<void> { | ||||||
|  |         return AddonModDataSync.syncAllDatabases(siteId, force); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritdoc | ||||||
|  |      */ | ||||||
|  |     getInterval(): number { | ||||||
|  |         return AddonModDataSync.syncInterval; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const AddonModDataSyncCronHandler = makeSingleton(AddonModDataSyncCronHandlerLazyService); | ||||||
| @ -12,32 +12,33 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { asyncInstance } from '@/core/utils/async-instance'; | ||||||
| import { CoreCronHandler } from '@services/cron'; | import { CoreCronHandler } from '@services/cron'; | ||||||
| import { makeSingleton } from '@singletons'; | import type { AddonModDataSyncCronHandlerLazyService } from '@addons/mod/data/services/handlers/sync-cron-lazy'; | ||||||
| import { AddonModDataSync } from '../data-sync'; |  | ||||||
| 
 | 
 | ||||||
| /** | export class AddonModDataSyncCronHandlerService { | ||||||
|  * Synchronization cron handler. |  | ||||||
|  */ |  | ||||||
| @Injectable({ providedIn: 'root' }) |  | ||||||
| export class AddonModDataSyncCronHandlerService implements CoreCronHandler { |  | ||||||
| 
 | 
 | ||||||
|     name = 'AddonModDataSyncCronHandler'; |     name = 'AddonModDataSyncCronHandler'; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     execute(siteId?: string, force?: boolean): Promise<void> { |  | ||||||
|         return AddonModDataSync.syncAllDatabases(siteId, force); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @inheritdoc |  | ||||||
|      */ |  | ||||||
|     getInterval(): number { |  | ||||||
|         return AddonModDataSync.syncInterval; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| export const AddonModDataSyncCronHandler = makeSingleton(AddonModDataSyncCronHandlerService); | 
 | ||||||
|  | /** | ||||||
|  |  * Get cron handler instance. | ||||||
|  |  * | ||||||
|  |  * @returns Cron handler. | ||||||
|  |  */ | ||||||
|  | export function getCronHandlerInstance(): CoreCronHandler { | ||||||
|  |     const lazyHandler = asyncInstance< | ||||||
|  |         AddonModDataSyncCronHandlerLazyService, | ||||||
|  |         AddonModDataSyncCronHandlerService | ||||||
|  |     >(async () => { | ||||||
|  |         const { AddonModDataSyncCronHandler } = await import('./sync-cron-lazy'); | ||||||
|  | 
 | ||||||
|  |         return AddonModDataSyncCronHandler.instance; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     lazyHandler.setEagerInstance(new AddonModDataSyncCronHandlerService()); | ||||||
|  |     lazyHandler.setLazyMethods(['execute', 'getInterval']); | ||||||
|  | 
 | ||||||
|  |     return lazyHandler; | ||||||
|  | } | ||||||
|  | |||||||
| @ -239,7 +239,7 @@ export class CoreDbProvider { | |||||||
|             await CorePlatform.ready(); |             await CorePlatform.ready(); | ||||||
| 
 | 
 | ||||||
|             return SQLite.create({ name, location: 'default' }); |             return SQLite.create({ name, location: 'default' }); | ||||||
|         }); |         }, {}); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -151,15 +151,32 @@ export type AsyncMethod<T> = | |||||||
|             : (...args: Params) => Promise<Return> |             : (...args: Params) => Promise<Return> | ||||||
|         : never; |         : never; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Get instance methods that don't return a promise. | ||||||
|  |  */ | ||||||
|  | export type GetEagerMethods<TEagerInstance extends AsyncObject> = { | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
|  |     [k in keyof TEagerInstance]: TEagerInstance[k] extends (...args: any[]) => infer TReturn | ||||||
|  |         // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
|  |         ? (TReturn extends Promise<any> ? never : k) | ||||||
|  |         : never | ||||||
|  | }[keyof TEagerInstance]; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Asynchronous instance. |  * Asynchronous instance. | ||||||
|  * |  * | ||||||
|  * All methods are converted to their asynchronous version, and properties are available asynchronously using |  * All methods are converted to their asynchronous version, and properties are available asynchronously using | ||||||
|  * the getProperty method. |  * the getProperty method. | ||||||
|  */ |  */ | ||||||
| export type AsyncInstance<TLazyInstance extends TEagerInstance, TEagerInstance extends AsyncObject = Partial<TLazyInstance>> = | export type AsyncInstance< | ||||||
|     AsyncInstanceWrapper<TLazyInstance, TEagerInstance> & { |     TLazyInstance extends TEagerInstance, | ||||||
|  |     TEagerInstance extends AsyncObject = Partial<TLazyInstance>, | ||||||
|  |     TEagerMethods extends keyof TEagerInstance = GetEagerMethods<TEagerInstance> | ||||||
|  | > = | ||||||
|  |     AsyncInstanceWrapper<TLazyInstance, TEagerInstance> & Omit<{ | ||||||
|         [k in keyof TLazyInstance]: AsyncMethod<TLazyInstance[k]>; |         [k in keyof TLazyInstance]: AsyncMethod<TLazyInstance[k]>; | ||||||
|  |     }, TEagerMethods> & { | ||||||
|  |         [k in TEagerMethods]: TEagerInstance[k]; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -177,9 +194,14 @@ export type LazyMethodsGuard<TMethods extends Array<string | number | symbol>, T | |||||||
|  */ |  */ | ||||||
| export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstance extends AsyncObject = Partial<TLazyInstance>>( | export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstance extends AsyncObject = Partial<TLazyInstance>>( | ||||||
|     lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>, |     lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>, | ||||||
|  |     eagerInstance?: TEagerInstance, | ||||||
| ): AsyncInstance<TLazyInstance, TEagerInstance> { | ): AsyncInstance<TLazyInstance, TEagerInstance> { | ||||||
|     const wrapper = createAsyncInstanceWrapper<TLazyInstance, TEagerInstance>(lazyConstructor); |     const wrapper = createAsyncInstanceWrapper<TLazyInstance, TEagerInstance>(lazyConstructor); | ||||||
| 
 | 
 | ||||||
|  |     if (eagerInstance) { | ||||||
|  |         wrapper.setEagerInstance(eagerInstance); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return new Proxy(wrapper, { |     return new Proxy(wrapper, { | ||||||
|         get: (target, p, receiver) => { |         get: (target, p, receiver) => { | ||||||
|             const property = p as keyof TEagerInstance; |             const property = p as keyof TEagerInstance; | ||||||
|  | |||||||
| @ -113,6 +113,20 @@ describe('AsyncInstance', () => { | |||||||
|         expectSameTypes<AsyncInstance<LazyService>['goodbye'], () => Promise<string>>(true); |         expectSameTypes<AsyncInstance<LazyService>['goodbye'], () => Promise<string>>(true); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('keeps eager methods synchronous', () => { | ||||||
|  |         // Arrange.
 | ||||||
|  |         const asyncService = asyncInstance<LazyService, EagerService>(() => new LazyService()); | ||||||
|  | 
 | ||||||
|  |         asyncService.setEagerInstance(new EagerService()); | ||||||
|  | 
 | ||||||
|  |         // Act.
 | ||||||
|  |         const message = asyncService.eagerHello(); | ||||||
|  | 
 | ||||||
|  |         // Assert.
 | ||||||
|  |         expect(message).toEqual('hello'); | ||||||
|  |         expectSameTypes<typeof message, string>(true); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| class EagerService { | class EagerService { | ||||||
| @ -121,6 +135,10 @@ class EagerService { | |||||||
| 
 | 
 | ||||||
|     notImplemented?(): void; |     notImplemented?(): void; | ||||||
| 
 | 
 | ||||||
|  |     eagerHello(): string { | ||||||
|  |         return 'hello'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async isEager(): Promise<boolean> { |     async isEager(): Promise<boolean> { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user