forked from EVOgeek/Vmeda.Online
		
	MOBILE-2975 blocks: Allow disabling blocks and always show courses
This commit is contained in:
		
							parent
							
								
									c6e01bfa66
								
							
						
					
					
						commit
						8723e5b684
					
				| @ -15,6 +15,7 @@ | |||||||
| import { CoreLoggerProvider } from '@providers/logger'; | import { CoreLoggerProvider } from '@providers/logger'; | ||||||
| import { CoreSitesProvider } from '@providers/sites'; | import { CoreSitesProvider } from '@providers/sites'; | ||||||
| import { CoreEventsProvider } from '@providers/events'; | import { CoreEventsProvider } from '@providers/events'; | ||||||
|  | import { CoreSite } from '@classes/site'; | ||||||
| 
 | 
 | ||||||
| export interface CoreDelegateHandler { | export interface CoreDelegateHandler { | ||||||
|     /** |     /** | ||||||
| @ -272,10 +273,10 @@ export class CoreDelegate { | |||||||
|      * Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name. |      * Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name. | ||||||
|      * |      * | ||||||
|      * @param  {CoreDelegateHandler} handler Handler to check. |      * @param  {CoreDelegateHandler} handler Handler to check. | ||||||
|      * @param  {any}                 site    Site to check. |      * @param  {CoreSite} site Site to check. | ||||||
|      * @return {boolean}                     Whether is enabled or disabled in site. |      * @return {boolean} Whether is enabled or disabled in site. | ||||||
|      */ |      */ | ||||||
|     protected isFeatureDisabled(handler: CoreDelegateHandler, site: any): boolean { |     protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean { | ||||||
|         return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name); |         return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events'; | |||||||
| import { CoreSitesProvider } from '@providers/sites'; | import { CoreSitesProvider } from '@providers/sites'; | ||||||
| import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; | import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; | ||||||
| import { CoreBlockDefaultHandler } from './default-block-handler'; | import { CoreBlockDefaultHandler } from './default-block-handler'; | ||||||
|  | import { CoreSite } from '@classes/site'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Interface that all blocks must implement. |  * Interface that all blocks must implement. | ||||||
| @ -87,6 +88,30 @@ export class CoreBlockDelegate extends CoreDelegate { | |||||||
|         super('CoreBlockDelegate', logger, sitesProvider, eventsProvider); |         super('CoreBlockDelegate', logger, sitesProvider, eventsProvider); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if blocks are disabled in a certain site. | ||||||
|  |      * | ||||||
|  |      * @param {CoreSite} [site] Site. If not defined, use current site. | ||||||
|  |      * @return {boolean} Whether it's disabled. | ||||||
|  |      */ | ||||||
|  |     areBlocksDisabledInSite(site?: CoreSite): boolean { | ||||||
|  |         site = site || this.sitesProvider.getCurrentSite(); | ||||||
|  | 
 | ||||||
|  |         return site.isFeatureDisabled('NoDelegate_SiteBlocks'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if blocks are disabled in a certain site. | ||||||
|  |      * | ||||||
|  |      * @param  {string} [siteId] Site Id. If not defined, use current site. | ||||||
|  |      * @return {Promise<boolean>}     Promise resolved with true if disabled, rejected or resolved with false otherwise. | ||||||
|  |      */ | ||||||
|  |     areBlocksDisabled(siteId?: string): Promise<boolean> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             return this.areBlocksDisabledInSite(site); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get the display data for a certain block. |      * Get the display data for a certain block. | ||||||
|      * |      * | ||||||
| @ -121,4 +146,15 @@ export class CoreBlockDelegate extends CoreDelegate { | |||||||
|     isBlockSupported(name: string): boolean { |     isBlockSupported(name: string): boolean { | ||||||
|         return this.hasHandler(name, true); |         return this.hasHandler(name, true); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name. | ||||||
|  |      * | ||||||
|  |      * @param  {CoreDelegateHandler} handler Handler to check. | ||||||
|  |      * @param  {CoreSite} site Site to check. | ||||||
|  |      * @return {boolean} Whether is enabled or disabled in site. | ||||||
|  |      */ | ||||||
|  |     protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean { | ||||||
|  |         return this.areBlocksDisabledInSite(site) || super.isFeatureDisabled(handler, site); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,15 +19,17 @@ import { TranslateModule } from '@ngx-translate/core'; | |||||||
| import { CoreComponentsModule } from '@components/components.module'; | import { CoreComponentsModule } from '@components/components.module'; | ||||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | import { CoreDirectivesModule } from '@directives/directives.module'; | ||||||
| import { CorePipesModule } from '@pipes/pipes.module'; | import { CorePipesModule } from '@pipes/pipes.module'; | ||||||
| import { CoreCoursesCourseProgressComponent } from '../components/course-progress/course-progress'; | import { CoreCoursesCourseProgressComponent } from './course-progress/course-progress'; | ||||||
| import { CoreCoursesCourseListItemComponent } from '../components/course-list-item/course-list-item'; | import { CoreCoursesCourseListItemComponent } from './course-list-item/course-list-item'; | ||||||
| import { CoreCoursesCourseOptionsMenuComponent } from '../components/course-options-menu/course-options-menu'; | import { CoreCoursesCourseOptionsMenuComponent } from './course-options-menu/course-options-menu'; | ||||||
|  | import { CoreCoursesMyCoursesComponent } from './my-courses/my-courses'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     declarations: [ |     declarations: [ | ||||||
|         CoreCoursesCourseProgressComponent, |         CoreCoursesCourseProgressComponent, | ||||||
|         CoreCoursesCourseListItemComponent, |         CoreCoursesCourseListItemComponent, | ||||||
|         CoreCoursesCourseOptionsMenuComponent |         CoreCoursesCourseOptionsMenuComponent, | ||||||
|  |         CoreCoursesMyCoursesComponent | ||||||
|     ], |     ], | ||||||
|     imports: [ |     imports: [ | ||||||
|         CommonModule, |         CommonModule, | ||||||
| @ -42,7 +44,8 @@ import { CoreCoursesCourseOptionsMenuComponent } from '../components/course-opti | |||||||
|     exports: [ |     exports: [ | ||||||
|         CoreCoursesCourseProgressComponent, |         CoreCoursesCourseProgressComponent, | ||||||
|         CoreCoursesCourseListItemComponent, |         CoreCoursesCourseListItemComponent, | ||||||
|         CoreCoursesCourseOptionsMenuComponent |         CoreCoursesCourseOptionsMenuComponent, | ||||||
|  |         CoreCoursesMyCoursesComponent | ||||||
|     ], |     ], | ||||||
|     entryComponents: [ |     entryComponents: [ | ||||||
|         CoreCoursesCourseOptionsMenuComponent |         CoreCoursesCourseOptionsMenuComponent | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								src/core/courses/components/my-courses/my-courses.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/core/courses/components/my-courses/my-courses.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | <core-loading [hideUntil]="coursesLoaded"> | ||||||
|  |     <ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate"> | ||||||
|  |     </ion-searchbar> | ||||||
|  |     <ion-grid no-padding> | ||||||
|  |         <ion-row no-padding> | ||||||
|  |             <ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch> | ||||||
|  |                 <core-courses-course-progress [course]="course" class="core-courseoverview" showAll="true"></core-courses-course-progress> | ||||||
|  |             </ion-col> | ||||||
|  |         </ion-row> | ||||||
|  |     </ion-grid> | ||||||
|  |     <core-empty-box *ngIf="!courses || !courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate"> | ||||||
|  |         <p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p> | ||||||
|  |     </core-empty-box> | ||||||
|  | </core-loading> | ||||||
							
								
								
									
										242
									
								
								src/core/courses/components/my-courses/my-courses.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								src/core/courses/components/my-courses/my-courses.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,242 @@ | |||||||
|  | // (C) Copyright 2015 Martin Dougiamas
 | ||||||
|  | //
 | ||||||
|  | // 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, OnDestroy, ViewChild } from '@angular/core'; | ||||||
|  | import { Searchbar } from 'ionic-angular'; | ||||||
|  | import { CoreEventsProvider } from '@providers/events'; | ||||||
|  | import { CoreSitesProvider } from '@providers/sites'; | ||||||
|  | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
|  | import { CoreCoursesProvider } from '../../providers/courses'; | ||||||
|  | import { CoreCoursesHelperProvider } from '../../providers/helper'; | ||||||
|  | import { CoreCourseHelperProvider } from '@core/course/providers/helper'; | ||||||
|  | import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Component that displays the list of courses the user is enrolled in. | ||||||
|  |  */ | ||||||
|  | @Component({ | ||||||
|  |     selector: 'core-courses-my-courses', | ||||||
|  |     templateUrl: 'my-courses.html', | ||||||
|  | }) | ||||||
|  | export class CoreCoursesMyCoursesComponent implements OnInit, OnDestroy { | ||||||
|  |     @ViewChild('searchbar') searchbar: Searchbar; | ||||||
|  | 
 | ||||||
|  |     courses: any[]; | ||||||
|  |     filteredCourses: any[]; | ||||||
|  |     searchEnabled: boolean; | ||||||
|  |     filter = ''; | ||||||
|  |     showFilter = false; | ||||||
|  |     coursesLoaded = false; | ||||||
|  |     prefetchCoursesData: any = {}; | ||||||
|  |     downloadAllCoursesEnabled: boolean; | ||||||
|  | 
 | ||||||
|  |     protected prefetchIconInitialized = false; | ||||||
|  |     protected myCoursesObserver; | ||||||
|  |     protected siteUpdatedObserver; | ||||||
|  |     protected isDestroyed = false; | ||||||
|  |     protected courseIds = ''; | ||||||
|  | 
 | ||||||
|  |     constructor(private coursesProvider: CoreCoursesProvider, | ||||||
|  |             private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider, | ||||||
|  |             private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, | ||||||
|  |             private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider) { } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Component being initialized. | ||||||
|  |      */ | ||||||
|  |     ngOnInit(): void { | ||||||
|  |         this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); | ||||||
|  |         this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); | ||||||
|  | 
 | ||||||
|  |         this.fetchCourses().finally(() => { | ||||||
|  |             this.coursesLoaded = true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { | ||||||
|  |             this.fetchCourses(); | ||||||
|  |         }, this.sitesProvider.getCurrentSiteId()); | ||||||
|  | 
 | ||||||
|  |         // Refresh the enabled flags if site is updated.
 | ||||||
|  |         this.siteUpdatedObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { | ||||||
|  |             const wasEnabled = this.downloadAllCoursesEnabled; | ||||||
|  | 
 | ||||||
|  |             this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); | ||||||
|  |             this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); | ||||||
|  | 
 | ||||||
|  |             if (!wasEnabled && this.downloadAllCoursesEnabled && this.coursesLoaded) { | ||||||
|  |                 // Download all courses is enabled now, initialize it.
 | ||||||
|  |                 this.initPrefetchCoursesIcon(); | ||||||
|  |             } | ||||||
|  |         }, this.sitesProvider.getCurrentSiteId()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch the user courses. | ||||||
|  |      * | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected fetchCourses(): Promise<any> { | ||||||
|  |         return this.coursesProvider.getUserCourses().then((courses) => { | ||||||
|  |             const promises = [], | ||||||
|  |                 courseIds = courses.map((course) => { | ||||||
|  |                 return course.id; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             this.courseIds = courseIds.join(','); | ||||||
|  | 
 | ||||||
|  |             promises.push(this.coursesHelper.loadCoursesExtraInfo(courses)); | ||||||
|  | 
 | ||||||
|  |             if (this.coursesProvider.canGetAdminAndNavOptions()) { | ||||||
|  |                 promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => { | ||||||
|  |                     courses.forEach((course) => { | ||||||
|  |                         course.navOptions = options.navOptions[course.id]; | ||||||
|  |                         course.admOptions = options.admOptions[course.id]; | ||||||
|  |                     }); | ||||||
|  |                 })); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return Promise.all(promises).then(() => { | ||||||
|  |                 this.courses = courses; | ||||||
|  |                 this.filteredCourses = this.courses; | ||||||
|  |                 this.filter = ''; | ||||||
|  | 
 | ||||||
|  |                 this.initPrefetchCoursesIcon(); | ||||||
|  |             }); | ||||||
|  |         }).catch((error) => { | ||||||
|  |             this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the courses. | ||||||
|  |      * | ||||||
|  |      * @param {any} refresher Refresher. | ||||||
|  |      */ | ||||||
|  |     refreshCourses(refresher: any): void { | ||||||
|  |         const promises = []; | ||||||
|  | 
 | ||||||
|  |         promises.push(this.coursesProvider.invalidateUserCourses()); | ||||||
|  |         promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); | ||||||
|  |         if (this.courseIds) { | ||||||
|  |             promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Promise.all(promises).finally(() => { | ||||||
|  | 
 | ||||||
|  |             this.prefetchIconInitialized = false; | ||||||
|  |             this.fetchCourses().finally(() => { | ||||||
|  |                 refresher.complete(); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Show or hide the filter. | ||||||
|  |      */ | ||||||
|  |     switchFilter(): void { | ||||||
|  |         this.filter = ''; | ||||||
|  |         this.showFilter = !this.showFilter; | ||||||
|  |         this.filteredCourses = this.courses; | ||||||
|  |         if (this.showFilter) { | ||||||
|  |             setTimeout(() => { | ||||||
|  |                 this.searchbar.setFocus(); | ||||||
|  |             }, 500); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The filter has changed. | ||||||
|  |      * | ||||||
|  |      * @param {any} Received Event. | ||||||
|  |      */ | ||||||
|  |     filterChanged(event: any): void { | ||||||
|  |         const newValue = event.target.value && event.target.value.trim().toLowerCase(); | ||||||
|  |         if (!newValue || !this.courses) { | ||||||
|  |             this.filteredCourses = this.courses; | ||||||
|  |         } else { | ||||||
|  |             // Use displayname if avalaible, or fullname if not.
 | ||||||
|  |             if (this.courses.length > 0 && typeof this.courses[0].displayname != 'undefined') { | ||||||
|  |                 this.filteredCourses = this.courses.filter((course) => { | ||||||
|  |                     return course.displayname.toLowerCase().indexOf(newValue) > -1; | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 this.filteredCourses = this.courses.filter((course) => { | ||||||
|  |                     return course.fullname.toLowerCase().indexOf(newValue) > -1; | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Prefetch all the courses. | ||||||
|  |      * | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     prefetchCourses(): Promise<any> { | ||||||
|  |         const initialIcon = this.prefetchCoursesData.icon; | ||||||
|  | 
 | ||||||
|  |         this.prefetchCoursesData.icon = 'spinner'; | ||||||
|  |         this.prefetchCoursesData.badge = ''; | ||||||
|  | 
 | ||||||
|  |         return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => { | ||||||
|  |             this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total; | ||||||
|  |         }).then(() => { | ||||||
|  |             this.prefetchCoursesData.icon = 'ion-android-refresh'; | ||||||
|  |         }).catch((error) => { | ||||||
|  |             if (!this.isDestroyed) { | ||||||
|  |                 this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); | ||||||
|  |                 this.prefetchCoursesData.icon = initialIcon; | ||||||
|  |             } | ||||||
|  |         }).finally(() => { | ||||||
|  |             this.prefetchCoursesData.badge = ''; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Initialize the prefetch icon for the list of courses. | ||||||
|  |      */ | ||||||
|  |     protected initPrefetchCoursesIcon(): void { | ||||||
|  |         if (this.prefetchIconInitialized || !this.downloadAllCoursesEnabled) { | ||||||
|  |             // Already initialized.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.prefetchIconInitialized = true; | ||||||
|  | 
 | ||||||
|  |         if (!this.courses || this.courses.length < 2) { | ||||||
|  |             // Not enough courses.
 | ||||||
|  |             this.prefetchCoursesData.icon = ''; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.courseHelper.determineCoursesStatus(this.courses).then((status) => { | ||||||
|  |             let icon = this.courseHelper.getCourseStatusIconAndTitleFromStatus(status).icon; | ||||||
|  |             if (icon == 'spinner') { | ||||||
|  |                 // It seems all courses are being downloaded, show a download button instead.
 | ||||||
|  |                 icon = 'cloud-download'; | ||||||
|  |             } | ||||||
|  |             this.prefetchCoursesData.icon = icon; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Page destroyed. | ||||||
|  |      */ | ||||||
|  |     ngOnDestroy(): void { | ||||||
|  |         this.isDestroyed = true; | ||||||
|  |         this.myCoursesObserver && this.myCoursesObserver.off(); | ||||||
|  |         this.siteUpdatedObserver && this.siteUpdatedObserver.off(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -7,7 +7,12 @@ | |||||||
|                 <ion-icon name="search"></ion-icon> |                 <ion-icon name="search"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|             <core-context-menu> |             <core-context-menu> | ||||||
|                 <core-context-menu-item *ngIf="downloadCourseEnabled || downloadCoursesEnabled" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item> |                 <!-- Action for dashboard and site home. --> | ||||||
|  |                 <core-context-menu-item *ngIf="(siteHomeEnabled || dashboardEnabled) && (downloadCourseEnabled || downloadCoursesEnabled)" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item> | ||||||
|  | 
 | ||||||
|  |                 <!-- Actions when both site home and dashboard are disabled. --> | ||||||
|  |                 <core-context-menu-item *ngIf="!siteHomeEnabled && !dashboardEnabled && mcComponent && mcComponent.downloadAllCoursesEnabled && mcComponent.courses && mcComponent.courses.length >= 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="mcComponent.prefetchCourses()" [iconAction]="mcComponent.prefetchCoursesData.icon" [closeOnClick]="false" [badge]="mcComponent.prefetchCoursesData.badge"></core-context-menu-item> | ||||||
|  |                 <core-context-menu-item *ngIf="!siteHomeEnabled && !dashboardEnabled && mcComponent && mcComponent.courses && mcComponent.courses.length > 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="mcComponent.switchFilter()" [iconAction]="'funnel'"></core-context-menu-item> | ||||||
|             </core-context-menu> |             </core-context-menu> | ||||||
|         </ion-buttons> |         </ion-buttons> | ||||||
|     </ion-navbar> |     </ion-navbar> | ||||||
| @ -46,5 +51,17 @@ | |||||||
|                 </ion-content> |                 </ion-content> | ||||||
|             </ng-template> |             </ng-template> | ||||||
|         </core-tab> |         </core-tab> | ||||||
|  | 
 | ||||||
|  |         <!-- Tab to display if both site home and dashboard are disabled. --> | ||||||
|  |         <core-tab [show]="!siteHomeEnabled && !dashboardEnabled" [title]="'core.courses.mymoodle' | translate"> | ||||||
|  |             <ng-template> | ||||||
|  |                 <ion-content> | ||||||
|  |                     <ion-refresher [enabled]="mcComponent && mcComponent.coursesLoaded" (ionRefresh)="refreshMyCourses($event)"> | ||||||
|  |                         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|  |                     </ion-refresher> | ||||||
|  |                     <core-courses-my-courses></core-courses-my-courses> | ||||||
|  |                 </ion-content> | ||||||
|  |             </ng-template> | ||||||
|  |         </core-tab> | ||||||
|     </core-tabs> |     </core-tabs> | ||||||
| </ion-content> | </ion-content> | ||||||
| @ -24,6 +24,7 @@ import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome'; | |||||||
| import { CoreSiteHomeIndexComponent } from '@core/sitehome/components/index/index'; | import { CoreSiteHomeIndexComponent } from '@core/sitehome/components/index/index'; | ||||||
| import { CoreCoursesProvider } from '../../providers/courses'; | import { CoreCoursesProvider } from '../../providers/courses'; | ||||||
| import { CoreCoursesDashboardProvider } from '../../providers/dashboard'; | import { CoreCoursesDashboardProvider } from '../../providers/dashboard'; | ||||||
|  | import { CoreCoursesMyCoursesComponent } from '../../components/my-courses/my-courses'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays the dashboard. |  * Page that displays the dashboard. | ||||||
| @ -37,6 +38,7 @@ export class CoreCoursesDashboardPage implements OnDestroy { | |||||||
|     @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; |     @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; | ||||||
|     @ViewChild(CoreSiteHomeIndexComponent) siteHomeComponent: CoreSiteHomeIndexComponent; |     @ViewChild(CoreSiteHomeIndexComponent) siteHomeComponent: CoreSiteHomeIndexComponent; | ||||||
|     @ViewChildren(CoreBlockComponent) blocksComponents: QueryList<CoreBlockComponent>; |     @ViewChildren(CoreBlockComponent) blocksComponents: QueryList<CoreBlockComponent>; | ||||||
|  |     @ViewChild(CoreCoursesMyCoursesComponent) mcComponent: CoreCoursesMyCoursesComponent; | ||||||
| 
 | 
 | ||||||
|     firstSelectedTab: number; |     firstSelectedTab: number; | ||||||
|     siteHomeEnabled = false; |     siteHomeEnabled = false; | ||||||
| @ -186,6 +188,26 @@ export class CoreCoursesDashboardPage implements OnDestroy { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the dashboard data and My Courses. | ||||||
|  |      * | ||||||
|  |      * @param {any} refresher Refresher. | ||||||
|  |      */ | ||||||
|  |     refreshMyCourses(refresher: any): void { | ||||||
|  |         // First of all, refresh dashboard blocks, maybe a new block was added and now we can display the dashboard.
 | ||||||
|  |         this.dashboardProvider.invalidateDashboardBlocks().finally(() => { | ||||||
|  |             return this.loadDashboardContent(); | ||||||
|  |         }).finally(() => { | ||||||
|  |             if (!this.dashboardEnabled) { | ||||||
|  |                 // Dashboard still not enabled. Refresh my courses.
 | ||||||
|  |                 this.mcComponent && this.mcComponent.refreshCourses(refresher); | ||||||
|  |             } else { | ||||||
|  |                 this.tabsComponent.selectTab(1); | ||||||
|  |                 refresher.complete(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Toggle download enabled. |      * Toggle download enabled. | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -3,33 +3,20 @@ | |||||||
|         <ion-title>{{ 'core.courses.mycourses' | translate }}</ion-title> |         <ion-title>{{ 'core.courses.mycourses' | translate }}</ion-title> | ||||||
| 
 | 
 | ||||||
|         <ion-buttons end> |         <ion-buttons end> | ||||||
|             <button *ngIf="searchEnabled" ion-button icon-only (click)="openSearch()" [attr.aria-label]="'core.courses.searchcourses' | translate"> |             <button *ngIf="mcComponent && mcComponent.searchEnabled" ion-button icon-only (click)="openSearch()" [attr.aria-label]="'core.courses.searchcourses' | translate"> | ||||||
|                 <ion-icon name="search"></ion-icon> |                 <ion-icon name="search"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|             <core-context-menu> |             <core-context-menu *ngIf="mcComponent"> | ||||||
|                 <core-context-menu-item [hidden]="!downloadAllCoursesEnabled || !courses || courses.length < 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="prefetchCourses()" [iconAction]="prefetchCoursesData.icon" [closeOnClick]="false" [badge]="prefetchCoursesData.badge"></core-context-menu-item> |                 <core-context-menu-item [hidden]="!mcComponent.downloadAllCoursesEnabled || !mcComponent.courses || mcComponent.courses.length < 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="mcComponent.prefetchCourses()" [iconAction]="mcComponent.prefetchCoursesData.icon" [closeOnClick]="false" [badge]="mcComponent.prefetchCoursesData.badge"></core-context-menu-item> | ||||||
|                 <core-context-menu-item [hidden]="!courses || courses.length <= 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="switchFilter()" [iconAction]="'funnel'"></core-context-menu-item> |                 <core-context-menu-item [hidden]="!mcComponent.courses || mcComponent.courses.length <= 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="mcComponent.switchFilter()" [iconAction]="'funnel'"></core-context-menu-item> | ||||||
|             </core-context-menu> |             </core-context-menu> | ||||||
|         </ion-buttons> |         </ion-buttons> | ||||||
|     </ion-navbar> |     </ion-navbar> | ||||||
| </ion-header> | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|     <ion-refresher [enabled]="coursesLoaded" (ionRefresh)="refreshCourses($event)"> |     <ion-refresher [enabled]="mcComponent && mcComponent.coursesLoaded" (ionRefresh)="mcComponent.refreshCourses($event)"> | ||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
| 
 | 
 | ||||||
|     <core-loading [hideUntil]="coursesLoaded"> |     <core-courses-my-courses></core-courses-my-courses> | ||||||
|         <ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate"> |  | ||||||
|         </ion-searchbar> |  | ||||||
|         <ion-grid no-padding> |  | ||||||
|             <ion-row no-padding> |  | ||||||
|                 <ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch> |  | ||||||
|                     <core-courses-course-progress [course]="course" class="core-courseoverview" showAll="true"></core-courses-course-progress> |  | ||||||
|                 </ion-col> |  | ||||||
|             </ion-row> |  | ||||||
|         </ion-grid> |  | ||||||
|         <core-empty-box *ngIf="!courses || !courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate"> |  | ||||||
|             <p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p> |  | ||||||
|         </core-empty-box> |  | ||||||
|     </core-loading> |  | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -12,15 +12,9 @@ | |||||||
| // 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 { Component, OnDestroy, ViewChild } from '@angular/core'; | import { Component, ViewChild } from '@angular/core'; | ||||||
| import { IonicPage, Searchbar, NavController } from 'ionic-angular'; | import { IonicPage, NavController } from 'ionic-angular'; | ||||||
| import { CoreEventsProvider } from '@providers/events'; | import { CoreCoursesMyCoursesComponent } from '../../components/my-courses/my-courses'; | ||||||
| import { CoreSitesProvider } from '@providers/sites'; |  | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; |  | ||||||
| import { CoreCoursesProvider } from '../../providers/courses'; |  | ||||||
| import { CoreCoursesHelperProvider } from '../../providers/helper'; |  | ||||||
| import { CoreCourseHelperProvider } from '@core/course/providers/helper'; |  | ||||||
| import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that displays the list of courses the user is enrolled in. |  * Page that displays the list of courses the user is enrolled in. | ||||||
| @ -30,131 +24,10 @@ import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delega | |||||||
|     selector: 'page-core-courses-my-courses', |     selector: 'page-core-courses-my-courses', | ||||||
|     templateUrl: 'my-courses.html', |     templateUrl: 'my-courses.html', | ||||||
| }) | }) | ||||||
| export class CoreCoursesMyCoursesPage implements OnDestroy { | export class CoreCoursesMyCoursesPage { | ||||||
|     @ViewChild('searchbar') searchbar: Searchbar; |     @ViewChild(CoreCoursesMyCoursesComponent) mcComponent: CoreCoursesMyCoursesComponent; | ||||||
| 
 | 
 | ||||||
|     courses: any[]; |     constructor(private navCtrl: NavController) { } | ||||||
|     filteredCourses: any[]; |  | ||||||
|     searchEnabled: boolean; |  | ||||||
|     filter = ''; |  | ||||||
|     showFilter = false; |  | ||||||
|     coursesLoaded = false; |  | ||||||
|     prefetchCoursesData: any = {}; |  | ||||||
|     downloadAllCoursesEnabled: boolean; |  | ||||||
| 
 |  | ||||||
|     protected prefetchIconInitialized = false; |  | ||||||
|     protected myCoursesObserver; |  | ||||||
|     protected siteUpdatedObserver; |  | ||||||
|     protected isDestroyed = false; |  | ||||||
|     protected courseIds = ''; |  | ||||||
| 
 |  | ||||||
|     constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, |  | ||||||
|             private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider, |  | ||||||
|             private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, |  | ||||||
|             private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider) { } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * View loaded. |  | ||||||
|      */ |  | ||||||
|     ionViewDidLoad(): void { |  | ||||||
|         this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); |  | ||||||
|         this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); |  | ||||||
| 
 |  | ||||||
|         this.fetchCourses().finally(() => { |  | ||||||
|             this.coursesLoaded = true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { |  | ||||||
|             this.fetchCourses(); |  | ||||||
|         }, this.sitesProvider.getCurrentSiteId()); |  | ||||||
| 
 |  | ||||||
|         // Refresh the enabled flags if site is updated.
 |  | ||||||
|         this.siteUpdatedObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => { |  | ||||||
|             const wasEnabled = this.downloadAllCoursesEnabled; |  | ||||||
| 
 |  | ||||||
|             this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); |  | ||||||
|             this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite(); |  | ||||||
| 
 |  | ||||||
|             if (!wasEnabled && this.downloadAllCoursesEnabled && this.coursesLoaded) { |  | ||||||
|                 // Download all courses is enabled now, initialize it.
 |  | ||||||
|                 this.initPrefetchCoursesIcon(); |  | ||||||
|             } |  | ||||||
|         }, this.sitesProvider.getCurrentSiteId()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Fetch the user courses. |  | ||||||
|      * |  | ||||||
|      * @return {Promise<any>} Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     protected fetchCourses(): Promise<any> { |  | ||||||
|         return this.coursesProvider.getUserCourses().then((courses) => { |  | ||||||
|             const promises = [], |  | ||||||
|                 courseIds = courses.map((course) => { |  | ||||||
|                 return course.id; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             this.courseIds = courseIds.join(','); |  | ||||||
| 
 |  | ||||||
|             promises.push(this.coursesHelper.loadCoursesExtraInfo(courses)); |  | ||||||
| 
 |  | ||||||
|             if (this.coursesProvider.canGetAdminAndNavOptions()) { |  | ||||||
|                 promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => { |  | ||||||
|                     courses.forEach((course) => { |  | ||||||
|                         course.navOptions = options.navOptions[course.id]; |  | ||||||
|                         course.admOptions = options.admOptions[course.id]; |  | ||||||
|                     }); |  | ||||||
|                 })); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return Promise.all(promises).then(() => { |  | ||||||
|                 this.courses = courses; |  | ||||||
|                 this.filteredCourses = this.courses; |  | ||||||
|                 this.filter = ''; |  | ||||||
| 
 |  | ||||||
|                 this.initPrefetchCoursesIcon(); |  | ||||||
|             }); |  | ||||||
|         }).catch((error) => { |  | ||||||
|             this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Refresh the courses. |  | ||||||
|      * |  | ||||||
|      * @param {any} refresher Refresher. |  | ||||||
|      */ |  | ||||||
|     refreshCourses(refresher: any): void { |  | ||||||
|         const promises = []; |  | ||||||
| 
 |  | ||||||
|         promises.push(this.coursesProvider.invalidateUserCourses()); |  | ||||||
|         promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); |  | ||||||
|         if (this.courseIds) { |  | ||||||
|             promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Promise.all(promises).finally(() => { |  | ||||||
| 
 |  | ||||||
|             this.prefetchIconInitialized = false; |  | ||||||
|             this.fetchCourses().finally(() => { |  | ||||||
|                 refresher.complete(); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Show or hide the filter. |  | ||||||
|      */ |  | ||||||
|     switchFilter(): void { |  | ||||||
|         this.filter = ''; |  | ||||||
|         this.showFilter = !this.showFilter; |  | ||||||
|         this.filteredCourses = this.courses; |  | ||||||
|         if (this.showFilter) { |  | ||||||
|             setTimeout(() => { |  | ||||||
|                 this.searchbar.setFocus(); |  | ||||||
|             }, 500); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Go to search courses. |      * Go to search courses. | ||||||
| @ -162,89 +35,4 @@ export class CoreCoursesMyCoursesPage implements OnDestroy { | |||||||
|     openSearch(): void { |     openSearch(): void { | ||||||
|         this.navCtrl.push('CoreCoursesSearchPage'); |         this.navCtrl.push('CoreCoursesSearchPage'); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * The filter has changed. |  | ||||||
|      * |  | ||||||
|      * @param {any} Received Event. |  | ||||||
|      */ |  | ||||||
|     filterChanged(event: any): void { |  | ||||||
|         const newValue = event.target.value && event.target.value.trim().toLowerCase(); |  | ||||||
|         if (!newValue || !this.courses) { |  | ||||||
|             this.filteredCourses = this.courses; |  | ||||||
|         } else { |  | ||||||
|             // Use displayname if avalaible, or fullname if not.
 |  | ||||||
|             if (this.courses.length > 0 && typeof this.courses[0].displayname != 'undefined') { |  | ||||||
|                 this.filteredCourses = this.courses.filter((course) => { |  | ||||||
|                     return course.displayname.toLowerCase().indexOf(newValue) > -1; |  | ||||||
|                 }); |  | ||||||
|             } else { |  | ||||||
|                 this.filteredCourses = this.courses.filter((course) => { |  | ||||||
|                     return course.fullname.toLowerCase().indexOf(newValue) > -1; |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Prefetch all the courses. |  | ||||||
|      * |  | ||||||
|      * @return {Promise<any>} Promise resolved when done. |  | ||||||
|      */ |  | ||||||
|     prefetchCourses(): Promise<any> { |  | ||||||
|         const initialIcon = this.prefetchCoursesData.icon; |  | ||||||
| 
 |  | ||||||
|         this.prefetchCoursesData.icon = 'spinner'; |  | ||||||
|         this.prefetchCoursesData.badge = ''; |  | ||||||
| 
 |  | ||||||
|         return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => { |  | ||||||
|             this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total; |  | ||||||
|         }).then(() => { |  | ||||||
|             this.prefetchCoursesData.icon = 'ion-android-refresh'; |  | ||||||
|         }).catch((error) => { |  | ||||||
|             if (!this.isDestroyed) { |  | ||||||
|                 this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); |  | ||||||
|                 this.prefetchCoursesData.icon = initialIcon; |  | ||||||
|             } |  | ||||||
|         }).finally(() => { |  | ||||||
|             this.prefetchCoursesData.badge = ''; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initialize the prefetch icon for the list of courses. |  | ||||||
|      */ |  | ||||||
|     protected initPrefetchCoursesIcon(): void { |  | ||||||
|         if (this.prefetchIconInitialized || !this.downloadAllCoursesEnabled) { |  | ||||||
|             // Already initialized.
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.prefetchIconInitialized = true; |  | ||||||
| 
 |  | ||||||
|         if (!this.courses || this.courses.length < 2) { |  | ||||||
|             // Not enough courses.
 |  | ||||||
|             this.prefetchCoursesData.icon = ''; |  | ||||||
| 
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.courseHelper.determineCoursesStatus(this.courses).then((status) => { |  | ||||||
|             let icon = this.courseHelper.getCourseStatusIconAndTitleFromStatus(status).icon; |  | ||||||
|             if (icon == 'spinner') { |  | ||||||
|                 // It seems all courses are being downloaded, show a download button instead.
 |  | ||||||
|                 icon = 'cloud-download'; |  | ||||||
|             } |  | ||||||
|             this.prefetchCoursesData.icon = icon; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Page destroyed. |  | ||||||
|      */ |  | ||||||
|     ngOnDestroy(): void { |  | ||||||
|         this.isDestroyed = true; |  | ||||||
|         this.myCoursesObserver && this.myCoursesObserver.off(); |  | ||||||
|         this.siteUpdatedObserver && this.siteUpdatedObserver.off(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user