Merge pull request #2693 from crazyserver/MOBILE-3652
MOBILE-3652 resource: Add resource activity module
This commit is contained in:
		
						commit
						69b8b6e3a2
					
				| @ -22,6 +22,7 @@ import { AddonModLabelModule } from './label/label.module'; | ||||
| import { AddonModLessonModule } from './lesson/lesson.module'; | ||||
| import { AddonModPageModule } from './page/page.module'; | ||||
| import { AddonModQuizModule } from './quiz/quiz.module'; | ||||
| import { AddonModResourceModule } from './resource/resource.module'; | ||||
| import { AddonModUrlModule } from './url/url.module'; | ||||
| 
 | ||||
| @NgModule({ | ||||
| @ -35,6 +36,7 @@ import { AddonModUrlModule } from './url/url.module'; | ||||
|         AddonModQuizModule, | ||||
|         AddonModUrlModule, | ||||
|         AddonModLabelModule, | ||||
|         AddonModResourceModule, | ||||
|         AddonModFolderModule, | ||||
|     ], | ||||
|     providers: [], | ||||
|  | ||||
							
								
								
									
										36
									
								
								src/addons/mod/resource/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/addons/mod/resource/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| // (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 { CoreSharedModule } from '@/core/shared.module'; | ||||
| import { CoreCourseComponentsModule } from '@features/course/components/components.module'; | ||||
| 
 | ||||
| import { AddonModResourceIndexComponent } from './index/index'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
|         AddonModResourceIndexComponent, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreSharedModule, | ||||
|         CoreCourseComponentsModule, | ||||
|     ], | ||||
|     providers: [ | ||||
|     ], | ||||
|     exports: [ | ||||
|         AddonModResourceIndexComponent, | ||||
|     ], | ||||
| }) | ||||
| export class AddonModResourceComponentsModule {} | ||||
| @ -0,0 +1,50 @@ | ||||
| <!-- Buttons to add to the header. --> | ||||
| <core-navbar-buttons slot="end"> | ||||
|     <core-context-menu> | ||||
|         <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" | ||||
|             [href]="externalUrl" iconAction="fas-external-link-alt"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" | ||||
|             (action)="expandDescription()" iconAction="fas-arrow-right"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" | ||||
|             iconAction="far-newspaper" (action)="gotoBlog()"></core-context-menu-item> | ||||
|         <core-context-menu-item [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" | ||||
|             [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="prefetchText" (action)="prefetch($event)" | ||||
|             [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item> | ||||
|         <core-context-menu-item *ngIf="size" [priority]="500" [content]="'core.clearstoreddata' | translate:{$a: size}" | ||||
|             iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false"> | ||||
|         </core-context-menu-item> | ||||
|     </core-context-menu> | ||||
| </core-navbar-buttons> | ||||
| 
 | ||||
| <!-- Content. --> | ||||
| <core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page"> | ||||
| 
 | ||||
|     <core-course-module-description *ngIf="mode != 'iframe' && (mode != 'embedded' || displayDescription)" | ||||
|         [description]="description" [component]="component" [componentId]="componentId" contextLevel="module" | ||||
|         [contextInstanceId]="module!.id" [courseId]="courseId"> | ||||
|     </core-course-module-description> | ||||
| 
 | ||||
|     <ion-card class="core-warning-card" *ngIf="warning"> | ||||
|         <ion-item> | ||||
|             <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon> | ||||
|             <ion-label><span [innerHTML]="warning"></span></ion-label> | ||||
|         </ion-item> | ||||
|     </ion-card> | ||||
| 
 | ||||
|     <ng-container *ngIf="mode == 'iframe'"> | ||||
|         <core-iframe [src]="src"></core-iframe> | ||||
|     </ng-container> | ||||
| 
 | ||||
|     <div *ngIf="mode == 'embedded'"> | ||||
|         <core-format-text [text]="contentText" [filter]="false"></core-format-text> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="ion-padding" *ngIf="mode == 'external'"> | ||||
|         <ion-button expand="block" (click)="open()"> | ||||
|             <ion-icon name="far-file" slot="start"></ion-icon> | ||||
|             {{ 'addon.mod_resource.openthefile' | translate }} | ||||
|         </ion-button> | ||||
|     </div> | ||||
| 
 | ||||
| </core-loading> | ||||
							
								
								
									
										177
									
								
								src/addons/mod/resource/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/addons/mod/resource/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| // (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 { Component, OnInit, Optional } from '@angular/core'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { | ||||
|     CoreCourseModuleMainResourceComponent, | ||||
| } from '@features/course/classes/main-resource-component'; | ||||
| import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; | ||||
| import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { | ||||
|     AddonModResource, | ||||
|     AddonModResourceCustomData, | ||||
|     AddonModResourceProvider, | ||||
|     AddonModResourceResource, | ||||
| } from '../../services/resource'; | ||||
| import { AddonModResourceHelper } from '../../services/resource-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Component that displays a resource. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'addon-mod-resource-index', | ||||
|     templateUrl: 'addon-mod-resource-index.html', | ||||
| }) | ||||
| export class AddonModResourceIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit { | ||||
| 
 | ||||
|     component = AddonModResourceProvider.COMPONENT; | ||||
| 
 | ||||
|     canGetResource = false; | ||||
|     mode = ''; | ||||
|     src = ''; | ||||
|     contentText = ''; | ||||
|     displayDescription = true; | ||||
|     warning = ''; | ||||
| 
 | ||||
|     constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { | ||||
|         super('AddonModResourceIndexComponent', courseContentsPage); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         super.ngOnInit(); | ||||
| 
 | ||||
|         this.canGetResource = AddonModResource.isGetResourceWSAvailable(); | ||||
| 
 | ||||
|         await this.loadContent(); | ||||
|         try { | ||||
|             await AddonModResource.logView(this.module!.instance!, this.module!.name); | ||||
|             CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata); | ||||
|         } catch { | ||||
|             // Ignore errors.
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Perform the invalidate content function. | ||||
|      * | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     protected async invalidateContent(): Promise<void> { | ||||
|         return AddonModResource.invalidateContent(this.module!.id, this.courseId!); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Download resource contents. | ||||
|      * | ||||
|      * @param refresh Whether we're refreshing data. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async fetchContent(refresh?: boolean): Promise<void> { | ||||
|         // Load module contents if needed. Passing refresh is needed to force reloading contents.
 | ||||
|         await CoreCourse.loadModuleContents(this.module!, this.courseId, undefined, false, refresh); | ||||
| 
 | ||||
|         if (!this.module!.contents || !this.module!.contents.length) { | ||||
|             throw new CoreError(Translate.instant('core.filenotfound')); | ||||
|         } | ||||
| 
 | ||||
|         let resource: AddonModResourceResource | CoreCourseWSModule | undefined; | ||||
|         let options: AddonModResourceCustomData = {}; | ||||
| 
 | ||||
|         // Get the resource instance to get the latest name/description and to know if it's embedded.
 | ||||
|         if (this.canGetResource) { | ||||
|             resource = await CoreUtils.ignoreErrors(AddonModResource.getResourceData(this.courseId!, this.module!.id)); | ||||
|             this.description = resource?.intro || ''; | ||||
|             options = resource?.displayoptions ? CoreTextUtils.unserialize(resource.displayoptions) : {}; | ||||
|         } else { | ||||
|             resource = await CoreUtils.ignoreErrors(CoreCourse.getModule(this.module!.id, this.courseId)); | ||||
|             this.description = resource?.description || ''; | ||||
|             options = resource?.customdata ? CoreTextUtils.unserialize(CoreTextUtils.parseJSON(resource.customdata)) : {}; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             if (resource) { | ||||
|                 this.displayDescription = typeof options.printintro == 'undefined' || !!options.printintro; | ||||
|                 this.dataRetrieved.emit(resource); | ||||
|             } | ||||
| 
 | ||||
|             if (AddonModResourceHelper.isDisplayedInIframe(this.module!)) { | ||||
|                 const downloadResult = await this.downloadResourceIfNeeded(refresh, true); | ||||
|                 const src = await AddonModResourceHelper.getIframeSrc(this.module!); | ||||
|                 this.mode = 'iframe'; | ||||
| 
 | ||||
|                 if (this.src && src.toString() == this.src.toString()) { | ||||
|                     // Re-loading same page.
 | ||||
|                     // Set it to empty and then re-set the src in the next digest so it detects it has changed.
 | ||||
|                     this.src = ''; | ||||
|                     setTimeout(() => { | ||||
|                         this.src = src; | ||||
|                     }); | ||||
|                 } else { | ||||
|                     this.src = src; | ||||
|                 } | ||||
| 
 | ||||
|                 this.warning = downloadResult.failed | ||||
|                     ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) | ||||
|                     : ''; | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (resource && 'display' in resource && AddonModResourceHelper.isDisplayedEmbedded(this.module!, resource.display)) { | ||||
|                 this.mode = 'embedded'; | ||||
|                 this.warning = ''; | ||||
| 
 | ||||
|                 this.contentText = await AddonModResourceHelper.getEmbeddedHtml(this.module!, this.courseId!); | ||||
|                 this.mode = this.contentText.length > 0 ? 'embedded' : 'external'; | ||||
|             } else { | ||||
|                 this.mode = 'external'; | ||||
|                 this.warning = ''; | ||||
|             } | ||||
|         } finally { | ||||
|             this.fillContextMenu(refresh); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Opens a file. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async open(): Promise<void> { | ||||
|         let downloadable = await CoreCourseModulePrefetchDelegate.isModuleDownloadable(this.module!, this.courseId!); | ||||
| 
 | ||||
|         if (downloadable) { | ||||
|             // Check if the main file is downloadle.
 | ||||
|             // This isn't done in "isDownloadable" to prevent extra WS calls in the course page.
 | ||||
|             downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module!); | ||||
| 
 | ||||
|             if (downloadable) { | ||||
|                 return AddonModResourceHelper.openModuleFile(this.module!, this.courseId!); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // The resource cannot be downloaded, open the activity in browser.
 | ||||
|         await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module!.url!); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/addons/mod/resource/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/addons/mod/resource/lang.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| { | ||||
|     "errorwhileloadingthecontent": "Error while loading the content.", | ||||
|     "modifieddate": "Modified {{$a}}", | ||||
|     "modulenameplural": "Files", | ||||
|     "openthefile": "Open the file", | ||||
|     "uploadeddate": "Uploaded {{$a}}" | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/addons/mod/resource/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/addons/mod/resource/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
|         <ion-title> | ||||
|             <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"> | ||||
|             </core-format-text> | ||||
|         </ion-title> | ||||
| 
 | ||||
|         <ion-buttons slot="end"> | ||||
|             <!-- The buttons defined by the component will be added in here. --> | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" | ||||
|         [disabled]="!activityComponent?.loaded || activityComponent?.mode != 'external'" | ||||
|         (ionRefresh)="activityComponent?.doRefresh($event)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
| 
 | ||||
|     <addon-mod-resource-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"> | ||||
|     </addon-mod-resource-index> | ||||
| </ion-content> | ||||
							
								
								
									
										30
									
								
								src/addons/mod/resource/pages/index/index.page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/addons/mod/resource/pages/index/index.page.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| // (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 { Component, ViewChild } from '@angular/core'; | ||||
| import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page'; | ||||
| import { AddonModResourceIndexComponent } from '../../components/index/index'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a resource. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'page-addon-mod-resource-index', | ||||
|     templateUrl: 'index.html', | ||||
| }) | ||||
| export class AddonModResourceIndexPage extends CoreCourseModuleMainActivityPage<AddonModResourceIndexComponent> { | ||||
| 
 | ||||
|     @ViewChild(AddonModResourceIndexComponent) activityComponent?: AddonModResourceIndexComponent; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/addons/mod/resource/resource-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/addons/mod/resource/resource-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| // (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 { CoreSharedModule } from '@/core/shared.module'; | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { AddonModResourceComponentsModule } from './components/components.module'; | ||||
| import { AddonModResourceIndexPage } from './pages/index/index.page'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: ':courseId/:cmId', | ||||
|         component: AddonModResourceIndexPage, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         RouterModule.forChild(routes), | ||||
|         CoreSharedModule, | ||||
|         AddonModResourceComponentsModule, | ||||
|     ], | ||||
|     declarations: [ | ||||
|         AddonModResourceIndexPage, | ||||
|     ], | ||||
| }) | ||||
| export class AddonModResourceLazyModule {} | ||||
							
								
								
									
										56
									
								
								src/addons/mod/resource/resource.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/addons/mod/resource/resource.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| // (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 { APP_INITIALIZER, NgModule } from '@angular/core'; | ||||
| import { Routes } from '@angular/router'; | ||||
| import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; | ||||
| import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; | ||||
| import { CorePluginFileDelegate } from '@services/plugin-file-delegate'; | ||||
| import { AddonModResourceComponentsModule } from './components/components.module'; | ||||
| import { AddonModResourceIndexLinkHandler } from './services/handlers/index-link'; | ||||
| import { AddonModResourceListLinkHandler } from './services/handlers/list-link'; | ||||
| import { AddonModResourceModuleHandlerService, AddonModResourceModuleHandler } from './services/handlers/module'; | ||||
| import { AddonModResourcePluginFileHandler } from './services/handlers/pluginfile'; | ||||
| import { AddonModResourcePrefetchHandler } from './services/handlers/prefetch'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: AddonModResourceModuleHandlerService.PAGE_NAME, | ||||
|         loadChildren: () => import('./resource-lazy.module').then(m => m.AddonModResourceLazyModule), | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         CoreMainMenuTabRoutingModule.forChild(routes), | ||||
|         AddonModResourceComponentsModule, | ||||
|     ], | ||||
|     providers: [ | ||||
|         { | ||||
|             provide: APP_INITIALIZER, | ||||
|             multi: true, | ||||
|             deps: [], | ||||
|             useFactory: () => () => { | ||||
|                 CoreCourseModuleDelegate.registerHandler(AddonModResourceModuleHandler.instance); | ||||
|                 CoreContentLinksDelegate.registerHandler(AddonModResourceIndexLinkHandler.instance); | ||||
|                 CoreContentLinksDelegate.registerHandler(AddonModResourceListLinkHandler.instance); | ||||
|                 CoreCourseModulePrefetchDelegate.registerHandler(AddonModResourcePrefetchHandler.instance); | ||||
|                 CorePluginFileDelegate.registerHandler(AddonModResourcePluginFileHandler.instance); | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
| }) | ||||
| export class AddonModResourceModule {} | ||||
							
								
								
									
										40
									
								
								src/addons/mod/resource/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/addons/mod/resource/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| // (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 { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonModResource } from '../resource'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to treat links to resource. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourceIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler { | ||||
| 
 | ||||
|     name = 'AddonModResourceLinkHandler'; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super('AddonModResource', 'resource', 'r'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     isEnabled(siteId: string): Promise<boolean> { | ||||
|         return AddonModResource.isPluginEnabled(siteId); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourceIndexLinkHandler = makeSingleton(AddonModResourceIndexLinkHandlerService); | ||||
							
								
								
									
										40
									
								
								src/addons/mod/resource/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/addons/mod/resource/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| // (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 { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonModResource } from '../resource'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to treat links to resource list page. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourceListLinkHandlerService extends CoreContentLinksModuleListHandler { | ||||
| 
 | ||||
|     name = 'AddonModResourceListLinkHandler'; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super('AddonModResource', 'resource'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     isEnabled(siteId: string): Promise<boolean> { | ||||
|         return AddonModResource.isPluginEnabled(siteId); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourceListLinkHandler = makeSingleton(AddonModResourceListLinkHandlerService); | ||||
							
								
								
									
										259
									
								
								src/addons/mod/resource/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/addons/mod/resource/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,259 @@ | ||||
| // (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 { CoreConstants } from '@/core/constants'; | ||||
| import { Injectable, Type } from '@angular/core'; | ||||
| import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from '@features/course/services/course'; | ||||
| import { CoreCourseModule } from '@features/course/services/course-helper'; | ||||
| import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; | ||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreWSExternalFile } from '@services/ws'; | ||||
| import { makeSingleton, Translate } from '@singletons'; | ||||
| import { AddonModResourceIndexComponent } from '../../components/index'; | ||||
| import { AddonModResource, AddonModResourceCustomData } from '../resource'; | ||||
| import { AddonModResourceHelper } from '../resource-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to support resource modules. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourceModuleHandlerService implements CoreCourseModuleHandler { | ||||
| 
 | ||||
|     static readonly PAGE_NAME = 'mod_resource'; | ||||
| 
 | ||||
|     name = 'AddonModResource'; | ||||
|     modName = 'resource'; | ||||
| 
 | ||||
|     supportedFeatures = { | ||||
|         [CoreConstants.FEATURE_MOD_ARCHETYPE]: CoreConstants.MOD_ARCHETYPE_RESOURCE, | ||||
|         [CoreConstants.FEATURE_GROUPS]: false, | ||||
|         [CoreConstants.FEATURE_GROUPINGS]: false, | ||||
|         [CoreConstants.FEATURE_MOD_INTRO]: true, | ||||
|         [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true, | ||||
|         [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false, | ||||
|         [CoreConstants.FEATURE_GRADE_OUTCOMES]: false, | ||||
|         [CoreConstants.FEATURE_BACKUP_MOODLE2]: true, | ||||
|         [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     isEnabled(): Promise<boolean> { | ||||
|         return AddonModResource.isPluginEnabled(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the data required to display the module in the course contents view. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @param courseId The course ID. | ||||
|      * @param sectionId The section ID. | ||||
|      * @return Data to render the module. | ||||
|      */ | ||||
|     getData(module: CoreCourseAnyModuleData, courseId: number): CoreCourseModuleHandlerData { | ||||
|         const updateStatus = (status: string): void => { | ||||
|             handlerData.buttons![0].hidden = status !== CoreConstants.DOWNLOADED || | ||||
|                 AddonModResourceHelper.isDisplayedInIframe(module); | ||||
|         }; | ||||
| 
 | ||||
|         const handlerData: CoreCourseModuleHandlerData = { | ||||
|             icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), | ||||
|             title: module.name, | ||||
|             class: 'addon-mod_resource-handler', | ||||
|             showDownloadButton: true, | ||||
|             action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void { | ||||
|                 options = options || {}; | ||||
|                 options.params = options.params || {}; | ||||
|                 Object.assign(options.params, { module }); | ||||
|                 const routeParams = '/' + courseId + '/' + module.id; | ||||
| 
 | ||||
|                 CoreNavigator.navigateToSitePath(AddonModResourceModuleHandlerService.PAGE_NAME + routeParams, options); | ||||
|             }, | ||||
|             updateStatus: updateStatus.bind(this), | ||||
|             buttons: [{ | ||||
|                 hidden: true, | ||||
|                 icon: 'document', | ||||
|                 label: 'addon.mod_resource.openthefile', | ||||
|                 action: async (event: Event, module: CoreCourseModule, courseId: number): Promise<void> => { | ||||
|                     const hide = await this.hideOpenButton(module, courseId); | ||||
|                     if (!hide) { | ||||
|                         AddonModResourceHelper.openModuleFile(module, courseId); | ||||
|                     } | ||||
|                 }, | ||||
|             }], | ||||
|         }; | ||||
| 
 | ||||
|         this.getResourceData(module, courseId, handlerData).then((data) => { | ||||
|             handlerData.icon = data.icon; | ||||
|             handlerData.extraBadge = data.extra; | ||||
|             handlerData.extraBadgeColor = 'light'; | ||||
| 
 | ||||
|             return; | ||||
|         }).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
| 
 | ||||
|         return handlerData; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns if contents are loaded to show open button. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @param courseId The course ID. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     protected async hideOpenButton(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> { | ||||
|         if (!('contentsinfo' in module) || !module.contentsinfo) { | ||||
|             await CoreCourse.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName); | ||||
|         } | ||||
| 
 | ||||
|         const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(module, courseId); | ||||
| 
 | ||||
|         return status !== CoreConstants.DOWNLOADED || AddonModResourceHelper.isDisplayedInIframe(module); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the activity icon and data. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @param courseId The course ID. | ||||
|      * @return Resource data. | ||||
|      */ | ||||
|     protected async getResourceData( | ||||
|         module: CoreCourseAnyModuleData, | ||||
|         courseId: number, | ||||
|         handlerData: CoreCourseModuleHandlerData, | ||||
|     ): Promise<AddonResourceHandlerData> { | ||||
|         const promises: Promise<void>[] = []; | ||||
|         let infoFiles: CoreWSExternalFile[] = []; | ||||
|         let options: AddonModResourceCustomData = {}; | ||||
| 
 | ||||
|         // Check if the button needs to be shown or not.
 | ||||
|         promises.push(this.hideOpenButton(module, courseId).then((hideOpenButton) => { | ||||
|             handlerData.buttons![0].hidden = hideOpenButton; | ||||
| 
 | ||||
|             return; | ||||
|         })); | ||||
| 
 | ||||
|         if ('customdata' in module && typeof module.customdata != 'undefined') { | ||||
|             options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata)); | ||||
|         } else if (AddonModResource.isGetResourceWSAvailable()) { | ||||
|             // Get the resource data.
 | ||||
|             promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => { | ||||
|                 infoFiles = info.contentfiles; | ||||
|                 options = CoreTextUtils.unserialize(info.displayoptions); | ||||
| 
 | ||||
|                 return; | ||||
|             })); | ||||
|         } | ||||
| 
 | ||||
|         await Promise.all(promises); | ||||
| 
 | ||||
|         const files: (CoreCourseModuleContentFile | CoreWSExternalFile)[] = module.contents && module.contents.length | ||||
|             ? module.contents | ||||
|             : infoFiles; | ||||
| 
 | ||||
|         const resourceData: AddonResourceHandlerData = { | ||||
|             icon: '', | ||||
|             extra: '', | ||||
|         }; | ||||
|         const extra: string[] = []; | ||||
| 
 | ||||
|         if ('contentsinfo' in module && module.contentsinfo) { | ||||
|             // No need to use the list of files.
 | ||||
|             const mimetype = module.contentsinfo.mimetypes[0]; | ||||
|             if (mimetype) { | ||||
|                 resourceData.icon = CoreMimetypeUtils.getMimetypeIcon(mimetype); | ||||
|             } | ||||
|             resourceData.extra = CoreTextUtils.cleanTags(module.afterlink); | ||||
| 
 | ||||
|         } else if (files && files.length) { | ||||
|             const file = files[0]; | ||||
| 
 | ||||
|             resourceData.icon = CoreMimetypeUtils.getFileIcon(file.filename || ''); | ||||
| 
 | ||||
|             if (options.showsize) { | ||||
|                 const size = options.filedetails | ||||
|                     ? options.filedetails.size | ||||
|                     : files.reduce((result, file) => result + (file.filesize || 0), 0); | ||||
| 
 | ||||
|                 extra.push(CoreTextUtils.bytesToSize(size, 1)); | ||||
|             } | ||||
| 
 | ||||
|             if (options.showtype) { | ||||
|                 // We should take it from options.filedetails.size if avalaible but it's already translated.
 | ||||
|                 extra.push(CoreMimetypeUtils.getMimetypeDescription(file)); | ||||
|             } | ||||
| 
 | ||||
|             if (options.showdate) { | ||||
|                 const timecreated = 'timecreated' in file ? file.timecreated : 0; | ||||
| 
 | ||||
|                 if (options.filedetails && options.filedetails.modifieddate) { | ||||
|                     extra.push(Translate.instant( | ||||
|                         'addon.mod_resource.modifieddate', | ||||
|                         { $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') }, | ||||
|                     )); | ||||
|                 } else if (options.filedetails && options.filedetails.uploadeddate) { | ||||
|                     extra.push(Translate.instant( | ||||
|                         'addon.mod_resource.uploadeddate', | ||||
|                         { $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') }, | ||||
|                     )); | ||||
|                 } else if ((file.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) { | ||||
|                     /* Modified date may be up to several minutes later than uploaded date just because | ||||
|                         teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */ | ||||
|                     extra.push(Translate.instant( | ||||
|                         'addon.mod_resource.modifieddate', | ||||
|                         { $a: CoreTimeUtils.userDate((file.timemodified || 0) * 1000, 'core.strftimedatetimeshort') }, | ||||
|                     )); | ||||
|                 } else { | ||||
|                     extra.push(Translate.instant( | ||||
|                         'addon.mod_resource.uploadeddate', | ||||
|                         { $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') }, | ||||
|                     )); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             resourceData.extra += extra.join(' '); | ||||
|         } | ||||
| 
 | ||||
|         // No previously set, just set the icon.
 | ||||
|         if (resourceData.icon == '') { | ||||
|             resourceData.icon = CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined); | ||||
|         } | ||||
| 
 | ||||
|         return resourceData; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async getMainComponent(): Promise<Type<unknown> | undefined> { | ||||
|         return AddonModResourceIndexComponent; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourceModuleHandler = makeSingleton(AddonModResourceModuleHandlerService); | ||||
| 
 | ||||
| 
 | ||||
| type AddonResourceHandlerData = { | ||||
|     icon: string; | ||||
|     extra: string; | ||||
| } | ||||
| ; | ||||
							
								
								
									
										55
									
								
								src/addons/mod/resource/services/handlers/pluginfile.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/addons/mod/resource/services/handlers/pluginfile.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| // (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 { CorePluginFileHandler } from '@services/plugin-file-delegate'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to treat links to resource. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourcePluginFileHandlerService implements CorePluginFileHandler { | ||||
| 
 | ||||
|     name = 'AddonModResourcePluginFileHandler'; | ||||
|     component = 'mod_resource'; | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getComponentRevisionRegExp(args: string[]): RegExp | undefined { | ||||
|         // Check filearea.
 | ||||
|         if (args[2] == 'content') { | ||||
|             // Component + Filearea + Revision
 | ||||
|             return new RegExp('/mod_resource/content/([0-9]+)/'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getComponentRevisionReplace(): string { | ||||
|         // Component + Filearea + Revision
 | ||||
|         return '/mod_resource/content/0/'; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabled(): Promise<boolean> { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourcePluginFileHandler = makeSingleton(AddonModResourcePluginFileHandlerService); | ||||
							
								
								
									
										120
									
								
								src/addons/mod/resource/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/addons/mod/resource/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| // (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 { CoreConstants } from '@/core/constants'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler'; | ||||
| import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonModResource, AddonModResourceProvider } from '../resource'; | ||||
| import { AddonModResourceHelper } from '../resource-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to prefetch resources. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourcePrefetchHandlerService extends CoreCourseResourcePrefetchHandlerBase { | ||||
| 
 | ||||
|     name = 'AddonModResource'; | ||||
|     modName = 'resource'; | ||||
|     component = AddonModResourceProvider.COMPONENT; | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     determineStatus(module: CoreCourseAnyModuleData, status: string): string { | ||||
|         if (status == CoreConstants.DOWNLOADED && module) { | ||||
|             // If the main file is an external file, always display the module as outdated.
 | ||||
|             if ('contentsinfo' in module && module.contentsinfo) { | ||||
|                 if (module.contentsinfo.repositorytype) { | ||||
|                     // It's an external file.
 | ||||
|                     return CoreConstants.OUTDATED; | ||||
|                 } | ||||
|             } else if (module.contents) { | ||||
|                 const mainFile = module.contents[0]; | ||||
|                 if (mainFile && mainFile.isexternalfile) { | ||||
|                     return CoreConstants.OUTDATED; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async downloadOrPrefetch(module: CoreCourseWSModule, courseId: number, prefetch?: boolean): Promise<void> { | ||||
|         let dirPath: string | undefined; | ||||
| 
 | ||||
|         if (AddonModResourceHelper.isDisplayedInIframe(module)) { | ||||
|             dirPath = await CoreFilepool.getPackageDirPathByUrl(CoreSites.getCurrentSiteId(), module.url!); | ||||
|         } | ||||
| 
 | ||||
|         const promises: Promise<any>[] = []; | ||||
| 
 | ||||
|         promises.push(super.downloadOrPrefetch(module, courseId, prefetch, dirPath)); | ||||
| 
 | ||||
|         if (AddonModResource.isGetResourceWSAvailable()) { | ||||
|             promises.push(AddonModResource.getResourceData(courseId, module.id)); | ||||
|         } | ||||
| 
 | ||||
|         await Promise.all(promises); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async invalidateContent(moduleId: number, courseId: number): Promise<void> { | ||||
|         await AddonModResource.invalidateContent(moduleId, courseId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> { | ||||
|         const promises: Promise<void>[] = []; | ||||
| 
 | ||||
|         promises.push(AddonModResource.invalidateResourceData(courseId)); | ||||
|         promises.push(CoreCourse.invalidateModule(module.id, undefined, this.modName)); | ||||
| 
 | ||||
|         await Promise.all(promises); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> { | ||||
|         if (CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.7')) { | ||||
|             // Nextcloud files are downloadable from 3.7 onwards.
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         // Don't allow downloading Nextcloud files in older sites.
 | ||||
|         await this.loadContents(module, courseId, false); | ||||
| 
 | ||||
|         return !AddonModResourceHelper.isNextcloudFile(module); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabled(): Promise<boolean> { | ||||
|         return AddonModResource.isPluginEnabled(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourcePrefetchHandler = makeSingleton(AddonModResourcePrefetchHandlerService); | ||||
							
								
								
									
										203
									
								
								src/addons/mod/resource/services/resource-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/addons/mod/resource/services/resource-helper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,203 @@ | ||||
| // (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 { CoreConstants } from '@/core/constants'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '@features/course/services/course'; | ||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreFile } from '@services/file'; | ||||
| import { CoreFileHelper } from '@services/file-helper'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonModResource, AddonModResourceProvider } from './resource'; | ||||
| 
 | ||||
| /** | ||||
|  * Service that provides helper functions for resources. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourceHelperProvider { | ||||
| 
 | ||||
|     /** | ||||
|      * Get the HTML to display an embedded resource. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @param courseId The course ID. | ||||
|      * @return Promise resolved with the HTML. | ||||
|      */ | ||||
|     async getEmbeddedHtml(module: CoreCourseWSModule, courseId: number): Promise<string> { | ||||
|         const result = await CoreCourseHelper.downloadModuleWithMainFileIfNeeded( | ||||
|             module, | ||||
|             courseId, | ||||
|             AddonModResourceProvider.COMPONENT, | ||||
|             module.id, | ||||
|             module.contents, | ||||
|         ); | ||||
| 
 | ||||
|         return CoreMimetypeUtils.getEmbeddedHtml(module.contents[0], result.path); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Download all the files needed and returns the src of the iframe. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @return Promise resolved with the iframe src. | ||||
|      */ | ||||
|     async getIframeSrc(module: CoreCourseWSModule): Promise<string> { | ||||
|         if (!module.contents.length) { | ||||
|             throw new CoreError('No contents available in module'); | ||||
|         } | ||||
| 
 | ||||
|         const mainFile = module.contents[0]; | ||||
|         let mainFilePath = mainFile.filename; | ||||
| 
 | ||||
|         if (mainFile.filepath !== '/') { | ||||
|             mainFilePath = mainFile.filepath.substr(1) + mainFilePath; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const dirPath = await CoreFilepool.getPackageDirUrlByUrl(CoreSites.getCurrentSiteId(), module.url!); | ||||
| 
 | ||||
|             // This URL is going to be injected in an iframe, we need trustAsResourceUrl to make it work in a browser.
 | ||||
|             return CoreTextUtils.concatenatePaths(dirPath, mainFilePath); | ||||
|         } catch (e) { | ||||
|             // Error getting directory, there was an error downloading or we're in browser. Return online URL.
 | ||||
|             if (CoreApp.isOnline() && mainFile.fileurl) { | ||||
|                 // This URL is going to be injected in an iframe, we need this to make it work.
 | ||||
|                 return CoreSites.getCurrentSite()!.checkAndFixPluginfileURL(mainFile.fileurl); | ||||
|             } | ||||
| 
 | ||||
|             throw e; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Whether the resource has to be displayed embedded. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @param display The display mode (if available). | ||||
|      * @return Whether the resource should be displayed embeded. | ||||
|      */ | ||||
|     isDisplayedEmbedded(module: CoreCourseWSModule, display: number): boolean { | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
| 
 | ||||
|         if ((!module.contents.length && !module.contentsinfo) || | ||||
|             !CoreFile.isAvailable() || | ||||
|             (currentSite && !currentSite.isVersionGreaterEqualThan('3.7') && this.isNextcloudFile(module))) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         const ext = module.contentsinfo | ||||
|             ? CoreMimetypeUtils.getExtension(module.contentsinfo.mimetypes[0]) | ||||
|             : CoreMimetypeUtils.getFileExtension(module.contents[0].filename); | ||||
| 
 | ||||
|         return (display == CoreConstants.RESOURCELIB_DISPLAY_EMBED || display == CoreConstants.RESOURCELIB_DISPLAY_AUTO) && | ||||
|             CoreMimetypeUtils.canBeEmbedded(ext); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Whether the resource has to be displayed in an iframe. | ||||
|      * | ||||
|      * @param module The module object. | ||||
|      * @return Whether the resource should be displayed in an iframe. | ||||
|      */ | ||||
|     isDisplayedInIframe(module: CoreCourseAnyModuleData): boolean { | ||||
|         if (!CoreFile.isAvailable()) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         let mimetype: string | undefined; | ||||
| 
 | ||||
|         if ('contentsinfo' in module && module.contentsinfo) { | ||||
|             mimetype = module.contentsinfo.mimetypes[0]; | ||||
|         } else if (module.contents) { | ||||
|             const ext = CoreMimetypeUtils.getFileExtension(module.contents[0].filename); | ||||
|             mimetype = CoreMimetypeUtils.getMimeType(ext); | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return mimetype == 'text/html'; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if main file of resource is downloadable. | ||||
|      * | ||||
|      * @param module Module instance. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with boolean: whether main file is downloadable. | ||||
|      */ | ||||
|     isMainFileDownloadable(module: CoreCourseWSModule, siteId?: string): Promise<boolean> { | ||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
|         const mainFile = module.contents[0]; | ||||
|         const timemodified = CoreFileHelper.getFileTimemodified(mainFile); | ||||
| 
 | ||||
|         return CoreFilepool.isFileDownloadable(siteId, mainFile.fileurl, timemodified); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the resource is a Nextcloud file. | ||||
|      * | ||||
|      * @param module Module to check. | ||||
|      * @return Whether it's a Nextcloud file. | ||||
|      */ | ||||
|     isNextcloudFile(module: CoreCourseAnyModuleData): boolean { | ||||
|         if ('contentsinfo' in module && module.contentsinfo) { | ||||
|             return module.contentsinfo.repositorytype == 'nextcloud'; | ||||
|         } | ||||
| 
 | ||||
|         return !!(module.contents && module.contents[0] && module.contents[0].repositorytype == 'nextcloud'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Opens a file of the resource activity. | ||||
|      * | ||||
|      * @param module Module where to get the contents. | ||||
|      * @param courseId Course Id, used for completion purposes. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     async openModuleFile(module: CoreCourseWSModule, courseId: number): Promise<void> { | ||||
|         const modal = await CoreDomUtils.showModalLoading(); | ||||
| 
 | ||||
|         try { | ||||
|             // Download and open the file from the resource contents.
 | ||||
|             await CoreCourseHelper.downloadModuleAndOpenFile( | ||||
|                 module, | ||||
|                 courseId, | ||||
|                 AddonModResourceProvider.COMPONENT, | ||||
|                 module.id, | ||||
|                 module.contents, | ||||
|             ); | ||||
| 
 | ||||
|             try { | ||||
|                 await AddonModResource.logView(module.instance!, module.name); | ||||
|                 CoreCourse.checkModuleCompletion(courseId, module.completiondata); | ||||
|             } catch { | ||||
|                 // Ignore errors.
 | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'addon.mod_resource.errorwhileloadingthecontent', true); | ||||
|         } finally { | ||||
|             modal.dismiss(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResourceHelper = makeSingleton(AddonModResourceHelperProvider); | ||||
							
								
								
									
										241
									
								
								src/addons/mod/resource/services/resource.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								src/addons/mod/resource/services/resource.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| // (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 { CoreError } from '@classes/errors/error'; | ||||
| import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; | ||||
| import { CoreCourse } from '@features/course/services/course'; | ||||
| import { CoreCourseLogHelper } from '@features/course/services/log-helper'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| 
 | ||||
| const ROOT_CACHE_KEY = 'mmaModResource:'; | ||||
| 
 | ||||
| /** | ||||
|  * Service that provides some features for resources. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonModResourceProvider { | ||||
| 
 | ||||
|     static readonly COMPONENT = 'mmaModResource'; | ||||
| 
 | ||||
|     /** | ||||
|      * Get cache key for resource data WS calls. | ||||
|      * | ||||
|      * @param courseId Course ID. | ||||
|      * @return Cache key. | ||||
|      */ | ||||
|     protected getResourceCacheKey(courseId: number): string { | ||||
|         return ROOT_CACHE_KEY + 'resource:' + courseId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a resource data. | ||||
|      * | ||||
|      * @param courseId Course ID. | ||||
|      * @param key Name of the property to check. | ||||
|      * @param value Value to search. | ||||
|      * @param options Other options. | ||||
|      * @return Promise resolved when the resource is retrieved. | ||||
|      */ | ||||
|     protected async getResourceDataByKey( | ||||
|         courseId: number, | ||||
|         key: string, | ||||
|         value: number, | ||||
|         options: CoreSitesCommonWSOptions = {}, | ||||
|     ): Promise<AddonModResourceResource> { | ||||
|         const site = await CoreSites.getSite(options.siteId); | ||||
| 
 | ||||
|         const params: AddonModResourceGetResourcesByCoursesWSParams = { | ||||
|             courseids: [courseId], | ||||
|         }; | ||||
| 
 | ||||
|         const preSets: CoreSiteWSPreSets = { | ||||
|             cacheKey: this.getResourceCacheKey(courseId), | ||||
|             updateFrequency: CoreSite.FREQUENCY_RARELY, | ||||
|             component: AddonModResourceProvider.COMPONENT, | ||||
|             ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), | ||||
|         }; | ||||
| 
 | ||||
|         const response = await site.read<AddonModResourceGetResourcesByCoursesWSResponse>( | ||||
|             'mod_resource_get_resources_by_courses', | ||||
|             params, | ||||
|             preSets, | ||||
|         ); | ||||
| 
 | ||||
|         const currentResource = response.resources.find((resource) => resource[key] == value); | ||||
|         if (currentResource) { | ||||
|             return currentResource; | ||||
|         } | ||||
| 
 | ||||
|         throw new CoreError('Resource not found'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a resource by course module ID. | ||||
|      * | ||||
|      * @param courseId Course ID. | ||||
|      * @param cmId Course module ID. | ||||
|      * @param options Other options. | ||||
|      * @return Promise resolved when the resource is retrieved. | ||||
|      */ | ||||
|     getResourceData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModResourceResource> { | ||||
|         return this.getResourceDataByKey(courseId, 'coursemodule', cmId, options); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidate the prefetched content. | ||||
|      * | ||||
|      * @param moduleId The module ID. | ||||
|      * @param courseId Course ID of the module. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved when the data is invalidated. | ||||
|      */ | ||||
|     async invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise<void> { | ||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
|         const promises: Promise<void>[] = []; | ||||
| 
 | ||||
|         promises.push(this.invalidateResourceData(courseId, siteId)); | ||||
|         promises.push(CoreFilepool.invalidateFilesByComponent(siteId, AddonModResourceProvider.COMPONENT, moduleId)); | ||||
|         promises.push(CoreCourse.invalidateModule(moduleId, siteId, 'resource')); | ||||
| 
 | ||||
|         await CoreUtils.allPromises(promises); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidates resource data. | ||||
|      * | ||||
|      * @param courseid Course ID. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved when the data is invalidated. | ||||
|      */ | ||||
|     async invalidateResourceData(courseId: number, siteId?: string): Promise<void> { | ||||
|         const site = await CoreSites.getSite(siteId); | ||||
| 
 | ||||
|         await site.invalidateWsCacheForKey(this.getResourceCacheKey(courseId)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns whether or not getResource WS available or not. | ||||
|      * | ||||
|      * @return If WS is abalaible. | ||||
|      * @since 3.3 | ||||
|      */ | ||||
|     isGetResourceWSAvailable(): boolean { | ||||
|         return CoreSites.wsAvailableInCurrentSite('mod_resource_get_resources_by_courses'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return whether or not the plugin is enabled. | ||||
|      * | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. | ||||
|      */ | ||||
|     async isPluginEnabled(siteId?: string): Promise<boolean> { | ||||
|         const site = await CoreSites.getSite(siteId); | ||||
| 
 | ||||
|         return site.canDownloadFiles(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Report the resource as being viewed. | ||||
|      * | ||||
|      * @param id Module ID. | ||||
|      * @param name Name of the resource. | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved when the WS call is successful. | ||||
|      */ | ||||
|     async logView(id: number, name?: string, siteId?: string): Promise<void> { | ||||
|         const params: AddonModResourceViewResourceWSParams = { | ||||
|             resourceid: id, | ||||
|         }; | ||||
| 
 | ||||
|         await CoreCourseLogHelper.logSingle( | ||||
|             'mod_resource_view_resource', | ||||
|             params, | ||||
|             AddonModResourceProvider.COMPONENT, | ||||
|             id, | ||||
|             name, | ||||
|             'resource', | ||||
|             {}, | ||||
|             siteId, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| export const AddonModResource = makeSingleton(AddonModResourceProvider); | ||||
| 
 | ||||
| /** | ||||
|  * Params of mod_resource_view_resource WS. | ||||
|  */ | ||||
| type AddonModResourceViewResourceWSParams = { | ||||
|     resourceid: number; // Resource instance id.
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Resource returned by mod_resource_get_resources_by_courses. | ||||
|  */ | ||||
| export type AddonModResourceResource = { | ||||
|     id: number; // Module id.
 | ||||
|     coursemodule: number; // Course module id.
 | ||||
|     course: number; // Course id.
 | ||||
|     name: string; // Page name.
 | ||||
|     intro: string; // Summary.
 | ||||
|     introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | ||||
|     introfiles: CoreWSExternalFile[]; | ||||
|     contentfiles: CoreWSExternalFile[]; | ||||
|     tobemigrated: number; // Whether this resource was migrated.
 | ||||
|     legacyfiles: number; // Legacy files flag.
 | ||||
|     legacyfileslast: number; // Legacy files last control flag.
 | ||||
|     display: number; // How to display the resource.
 | ||||
|     displayoptions: string; // Display options (width, height).
 | ||||
|     filterfiles: number; // If filters should be applied to the resource content.
 | ||||
|     revision: number; // Incremented when after each file changes, to avoid cache.
 | ||||
|     timemodified: number; // Last time the resource was modified.
 | ||||
|     section: number; // Course section id.
 | ||||
|     visible: number; // Module visibility.
 | ||||
|     groupmode: number; // Group mode.
 | ||||
|     groupingid: number; // Grouping id.
 | ||||
| }; | ||||
| 
 | ||||
| export type AddonModResourceCustomData = { | ||||
|     showsize?: boolean; | ||||
|     filedetails?: { | ||||
|         size: number; | ||||
|         modifieddate: number; | ||||
|         uploadeddate: number; | ||||
|     }; | ||||
|     showtype?: boolean; | ||||
|     showdate?: boolean; | ||||
|     printintro?: boolean; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Params of mod_resource_get_resources_by_courses WS. | ||||
|  */ | ||||
| type AddonModResourceGetResourcesByCoursesWSParams = { | ||||
|     courseids?: number[]; // Array of course ids.
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Data returned by mod_resource_get_resources_by_courses WS. | ||||
|  */ | ||||
| type AddonModResourceGetResourcesByCoursesWSResponse = { | ||||
|     resources: AddonModResourceResource[]; | ||||
|     warnings?: CoreWSExternalWarning[]; | ||||
| }; | ||||
| @ -19,7 +19,7 @@ import { CoreApp } from '@services/app'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreWSExternalFile } from '@services/ws'; | ||||
| import { CoreCourse, CoreCourseWSModule } from '../services/course'; | ||||
| import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from '../services/course'; | ||||
| import { CoreCourseModulePrefetchHandlerBase } from './module-prefetch-handler'; | ||||
| 
 | ||||
| /** | ||||
| @ -178,7 +178,7 @@ export class CoreCourseResourcePrefetchHandlerBase extends CoreCourseModulePrefe | ||||
|      * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). | ||||
|      * @return Promise resolved when loaded. | ||||
|      */ | ||||
|     loadContents(module: CoreCourseWSModule, courseId: number, ignoreCache?: boolean): Promise<void> { | ||||
|     loadContents(module: CoreCourseAnyModuleData, courseId: number, ignoreCache?: boolean): Promise<void> { | ||||
|         return CoreCourse.loadModuleContents(module, courseId, undefined, false, ignoreCache); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1439,18 +1439,20 @@ export type CoreCourseModuleWSCompletionData = { | ||||
| }; | ||||
| 
 | ||||
| export type CoreCourseModuleContentFile = { | ||||
|     type: string; // A file or a folder or external link.
 | ||||
|     // Common properties with CoreWSExternalFile.
 | ||||
|     filename: string; // Filename.
 | ||||
|     filepath: string; // Filepath.
 | ||||
|     filesize: number; // Filesize.
 | ||||
|     fileurl: string; // Downloadable file url.
 | ||||
|     content?: string; // Raw content, will be used when type is content.
 | ||||
|     timecreated: number; // Time created.
 | ||||
|     timemodified: number; // Time modified.
 | ||||
|     sortorder: number; // Content sort order.
 | ||||
|     mimetype?: string; // File mime type.
 | ||||
|     isexternalfile?: number; // Whether is an external file.
 | ||||
|     repositorytype?: string; // The repository type for external files.
 | ||||
| 
 | ||||
|     type: string; // A file or a folder or external link.
 | ||||
|     content?: string; // Raw content, will be used when type is content.
 | ||||
|     timecreated: number; // Time created.
 | ||||
|     sortorder: number; // Content sort order.
 | ||||
|     userid: number; // User who added this content to moodle.
 | ||||
|     author: string; // Content owner.
 | ||||
|     license: string; // Content license.
 | ||||
|  | ||||
| @ -65,7 +65,7 @@ export class CoreMimetypeUtilsProvider { | ||||
|      * @param extension Extension. | ||||
|      * @return Whether it can be embedded. | ||||
|      */ | ||||
|     canBeEmbedded(extension: string): boolean { | ||||
|     canBeEmbedded(extension?: string): boolean { | ||||
|         return this.isExtensionInGroup(extension, ['web_image', 'web_video', 'web_audio']); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -238,11 +238,7 @@ export class CoreTextUtilsProvider { | ||||
|      * @param singleLine True if new lines should be removed (all the text in a single line). | ||||
|      * @return Clean text. | ||||
|      */ | ||||
|     cleanTags(text: string, singleLine?: boolean): string { | ||||
|         if (typeof text != 'string') { | ||||
|             return text; | ||||
|         } | ||||
| 
 | ||||
|     cleanTags(text: string | undefined, singleLine?: boolean): string { | ||||
|         if (!text) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
| @ -1077,46 +1077,14 @@ export type CoreWarningsWSResponse = { | ||||
|  * Structure of files returned by WS. | ||||
|  */ | ||||
| export type CoreWSExternalFile = { | ||||
|     /** | ||||
|      * Downloadable file url. | ||||
|      */ | ||||
|     fileurl: string; | ||||
| 
 | ||||
|     /** | ||||
|      * File name. | ||||
|      */ | ||||
|     filename?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * File path. | ||||
|      */ | ||||
|     filepath?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * File size. | ||||
|      */ | ||||
|     filesize?: number; | ||||
| 
 | ||||
|     /** | ||||
|      * Time modified. | ||||
|      */ | ||||
|     timemodified?: number; | ||||
| 
 | ||||
|     /** | ||||
|      * File mime type. | ||||
|      */ | ||||
|     mimetype?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * Whether is an external file. | ||||
|      */ | ||||
|     isexternalfile?: number; | ||||
| 
 | ||||
|     /** | ||||
|      * The repository type for external files. | ||||
|      */ | ||||
|     repositorytype?: string; | ||||
| 
 | ||||
|     fileurl: string; // Downloadable file url.
 | ||||
|     filename?: string; // File name.
 | ||||
|     filepath?: string; // File path.
 | ||||
|     filesize?: number; // File size.
 | ||||
|     timemodified?: number; // Time modified.
 | ||||
|     mimetype?: string; // File mime type.
 | ||||
|     isexternalfile?: number; // Whether is an external file.
 | ||||
|     repositorytype?: string; // The repository type for external files.
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user