MOBILE-3413 h5pactivity: Implement viewing attempt results
This commit is contained in:
		
							parent
							
								
									22caea43ba
								
							
						
					
					
						commit
						b48a27e656
					
				| @ -659,12 +659,39 @@ | |||||||
|   "addon.mod_glossary.noentriesfound": "local_moodlemobileapp", |   "addon.mod_glossary.noentriesfound": "local_moodlemobileapp", | ||||||
|   "addon.mod_glossary.searchquery": "local_moodlemobileapp", |   "addon.mod_glossary.searchquery": "local_moodlemobileapp", | ||||||
|   "addon.mod_glossary.tagarea_glossary_entries": "glossary", |   "addon.mod_glossary.tagarea_glossary_entries": "glossary", | ||||||
|  |   "addon.mod_h5pactivity.all_attempts": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.answer_checked": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.answer_correct": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.answer_fail": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.answer_incorrect": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.answer_pass": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt_completion_no": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt_completion_yes": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt_success_fail": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt_success_pass": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempt_success_unknown": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.attempts_none": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.completion": "h5pactivity", | ||||||
|   "addon.mod_h5pactivity.downloadh5pfile": "local_moodlemobileapp", |   "addon.mod_h5pactivity.downloadh5pfile": "local_moodlemobileapp", | ||||||
|  |   "addon.mod_h5pactivity.duration": "h5pactivity", | ||||||
|   "addon.mod_h5pactivity.errorgetactivity": "local_moodlemobileapp", |   "addon.mod_h5pactivity.errorgetactivity": "local_moodlemobileapp", | ||||||
|   "addon.mod_h5pactivity.filestatenotdownloaded": "local_moodlemobileapp", |   "addon.mod_h5pactivity.filestatenotdownloaded": "local_moodlemobileapp", | ||||||
|   "addon.mod_h5pactivity.filestateoutdated": "local_moodlemobileapp", |   "addon.mod_h5pactivity.filestateoutdated": "local_moodlemobileapp", | ||||||
|  |   "addon.mod_h5pactivity.maxscore": "h5pactivity", | ||||||
|   "addon.mod_h5pactivity.modulenameplural": "h5pactivity", |   "addon.mod_h5pactivity.modulenameplural": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.myattempts": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.no_compatible_track": "h5pactivity", | ||||||
|   "addon.mod_h5pactivity.offlinedisabledwarning": "local_moodlemobileapp", |   "addon.mod_h5pactivity.offlinedisabledwarning": "local_moodlemobileapp", | ||||||
|  |   "addon.mod_h5pactivity.outcome": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.result_fill-in": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.result_other": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.review_my_attempts": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.score": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.score_out_of": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.startdate": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.totalscore": "h5pactivity", | ||||||
|  |   "addon.mod_h5pactivity.viewattempt": "local_moodlemobileapp", | ||||||
|   "addon.mod_imscp.deploymenterror": "imscp", |   "addon.mod_imscp.deploymenterror": "imscp", | ||||||
|   "addon.mod_imscp.modulenameplural": "imscp", |   "addon.mod_imscp.modulenameplural": "imscp", | ||||||
|   "addon.mod_imscp.showmoduledescription": "local_moodlemobileapp", |   "addon.mod_imscp.showmoduledescription": "local_moodlemobileapp", | ||||||
|  | |||||||
| @ -1,5 +1,11 @@ | |||||||
| { | { | ||||||
|     "all_attempts": "All user attempts", |     "all_attempts": "All user attempts", | ||||||
|  |     "answer_checked": "Answer checked", | ||||||
|  |     "answer_correct": "Your answer is correct", | ||||||
|  |     "answer_fail": "Incorrect answer", | ||||||
|  |     "answer_incorrect": "Your answer is incorrect", | ||||||
|  |     "answer_pass": "Correct answer", | ||||||
|  |     "attempt": "Attempt", | ||||||
|     "attempt_completion_no": "This attempt is not marked as completed", |     "attempt_completion_no": "This attempt is not marked as completed", | ||||||
|     "attempt_completion_yes": "This attempt is completed", |     "attempt_completion_yes": "This attempt is completed", | ||||||
|     "attempts_none": "This user has no attempts to display.", |     "attempts_none": "This user has no attempts to display.", | ||||||
| @ -15,8 +21,15 @@ | |||||||
|     "maxscore": "Max score", |     "maxscore": "Max score", | ||||||
|     "modulenameplural": "H5P", |     "modulenameplural": "H5P", | ||||||
|     "myattempts": "My attempts", |     "myattempts": "My attempts", | ||||||
|  |     "no_compatible_track": "This interaction ({{$a}}) does not provide tracking information or the tracking provided is not compatible with the current activity version.", | ||||||
|     "offlinedisabledwarning": "You will need to be online to view the H5P package.", |     "offlinedisabledwarning": "You will need to be online to view the H5P package.", | ||||||
|  |     "outcome": "Outcome", | ||||||
|  |     "result_fill-in": "Fill-in text", | ||||||
|  |     "result_other": "Unkown interaction type", | ||||||
|     "review_my_attempts": "View my attempts", |     "review_my_attempts": "View my attempts", | ||||||
|     "score": "Score", |     "score": "Score", | ||||||
|  |     "score_out_of": "{{$a.rawscore}} out of {{$a.maxscore}}", | ||||||
|  |     "startdate": "Start date", | ||||||
|  |     "totalscore": "Total score", | ||||||
|     "viewattempt": "View attempt {{$a}}" |     "viewattempt": "View attempt {{$a}}" | ||||||
| } | } | ||||||
| @ -0,0 +1,140 @@ | |||||||
|  | <ion-header> | ||||||
|  |     <ion-navbar core-back-button> | ||||||
|  |         <ion-title><core-format-text *ngIf="h5pActivity" [text]="h5pActivity.name" contextLevel="module" [contextInstanceId]="h5pActivity.coursemodule" [courseId]="courseId"></core-format-text></ion-title> | ||||||
|  |     </ion-navbar> | ||||||
|  | </ion-header> | ||||||
|  | <ion-content> | ||||||
|  |     <ion-refresher [enabled]="loaded" (ionRefresh)="doRefresh($event)"> | ||||||
|  |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|  |     </ion-refresher> | ||||||
|  |     <core-loading [hideUntil]="loaded"> | ||||||
|  |         <ng-container *ngIf="attempt"> | ||||||
|  |             <!-- Attempt number and user that did the attempt. --> | ||||||
|  |             <a ion-item text-wrap *ngIf="user" core-user-link [userId]="user.id" [courseId]="courseId" [title]="user.fullname"> | ||||||
|  |                 <ion-avatar core-user-avatar [user]="user" item-start></ion-avatar> | ||||||
|  |                 <h2>{{ 'addon.mod_h5pactivity.attempt' | translate }} #{{attempt.attempt}}: {{user.fullname}}</h2> | ||||||
|  |             </a> | ||||||
|  |             <!-- Attempt number (if user not known). --> | ||||||
|  |             <ion-item text-wrap *ngIf="!user"> | ||||||
|  |                 <h2>{{ 'addon.mod_h5pactivity.attempt' | translate }} #{{attempt.attempt}}</h2> | ||||||
|  |             </ion-item> | ||||||
|  | 
 | ||||||
