forked from EVOgeek/Vmeda.Online
		
	
						commit
						8471b549d1
					
				| @ -6,9 +6,11 @@ | ||||
|         <div *ngFor="let item of items"> | ||||
|             <ion-card> | ||||
|                 <ion-item class="core-course-module-handler item-media ion-text-wrap" detail="false" (click)="action($event, item)" | ||||
|                     [title]="item.name" button> | ||||
|                     button> | ||||
|                     <img slot="start" [src]="item.iconUrl" alt="" role="presentation" *ngIf="item.iconUrl" class="core-module-icon"> | ||||
|                     <ion-label> | ||||
|                         <!-- Add the icon title so accessibility tools read it. --> | ||||
|                         <span class="sr-only" *ngIf="item.iconTitle">{{ item.iconTitle }}</span> | ||||
|                         <h2> | ||||
|                             <core-format-text [text]="item.name" contextLevel="module" [contextInstanceId]="item.cmid" | ||||
|                                 [courseId]="item.courseid"></core-format-text> | ||||
|  | ||||
| @ -56,6 +56,7 @@ export class AddonBlockRecentlyAccessedItemsProvider { | ||||
|             const modicon = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'src'); | ||||
| 
 | ||||
|             item.iconUrl = CoreCourse.getModuleIconSrc(item.modname, modicon || undefined); | ||||
|             item.iconTitle = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'title'); | ||||
| 
 | ||||
