| @ -2,6 +2,10 @@ ion-app.app-root addon-mod-forum-post .addon-mod_forum-post { | |||||||
|     background-color: $white; |     background-color: $white; | ||||||
|     border-bottom: 1px solid $list-md-border-color; |     border-bottom: 1px solid $list-md-border-color; | ||||||
| 
 | 
 | ||||||
|  |     @include darkmode() { | ||||||
|  |         background-color: $core-dark-item-bg-color; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     .addon-forum-star { |     .addon-forum-star { | ||||||
|         color: $core-star-color; |         color: $core-star-color; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ ion-app.app-root page-addon-mod-forum-discussion { | |||||||
|     .highlight .card-header .item { |     .highlight .card-header .item { | ||||||
|         background-color: $gray-lighter; |         background-color: $gray-lighter; | ||||||
|         @include darkmode() { |         @include darkmode() { | ||||||
|             background-color: $black; |             background-color: $gray-dark; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ import { CoreSitesProvider } from '@providers/sites'; | |||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||||
| import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; | import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; | ||||||
|  | import { CoreUserProvider } from '@core/user/providers/user'; | ||||||
| import { CoreSplitViewComponent } from '@components/split-view/split-view'; | import { CoreSplitViewComponent } from '@components/split-view/split-view'; | ||||||
| import { CoreRatingProvider, CoreRatingInfo } from '@core/rating/providers/rating'; | import { CoreRatingProvider, CoreRatingInfo } from '@core/rating/providers/rating'; | ||||||
| import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; | import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; | ||||||
| @ -57,7 +58,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { | |||||||
|     isOnline: boolean; |     isOnline: boolean; | ||||||
|     isSplitViewOn: boolean; |     isSplitViewOn: boolean; | ||||||
|     postHasOffline: boolean; |     postHasOffline: boolean; | ||||||
|     sort: SortType = 'flat-oldest'; |     sort: SortType = 'nested'; | ||||||
|     trackPosts: boolean; |     trackPosts: boolean; | ||||||
|     replyData = { |     replyData = { | ||||||
|         replyingTo: 0, |         replyingTo: 0, | ||||||
| @ -96,19 +97,20 @@ export class AddonModForumDiscussionPage implements OnDestroy { | |||||||
|     constructor(navParams: NavParams, |     constructor(navParams: NavParams, | ||||||
|             network: Network, |             network: Network, | ||||||
|             zone: NgZone, |             zone: NgZone, | ||||||
|             private appProvider: CoreAppProvider, |             protected appProvider: CoreAppProvider, | ||||||
|             private eventsProvider: CoreEventsProvider, |             protected eventsProvider: CoreEventsProvider, | ||||||
|             private sitesProvider: CoreSitesProvider, |             protected sitesProvider: CoreSitesProvider, | ||||||
|             private domUtils: CoreDomUtilsProvider, |             protected domUtils: CoreDomUtilsProvider, | ||||||
|             private utils: CoreUtilsProvider, |             protected utils: CoreUtilsProvider, | ||||||
|             private translate: TranslateService, |             protected translate: TranslateService, | ||||||
|             private uploaderProvider: CoreFileUploaderProvider, |             protected uploaderProvider: CoreFileUploaderProvider, | ||||||
|             private forumProvider: AddonModForumProvider, |             protected forumProvider: AddonModForumProvider, | ||||||
|             private forumOffline: AddonModForumOfflineProvider, |             protected forumOffline: AddonModForumOfflineProvider, | ||||||
|             private forumHelper: AddonModForumHelperProvider, |             protected forumHelper: AddonModForumHelperProvider, | ||||||
|             private forumSync: AddonModForumSyncProvider, |             protected forumSync: AddonModForumSyncProvider, | ||||||
|             private ratingOffline: CoreRatingOfflineProvider, |             protected ratingOffline: CoreRatingOfflineProvider, | ||||||
|             @Optional() private svComponent: CoreSplitViewComponent, |             protected userProvider: CoreUserProvider, | ||||||
|  |             @Optional() protected svComponent: CoreSplitViewComponent, | ||||||
|             protected navCtrl: NavController) { |             protected navCtrl: NavController) { | ||||||
|         this.courseId = navParams.get('courseId'); |         this.courseId = navParams.get('courseId'); | ||||||
|         this.cmId = navParams.get('cmId'); |         this.cmId = navParams.get('cmId'); | ||||||
| @ -134,7 +136,29 @@ export class AddonModForumDiscussionPage implements OnDestroy { | |||||||
|      * View loaded. |      * View loaded. | ||||||
|      */ |      */ | ||||||
|     ionViewDidLoad(): void { |     ionViewDidLoad(): void { | ||||||
|         this.sitesProvider.getCurrentSite().getLocalSiteConfig('AddonModForumDiscussionSort', this.sort).then((value) => { |         this.sitesProvider.getCurrentSite().getLocalSiteConfig('AddonModForumDiscussionSort').catch(() => { | ||||||
|  |             this.userProvider.getUserPreference('forum_displaymode').catch(() => { | ||||||
|  |                 // Ignore errors.
 | ||||||
|  |             }).then((value) => { | ||||||
|  |                 const sortValue = value && parseInt(value, 10); | ||||||
|  | 
 | ||||||
|  |                 switch (sortValue) { | ||||||
|  |                     case 1: | ||||||
|  |                         this.sort = 'flat-oldest'; | ||||||
|  |                         break; | ||||||
|  |                     case -1: | ||||||
|  |                         this.sort = 'flat-newest'; | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         this.sort = 'nested'; | ||||||
|  |                         break; | ||||||
|  |                     case 2: // Threaded not implemented.
 | ||||||
|  |                     default: | ||||||
|  |                         // Not set, use default sort.
 | ||||||
|  |                         // @TODO add fallback to $CFG->forum_displaymode.
 | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }).then((value) => { | ||||||
|             this.sort = value; |             this.sort = value; | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
|             this.fetchPosts(true, false, true).then(() => { |             this.fetchPosts(true, false, true).then(() => { | ||||||
|  | |||||||
| @ -51,8 +51,8 @@ | |||||||
|                     <h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.number}}</h3> |                     <h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.number}}</h3> | ||||||
|                     <p item-content *ngIf="attempt.grade != -1">{{ attempt.grade }}</p> |                     <p item-content *ngIf="attempt.grade != -1">{{ attempt.grade }}</p> | ||||||
|                     <p item-content *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</p> |                     <p item-content *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</p> | ||||||
|                     <p item-content *ngIf="scorm.maxattempt == 0 || attempt.number <= scorm.maxattempt">{{ 'addon.mod_scorm.offlineattemptnote' | translate }}</p> |                     <p *ngIf="scorm.maxattempt == 0 || attempt.number <= scorm.maxattempt">{{ 'addon.mod_scorm.offlineattemptnote' | translate }}</p> | ||||||
|                     <p item-content *ngIf="scorm.maxattempt != 0 && attempt.number > scorm.maxattempt">{{ 'addon.mod_scorm.offlineattemptovermax' | translate }}</p> |                     <p *ngIf="scorm.maxattempt != 0 && attempt.number > scorm.maxattempt">{{ 'addon.mod_scorm.offlineattemptovermax' | translate }}</p> | ||||||
|                 </ion-item> |                 </ion-item> | ||||||
|                 <ion-item text-wrap *ngIf="scorm.displayattemptstatus && scorm.gradeMethodReadable"> |                 <ion-item text-wrap *ngIf="scorm.displayattemptstatus && scorm.gradeMethodReadable"> | ||||||
|                     <h3>{{ 'addon.mod_scorm.grademethod' | translate }}</h3> |                     <h3>{{ 'addon.mod_scorm.grademethod' | translate }}</h3> | ||||||
| @ -95,9 +95,9 @@ | |||||||
|                     <!-- If data shown doesn't belong to last attempt, show a warning. --> |                     <!-- If data shown doesn't belong to last attempt, show a warning. --> | ||||||
|                     <p *ngIf="attemptToContinue">{{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}</p> |                     <p *ngIf="attemptToContinue">{{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}</p> | ||||||
|                     <p>{{ currentOrganization.title }}</p> |                     <p>{{ currentOrganization.title }}</p> | ||||||
|                     <div *ngFor="let sco of toc" class="core-padding-{{sco.level}}"> |                     <div *ngFor="let sco of toc" class="core-padding-{{sco.level}} addon-mod_scorm-type-{{sco.scormtype}}"> | ||||||
|                         <p *ngIf="sco.isvisible"> |                         <p *ngIf="sco.isvisible"> | ||||||
|                             <img [src]="sco.image.url" [alt]="sco.image.description" /> |                             <core-icon [name]="sco.image.icon" [label]="sco.image.description" item-start></core-icon> | ||||||
|                             <a *ngIf="sco.prereq && sco.launch" (click)="open($event, sco.id)"><core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text></a> |                             <a *ngIf="sco.prereq && sco.launch" (click)="open($event, sco.id)"><core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text></a> | ||||||
|                             <span *ngIf="!sco.prereq || !sco.launch"><core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text></span> |                             <span *ngIf="!sco.prereq || !sco.launch"><core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text></span> | ||||||
|                             <span *ngIf="accessInfo && accessInfo.canviewscores && sco.score_raw">({{ 'addon.mod_scorm.score' | translate }}: {{sco.score_raw}})</span> |                             <span *ngIf="accessInfo && accessInfo.canviewscores && sco.score_raw">({{ 'addon.mod_scorm.score' | translate }}: {{sco.score_raw}})</span> | ||||||
|  | |||||||
| @ -1,9 +1,13 @@ | |||||||
| ion-app.app-root addon-mod-scorm-index { | ion-app.app-root addon-mod-scorm-index, | ||||||
| 
 | ion-app.app-root page-addon-mod-scorm-toc { | ||||||
|     .addon-mod_scorm-toc { |     .addon-mod_scorm-toc { | ||||||
|         img { |         // Hide all non sco icons using css to maintain padding. | ||||||
|             width: auto; |         ion-icon { | ||||||
|             display: inline; |             opacity: 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .addon-mod_scorm-type-sco ion-icon { | ||||||
|  |             opacity: 1 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
| </ion-header> | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|     <nav> |     <nav> | ||||||
|         <ion-list> |         <ion-list class="addon-mod_scorm-toc"> | ||||||
|             <ion-item text-wrap *ngIf="attemptToContinue"> |             <ion-item text-wrap *ngIf="attemptToContinue"> | ||||||
|                 <p>{{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}</p> |                 <p>{{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}</p> | ||||||
|             </ion-item> |             </ion-item> | ||||||
| @ -23,8 +23,8 @@ | |||||||
| 
 | 
 | ||||||
|             <!-- List of SCOs. --> |             <!-- List of SCOs. --> | ||||||
|             <ng-container *ngFor="let sco of toc"> |             <ng-container *ngFor="let sco of toc"> | ||||||
|                 <a *ngIf="sco.isvisible" ion-item text-wrap [ngClass]="'core-padding-' + sco.level" [class.core-nav-item-selected]="selected == sco.id" (click)="loadSco(sco)" [attr.disabled]="!sco.prereq || !sco.launch ? true : null" [attr.detail-none]="!sco.prereq || !sco.launch ? true : null"> |                 <a *ngIf="sco.isvisible" ion-item text-wrap [ngClass]="'core-padding-' + sco.level + ' addon-mod_scorm-type-' + sco.scormtype" [class.core-nav-item-selected]="selected == sco.id" [class]="" (click)="loadSco(sco)" [attr.disabled]="!sco.prereq || !sco.launch ? true : null" [attr.detail-none]="!sco.prereq || !sco.launch ? true : null"> | ||||||
|                     <img [src]="sco.image.url" [alt]="sco.image.description" /> |                     <core-icon [name]="sco.image.icon" [label]="sco.image.description" item-start></core-icon> | ||||||
|                     <core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"></core-format-text> |                     <core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId"></core-format-text> | ||||||
|                     <span *ngIf="accessInfo && accessInfo.canviewscores && sco.score_raw">({{ 'addon.mod_scorm.score' | translate }}: {{sco.score_raw}})</span> |                     <span *ngIf="accessInfo && accessInfo.canviewscores && sco.score_raw">({{ 'addon.mod_scorm.score' | translate }}: {{sco.score_raw}})</span> | ||||||
|                 </a> |                 </a> | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; | |||||||
| import { IonicPageModule } from 'ionic-angular'; | import { IonicPageModule } from 'ionic-angular'; | ||||||
| import { TranslateModule } from '@ngx-translate/core'; | import { TranslateModule } from '@ngx-translate/core'; | ||||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | import { CoreDirectivesModule } from '@directives/directives.module'; | ||||||
|  | import { CoreComponentsModule } from '@components/components.module'; | ||||||
| import { AddonModScormTocPage } from './toc'; | import { AddonModScormTocPage } from './toc'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
| @ -24,6 +25,7 @@ import { AddonModScormTocPage } from './toc'; | |||||||
|     ], |     ], | ||||||
|     imports: [ |     imports: [ | ||||||
|         CoreDirectivesModule, |         CoreDirectivesModule, | ||||||
|  |         CoreComponentsModule, | ||||||
|         IonicPageModule.forChild(AddonModScormTocPage), |         IonicPageModule.forChild(AddonModScormTocPage), | ||||||
|         TranslateModule.forChild() |         TranslateModule.forChild() | ||||||
|     ], |     ], | ||||||
|  | |||||||
| @ -106,6 +106,22 @@ export class AddonModScormProvider { | |||||||
|         'b': 'browsed', |         'b': 'browsed', | ||||||
|         'n': 'notattempted' |         'n': 'notattempted' | ||||||
|     }; |     }; | ||||||
|  |     protected static STATUS_TO_ICON = { | ||||||
|  |         assetc: 'fa-file-archive-o', | ||||||
|  |         asset: 'fa-file-archive-o', | ||||||
|  |         browsed: 'fa-book', | ||||||
|  |         completed: 'fa-check-square-o', | ||||||
|  |         failed: 'fa-times', | ||||||
|  |         incomplete: 'fa-pencil-square-o', | ||||||
|  |         minus: 'fa-minus', | ||||||
|  |         notattempted: 'fa-square-o', | ||||||
|  |         passed: 'fa-check', | ||||||
|  |         plus: 'fa-plus', | ||||||
|  |         popdown: 'fa-window-close-o', | ||||||
|  |         popup: 'fa-window-restore', | ||||||
|  |         suspend: 'fa-pause', | ||||||
|  |         wait: 'fa-clock-o', | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     protected ROOT_CACHE_KEY = 'mmaModScorm:'; |     protected ROOT_CACHE_KEY = 'mmaModScorm:'; | ||||||
|     protected logger; |     protected logger; | ||||||
| @ -1048,17 +1064,21 @@ export class AddonModScormProvider { | |||||||
|      * @param incomplete Whether the SCORM is incomplete. |      * @param incomplete Whether the SCORM is incomplete. | ||||||
|      * @return Image URL and description. |      * @return Image URL and description. | ||||||
|      */ |      */ | ||||||
|     getScoStatusIcon(sco: any, incomplete?: boolean): {url: string, description: string} { |     getScoStatusIcon(sco: any, incomplete?: boolean): {icon: string, description: string} { | ||||||
|         let imageName = '', |         let imageName = '', | ||||||
|             descName = '', |             descName = '', | ||||||
|             status; |             suspendedStr = ''; | ||||||
| 
 | 
 | ||||||
|  |         const status = sco.status; | ||||||
|  | 
 | ||||||
|  |         if (sco.isvisible) { | ||||||
|  |             if (this.VALID_STATUSES.indexOf(status) >= 0) { | ||||||
|                 if (sco.scormtype == 'sco') { |                 if (sco.scormtype == 'sco') { | ||||||
|             // Not an asset, calculate image using status.
 |                     imageName = status; | ||||||
|             status = sco.status; |                     descName = status; | ||||||
|             if (this.VALID_STATUSES.indexOf(status) < 0) { |                 } else { | ||||||
|                 // Status empty or not valid, use 'notattempted'.
 |                     imageName = 'asset'; | ||||||
|                 status = 'notattempted'; |                     descName = 'assetlaunched'; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (!incomplete) { |                 if (!incomplete) { | ||||||
| @ -1068,19 +1088,32 @@ export class AddonModScormProvider { | |||||||
| 
 | 
 | ||||||
|                 if (incomplete && sco.exitvalue == 'suspend') { |                 if (incomplete && sco.exitvalue == 'suspend') { | ||||||
|                     imageName = 'suspend'; |                     imageName = 'suspend'; | ||||||
|                 descName = 'suspended'; |                     suspendedStr = ' - ' + this.translate.instant('addon.mod_scorm.suspended'); | ||||||
|             } else { |  | ||||||
|                 imageName = sco.status; |  | ||||||
|                 descName = sco.status; |  | ||||||
|                 } |                 } | ||||||
|  |             } else { | ||||||
|  |                 incomplete = true; | ||||||
|  | 
 | ||||||
|  |                 if (sco.scormtype == 'sco') { | ||||||
|  |                     // Status empty or not valid, use 'notattempted'.
 | ||||||
|  |                     imageName = 'notattempted'; | ||||||
|                 } else { |                 } else { | ||||||
|                     imageName = 'asset'; |                     imageName = 'asset'; | ||||||
|             descName = (!sco.status || sco.status == 'notattempted') ? 'asset' : 'assetlaunched'; |                 } | ||||||
|  |                 descName = imageName; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (imageName == '') { | ||||||
|  |             imageName = 'notattempted'; | ||||||
|  |             descName = 'notattempted'; | ||||||
|  |             suspendedStr = ''; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         sco.incomplete = incomplete; | ||||||
|  | 
 | ||||||
|         return { |         return { | ||||||
|             url: 'assets/img/scorm/' + imageName + '.gif', |             icon: AddonModScormProvider.STATUS_TO_ICON[imageName], | ||||||
|             description: this.translate.instant('addon.mod_scorm.' + descName) |             description: this.translate.instant('addon.mod_scorm.' + descName) + suspendedStr | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,8 +39,8 @@ | |||||||
|             <div *ngIf="question.multiArray && question.multiArray.length" [attr.padding-top]="index == 1"> |             <div *ngIf="question.multiArray && question.multiArray.length" [attr.padding-top]="index == 1"> | ||||||
|                 <h3 padding-horizontal>{{ question.text }}</h3> |                 <h3 padding-horizontal>{{ question.text }}</h3> | ||||||
|                 <ion-grid no-padding> |                 <ion-grid no-padding> | ||||||
|                     <ion-row no-padding align-items-center class="hidden-phone"> |                     <ion-row no-padding nowrap align-items-center class="hidden-phone"> | ||||||
|                         <ion-col col-7> |                         <ion-col col-6> | ||||||
|                             <div padding>{{ 'addon.mod_survey.responses' | translate }}</div> |                             <div padding>{{ 'addon.mod_survey.responses' | translate }}</div> | ||||||
|                         </ion-col> |                         </ion-col> | ||||||
|                         <ion-col text-center *ngFor="let option of question.optionsArray"> |                         <ion-col text-center *ngFor="let option of question.optionsArray"> | ||||||
| @ -56,7 +56,7 @@ | |||||||
|             <!-- Subquestion --> |             <!-- Subquestion --> | ||||||
|             <ion-grid no-padding *ngIf="question.parent !== 0" text-wrap [class.even]="isEven"> |             <ion-grid no-padding *ngIf="question.parent !== 0" text-wrap [class.even]="isEven"> | ||||||
|                 <ion-row no-padding nowrap align-items-center radio-group [(ngModel)]="answers[question.name]" [required]="question.required"> |                 <ion-row no-padding nowrap align-items-center radio-group [(ngModel)]="answers[question.name]" [required]="question.required"> | ||||||
|                     <ion-col col-7> |                     <ion-col col-6> | ||||||
|                         <ion-label padding-horizontal [core-mark-required]="question.required" id="addon-mod_survey-{{question.name}}"><strong>{{question.num}}.</strong> {{ question.text }}</ion-label> |                         <ion-label padding-horizontal [core-mark-required]="question.required" id="addon-mod_survey-{{question.name}}"><strong>{{question.num}}.</strong> {{ question.text }}</ion-label> | ||||||
|                     </ion-col> |                     </ion-col> | ||||||
| 
 | 
 | ||||||
| @ -77,10 +77,10 @@ | |||||||
|             <ng-container *ngIf="(!question.multiArray || question.multiArray.length == 0) && question.parent === 0"> |             <ng-container *ngIf="(!question.multiArray || question.multiArray.length == 0) && question.parent === 0"> | ||||||
|                 <ion-grid no-padding text-wrap *ngIf="question.type > 0" [class.even]="isEven"> |                 <ion-grid no-padding text-wrap *ngIf="question.type > 0" [class.even]="isEven"> | ||||||
|                     <ion-row no-padding align-items-center> |                     <ion-row no-padding align-items-center> | ||||||
|                         <ion-col col-7> |                         <ion-col col-6> | ||||||
|                             <ion-label [core-mark-required]="question.required" padding-horizontal id="addon-mod_survey-{{question.name}}"><strong>{{question.num}}.</strong> {{ question.text }}</ion-label> |                             <ion-label [core-mark-required]="question.required" padding-horizontal id="addon-mod_survey-{{question.name}}"><strong>{{question.num}}.</strong> {{ question.text }}</ion-label> | ||||||
|                         </ion-col> |                         </ion-col> | ||||||
|                         <ion-col col-5> |                         <ion-col col-6> | ||||||
|                             <ion-select padding [(ngModel)]="answers[question.name]" [attr.aria-labelledby]="'addon-mod_survey-'+question.name" interface="action-sheet" [required]="question.required"> |                             <ion-select padding [(ngModel)]="answers[question.name]" [attr.aria-labelledby]="'addon-mod_survey-'+question.name" interface="action-sheet" [required]="question.required"> | ||||||
|                                 <ion-option *ngFor="let option of question.optionsArray; let value=index;" [value]="value">{{option}}</ion-option> |                                 <ion-option *ngFor="let option of question.optionsArray; let value=index;" [value]="value">{{option}}</ion-option> | ||||||
|                             </ion-select> |                             </ion-select> | ||||||
|  | |||||||
| @ -163,13 +163,10 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo | |||||||
|      * @return If answers are valid |      * @return If answers are valid | ||||||
|      */ |      */ | ||||||
|     isValidResponse(): boolean { |     isValidResponse(): boolean { | ||||||
|         for (const x in this.answers) { |         return !this.questions.some((question) => { | ||||||
|             if (this.answers[x] === -1) { |             return question.required && question.name && | ||||||
|                 return false; |                 (question.type === 0 ? this.answers[question.name] == '' : parseInt(this.answers[question.name], 10) === -1); | ||||||
|             } |         }); | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -4,6 +4,11 @@ $addon-mod-wiki-toc-title-color:      $gray-darker !default; | |||||||
| $addon-mod-wiki-toc-border-color:     $gray-dark !default; | $addon-mod-wiki-toc-border-color:     $gray-dark !default; | ||||||
| $addon-mod-wiki-toc-background-color: $gray-light !default; | $addon-mod-wiki-toc-background-color: $gray-light !default; | ||||||
| 
 | 
 | ||||||
|  | $addon-mod-wiki-dark-toc-title-color:      $white !default; | ||||||
|  | $addon-mod-wiki-dark-toc-border-color:     $gray-dark !default; | ||||||
|  | $addon-mod-wiki-dark-toc-background-color: $gray-darker !default; | ||||||
|  | $addon-mod-wiki-dark-newentry-link-color: $red-light !default; | ||||||
|  | 
 | ||||||
| ion-app.app-root addon-mod-wiki-index { | ion-app.app-root addon-mod-wiki-index { | ||||||
|     background-color: $white; |     background-color: $white; | ||||||
|     @include darkmode() { |     @include darkmode() { | ||||||
| @ -28,6 +33,17 @@ ion-app.app-root addon-mod-wiki-index { | |||||||
|         a { |         a { | ||||||
|             color: $link-color; |             color: $link-color; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         @include darkmode() { | ||||||
|  |             border: 1px solid $addon-mod-wiki-dark-toc-border-color; | ||||||
|  |             background: $addon-mod-wiki-dark-toc-background-color; | ||||||
|  |             p { | ||||||
|  |                 color: $core-dark-text-color !important; | ||||||
|  |             } | ||||||
|  |             a { | ||||||
|  |                 color: $core-dark-link-color; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         margin: 16px; |         margin: 16px; | ||||||
|         padding: 8px; |         padding: 8px; | ||||||
|     } |     } | ||||||
| @ -55,6 +71,9 @@ ion-app.app-root addon-mod-wiki-index { | |||||||
|     .wiki_newentry { |     .wiki_newentry { | ||||||
|         color: $addon-mod-wiki-newentry-link-color; |         color: $addon-mod-wiki-newentry-link-color; | ||||||
|         font-style: italic; |         font-style: italic; | ||||||
|  |         @include darkmode() { | ||||||
|  |             color: $addon-mod-wiki-dark-newentry-link-color !important; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Hide edit section links */ |     /* Hide edit section links */ | ||||||
|  | |||||||
| @ -500,7 +500,7 @@ ion-app.app-root { | |||||||
|   @include darkmode() { |   @include darkmode() { | ||||||
|     ion-select.core-button-select, |     ion-select.core-button-select, | ||||||
|     .core-button-select { |     .core-button-select { | ||||||
|       background-color: $core-dark-item-divider-bg-color; |       background-color: $core-dark-item-bg-color; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|       &.select-md, |       &.select-md, | ||||||
| @ -509,7 +509,7 @@ ion-app.app-root { | |||||||
|       &.button-ios, |       &.button-ios, | ||||||
|       &.select-wp, |       &.select-wp, | ||||||
|       &.button-wp { |       &.button-wp { | ||||||
|         background: $core-dark-item-divider-bg-color; |         background: $core-dark-item-bg-color; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| Before Width: | Height: | Size: 178 B | 
| Before Width: | Height: | Size: 105 B | 
| Before Width: | Height: | Size: 190 B | 
| Before Width: | Height: | Size: 190 B | 
| Before Width: | Height: | Size: 597 B | 
| Before Width: | Height: | Size: 79 B | 
| Before Width: | Height: | Size: 190 B | 
| Before Width: | Height: | Size: 362 B | 
| @ -82,6 +82,7 @@ export class CoreIconComponent implements OnChanges, OnDestroy { | |||||||
|         !this.ariaLabel && this.newElement.setAttribute('aria-hidden', 'true'); |         !this.ariaLabel && this.newElement.setAttribute('aria-hidden', 'true'); | ||||||
|         !this.ariaLabel && this.newElement.setAttribute('role', 'presentation'); |         !this.ariaLabel && this.newElement.setAttribute('role', 'presentation'); | ||||||
|         this.ariaLabel && this.newElement.setAttribute('aria-label', this.ariaLabel); |         this.ariaLabel && this.newElement.setAttribute('aria-label', this.ariaLabel); | ||||||
|  |         this.ariaLabel && this.newElement.setAttribute('title', this.ariaLabel); | ||||||
| 
 | 
 | ||||||
|         const attrs = this.element.attributes; |         const attrs = this.element.attributes; | ||||||
|         for (let i = attrs.length - 1; i >= 0; i--) { |         for (let i = attrs.length - 1; i >= 0; i--) { | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ | |||||||
|         <core-loading [hideUntil]="loaded"> |         <core-loading [hideUntil]="loaded"> | ||||||
|             <!-- Section selector. --> |             <!-- Section selector. --> | ||||||
|             <core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> |             <core-dynamic-component [component]="sectionSelectorComponent" [data]="data"> | ||||||
|                 <div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal" [class.core-section-download]="downloadEnabled"> |                 <div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal core-button-selector-row" [class.core-section-download]="downloadEnabled"> | ||||||
|                     <button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button"> |                     <button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button"> | ||||||
|                         <core-icon name="fa-folder"></core-icon> |                         <core-icon name="fa-folder"></core-icon> | ||||||
|                         <span class="core-button-select-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span> |                         <span class="core-button-select-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span> | ||||||
|  | |||||||
| @ -64,6 +64,10 @@ ion-app.app-root core-course-format { | |||||||
|         @include padding(null, 0, null, null); |         @include padding(null, 0, null, null); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     .core-button-selector-row { | ||||||
|  |         @include safe-area-padding-start($content-padding !important, $content-padding); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     .core-course-section-nav-buttons { |     .core-course-section-nav-buttons { | ||||||
|         .button-inner core-format-text { |         .button-inner core-format-text { | ||||||
|             white-space: nowrap; |             white-space: nowrap; | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ export class ZipMock extends Zip { | |||||||
|             const destParent = destination.substring(0, source.lastIndexOf('/')), |             const destParent = destination.substring(0, source.lastIndexOf('/')), | ||||||
|                 destFolderName = destination.substr(source.lastIndexOf('/') + 1); |                 destFolderName = destination.substr(source.lastIndexOf('/') + 1); | ||||||
| 
 | 
 | ||||||
|             return this.file.createDir(destParent, destFolderName, false); |             return this.file.createDir(destParent, destFolderName, true); | ||||||
|         }).then(() => { |         }).then(() => { | ||||||
| 
 | 
 | ||||||
|             const promises = [], |             const promises = [], | ||||||
|  | |||||||
| @ -1017,7 +1017,7 @@ export class CoreFileProvider { | |||||||
|             return this.zip.unzip(fileEntry.toURL(), destFolder, onProgress); |             return this.zip.unzip(fileEntry.toURL(), destFolder, onProgress); | ||||||
|         }).then((result) => { |         }).then((result) => { | ||||||
|             if (result == -1) { |             if (result == -1) { | ||||||
|                 return Promise.reject(null); |                 return Promise.reject('Unzip failed.'); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,8 +3,8 @@ $core-dark-text-secondary-color: $gray-light !default; | |||||||
| $core-dark-link-color: $blue-light !default; | $core-dark-link-color: $blue-light !default; | ||||||
| 
 | 
 | ||||||
| $core-dark-item-bg-color: $gray-darker !default; | $core-dark-item-bg-color: $gray-darker !default; | ||||||
| $core-dark-item-divider-bg-color: $black !default; | $core-dark-item-divider-bg-color: $gray-dark !default; | ||||||
| $core-dark-background-color: $gray-dark !default; | $core-dark-background-color: $black !default; | ||||||
| 
 | 
 | ||||||
| // Login. | // Login. | ||||||
| $core-dark-login-page-background-color: radial-gradient(white, $gray-dark) !default; | $core-dark-login-page-background-color: radial-gradient(white, $gray-dark) !default; | ||||||
| @ -31,7 +31,8 @@ ion-app.app-root .ion-page { | |||||||
|     .item, |     .item, | ||||||
|     .item-select, |     .item-select, | ||||||
|     ion-card, |     ion-card, | ||||||
|     .card-header { |     .card-header, | ||||||
|  |     .card-content { | ||||||
|         color: $core-dark-text-color; |         color: $core-dark-text-color; | ||||||
|         background-color: $core-dark-item-bg-color; |         background-color: $core-dark-item-bg-color; | ||||||
| 
 | 
 | ||||||
|  | |||||||