|  |             <!-- Attempt summary. --> | ||||||
|  |             <ion-card class="addon-mod_h5pactivity-attempt-result-summary"> | ||||||
|  |                 <ion-list> | ||||||
|  |                     <ion-item text-wrap no-lines> | ||||||
|  |                         <h2>{{ 'addon.mod_h5pactivity.startdate' | translate }}</h2> | ||||||
|  |                         <p>{{ attempt.timecreated | coreFormatDate:'strftimedatetime' }}</p> | ||||||
|  |                     </ion-item> | ||||||
|  |                     <ion-item text-wrap no-lines> | ||||||
|  |                         <h2>{{ 'addon.mod_h5pactivity.completion' | translate }}</h2> | ||||||
|  |                         <p *ngIf="attempt.completion"> | ||||||
|  |                             <img src="assets/img/completion/completion-auto-y.svg" role="presentation" alt=""> | ||||||
|  |                             {{ 'addon.mod_h5pactivity.attempt_completion_yes' | translate }} | ||||||
|  |                         </p> | ||||||
|  |                         <p *ngIf="!attempt.completion"> | ||||||
|  |                             <img src="assets/img/completion/completion-auto-n.svg" role="presentation" alt=""> | ||||||
|  |                             {{ 'addon.mod_h5pactivity.attempt_completion_no' | translate }} | ||||||
|  |                         </p> | ||||||
|  |                     </ion-item> | ||||||
|  |                     <ion-item text-wrap no-lines> | ||||||
|  |                         <h2>{{ 'addon.mod_h5pactivity.duration' | translate }}</h2> | ||||||
|  |                         <p>{{ attempt.durationReadable }}</p> | ||||||
|  |                     </ion-item> | ||||||
|  |                     <ion-item text-wrap no-lines> | ||||||
|  |                         <h2>{{ 'addon.mod_h5pactivity.outcome' | translate }}</h2> | ||||||
|  |                         <p *ngIf="attempt.success !== null && attempt.success" > | ||||||
|  |                             <core-icon name="fa-check-circle"></core-icon> | ||||||
|  |                             {{ 'addon.mod_h5pactivity.attempt_success_pass' | translate }} | ||||||
|  |                         </p> | ||||||
|  |                         <p *ngIf="attempt.success !== null && !attempt.success" > | ||||||
|  |                             <core-icon name="fa-circle-o"></core-icon> | ||||||
|  |                             {{ 'addon.mod_h5pactivity.attempt_success_fail' | translate }} | ||||||
|  |                         </p> | ||||||
|  |                         <p *ngIf="attempt.success === null" > | ||||||
|  |                             {{ 'addon.mod_h5pactivity.attempt_success_unknown' | translate }} | ||||||
|  |                         </p> | ||||||
|  |                     </ion-item> | ||||||
|  |                     <ion-item *ngIf="attempt.maxscore" text-wrap no-lines> | ||||||
|  |                         <h2>{{ 'addon.mod_h5pactivity.totalscore' | translate }}</h2> | ||||||
|  |                         <p>{{ 'addon.mod_h5pactivity.score_out_of' | translate:{$a: attempt} }}</p> | ||||||
|  |                     </ion-item> | ||||||
|  |                 </ion-list> | ||||||
|  |             </ion-card> | ||||||
|  | 
 | ||||||