|             return item; | ||||
|         }); | ||||
| @ -99,4 +100,5 @@ export type AddonBlockRecentlyAccessedItemsItem = { | ||||
|  */ | ||||
| export type AddonBlockRecentlyAccessedItemsItemCalculatedData = { | ||||
|     iconUrl: string; // Icon URL. Calculated by the app.
 | ||||
|     iconTitle?: string | null; // Icon title.
 | ||||
| }; | ||||
|  | ||||
| @ -58,7 +58,7 @@ | ||||
|                             <ion-label position="stacked">{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label> | ||||
|                             <core-show-password name="password"> | ||||
|                                 <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                                     [core-auto-focus] #passwordinput [clearOnEdit]="false"> | ||||
|                                     [autofocus]="true" #passwordinput [clearOnEdit]="false"> | ||||
|                                 </ion-input> | ||||
|                             </core-show-password> | ||||
|                         </ion-item> | ||||
|  | ||||
| @ -424,7 +424,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo | ||||
|             pageId = continueLast ? this.accessInfo.lastpageseen : this.accessInfo.firstpageid; | ||||
|         } | ||||
| 
 | ||||
|         await CoreNavigator.navigate(`../player/${this.courseId}/${this.lesson.id}`, { | ||||
|         await CoreNavigator.navigate('player', { | ||||
|             params: { | ||||
|                 pageId: pageId, | ||||
|                 password: this.password, | ||||
| @ -472,7 +472,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         CoreNavigator.navigate(`../player/${this.courseId}/${this.lesson.id}`, { | ||||
|         CoreNavigator.navigate('player', { | ||||
|             params: { | ||||
|                 pageId: this.retakeToReview.pageid, | ||||
|                 password: this.password, | ||||
| @ -695,7 +695,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async openRetake(userId: number): Promise<void> { | ||||
|         await CoreNavigator.navigate(`../user-retake/${this.courseId}/${this.lesson!.id}`, { | ||||
|         await CoreNavigator.navigate('user-retake', { | ||||
|             params: { | ||||
|                 userId, | ||||
|             }, | ||||
|  | ||||
| @ -15,7 +15,8 @@ | ||||
|             <ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label> | ||||
|             <core-show-password name="password"> | ||||
|                 <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                     [core-auto-focus] #passwordinput [clearOnEdit]="false"></ion-input> | ||||
|                     [autofocus]="true" #passwordinput [clearOnEdit]="false"> | ||||
|                 </ion-input> | ||||
|             </core-show-password> | ||||
|         </ion-item> | ||||
|         <ion-button expand="block" type="submit"> | ||||
|  | ||||
| @ -26,11 +26,11 @@ const routes: Routes = [ | ||||
|         component: AddonModLessonIndexPage, | ||||
|     }, | ||||
|     { | ||||
|         path: 'player/:courseId/:lessonId', | ||||
|         path: ':courseId/:cmId/player', | ||||
|         loadChildren: () => import('./pages/player/player.module').then( m => m.AddonModLessonPlayerPageModule), | ||||
|     }, | ||||
|     { | ||||
|         path: 'user-retake/:courseId/:lessonId', | ||||
|         path: ':courseId/:cmId/user-retake', | ||||
|         loadChildren: () => import('./pages/user-retake/user-retake.module').then( m => m.AddonModLessonUserRetakePageModule), | ||||
|     }, | ||||
| ]; | ||||
|  | ||||
| @ -98,7 +98,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|     mediaFile?: CoreWSExternalFile; // Media file of the lesson.
 | ||||
|     activityLink?: AddonModLessonActivityLink; // Next activity link data.
 | ||||
| 
 | ||||
|     protected lessonId!: number; // Lesson ID.
 | ||||
|     protected cmId!: number; // Course module ID.
 | ||||
|     protected password?: string; // Lesson password (if any).
 | ||||
|     protected forceLeave = false; // If true, don't perform any check when leaving the view.
 | ||||
|     protected offline?: boolean; // Whether we are in offline mode.
 | ||||
| @ -118,22 +118,19 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         this.lessonId = CoreNavigator.getRouteNumberParam('lessonId')!; | ||||
|         this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; | ||||
|         this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; | ||||
|         this.password = CoreNavigator.getRouteParam('password'); | ||||
|         this.review = !!CoreNavigator.getRouteBooleanParam('review'); | ||||
|         this.currentPage = CoreNavigator.getRouteNumberParam('pageId'); | ||||
|         this.retakeToReview = CoreNavigator.getRouteNumberParam('retake'); | ||||
| 
 | ||||
|         // Block the lesson so it cannot be synced.
 | ||||
|         CoreSync.blockOperation(this.component, this.lessonId); | ||||
| 
 | ||||
|         try { | ||||
|             // Fetch the Lesson data.
 | ||||
|             const success = await this.fetchLessonData(); | ||||
|             if (success) { | ||||
|                 // Review data loaded or new retake started, remove any retake being finished in sync.
 | ||||
|                 AddonModLessonSync.deleteRetakeFinishedInSync(this.lessonId); | ||||
|                 AddonModLessonSync.deleteRetakeFinishedInSync(this.lesson!.id); | ||||
|             } | ||||
|         } finally { | ||||
|             this.loaded = true; | ||||
| @ -144,8 +141,10 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|      * Component being destroyed. | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         if (this.lesson) { | ||||
|             // Unblock the lesson so it can be synced.
 | ||||
|         CoreSync.unblockOperation(this.component, this.lessonId); | ||||
|             CoreSync.unblockOperation(this.component, this.lesson.id); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -214,7 +213,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
| 
 | ||||
|             // Get the possible jumps now.
 | ||||
|             this.jumps = await AddonModLesson.getPagesPossibleJumps(this.lesson!.id, { | ||||
|                 cmId: this.lesson!.coursemodule, | ||||
|                 cmId: this.cmId, | ||||
|                 readingStrategy: CoreSitesReadingStrategy.PreferCache, | ||||
|             }); | ||||
| 
 | ||||
| @ -259,14 +258,18 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|      */ | ||||
|     protected async fetchLessonData(): Promise<boolean> { | ||||
|         try { | ||||
|             // Wait for any ongoing sync to finish. We won't sync a lesson while it's being played.
 | ||||
|             await AddonModLessonSync.waitForSync(this.lessonId); | ||||
|             this.lesson = await AddonModLesson.getLesson(this.courseId, this.cmId); | ||||
| 
 | ||||
|             this.lesson = await AddonModLesson.getLessonById(this.courseId, this.lessonId); | ||||
|             this.title = this.lesson.name; // Temporary title.
 | ||||
| 
 | ||||
|             // Block the lesson so it cannot be synced.
 | ||||
|             CoreSync.blockOperation(this.component, this.lesson.id); | ||||
| 
 | ||||
|             // Wait for any ongoing sync to finish. We won't sync a lesson while it's being played.
 | ||||
|             await AddonModLessonSync.waitForSync(this.lesson.id); | ||||
| 
 | ||||
|             // If lesson has offline data already, use offline mode.
 | ||||
|             this.offline = await AddonModLessonOffline.hasOfflineData(this.lessonId); | ||||
|             this.offline = await AddonModLessonOffline.hasOfflineData(this.lesson.id); | ||||
| 
 | ||||
|             if (!this.offline && !CoreApp.isOnline() && AddonModLesson.isLessonOffline(this.lesson) && | ||||
|                 !this.review) { | ||||
| @ -275,7 +278,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|             } | ||||
| 
 | ||||
|             const options = { | ||||
|                 cmId: this.lesson.coursemodule, | ||||
|                 cmId: this.cmId, | ||||
|                 readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, | ||||
|             }; | ||||
|             this.accessInfo = await this.callFunction<AddonModLessonGetAccessInformationWSResponse>( | ||||
| @ -306,7 +309,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|                 // Lesson uses password, get the whole lesson object.
 | ||||
|                 const options = { | ||||
|                     password: this.password, | ||||
|                     cmId: this.lesson.coursemodule, | ||||
|                     cmId: this.cmId, | ||||
|                     readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, | ||||
|                 }; | ||||
|                 promises.push(this.callFunction<AddonModLessonLessonWSData>( | ||||
| @ -322,7 +325,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|             if (this.offline) { | ||||
|                 // Offline mode, get the list of possible jumps to allow navigation.
 | ||||
|                 promises.push(AddonModLesson.getPagesPossibleJumps(this.lesson.id, { | ||||
|                     cmId: this.lesson.coursemodule, | ||||
|                     cmId: this.cmId, | ||||
|                     readingStrategy: CoreSitesReadingStrategy.PreferCache, | ||||
|                 }).then((jumpList) => { | ||||
|                     this.jumps = jumpList; | ||||
| @ -344,7 +347,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
| 
 | ||||
|             if (this.review && this.retakeToReview && CoreUtils.isWebServiceError(error)) { | ||||
|                 // The user cannot review the retake. Unmark the retake as being finished in sync.
 | ||||
|                 await AddonModLessonSync.deleteRetakeFinishedInSync(this.lessonId); | ||||
|                 await AddonModLessonSync.deleteRetakeFinishedInSync(this.lesson!.id); | ||||
|             } | ||||
| 
 | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); | ||||
| @ -373,7 +376,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|             if (result?.warnings?.length) { | ||||
|                 // Some data was deleted. Check if the retake has changed.
 | ||||
|                 const info = await AddonModLesson.getAccessInformation(this.lesson!.id, { | ||||
|                     cmId: this.lesson!.coursemodule, | ||||
|                     cmId: this.cmId, | ||||
|                 }); | ||||
| 
 | ||||
|                 if (info.attemptscount != this.accessInfo!.attemptscount) { | ||||
| @ -485,7 +488,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|         if (this.lesson!.timelimit && !this.accessInfo!.canmanage) { | ||||
|             // Get the last lesson timer.
 | ||||
|             const timers = await AddonModLesson.getTimers(this.lesson!.id, { | ||||
|                 cmId: this.lesson!.coursemodule, | ||||
|                 cmId: this.cmId, | ||||
|                 readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, | ||||
|             }); | ||||
| 
 | ||||
| @ -510,12 +513,12 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|             this.loadingMenu = true; | ||||
|             const options = { | ||||
|                 password: this.password, | ||||
|                 cmId: this.lesson!.coursemodule, | ||||
|                 cmId: this.cmId, | ||||
|                 readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, | ||||
|             }; | ||||
| 
 | ||||
|             const pages = await this.callFunction<AddonModLessonGetPagesPageWSData[]>( | ||||
|                 AddonModLesson.getPages.bind(AddonModLesson.instance, this.lessonId, options), | ||||
|                 AddonModLesson.getPages.bind(AddonModLesson.instance, this.lesson!.id, options), | ||||
|                 options, | ||||
|             ); | ||||
| 
 | ||||
| @ -543,7 +546,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|             password: this.password, | ||||
|             review: this.review, | ||||
|             includeContents: true, | ||||
|             cmId: this.lesson!.coursemodule, | ||||
|             cmId: this.cmId, | ||||
|             readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, | ||||
|             accessInfo: this.accessInfo, | ||||
|             jumps: this.jumps, | ||||
| @ -638,15 +641,15 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy, CanLeave { | ||||
|                 // Lesson allows offline and the user changed some data in server. Update cached data.
 | ||||
|                 const retake = this.accessInfo!.attemptscount; | ||||
|                 const options = { | ||||
|                     cmId: this.lesson!.coursemodule, | ||||
|                     cmId: this.cmId, | ||||
|                     readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, | ||||
|                 }; | ||||
| 
 | ||||
|                 // Update in background the list of content pages viewed or question attempts.
 | ||||
|                 if (AddonModLesson.isQuestionPage(this.pageData?.page?.type || -1)) { | ||||
|                     AddonModLesson.getQuestionsAttemptsOnline(this.lessonId, retake, options); | ||||
|                     AddonModLesson.getQuestionsAttemptsOnline(this.lesson!.id, retake, options); | ||||
|                 } else { | ||||
|                     AddonModLesson.getContentPagesViewedOnline(this.lessonId, retake, options); | ||||
|                     AddonModLesson.getContentPagesViewedOnline(this.lesson!.id, retake, options); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -55,7 +55,7 @@ export class AddonModLessonUserRetakePage implements OnInit { | ||||
|     loaded?: boolean; // Whether the data has been loaded.
 | ||||
|     timeTakenReadable?: string; // Time taken in a readable format.
 | ||||
| 
 | ||||
|     protected lessonId!: number; // The lesson ID the retake belongs to.
 | ||||
|     protected cmId!: number; // The lesson ID the retake belongs to.
 | ||||
|     protected userId?: number; // User ID to see the retakes.
 | ||||
|     protected retakeNumber?: number; // Number of the initial retake to see.
 | ||||
|     protected previousSelectedRetake?: number; // To be able to detect the previous selected retake when it has changed.
 | ||||
| @ -64,7 +64,7 @@ export class AddonModLessonUserRetakePage implements OnInit { | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.lessonId = CoreNavigator.getRouteNumberParam('lessonId')!; | ||||
|         this.cmId = CoreNavigator.getRouteNumberParam('cmId')!; | ||||
|         this.courseId = CoreNavigator.getRouteNumberParam('courseId')!; | ||||
|         this.userId = CoreNavigator.getRouteNumberParam('userId') || CoreSites.getCurrentSiteUserId(); | ||||
|         this.retakeNumber = CoreNavigator.getRouteNumberParam('retake'); | ||||
| @ -111,11 +111,11 @@ export class AddonModLessonUserRetakePage implements OnInit { | ||||
|      */ | ||||
|     protected async fetchData(): Promise<void> { | ||||
|         try { | ||||
|             this.lesson = await AddonModLesson.getLessonById(this.courseId, this.lessonId); | ||||
|             this.lesson = await AddonModLesson.getLesson(this.courseId, this.cmId); | ||||
| 
 | ||||
|             // Get the retakes overview for all participants.
 | ||||
|             const data = await AddonModLesson.getRetakesOverview(this.lesson.id, { | ||||
|                 cmId: this.lesson.coursemodule, | ||||
|                 cmId: this.cmId, | ||||
|             }); | ||||
| 
 | ||||
|             // Search the student.
 | ||||
| @ -185,8 +185,8 @@ export class AddonModLessonUserRetakePage implements OnInit { | ||||
|     protected async setRetake(retakeNumber: number): Promise<void> { | ||||
|         this.selectedRetake = retakeNumber; | ||||
| 
 | ||||
|         const retakeData = await AddonModLesson.getUserRetake(this.lessonId, retakeNumber, { | ||||
|             cmId: this.lesson!.coursemodule, | ||||
|         const retakeData = await AddonModLesson.getUserRetake(this.lesson!.id, retakeNumber, { | ||||
|             cmId: this.cmId, | ||||
|             userId: this.userId, | ||||
|         }); | ||||
| 
 | ||||
|  | ||||
| @ -65,7 +65,7 @@ export class AddonModLessonGradeLinkHandlerService extends CoreContentLinksModul | ||||
|             if (accessInfo.canviewreports) { | ||||
|                 // User can view reports, go to view the report.
 | ||||
|                 CoreNavigator.navigateToSitePath( | ||||
|                     AddonModLessonModuleHandlerService.PAGE_NAME + `/user-retake/${courseId}/${module.instance}`, | ||||
|                     AddonModLessonModuleHandlerService.PAGE_NAME + `/${courseId}/${module.id}/user-retake`, | ||||
|                     { | ||||
|                         params: { userId: Number(params.userid) }, | ||||
|                         siteId, | ||||
|  | ||||
| @ -122,11 +122,9 @@ export class AddonModLessonReportLinkHandlerService extends CoreContentLinksHand | ||||
|      * | ||||
|      * @param moduleId Module ID. | ||||
|      * @param userId User ID. | ||||
|      * @param courseId Course ID. | ||||
|      * @param retake Retake to open. | ||||
|      * @param groupId Group ID. | ||||
|      * @param siteId Site ID. | ||||
|      * @param navCtrl The NavController to use to navigate. | ||||
|      * @param courseId Course ID. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async openUserRetake( | ||||
| @ -150,7 +148,7 @@ export class AddonModLessonReportLinkHandlerService extends CoreContentLinksHand | ||||
|             }; | ||||
| 
 | ||||
|             CoreNavigator.navigateToSitePath( | ||||
|                 AddonModLessonModuleHandlerService.PAGE_NAME + `/user-retake/${courseId}/${module.instance}`, | ||||
|                 AddonModLessonModuleHandlerService.PAGE_NAME + `/${courseId}/${module.id}/user-retake`, | ||||
|                 { params, siteId }, | ||||
|             ); | ||||
|         } catch (error) { | ||||
|  | ||||
| @ -1486,7 +1486,7 @@ export class AddonModLessonProvider { | ||||
| 
 | ||||
|         const response = await site.read<AddonModLessonGetLessonWSResponse>('mod_lesson_get_lesson', params, preSets); | ||||
| 
 | ||||
|         if (typeof response.lesson.ongoing != 'undefined') { | ||||
|         if (typeof response.lesson.ongoing == 'undefined') { | ||||
|             // Basic data not received, password is wrong. Remove stored password.
 | ||||
|             this.removeStoredPassword(lessonId, site.id); | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| 
 | ||||
|         <!-- Form to edit the file's name. --> | ||||
|         <ion-input type="text" name="filename" [placeholder]="'core.filename' | translate" autocapitalize="none" autocorrect="off" | ||||
|             (click)="$event.stopPropagation()" [core-auto-focus] [(ngModel)]="newFileName" *ngIf="editMode"> | ||||
|             (click)="$event.stopPropagation()" [autofocus]="true" [(ngModel)]="newFileName" *ngIf="editMode"> | ||||
|         </ion-input> | ||||
| 
 | ||||
|         <div class="buttons" slot="end" *ngIf="manage"> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <form #messageForm> | ||||
|     <textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows | ||||
|     <textarea class="core-send-message-input" [autofocus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows | ||||
|         [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" | ||||
|         (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" | ||||
|         aria-multiline="true"></textarea> | ||||
| @ -10,4 +10,3 @@ | ||||
|         </ion-button> | ||||
|     </ion-buttons> | ||||
| </form> | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,8 @@ import { CoreUtils } from '@services/utils/utils'; | ||||
|  * Directive to auto focus an element when a view is loaded. | ||||
|  * | ||||
|  * You can apply it conditionallity assigning it a boolean value: <ion-input [core-auto-focus]="{{showKeyboard}}"> | ||||
|  * | ||||
|  * @deprecated since 3.9.5. ion-input now supports an [autofocus] attribute, please use that one instead. | ||||
|  */ | ||||
| @Directive({ | ||||
|     selector: '[core-auto-focus]', | ||||
| @ -39,16 +41,7 @@ export class CoreAutoFocusDirective implements OnInit { | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         // @todo
 | ||||
|         // if (this.navCtrl.isTransitioning()) {
 | ||||
|         //     // Navigating to a new page. Wait for the transition to be over.
 | ||||
|         //     const subscription = this.navCtrl.viewDidEnter.subscribe(() => {
 | ||||
|         //         this.autoFocus();
 | ||||
|         //         subscription.unsubscribe();
 | ||||
|         //     });
 | ||||
|         // } else {
 | ||||
|         this.autoFocus(); | ||||
|         // }
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -21,8 +21,9 @@ | ||||
|                     [class.core-section-download]="downloadEnabled"> | ||||
|                     <ion-button class="core-button-select button-no-uppercase" (click)="showSectionSelector()" | ||||
|                         aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" | ||||
|                         id="core-course-section-button" expand="block"> <!-- @todo: attr.aria-label? --> | ||||
|                         <ion-icon name="fas-folder" slot="start"></ion-icon> | ||||
|                         id="core-course-section-button" expand="block"> | ||||
|                         <ion-icon name="fas-folder" slot="start" aria-hidden="true"></ion-icon> | ||||
|                         <span class="sr-only" *ngIf="selectedSection">{{ 'core.course.sections' | translate }}:</span> | ||||
|                         <span class="core-button-select-text"> | ||||
|                             <core-format-text *ngIf="selectedSection" [text]="selectedSection.name" contextLevel="course" | ||||
|                                 [contextInstanceId]="course?.id" [clean]="true" [singleLine]="true"> | ||||
|  | ||||
| @ -9,23 +9,25 @@ | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-list id="core-course-section-selector" role="menu"> | ||||
|     <ion-list id="core-course-section-selector"> | ||||
|         <ng-container *ngFor="let section of sections"> | ||||
|             <ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" class="ion-text-wrap" | ||||
|                 (click)="selectSection(section)" [class.core-selected-item]="selected?.id == section.id" | ||||
|                 [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false" role="menuitem" | ||||
|                 [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail="false" | ||||
|                 [attr.aria-hidden]="section.uservisible === false" button> | ||||
| 
 | ||||
|                 <ion-icon name="fas-folder" slot="start"></ion-icon> | ||||
|                 <ion-icon name="fas-folder" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 <ion-label> | ||||
|                     <h2><core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id"> | ||||
|                     </core-format-text></h2> | ||||
|                     <core-progress-bar *ngIf="section.progress >= 0" [progress]="section.progress"></core-progress-bar> | ||||
| 
 | ||||
|                     <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false" class="ion-text-wrap"> | ||||
|                     <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false" | ||||
|                         class="ion-text-wrap"> | ||||
|                         {{ 'core.course.hiddenfromstudents' | translate }} | ||||
|                     </ion-badge> | ||||
|                     <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible === false" class="ion-text-wrap"> | ||||
|                     <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible === false" | ||||
|                         class="ion-text-wrap"> | ||||
|                         {{ 'core.notavailable' | translate }} | ||||
|                     </ion-badge> | ||||
|                     <ion-badge color="secondary" *ngIf="section.availabilityinfo" class="ion-text-wrap"> | ||||
|  | ||||
| @ -11,7 +11,7 @@ | ||||
|             [class.core-course-with-buttons]="courseOptionMenuEnabled || (downloadCourseEnabled && showDownload)" | ||||
|             [class.core-course-with-spinner]="(downloadCourseEnabled && prefetchCourseData.icon == 'spinner') || showSpinner"> | ||||
|             <p *ngIf="course.categoryname || (course.displayname && course.shortname && course.fullname != course.displayname)" | ||||
|                 class="core-course-additional-info"> | ||||
|                 class="core-course-additional-info" aria-hidden="true"> | ||||
|                 <span *ngIf="course.categoryname" class="core-course-category"> | ||||
|                     <core-format-text [text]="course.categoryname"></core-format-text> | ||||
|                 </span> | ||||
| @ -24,7 +24,10 @@ | ||||
|                 </span> | ||||
|             </p> | ||||
|             <h2> | ||||
|                 <ion-icon name="fas-star" *ngIf="course.isfavourite"></ion-icon> | ||||
|                 <ion-icon name="fas-star" *ngIf="course.isfavourite" [attr.aria-label]="'core.courses.favourite' | translate"> | ||||
|                 </ion-icon> | ||||
|                 <span class="sr-only" *ngIf="course.isfavourite">{{ 'core.courses.aria:favourite' | translate }}</span> | ||||
|                 <span class="sr-only">{{ 'core.courses.aria:coursename' | translate }}</span> | ||||
|                 <core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="course.id"></core-format-text> | ||||
|             </h2> | ||||
|         </ion-label> | ||||
| @ -50,9 +53,10 @@ | ||||
|             </ion-button> | ||||
|         </div> | ||||
|     </ion-item> | ||||
|     <ion-item *ngIf="showAll && course.progress! >= 0 && course.completionusertracked !== false" | ||||
|         lines="none"> | ||||
|         <ion-label><core-progress-bar [progress]="course.progress"></core-progress-bar></ion-label> | ||||
|     <ion-item *ngIf="showAll && course.progress! >= 0 && course.completionusertracked !== false" lines="none"> | ||||
|         <ion-label> | ||||
|             <core-progress-bar [progress]="course.progress"></core-progress-bar> | ||||
|         </ion-label> | ||||
|     </ion-item> | ||||
|     <ng-content></ng-content> | ||||
| </ion-card> | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
|                     type="password" | ||||
|                     placeholder="{{ 'core.courses.password' | translate }}" | ||||
|                     [(ngModel)]="password" | ||||
|                     [core-auto-focus] | ||||
|                     [autofocus]="true" | ||||
|                     [clearOnEdit]="false"> | ||||
|                 </ion-input> | ||||
|             </core-show-password> | ||||
|  | ||||
| @ -1,6 +1,9 @@ | ||||
| { | ||||
|     "addtofavourites": "Star this course", | ||||
|     "allowguests": "This course allows guest users to enter", | ||||
|     "aria:coursename": "Course name", | ||||
|     "aria:courseprogress": "Course progress:", | ||||
|     "aria:favourite": "Course is starred", | ||||
|     "availablecourses": "Available courses", | ||||
|     "cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.", | ||||
|     "categories": "Course categories", | ||||
| @ -13,6 +16,7 @@ | ||||
|     "errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.", | ||||
|     "errorsearching": "An error occurred while searching.", | ||||
|     "errorselfenrol": "An error occurred while self enrolling.", | ||||
|     "favourite": "Starred course", | ||||
|     "filtermycourses": "Filter my courses", | ||||
|     "frontpage": "Front page", | ||||
|     "hidecourse": "Remove from view", | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|             <ion-item> | ||||
|                 <ion-label></ion-label> | ||||
|                 <ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" | ||||
|                     formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]="autoFocus"> | ||||
|                     formControlName="value" autocapitalize="none" autocorrect="off" [autofocus]="autoFocus"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|             <ion-button type="submit" class="ion-margin" expand="block" [disabled]="!myForm.valid"> | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
|                     <h2>{{ 'core.login.siteaddress' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|                 <ion-input name="url" type="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}" | ||||
|                     formControlName="siteUrl" [core-auto-focus]="showKeyboard && !showScanQR"> | ||||
|                     formControlName="siteUrl" [autofocus]="showKeyboard && !showScanQR"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|         </ng-container> | ||||
| @ -37,7 +37,7 @@ | ||||
|                     <h2>{{ 'core.login.siteaddress' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|                 <ion-input name="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}" formControlName="siteUrl" | ||||
|                     [core-auto-focus]="showKeyboard && !showScanQR" (ionChange)="searchSite($event, siteForm.value.siteUrl)"> | ||||
|                     [autofocus]="showKeyboard && !showScanQR" (ionChange)="searchSite($event, siteForm.value.siteUrl)"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
| 
 | ||||
| @ -49,7 +49,7 @@ | ||||
|                 </ion-item> | ||||
|                 <ion-item button *ngIf="enteredSiteUrl" (click)="connect($event, enteredSiteUrl.url)" | ||||
|                     [attr.aria-label]="'core.login.connect' | translate" detail-push class="core-login-entered-site"> | ||||
|                     <ion-thumbnail slot="start"> | ||||
|                     <ion-thumbnail slot="start" aria-hidden="true"> | ||||
|                         <ion-icon name="fas-pen"></ion-icon> | ||||
|                     </ion-thumbnail> | ||||
|                     <ion-label> | ||||
| @ -101,7 +101,8 @@ | ||||
| 
 | ||||
|     <ng-container *ngIf="showScanQR && !hasSites && !enteredSiteUrl"> | ||||
|         <div class="ion-text-center ion-padding ion-margin-top">{{ 'core.login.or' | translate }}</div> | ||||
|         <ion-button expand="block" color="light" class="ion-margin" lines="none" (click)="showInstructionsAndScanQR()"> | ||||
|         <ion-button expand="block" color="light" class="ion-margin" lines="none" (click)="showInstructionsAndScanQR()" | ||||
|             aria-haspopup="true"> | ||||
|             <ion-icon slot="start" name="fas-qrcode" aria-hidden="true"></ion-icon> | ||||
|             <ion-label>{{ 'core.scanqr' | translate }}</ion-label> | ||||
|         </ion-button> | ||||
| @ -109,7 +110,8 @@ | ||||
| 
 | ||||
|     <!-- Help. --> | ||||
|     <ion-list lines="none" class="ion-margin-top"> | ||||
|         <ion-item button class="ion-text-center ion-text-wrap core-login-need-help" (click)="showHelp()" detail="false"> | ||||
|         <ion-item button class="ion-text-center ion-text-wrap core-login-need-help" (click)="showHelp()" detail="false" | ||||
|             aria-haspopup="true"> | ||||
|             <ion-label>{{ 'core.needhelp' | translate }}</ion-label> | ||||
|         </ion-item> | ||||
|     </ion-list> | ||||
|  | ||||
| @ -13,7 +13,6 @@ | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <!-- @todo --> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <core-tabs-outlet *ngIf="tabs.length > 0" [selectedIndex]="selectedTab" [hideUntil]="loaded" [tabs]="tabs"> | ||||
|         </core-tabs-outlet> | ||||
|  | ||||
| @ -14,7 +14,6 @@ | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreMainMenuHomeDelegate } from '../home-delegate'; | ||||
| import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../mainmenu-delegate'; | ||||
| 
 | ||||
| /** | ||||
| @ -29,27 +28,14 @@ export class CoreMainMenuHomeHandlerService implements CoreMainMenuHandler { | ||||
|     priority = 1100; | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the handler is enabled on a site level. | ||||
|      * | ||||
|      * @return Whether or not the handler is enabled on a site level. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     isEnabled(): Promise<boolean> { | ||||
|         return this.isEnabledForSite(); | ||||
|     async isEnabled(): Promise<boolean> { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the handler is enabled on a certain site. | ||||
|      * | ||||
|      * @return Whether or not the handler is enabled on a site level. | ||||
|      */ | ||||
|     async isEnabledForSite(): Promise<boolean> { | ||||
|         return CoreMainMenuHomeDelegate.getHandlers().length > 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the data needed to render the handler. | ||||
|      * | ||||
|      * @return Data needed to render the handler. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getDisplayData(): CoreMainMenuHandlerData { | ||||
|         return { | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
|         <ion-item> | ||||
|             <ion-label></ion-label> | ||||
|             <ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" | ||||
|                 [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" | ||||
|                 [autocorrect]="autocorrect" [spellcheck]="spellcheck" [autofocus]="autoFocus" | ||||
|                 [disabled]="disabled" role="searchbox" (ionFocus)="focus($event)"> | ||||
|             </ion-input> | ||||
|             <ion-button slot="end" fill="clear" type="submit" size="small" [attr.aria-label]="searchLabel" | ||||
|  | ||||
| @ -655,7 +655,7 @@ export class CoreSitesProvider { | ||||
|      * @return Release number or empty. | ||||
|      */ | ||||
|     getReleaseNumber(rawRelease: string): string { | ||||
|         const matches = rawRelease.match(/^\d(\.\d(\.\d+)?)?/); | ||||
|         const matches = rawRelease.match(/^\d+(\.\d+(\.\d+)?)?/); | ||||
|         if (matches) { | ||||
|             return matches[0]; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										21
									
								
								src/theme/bootstrap.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/theme/bootstrap.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| // Text for accessibility, hidden from the view. | ||||
| .sr-only, .accesshide { | ||||
|     position: absolute; | ||||
|     width: 1px; | ||||
|     height: 1px; | ||||
|     padding: 0; | ||||
|     margin: -1px; | ||||
|     overflow: hidden; | ||||
|     clip: rect(0, 0, 0, 0); | ||||
|     white-space: nowrap; | ||||
|     border: 0; | ||||
| } | ||||
| 
 | ||||
| .sr-only-focusable:active, .sr-only-focusable:focus { | ||||
|     position: static; | ||||
|     width: auto; | ||||
|     height: auto; | ||||
|     overflow: visible; | ||||
|     clip: auto; | ||||
|     white-space: normal; | ||||
| } | ||||
| @ -436,14 +436,6 @@ ion-button.core-button-select { | ||||
|     background-color: var(--text-hightlight-background-color); | ||||
| } | ||||
| 
 | ||||
| // Text for accessibility, hidden from the view. | ||||
| .accesshide { | ||||
|     position: absolute; | ||||
|     left: -10000px; | ||||
|     font-weight: normal; | ||||
|     font-size: 1em; | ||||
| } | ||||
| 
 | ||||
| // Monospaced font. | ||||
| .core-monospaced { | ||||
|     font-family: Andale Mono,Monaco,Courier New,DejaVu Sans Mono,monospace; | ||||
|  | ||||
| @ -23,6 +23,9 @@ | ||||
| @import "./components/rubrics.scss"; | ||||
| @import "./components/mod-label.scss"; | ||||
| 
 | ||||
| /* Some styles from 3rd party libraries. */ | ||||
| @import "./bootstrap.scss"; | ||||
| 
 | ||||
| /* Core CSS required for Ionic components to work properly */ | ||||
| @import "~@ionic/angular/css/core.css"; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user