|  |             <!-- Results. --> | ||||||
|  |             <ng-container *ngIf="attempt.results"> | ||||||
|  |                 <ion-card *ngFor="let result of attempt.results"> | ||||||
|  |                     <ion-card-header text-wrap> | ||||||
|  |                         <core-format-text [text]="result.description" [component]="component" [componentId]="h5pActivity.cmid" contextLevel="module" [contextInstanceId]="h5pActivity.cmid" [courseId]="courseId"></core-format-text> | ||||||
|  |                     </ion-card-header> | ||||||
|  |                     <ion-item *ngIf="result.content" text-wrap> | ||||||
|  |                         <core-format-text [text]="result.content" [component]="component" [componentId]="h5pActivity.cmid" contextLevel="module" [contextInstanceId]="h5pActivity.cmid" [courseId]="courseId"></core-format-text> | ||||||
|  |                     </ion-item> | ||||||
|  | 
 | ||||||
|  |                     <!-- Options. --> | ||||||
|  |                     <ng-container *ngIf="result.options && result.options.length"> | ||||||
|  |                         <ion-item text-wrap class="addon-mod_h5pactivity-result-table-header"> | ||||||
|  |                             <ion-row align-items-center> | ||||||
|  |                                 <ion-col text-center>{{ result.optionslabel }}</ion-col> | ||||||
|  |                                 <ion-col text-center>{{ result.correctlabel }}</ion-col> | ||||||
|  |                                 <ion-col text-center>{{ result.answerlabel }}</ion-col> | ||||||
|  |                             </ion-row> | ||||||
|  |                         </ion-item> | ||||||
|  |                         <ion-item text-wrap *ngFor="let option of result.options" class="addon-mod_h5pactivity-result-table-row"> | ||||||
|  |                             <ion-row align-items-center> | ||||||
|  |                                 <ion-col text-center> | ||||||
|  |                                     <core-format-text [text]="option.description" [component]="component" [componentId]="h5pActivity.cmid" contextLevel="module" [contextInstanceId]="h5pActivity.cmid" [courseId]="courseId"></core-format-text> | ||||||
|  |                                 </ion-col> | ||||||
|  |                                 <ion-col text-center> | ||||||
|  |                                     <ng-container *ngIf="option.correctanswer"> | ||||||
|  |                                         <ng-container *ngTemplateOutlet="answerTemplate; context: {answer: option.correctanswer}"></ng-container> | ||||||
|  |                                     </ng-container> | ||||||
|  |                                 </ion-col> | ||||||
|  |                                 <ion-col text-center> | ||||||
|  |                                     <ng-container *ngIf="option.useranswer"> | ||||||
|  |                                         <ng-container *ngTemplateOutlet="answerTemplate; context: {answer: option.useranswer}"></ng-container> | ||||||
|  |                                     </ng-container> | ||||||
|  |                                 </ion-col> | ||||||
|  |                             </ion-row> | ||||||
|  |                         </ion-item> | ||||||
|  | 
 | ||||||
|  |                         <!-- Result score. --> | ||||||
|  |                         <ion-item *ngIf="result.maxscore" text-wrap text-end class="addon-mod_h5pactivity-result-score"> | ||||||
|  |                             <p><strong>{{ 'addon.mod_h5pactivity.score' | translate }}: {{ 'addon.mod_h5pactivity.score_out_of' | translate:{$a: result} }}</strong></p> | ||||||
|  |                         </ion-item> | ||||||
|  |                     </ng-container> | ||||||
|  | 
 | ||||||
|  |                     <!-- Result doesn't support tracking. --> | ||||||
|  |                     <ion-item text-wrap class="core-warning-item" *ngIf="!result.track"> | ||||||
|  |                         <ion-icon item-start name="warning" color="warning"></ion-icon> {{ 'addon.mod_h5pactivity.no_compatible_track' | translate:{$a: result.interactiontype} }} | ||||||
|  |                     </ion-item> | ||||||
|  |                 </ion-card> | ||||||
|  |             </ng-container> | ||||||
|  |         </ng-container> | ||||||
|  |     </core-loading> | ||||||
|  | </ion-content> | ||||||
|  | 
 | ||||||
|  | <!-- Template to render an answer. --> | ||||||
|  | <ng-template #answerTemplate let-answer="answer"> | ||||||
|  |     <p *ngIf="answer.correct"> | ||||||
|  |         <core-icon name="fa-check" [label]="'addon.mod_h5pactivity.answer_correct' | translate" color="success"></core-icon> | ||||||
|  |         {{ answer.answer }} | ||||||
|  |     </p> | ||||||
|  |     <p *ngIf="answer.incorrect"> | ||||||
|  |         <core-icon name="fa-remove" [label]="'addon.mod_h5pactivity.answer_incorrect' | translate" color="danger"></core-icon> | ||||||
|  |         {{ answer.answer }} | ||||||
|  |     </p> | ||||||
|  |     <p *ngIf="answer.text"> | ||||||
|  |         {{ answer.answer }} | ||||||
|  |     </p> | ||||||
|  |     <p *ngIf="answer.checked"> | ||||||
|  |         <core-icon name="fa-check-circle" [label]="'addon.mod_h5pactivity.answer_checked' | translate"></core-icon> | ||||||
|  |     </p> | ||||||
|  |     <p *ngIf="answer.pass"> | ||||||
|  |         <core-icon name="fa-check" [label]="'addon.mod_h5pactivity.answer_pass' | translate" color="success"></core-icon> | ||||||
|  |     </p> | ||||||
|  |     <p *ngIf="answer.fail"> | ||||||
|  |         <core-icon name="fa-remove" [label]="'addon.mod_h5pactivity.answer_fail' | translate" color="danger"></core-icon> | ||||||
|  |     </p> | ||||||
|  | </ng-template> | ||||||
| @ -0,0 +1,35 @@ | |||||||
|  | // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | import { NgModule } from '@angular/core'; | ||||||
|  | import { IonicPageModule } from 'ionic-angular'; | ||||||
|  | import { TranslateModule } from '@ngx-translate/core'; | ||||||
|  | import { CoreComponentsModule } from '@components/components.module'; | ||||||
|  | import { CoreDirectivesModule } from '@directives/directives.module'; | ||||||
|  | import { CorePipesModule } from '@pipes/pipes.module'; | ||||||
|  | import { AddonModH5PActivityAttemptResultsPage } from './attempt-results'; | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |     declarations: [ | ||||||
|  |         AddonModH5PActivityAttemptResultsPage, | ||||||
|  |     ], | ||||||
|  |     imports: [ | ||||||
|  |         CoreComponentsModule, | ||||||
|  |         CoreDirectivesModule, | ||||||
|  |         CorePipesModule, | ||||||
|  |         IonicPageModule.forChild(AddonModH5PActivityAttemptResultsPage), | ||||||
|  |         TranslateModule.forChild(), | ||||||
|  |     ], | ||||||
|  | }) | ||||||
|  | export class AddonModH5PActivityAttemptResultsPageModule {} | ||||||
| @ -0,0 +1,55 @@ | |||||||
|  | ion-app.app-root page-addon-mod-h5pactivity-attempt-results { | ||||||
|  | 
 | ||||||
|  |     .addon-mod_h5pactivity-attempt-result-summary { | ||||||
|  |         img { | ||||||
|  |             width: 16px; | ||||||
|  |             height: 16px; | ||||||
|  |             display: inline; | ||||||
|  |             @include margin-horizontal(0, 4px); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .icon { | ||||||
|  |             font-size: 1.4em; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .addon-mod_h5pactivity-result-table-header .item-inner { | ||||||
|  |         font-size: 0.9em; | ||||||
|  |         font-weight: bold; | ||||||
|  | 
 | ||||||
|  |         .col[text-center] { | ||||||
|  |             @include padding-horizontal(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .addon-mod_h5pactivity-result-table-header, .addon-mod_h5pactivity-result-table-row { | ||||||
|  | 
 | ||||||
|  |         .item-inner ion-label { | ||||||
|  |             @include margin(null, 0, null, null); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .item { | ||||||
|  |             @include padding(null, null, null, 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .label { | ||||||
|  |             margin-top: 0; | ||||||
|  |             margin-bottom: 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .icon { | ||||||
|  |             font-size: 1.2em; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .addon-mod_h5pactivity-result-table-row.item:nth-child(even) { | ||||||
|  |         background-color: $gray-lighter; | ||||||
|  |         @include darkmode() { | ||||||
|  |             background-color: $core-dark-item-divider-bg-color; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .addon-mod_h5pactivity-result-score { | ||||||
|  |         border-top: 1px solid black; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,133 @@ | |||||||
|  | // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | import { Component, OnInit } from '@angular/core'; | ||||||
|  | import { IonicPage, NavParams } from 'ionic-angular'; | ||||||
|  | import { CoreDomUtils } from '@providers/utils/dom'; | ||||||
|  | import { CoreUser } from '@core/user/providers/user'; | ||||||
|  | import { | ||||||
|  |     AddonModH5PActivity, AddonModH5PActivityProvider, AddonModH5PActivityData, AddonModH5PActivityAttemptResults | ||||||
|  | } from '../../providers/h5pactivity'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Page that displays results of an attempt. | ||||||
|  |  */ | ||||||
|  | @IonicPage({ segment: 'addon-mod-h5pactivity-attempt-results' }) | ||||||
|  | @Component({ | ||||||
|  |     selector: 'page-addon-mod-h5pactivity-attempt-results', | ||||||
|  |     templateUrl: 'attempt-results.html', | ||||||
|  | }) | ||||||
|  | export class AddonModH5PActivityAttemptResultsPage implements OnInit { | ||||||
|  |     loaded: boolean; | ||||||
|  |     h5pActivity: AddonModH5PActivityData; | ||||||
|  |     attempt: AddonModH5PActivityAttemptResults; | ||||||
|  |     user: any; | ||||||
|  |     component = AddonModH5PActivityProvider.COMPONENT; | ||||||
|  | 
 | ||||||
|  |     protected courseId: number; | ||||||
|  |     protected h5pActivityId: number; | ||||||
|  |     protected attemptId: number; | ||||||
|  | 
 | ||||||
|  |     constructor(navParams: NavParams) { | ||||||
|  |         this.courseId = navParams.get('courseId'); | ||||||
|  |         this.h5pActivityId = navParams.get('h5pActivityId'); | ||||||
|  |         this.attemptId = navParams.get('attemptId'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Component being initialized. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async ngOnInit(): Promise<void> { | ||||||
|  |         try { | ||||||
|  |             await this.fetchData(); | ||||||
|  |         } catch (error) { | ||||||
|  |             CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading attempt.'); | ||||||
|  |         } finally { | ||||||
|  |             this.loaded = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the data. | ||||||
|  |      * | ||||||
|  |      * @param refresher Refresher. | ||||||
|  |      */ | ||||||
|  |     doRefresh(refresher: any): void { | ||||||
|  |         this.refreshData().finally(() => { | ||||||
|  |             refresher.complete(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get quiz data and attempt data. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async fetchData(): Promise<void> { | ||||||
|  |         await Promise.all([ | ||||||
|  |             this.fetchActivity(), | ||||||
|  |             this.fetchAttempt(), | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         await this.fetchUserProfile(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get activity data. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async fetchActivity(): Promise<void> { | ||||||
|  |         this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get attempts. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async fetchAttempt(): Promise<void> { | ||||||
|  |         this.attempt = await AddonModH5PActivity.instance.getResults(this.h5pActivityId, this.attemptId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get user profile. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async fetchUserProfile(): Promise<void> { | ||||||
|  |         this.user = await CoreUser.instance.getProfile(this.attempt.userid, this.courseId, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the data. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async refreshData(): Promise<void> { | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             await Promise.all([ | ||||||
|  |                 AddonModH5PActivity.instance.invalidateActivityData(this.courseId), | ||||||
|  |                 AddonModH5PActivity.instance.invalidateAttemptResults(this.h5pActivityId, this.attemptId), | ||||||
|  |             ]); | ||||||
|  |         } catch (error) { | ||||||
|  |             // Ignore errors.
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         await this.fetchData(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -9,13 +9,11 @@ | |||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
|     <core-loading [hideUntil]="loaded"> |     <core-loading [hideUntil]="loaded"> | ||||||
|         <!-- User viewed. --> |         <!-- User viewed. --> | ||||||
|         <ion-list *ngIf="user"> |         <a ion-item text-wrap *ngIf="user" core-user-link [userId]="user.id" [courseId]="courseId" [title]="user.fullname"> | ||||||
|              <ion-item text-wrap> |  | ||||||
|             <ion-avatar core-user-avatar [user]="user" item-start></ion-avatar> |             <ion-avatar core-user-avatar [user]="user" item-start></ion-avatar> | ||||||
|             <h2 *ngIf="!isCurrentUser">{{ user.fullname }}</h2> |             <h2 *ngIf="!isCurrentUser">{{ user.fullname }}</h2> | ||||||
|             <h2 *ngIf="isCurrentUser">{{ 'addon.mod_h5pactivity.myattempts' | translate }}</h2> |             <h2 *ngIf="isCurrentUser">{{ 'addon.mod_h5pactivity.myattempts' | translate }}</h2> | ||||||
|             </ion-item> |         </a> | ||||||
|         </ion-list> |  | ||||||
| 
 | 
 | ||||||
|         <ion-list *ngIf="attemptsData"> |         <ion-list *ngIf="attemptsData"> | ||||||
|             <!-- Attempts used to calculate the score. --> |             <!-- Attempts used to calculate the score. --> | ||||||
| @ -57,7 +55,7 @@ | |||||||
|     </ion-item> |     </ion-item> | ||||||
| 
 | 
 | ||||||
|     <!-- List of attempts. --> |     <!-- List of attempts. --> | ||||||
|     <a ion-item text-wrap *ngFor="let attempt of attempts" [attr.aria-label]="'addon.mod_h5pactivity.viewattempt' | translate:{$a: attempt.attempt}" class="addon-mod_h5pactivity-table-row"> |     <a ion-item text-wrap *ngFor="let attempt of attempts" [attr.aria-label]="'addon.mod_h5pactivity.viewattempt' | translate:{$a: attempt.attempt}" class="addon-mod_h5pactivity-table-row" navPush="AddonModH5PActivityAttemptResultsPage" [navParams]="{courseId: courseId, h5pActivityId: h5pActivityId, attemptId: attempt.id}"> | ||||||
|         <ion-row align-items-center> |         <ion-row align-items-center> | ||||||
|             <ion-col text-center>{{ attempt.attempt }}</ion-col> |             <ion-col text-center>{{ attempt.attempt }}</ion-col> | ||||||
|             <ion-col text-center col-5 col-md-2>{{ attempt.timemodified | coreFormatDate:'strftimedatetimeshort' }}</ion-col> |             <ion-col text-center col-5 col-md-2>{{ attempt.timemodified | coreFormatDate:'strftimedatetimeshort' }}</ion-col> | ||||||
|  | |||||||
| @ -31,13 +31,13 @@ import { | |||||||
| }) | }) | ||||||
| export class AddonModH5PActivityUserAttemptsPage implements OnInit { | export class AddonModH5PActivityUserAttemptsPage implements OnInit { | ||||||
|     loaded: boolean; |     loaded: boolean; | ||||||
|  |     courseId: number; | ||||||
|  |     h5pActivityId: number; | ||||||
|     h5pActivity: AddonModH5PActivityData; |     h5pActivity: AddonModH5PActivityData; | ||||||
|     attemptsData: AddonModH5PActivityUserAttempts; |     attemptsData: AddonModH5PActivityUserAttempts; | ||||||
|     user: any; |     user: any; | ||||||
|     isCurrentUser: boolean; |     isCurrentUser: boolean; | ||||||
| 
 | 
 | ||||||
|     protected courseId: number; |  | ||||||
|     protected h5pActivityId: number; |  | ||||||
|     protected userId: number; |     protected userId: number; | ||||||
| 
 | 
 | ||||||
|     constructor(navParams: NavParams) { |     constructor(navParams: NavParams) { | ||||||
|  | |||||||
| @ -41,9 +41,32 @@ export class AddonModH5PActivityProvider { | |||||||
|     protected formatAttempt(attempt: AddonModH5PActivityWSAttempt): AddonModH5PActivityAttempt { |     protected formatAttempt(attempt: AddonModH5PActivityWSAttempt): AddonModH5PActivityAttempt { | ||||||
|         const formattedAttempt: AddonModH5PActivityAttempt = attempt; |         const formattedAttempt: AddonModH5PActivityAttempt = attempt; | ||||||
| 
 | 
 | ||||||
|  |         formattedAttempt.timecreated = attempt.timecreated * 1000; // Convert to milliseconds.
 | ||||||
|         formattedAttempt.timemodified = attempt.timemodified * 1000; // Convert to milliseconds.
 |         formattedAttempt.timemodified = attempt.timemodified * 1000; // Convert to milliseconds.
 | ||||||
|  |         formattedAttempt.success = typeof formattedAttempt.success == 'undefined' ? null : formattedAttempt.success; | ||||||
|  | 
 | ||||||
|  |         if (!attempt.duration) { | ||||||
|  |             formattedAttempt.durationReadable = '-'; | ||||||
|  |             formattedAttempt.durationCompact = '-'; | ||||||
|  |         } else { | ||||||
|             formattedAttempt.durationReadable = CoreTimeUtils.instance.formatTime(attempt.duration); |             formattedAttempt.durationReadable = CoreTimeUtils.instance.formatTime(attempt.duration); | ||||||
|             formattedAttempt.durationCompact = CoreTimeUtils.instance.formatDurationShort(attempt.duration); |             formattedAttempt.durationCompact = CoreTimeUtils.instance.formatDurationShort(attempt.duration); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return formattedAttempt; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Format attempt data and results. | ||||||
|  |      * | ||||||
|  |      * @param attempt Attempt and results to format. | ||||||
|  |      */ | ||||||
|  |     protected formatAttemptResults(attempt: AddonModH5PActivityWSAttemptResults): AddonModH5PActivityAttemptResults { | ||||||
|  |         const formattedAttempt: AddonModH5PActivityAttemptResults = this.formatAttempt(attempt); | ||||||
|  | 
 | ||||||
|  |         for (const i in formattedAttempt.results) { | ||||||
|  |             formattedAttempt.results[i] = this.formatResult(formattedAttempt.results[i]); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return formattedAttempt; |         return formattedAttempt; | ||||||
|     } |     } | ||||||
| @ -70,6 +93,17 @@ export class AddonModH5PActivityProvider { | |||||||
|         return formatted; |         return formatted; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Format an attempt's result. | ||||||
|  |      * | ||||||
|  |      * @param result Result to format. | ||||||
|  |      */ | ||||||
|  |     protected formatResult(result: AddonModH5PActivityWSResult): AddonModH5PActivityWSResult { | ||||||
|  |         result.timecreated = result.timecreated * 1000; // Convert to milliseconds.
 | ||||||
|  | 
 | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get cache key for access information WS calls. |      * Get cache key for access information WS calls. | ||||||
|      * |      * | ||||||
| @ -210,6 +244,61 @@ export class AddonModH5PActivityProvider { | |||||||
|         return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId); |         return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Get cache key for results WS calls. | ||||||
|  |      * | ||||||
|  |      * @param id Instance ID. | ||||||
|  |      * @param attemptsIds Attempts IDs. | ||||||
|  |      * @return Cache key. | ||||||
|  |      */ | ||||||
|  |     protected getResultsCacheKey(id: number, attemptsIds: number[]): string { | ||||||
|  |         return this.getResultsCommonCacheKey(id) + ':' + JSON.stringify(attemptsIds); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get common cache key for results WS calls. | ||||||
|  |      * | ||||||
|  |      * @param id Instance ID. | ||||||
|  |      * @return Cache key. | ||||||
|  |      */ | ||||||
|  |     protected getResultsCommonCacheKey(id: number): string { | ||||||
|  |         return this.ROOT_CACHE_KEY + 'results:' + id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get attempt results. | ||||||
|  |      * | ||||||
|  |      * @param id Activity ID. | ||||||
|  |      * @param attemptId Attempt ID. | ||||||
|  |      * @param options Other options. | ||||||
|  |      * @return Promise resolved with the attempts of the user. | ||||||
|  |      */ | ||||||
|  |     async getResults(id: number, attemptId: number, options?: AddonModH5PActivityGetResultsOptions) | ||||||
|  |             : Promise<AddonModH5PActivityAttemptResults> { | ||||||
|  | 
 | ||||||
|  |         options = options || {}; | ||||||
|  | 
 | ||||||
|  |         const site = await CoreSites.instance.getSite(options.siteId); | ||||||
|  | 
 | ||||||
|  |         const params = { | ||||||
|  |             h5pactivityid: id, | ||||||
|  |             attemptids: [attemptId], | ||||||
|  |         }; | ||||||
|  |         const preSets: CoreSiteWSPreSets = { | ||||||
|  |             cacheKey: this.getResultsCacheKey(id, params.attemptids), | ||||||
|  |             updateFrequency: CoreSite.FREQUENCY_SOMETIMES, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if (options.ignoreCache) { | ||||||
|  |             preSets.getFromCache = false; | ||||||
|  |             preSets.emergencyCache = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets); | ||||||
|  | 
 | ||||||
|  |         return this.formatAttemptResults(response.attempts[0]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get cache key for attemps WS calls. |      * Get cache key for attemps WS calls. | ||||||
|      * |      * | ||||||
| @ -290,6 +379,33 @@ export class AddonModH5PActivityProvider { | |||||||
|         await site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId)); |         await site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Invalidates all attempts results for H5P activity. | ||||||
|  |      * | ||||||
|  |      * @param id Activity ID. | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      * @return Promise resolved when the data is invalidated. | ||||||
|  |      */ | ||||||
|  |     async invalidateAllResults(id: number, siteId?: string): Promise<void> { | ||||||
|  |         const site = await CoreSites.instance.getSite(siteId); | ||||||
|  | 
 | ||||||
|  |         await site.invalidateWsCacheForKey(this.getResultsCommonCacheKey(id)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Invalidates results of a certain attempt for H5P activity. | ||||||
|  |      * | ||||||
|  |      * @param id Activity ID. | ||||||
|  |      * @param attemptId Attempt ID. | ||||||
|  |      * @param siteId Site ID. If not defined, current site. | ||||||
|  |      * @return Promise resolved when the data is invalidated. | ||||||
|  |      */ | ||||||
|  |     async invalidateAttemptResults(id: number, attemptId: number, siteId?: string): Promise<void> { | ||||||
|  |         const site = await CoreSites.instance.getSite(siteId); | ||||||
|  | 
 | ||||||
|  |         await site.invalidateWsCacheForKey(this.getResultsCacheKey(id, [attemptId])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Invalidates all users attempts for H5P activity. |      * Invalidates all users attempts for H5P activity. | ||||||
|      * |      * | ||||||
| @ -416,7 +532,16 @@ export type AddonModH5PActivityGetAttemptsResult = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Attempts data for a user as returned by the WS. |  * Result of WS mod_h5pactivity_get_results. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityGetResultsResult = { | ||||||
|  |     activityid: number; // Activity course module ID.
 | ||||||
|  |     attempts: AddonModH5PActivityWSAttemptResults[]; // The complete attempts list.
 | ||||||
|  |     warnings?: CoreWSExternalWarning[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Attempts data for a user as returned by the WS mod_h5pactivity_get_attempts. | ||||||
|  */ |  */ | ||||||
| export type AddonModH5PActivityWSUserAttempts = { | export type AddonModH5PActivityWSUserAttempts = { | ||||||
|     userid: number; // The user id.
 |     userid: number; // The user id.
 | ||||||
| @ -429,7 +554,7 @@ export type AddonModH5PActivityWSUserAttempts = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Attempt data as returned by the WS. |  * Attempt data as returned by the WS mod_h5pactivity_get_attempts. | ||||||
|  */ |  */ | ||||||
| export type AddonModH5PActivityWSAttempt = { | export type AddonModH5PActivityWSAttempt = { | ||||||
|     id: number; // ID of the context.
 |     id: number; // ID of the context.
 | ||||||
| @ -447,7 +572,56 @@ export type AddonModH5PActivityWSAttempt = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Attempts data with some calculated data. |  * Attempt and results data as returned by the WS mod_h5pactivity_get_results. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityWSAttemptResults = AddonModH5PActivityWSAttempt & { | ||||||
|  |     results?: AddonModH5PActivityWSResult[]; // The results of the attempt.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Attempt result data as returned by the WS mod_h5pactivity_get_results. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityWSResult = { | ||||||
|  |     id: number; // ID of the context.
 | ||||||
|  |     attemptid: number; // ID of the H5P attempt.
 | ||||||
|  |     subcontent: string; // Subcontent identifier.
 | ||||||
|  |     timecreated: number; // Result creation.
 | ||||||
|  |     interactiontype: string; // Interaction type.
 | ||||||
|  |     description: string; // Result description.
 | ||||||
|  |     content?: string; // Result extra content.
 | ||||||
|  |     rawscore: number; // Result score value.
 | ||||||
|  |     maxscore: number; // Result max score.
 | ||||||
|  |     duration?: number; // Result duration in seconds.
 | ||||||
|  |     completion?: number; // Result completion.
 | ||||||
|  |     success?: number; // Result success.
 | ||||||
|  |     optionslabel?: string; // Label used for result options.
 | ||||||
|  |     correctlabel?: string; // Label used for correct answers.
 | ||||||
|  |     answerlabel?: string; // Label used for user answers.
 | ||||||
|  |     track?: boolean; // If the result has valid track information.
 | ||||||
|  |     options?: { // The statement options.
 | ||||||
|  |         description: string; // Option description.
 | ||||||
|  |         id: number; // Option identifier.
 | ||||||
|  |         correctanswer: AddonModH5PActivityWSResultAnswer; // The option correct answer.
 | ||||||
|  |         useranswer: AddonModH5PActivityWSResultAnswer; // The option user answer.
 | ||||||
|  |     }[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Result answer as returned by the WS mod_h5pactivity_get_results. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityWSResultAnswer = { | ||||||
|  |     answer?: string; // Option text value.
 | ||||||
|  |     correct?: boolean; // If has to be displayed as correct.
 | ||||||
|  |     incorrect?: boolean; // If has to be displayed as incorrect.
 | ||||||
|  |     text?: boolean; // If has to be displayed as simple text.
 | ||||||
|  |     checked?: boolean; // If has to be displayed as a checked option.
 | ||||||
|  |     unchecked?: boolean; // If has to be displayed as a unchecked option.
 | ||||||
|  |     pass?: boolean; // If has to be displayed as passed.
 | ||||||
|  |     fail?: boolean; // If has to be displayed as failed.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * User attempts data with some calculated data. | ||||||
|  */ |  */ | ||||||
| export type AddonModH5PActivityUserAttempts = { | export type AddonModH5PActivityUserAttempts = { | ||||||
|     userid: number; // The user id.
 |     userid: number; // The user id.
 | ||||||
| @ -467,6 +641,13 @@ export type AddonModH5PActivityAttempt = AddonModH5PActivityWSAttempt & { | |||||||
|     durationCompact?: string; // Duration in a "short" human readable format.
 |     durationCompact?: string; // Duration in a "short" human readable format.
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Attempt and results data with some calculated data. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityAttemptResults = AddonModH5PActivityAttempt & { | ||||||
|  |     results?: AddonModH5PActivityWSResult[]; // The results of the attempt.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Options to pass to getDeployedFile function. |  * Options to pass to getDeployedFile function. | ||||||
|  */ |  */ | ||||||
| @ -476,6 +657,14 @@ export type AddonModH5PActivityGetDeployedFileOptions = { | |||||||
|     siteId?: string; // Site ID. If not defined, current site.
 |     siteId?: string; // Site ID. If not defined, current site.
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Options to pass to getResults function. | ||||||
|  |  */ | ||||||
|  | export type AddonModH5PActivityGetResultsOptions = { | ||||||
|  |     ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down.
 | ||||||
|  |     siteId?: string; // Site ID. If not defined, current site.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Options to pass to getAttempts function. |  * Options to pass to getAttempts function. | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -660,6 +660,12 @@ | |||||||
|     "addon.mod_glossary.searchquery": "Search query", |     "addon.mod_glossary.searchquery": "Search query", | ||||||
|     "addon.mod_glossary.tagarea_glossary_entries": "Glossary entries", |     "addon.mod_glossary.tagarea_glossary_entries": "Glossary entries", | ||||||
|     "addon.mod_h5pactivity.all_attempts": "All user attempts", |     "addon.mod_h5pactivity.all_attempts": "All user attempts", | ||||||
|  |     "addon.mod_h5pactivity.answer_checked": "Answer checked", | ||||||
|  |     "addon.mod_h5pactivity.answer_correct": "Your answer is correct", | ||||||
|  |     "addon.mod_h5pactivity.answer_fail": "Incorrect answer", | ||||||
|  |     "addon.mod_h5pactivity.answer_incorrect": "Your answer is incorrect", | ||||||
|  |     "addon.mod_h5pactivity.answer_pass": "Correct answer", | ||||||
|  |     "addon.mod_h5pactivity.attempt": "Attempt", | ||||||
|     "addon.mod_h5pactivity.attempt_completion_no": "This attempt is not marked as completed", |     "addon.mod_h5pactivity.attempt_completion_no": "This attempt is not marked as completed", | ||||||
|     "addon.mod_h5pactivity.attempt_completion_yes": "This attempt is completed", |     "addon.mod_h5pactivity.attempt_completion_yes": "This attempt is completed", | ||||||
|     "addon.mod_h5pactivity.attempt_success_fail": "Fail", |     "addon.mod_h5pactivity.attempt_success_fail": "Fail", | ||||||
| @ -675,9 +681,16 @@ | |||||||
|     "addon.mod_h5pactivity.maxscore": "Max score", |     "addon.mod_h5pactivity.maxscore": "Max score", | ||||||
|     "addon.mod_h5pactivity.modulenameplural": "H5P", |     "addon.mod_h5pactivity.modulenameplural": "H5P", | ||||||
|     "addon.mod_h5pactivity.myattempts": "My attempts", |     "addon.mod_h5pactivity.myattempts": "My attempts", | ||||||
|  |     "addon.mod_h5pactivity.no_compatible_track": "This interaction ({{$a}}) does not provide tracking information or the tracking provided is not compatible with the current activity version.", | ||||||
|     "addon.mod_h5pactivity.offlinedisabledwarning": "You will need to be online to view the H5P package.", |     "addon.mod_h5pactivity.offlinedisabledwarning": "You will need to be online to view the H5P package.", | ||||||
|  |     "addon.mod_h5pactivity.outcome": "Outcome", | ||||||
|  |     "addon.mod_h5pactivity.result_fill-in": "Fill-in text", | ||||||
|  |     "addon.mod_h5pactivity.result_other": "Unkown interaction type", | ||||||
|     "addon.mod_h5pactivity.review_my_attempts": "View my attempts", |     "addon.mod_h5pactivity.review_my_attempts": "View my attempts", | ||||||
|     "addon.mod_h5pactivity.score": "Score", |     "addon.mod_h5pactivity.score": "Score", | ||||||
|  |     "addon.mod_h5pactivity.score_out_of": "{{$a.rawscore}} out of {{$a.maxscore}}", | ||||||
|  |     "addon.mod_h5pactivity.startdate": "Start date", | ||||||
|  |     "addon.mod_h5pactivity.totalscore": "Total score", | ||||||
|     "addon.mod_h5pactivity.viewattempt": "View attempt {{$a}}", |     "addon.mod_h5pactivity.viewattempt": "View attempt {{$a}}", | ||||||
|     "addon.mod_imscp.deploymenterror": "Content package error!", |     "addon.mod_imscp.deploymenterror": "Content package error!", | ||||||
|     "addon.mod_imscp.modulenameplural": "IMS content packages", |     "addon.mod_imscp.modulenameplural": "IMS content packages", | ||||||
|  | |||||||
| @ -7,9 +7,11 @@ | |||||||
| 
 | 
 | ||||||
| @each $color-name, $color-base, $color-contrast in get-colors($colors-dark) { | @each $color-name, $color-base, $color-contrast in get-colors($colors-dark) { | ||||||
|   .fa-#{$color-name} { |   .fa-#{$color-name} { | ||||||
|  |     @include darkmode() { | ||||||
|       color: $color-base !important; |       color: $color-base !important; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| [dir=rtl] .icon { | [dir=rtl] .icon { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user