commit
e37c75ff54
|
@ -576,7 +576,6 @@
|
|||
"addon.mod_feedback.minimal": "feedback",
|
||||
"addon.mod_feedback.mode": "feedback",
|
||||
"addon.mod_feedback.modulenameplural": "feedback",
|
||||
"addon.mod_feedback.next_page": "feedback",
|
||||
"addon.mod_feedback.non_anonymous": "feedback",
|
||||
"addon.mod_feedback.non_anonymous_entries": "feedback",
|
||||
"addon.mod_feedback.non_respondents_students": "feedback",
|
||||
|
@ -586,7 +585,6 @@
|
|||
"addon.mod_feedback.overview": "feedback",
|
||||
"addon.mod_feedback.page_after_submit": "feedback",
|
||||
"addon.mod_feedback.preview": "moodle",
|
||||
"addon.mod_feedback.previous_page": "feedback",
|
||||
"addon.mod_feedback.questions": "feedback",
|
||||
"addon.mod_feedback.questionscountdescription": "local_moodlemobileapp",
|
||||
"addon.mod_feedback.response_nr": "feedback",
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
<div class="ion-padding ion-text-center" *ngIf="canLoadMore && !empty">
|
||||
<!-- Button and spinner to show more attempts. -->
|
||||
<ion-button expand="block" (click)="loadMoreEvents()" color="light" *ngIf="!loadingMore">
|
||||
<ion-button expand="block" (click)="loadMoreEvents()" fill="outline" *ngIf="!loadingMore">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</ion-button>
|
||||
<ion-spinner *ngIf="loadingMore" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
|
||||
|
|
|
@ -235,16 +235,16 @@
|
|||
<ion-item>
|
||||
<ion-label>
|
||||
<ion-row>
|
||||
<ion-col *ngIf="hasOffline && eventId && eventId < 0">
|
||||
<ion-button expand="block" fill="outline" (click)="discard()">
|
||||
{{ 'core.discard' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" (click)="submit()" [disabled]="!form.valid" type="submit">
|
||||
{{ 'core.save' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="hasOffline && eventId && eventId < 0">
|
||||
<ion-button expand="block" color="light" (click)="discard()">
|
||||
{{ 'core.discard' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -157,7 +157,7 @@
|
|||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" (click)="confirmContactRequest()">
|
||||
{{ 'addon.messages.acceptandaddcontact' | translate }}
|
||||
</ion-button>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" color="light" (click)="declineContactRequest()">
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin-bottom" fill="outline" (click)="declineContactRequest()">
|
||||
{{ 'addon.messages.decline' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
<!-- Load more button for contacts and non-contacts -->
|
||||
<ng-container *ngIf="item.type != 'messages'">
|
||||
<div class="ion-padding-horizontal" *ngIf="item.canLoadMore && !item.loadingMore">
|
||||
<ion-button expand="block" color="light" (click)="search(query, item.type)">
|
||||
<ion-button expand="block" fill="outline" (click)="search(query, item.type)">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
<!-- Summary of submissions with draft status. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="assign.submissiondrafts && summary && summary.submissionsenabled"
|
||||
[class.hide-detail]="!summary.submissiondraftscount" detail="true" [button]="summary.submissiondraftscount"
|
||||
[detail]="summary.submissiondraftscount" [button]="summary.submissiondraftscount"
|
||||
(click)="goToSubmissionList(submissionStatusDraft, !!summary.submissiondraftscount)">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_assign.numberofdraftsubmissions' | translate }}</h2>
|
||||
|
@ -80,8 +80,8 @@
|
|||
</ion-item>
|
||||
|
||||
<!-- Summary of submissions with submitted status. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="summary && summary.submissionsenabled"
|
||||
[class.hide-detail]="!summary.submissionssubmittedcount" detail="true" [button]="summary.submissionssubmittedcount"
|
||||
<ion-item class="ion-text-wrap" *ngIf="summary && summary.submissionsenabled" [detail]="summary.submissionssubmittedcount"
|
||||
[button]="summary.submissionssubmittedcount"
|
||||
(click)="goToSubmissionList(submissionStatusSubmitted, !!summary.submissionssubmittedcount)">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_assign.numberofsubmittedassignments' | translate }}</h2>
|
||||
|
@ -97,7 +97,7 @@
|
|||
|
||||
<!-- Summary of submissions that need grading. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="summary && summary.submissionsenabled && !assign.teamsubmission"
|
||||
[class.hide-detail]="!needsGradingAvailable" detail="true" [button]="needsGradingAvailable"
|
||||
[detail]="needsGradingAvailable" [button]="needsGradingAvailable"
|
||||
(click)="goToSubmissionList(needGrading, needsGradingAvailable)">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_assign.numberofsubmissionsneedgrading' | translate }}</h2>
|
||||
|
|
|
@ -94,15 +94,6 @@
|
|||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<ion-button *ngIf="meetingInfo.canjoin" class="ion-margin" expand="block" (click)="joinRoom()">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_conference_action_join' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-button *ngIf="meetingInfo.statusrunning && meetingInfo.ismoderator" color="light" class="ion-margin" expand="block"
|
||||
(click)="endMeeting()">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_conference_action_end' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-card *ngIf="!meetingInfo.canjoin" class="core-warning-card">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
|
||||
|
@ -110,8 +101,20 @@
|
|||
</ion-item>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width adaptable-buttons-row"
|
||||
*ngIf="meetingInfo && (meetingInfo.canjoin || meetingInfo.statusrunning && meetingInfo.ismoderator)">
|
||||
<ion-button *ngIf="meetingInfo.statusrunning && meetingInfo.ismoderator" fill="outline" class="ion-margin ion-text-wrap"
|
||||
expand="block" (click)="endMeeting()">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_conference_action_end' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="meetingInfo.canjoin" class="ion-margin ion-text-wrap" expand="block" (click)="joinRoom()">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_conference_action_join' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -31,16 +31,17 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-button class="ion-margin" expand="block" (click)="openBook()">
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width">
|
||||
<ion-button expand="block" (click)="openBook()" class="ion-margin ion-text-wrap">
|
||||
<span *ngIf="!hasStartedBook">{{ 'core.start' | translate }}</span>
|
||||
<span *ngIf="hasStartedBook">{{ 'core.resume' | translate }}</span>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -20,16 +20,18 @@
|
|||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ng-container *ngIf="chat">
|
||||
<ion-button *ngIf="chat" class="ion-margin ion-text-wrap" expand="block" fill="outline" (click)="viewSessions()">
|
||||
{{ 'addon.mod_chat.viewreport' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="chat">
|
||||
<ion-button class="ion-margin ion-text-wrap" expand="block" color="primary" (click)="enterChat()">
|
||||
{{ 'addon.mod_chat.enterchat' | translate }}
|
||||
</ion-button>
|
||||
<ion-button class="ion-margin ion-text-wrap" expand="block" color="light" (click)="viewSessions()">
|
||||
{{ 'addon.mod_chat.viewreport' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
(onSubmit)="sendMessage($event)" [placeholder]="'addon.messages.newmessage' | translate">
|
||||
</core-send-message-form>
|
||||
|
||||
<ion-button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" expand="block" color="light">
|
||||
<ion-button *ngIf="isOnline && !polling && loaded" (click)="reconnect()" expand="block" fill="outline">
|
||||
{{ 'core.login.reconnect' | translate }}
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
|
|
|
@ -64,12 +64,6 @@
|
|||
<ion-radio slot="end" [value]="option.id" [disabled]="option.disabled || !canEdit"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
<ion-button *ngIf="canEdit" expand="block" (click)="save()" [disabled]="!canSave()" class="ion-margin">
|
||||
{{ 'addon.mod_choice.savemychoice' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="canDelete" expand="block" color="light" (click)="delete()" class="ion-margin">
|
||||
{{ 'addon.mod_choice.removemychoice' | translate }}
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
|
||||
<!-- Choice results -->
|
||||
|
@ -96,7 +90,11 @@
|
|||
</ion-col>
|
||||
<ion-col *ngIf="choice.publish && results" size="12" size-lg="7">
|
||||
<ion-item-group *ngFor="let result of results">
|
||||
<ion-item-divider class="ion-text-wrap">
|
||||
<ion-item [button]="result.numberofuser > 0" class="divider ion-text-wrap" (click)="toggle(result)"
|
||||
[attr.aria-label]="(result.expanded ? 'core.collapse' : 'core.expand') | translate" detail="false">
|
||||
<ion-icon [name]="result.numberofuser > 0 ? 'fas-chevron-right' : ''" flip-rtl slot="start" aria-hidden="true"
|
||||
class="expandable-status-icon" [class.expandable-status-icon-expanded]="result.expanded">
|
||||
</ion-icon>
|
||||
<ion-label>
|
||||
<h3 class="item-heading">
|
||||
<core-format-text [text]="result.text" contextLevel="module" [contextInstanceId]="module.id"
|
||||
|
@ -111,7 +109,8 @@
|
|||
{{ 'addon.mod_choice.limita' | translate:{$a: result.maxanswer} }}
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item-divider>
|
||||
</ion-item>
|
||||
<ng-container *ngIf="result.expanded">
|
||||
<ion-item *ngFor="let user of result.userresponses" core-user-link [courseId]="courseId" [userId]="user.userid"
|
||||
[attr.aria-label]="user.fullname" class="ion-text-wrap">
|
||||
<core-user-avatar [user]="user" slot="start" [courseId]="courseId"></core-user-avatar>
|
||||
|
@ -119,6 +118,7 @@
|
|||
<p>{{user.fullname}}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
@ -133,11 +133,24 @@
|
|||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width adaptable-buttons-row" *ngIf="options.length && choice && (canEdit || canDelete)">
|
||||
<ion-button *ngIf="canDelete" expand="block" fill="outline" (click)="delete()" class="ion-margin ion-text-wrap">
|
||||
{{ 'addon.mod_choice.removemychoice' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="canEdit" expand="block" (click)="save()" [disabled]="!canSave()" class="ion-margin ion-text-wrap">
|
||||
{{ 'addon.mod_choice.savemychoice' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
|
||||
|
||||
<!-- Template to render a choice option label. -->
|
||||
<ng-template #optionLabelTemplate let-option="option">
|
||||
|
|
|
@ -446,6 +446,15 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle list of users in a result visible.
|
||||
*
|
||||
* @param result Result to expand.
|
||||
*/
|
||||
toggle(result: AddonModChoiceResultFormatted): void {
|
||||
result.expanded = !result.expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the sync of the activity.
|
||||
*
|
||||
|
@ -472,4 +481,5 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
*/
|
||||
export type AddonModChoiceResultFormatted = AddonModChoiceResult & {
|
||||
percentageamountfixed: string; // Percentage of users answers with fixed decimals.
|
||||
expanded?: boolean;
|
||||
};
|
||||
|
|
|
@ -60,21 +60,23 @@
|
|||
(onLoading)="setLoadingComments($event)" [showItem]="true">
|
||||
</core-comments>
|
||||
|
||||
<ion-grid *ngIf="hasPrevious || hasNext">
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col *ngIf="hasPrevious">
|
||||
<ion-button expand="block" fill="outline" (click)="gotoEntry(offset! -1)">
|
||||
<ion-icon name="fas-chevron-left" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.previous' | translate }}
|
||||
<div collapsible-footer *ngIf="entryLoaded && hasPrevious || hasNext" slot="fixed">
|
||||
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding ion-wrap">
|
||||
<ion-col class="ion-text-start ion-no-padding core-navigation-arrow" size="auto">
|
||||
<ion-button [disabled]="!hasPrevious" fill="clear" color="dark" [attr.aria-label]="'core.previous' | translate"
|
||||
(click)="gotoEntry(offset! -1)">
|
||||
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="hasNext">
|
||||
<ion-button expand="block" (click)="gotoEntry(offset! + 1)">
|
||||
{{ 'core.next' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
<ion-col class="ion-text-center">
|
||||
</ion-col>
|
||||
<ion-col class="ion-text-end ion-no-padding core-navigation-arrow" size="auto">
|
||||
<ion-button [disabled]="!hasNext" fill="clear" color="dark" [attr.aria-label]=" 'core.next' | translate"
|
||||
(click)="gotoEntry(offset! + 1)">
|
||||
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -33,11 +33,29 @@
|
|||
</ng-template>
|
||||
</core-tab>
|
||||
</core-tabs>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width adaptable-buttons-row"
|
||||
*ngIf="access && (access.canedititems || access.canviewreports || !access.isempty)">
|
||||
<ion-button expand="block" fill="outline" (click)="gotoAnswerQuestions(true)" class="ion-margin ion-text-wrap">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_feedback.preview' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="access.cancomplete && access.cansubmit && access.isopen" expand="block" (click)="gotoAnswerQuestions()"
|
||||
class="ion-margin ion-text-wrap">
|
||||
<ng-container *ngIf="!goPage">
|
||||
{{ 'addon.mod_feedback.complete_the_form' | translate }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="goPage">
|
||||
{{ 'addon.mod_feedback.continue_the_form' | translate }}
|
||||
</ng-container>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<ng-template #basicInfo>
|
||||
<ion-list *ngIf="access && access.canviewanalysis && !access.isempty">
|
||||
|
@ -53,8 +71,8 @@
|
|||
</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" (click)="openAttempts()" [class.hide-detail]="!(access.canviewreports && completedCount > 0)"
|
||||
detail="true" [button]="access.canviewreports && completedCount > 0">
|
||||
<ion-item class="ion-text-wrap" (click)="openAttempts()" [detail]="access.canviewreports && completedCount > 0"
|
||||
[button]="access.canviewreports && completedCount > 0">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_feedback.completed_feedbacks' | translate }}</h2>
|
||||
</ion-label>
|
||||
|
@ -133,28 +151,6 @@
|
|||
<p *ngIf="!access.isanonymous">{{ 'addon.mod_feedback.non_anonymous' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-grid>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col>
|
||||
<ion-button expand="block" fill="outline" (click)="gotoAnswerQuestions(true)" class="ion-text-wrap">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_feedback.preview' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="access.cancomplete && access.cansubmit && access.isopen">
|
||||
<ion-button expand="block" (click)="gotoAnswerQuestions()" class="ion-text-wrap">
|
||||
<ng-container *ngIf="!goPage">
|
||||
{{ 'addon.mod_feedback.complete_the_form' | translate }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="goPage">
|
||||
{{ 'addon.mod_feedback.continue_the_form' | translate }}
|
||||
</ng-container>
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
"minimal": "Minimum",
|
||||
"mode": "Mode",
|
||||
"modulenameplural": "Feedback",
|
||||
"next_page": "Next page",
|
||||
"non_anonymous": "User's name will be logged and shown with answers",
|
||||
"non_anonymous_entries": "Non anonymous entries ({{$a}})",
|
||||
"non_respondents_students": "Non-respondent students ({{$a}})",
|
||||
|
@ -28,7 +27,6 @@
|
|||
"overview": "Overview",
|
||||
"page_after_submit": "Completion message",
|
||||
"preview": "Preview",
|
||||
"previous_page": "Previous page",
|
||||
"questions": "Questions",
|
||||
"questionscountdescription": "There are {{count}} questions.",
|
||||
"response_nr": "Response number",
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="feedbackLoaded">
|
||||
<core-loading [hideUntil]="feedbackLoaded" class="has-spacer">
|
||||
<ng-container *ngIf="items && items.length">
|
||||
<ion-list class="ion-no-margin">
|
||||
<ion-list class="ion-no-margin has-spacer">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_feedback.mode' | translate }}</h2>
|
||||
|
@ -109,32 +109,31 @@
|
|||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ion-grid *ngIf="!preview">
|
||||
<ion-row class="ion-align-items-center">
|
||||
|
||||
<ion-row class="ion-align-items-center spacer-top" *ngIf="!preview">
|
||||
<ion-col *ngIf="hasPrevPage">
|
||||
<ion-button expand="block" fill="outline" (click)="gotoPage(true)" class="ion-text-wrap">
|
||||
<ion-icon name="fas-chevron-left" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_feedback.previous_page' | translate }}
|
||||
{{ 'core.previous' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="hasNextPage">
|
||||
<ion-button expand="block" (click)="gotoPage(false)" class="ion-text-wrap">
|
||||
{{ 'addon.mod_feedback.next_page' | translate }}
|
||||
{{ 'core.next' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="!hasNextPage">
|
||||
<ion-button expand="block" (click)="gotoPage(false)" class="ion-text-wrap">
|
||||
{{ 'addon.mod_feedback.save_entries' | translate }}
|
||||
{{ 'core.submit' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-list>
|
||||
</ng-container>
|
||||
|
||||
<ion-card class="core-success-card" *ngIf="completed">
|
||||
<ion-item>
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-check" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p *ngIf="!completionPageContents && !completedOffline">
|
||||
|
@ -143,32 +142,25 @@
|
|||
<p *ngIf="!completionPageContents && completedOffline">
|
||||
{{ 'addon.mod_feedback.feedback_submitted_offline' | translate }}
|
||||
</p>
|
||||
<p *ngIf="completionPageContents">
|
||||
<core-format-text [component]="component" componentId="componentId" [text]="completionPageContents"
|
||||
contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
|
||||
<core-format-text *ngIf="completionPageContents" [component]="component" componentId="componentId"
|
||||
[text]="completionPageContents" contextLevel="module" [contextInstanceId]="cmId" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ion-card *ngIf="completed && (access!.canviewanalysis || hasNextPage)">
|
||||
<ion-grid>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col *ngIf="access!.canviewanalysis">
|
||||
<ion-button expand="block" fill="outline" (click)="showAnalysis()" class="ion-text-wrap">
|
||||
<div collapsible-footer *ngIf="feedbackLoaded && completed" slot="fixed">
|
||||
<div class="list-item-limited-width adaptable-buttons-row">
|
||||
<ion-button expand="block" fill="outline" (click)="showAnalysis()" class="ion-text-wrap ion-margin"
|
||||
*ngIf="access!.canviewanalysis">
|
||||
<ion-icon name="fas-chart-bar" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_feedback.completed_feedbacks' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="hasNextPage">
|
||||
<ion-button expand="block" (click)="continue()" class="ion-text-wrap">
|
||||
{{ 'core.continue' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
<ion-button expand="block" (click)="continue()" class="ion-text-wrap ion-margin">
|
||||
<ng-container *ngIf="!siteAfterSubmit">{{ 'core.continue' | translate }}</ng-container>
|
||||
<ng-container *ngIf="siteAfterSubmit">{{ 'core.course.gotonextactivity' | translate }}</ng-container>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-card>
|
||||
</div>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|||
import { CoreSite } from '@classes/site';
|
||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
||||
import { CoreCourse, CoreCourseCommonModWSOptions } from '@features/course/services/course';
|
||||
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
|
||||
import { CoreCourseModuleData } from '@features/course/services/course-helper';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { CoreApp } from '@services/app';
|
||||
|
@ -53,7 +53,6 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
|
|||
|
||||
protected module?: CoreCourseModuleData;
|
||||
protected currentPage?: number;
|
||||
protected siteAfterSubmit?: string;
|
||||
protected onlineObserver: Subscription;
|
||||
protected originalData?: Record<string, AddonModFeedbackResponseValue>;
|
||||
protected currentSite: CoreSite;
|
||||
|
@ -75,6 +74,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
|
|||
hasNextPage = false;
|
||||
completed = false;
|
||||
completedOffline = false;
|
||||
siteAfterSubmit?: string;
|
||||
|
||||
constructor() {
|
||||
this.currentSite = CoreSites.getRequiredCurrentSite();
|
||||
|
@ -409,7 +409,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
|
|||
*/
|
||||
async continue(): Promise<void> {
|
||||
if (!this.siteAfterSubmit) {
|
||||
return CoreCourseHelper.getAndOpenCourse(this.courseId, {}, this.currentSite.getId());
|
||||
return CoreNavigator.back();
|
||||
}
|
||||
|
||||
const modal = await CoreDomUtils.showModalLoading();
|
||||
|
|
|
@ -134,15 +134,15 @@
|
|||
</ng-container>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button expand="block" fill="outline" (click)="cancel()">{{ 'core.cancel' | translate }}</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" (click)="send()" [disabled]="formData.subject == '' || formData.message == null">
|
||||
<span *ngIf="formData.isEditing">{{ 'core.savechanges' | translate }}</span>
|
||||
<span *ngIf="!formData.isEditing">{{ 'addon.mod_forum.posttoforum' | translate }}</span>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" color="light" (click)="cancel()">{{ 'core.cancel' | translate }}</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</form>
|
||||
|
|
|
@ -69,15 +69,15 @@
|
|||
<ion-item>
|
||||
<ion-label>
|
||||
<ion-row>
|
||||
<ion-col *ngIf="hasOffline">
|
||||
<ion-button expand="block" fill="outline" (click)="discard()">{{ 'core.discard' | translate }}</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" [disabled]="newDiscussion.subject == '' || newDiscussion.message == null"
|
||||
(click)="add()">
|
||||
{{ 'addon.mod_forum.posttoforum' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="hasOffline">
|
||||
<ion-button expand="block" color="light" (click)="discard()">{{ 'core.discard' | translate }}</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
<!-- Template to render a list of conversations. -->
|
||||
<ng-template #attemptsTemplate let-attempts="attempts">
|
||||
<!-- "Header" of the table -->
|
||||
<ion-item class="ion-text-wrap addon-mod_h5pactivity-table-header hide-detail" detail="true">
|
||||
<ion-item class="ion-text-wrap addon-mod_h5pactivity-table-header" [detail]="false">
|
||||
<ion-label>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col class="ion-text-center">#</ion-col>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<core-loading [hideUntil]="loaded">
|
||||
<ion-list *ngIf="users.length">
|
||||
<!-- "Header" of the table -->
|
||||
<ion-item class="addon-mod_h5pactivity-table-header hide-detail font-bold" detail="true">
|
||||
<ion-item class="addon-mod_h5pactivity-table-header font-bold" [detail]="false">
|
||||
<ion-label>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col class="ion-text-center" size="4">{{ 'core.user' | translate }}</ion-col>
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
<div class="addon-mod-imscp-container">
|
||||
<core-iframe *ngIf="!showLoading" [src]="src" [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="true"></core-iframe>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<!-- TODO Add a contents page to avoid having both bars -->
|
||||
<core-navigation-bar collapsible-footer *ngIf="!showLoading && navigationItems.length > 1 && false" [items]="navigationItems"
|
||||
(action)="loadItem($event)">
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<!-- TODO Add a contents page to avoid having both bars. Please add here start/resume buttons. -->
|
||||
<core-navigation-bar *ngIf="navigationItems.length > 1" [items]="navigationItems" (action)="loadItem($event)">
|
||||
</core-navigation-bar>
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -38,84 +38,15 @@
|
|||
</ion-item>
|
||||
<ion-button expand="block" type="submit">
|
||||
{{ 'addon.mod_lesson.continue' | translate }}
|
||||
<ion-icon slot="end" name="fas-chevron-right" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
|
||||
<input type="submit" class="core-submit-hidden-enter" />
|
||||
</form>
|
||||
</ion-card>
|
||||
|
||||
<core-loading [hideUntil]="!showSpinner">
|
||||
<core-loading [hideUntil]="!showSpinner" *ngIf="canViewReports">
|
||||
<ion-list *ngIf="(lesson && !preventReasons.length) || retakeToReview">
|
||||
<ng-container *ngIf="retakeToReview">
|
||||
<!-- A retake was finished in a synchronization, allow reviewing it. -->
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label class="ion-padding-bottom">
|
||||
{{ 'addon.mod_lesson.retakefinishedinsync' | translate }}
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" (click)="review()">
|
||||
{{ 'addon.mod_lesson.review' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="lesson && !preventReasons.length">
|
||||
<ion-item class="ion-text-wrap" *ngIf="leftDuringTimed && !lesson.timelimit && !finishedOffline">
|
||||
<!-- User left during the session and there is no time limit, ask to continue. -->
|
||||
<ion-label>
|
||||
<p [innerHTML]="'addon.mod_lesson.youhaveseen' | translate"></p>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button expand="block" color="light" (click)="start(false)">
|
||||
{{ 'core.no' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" (click)="start(true)">
|
||||
{{ 'core.yes' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="leftDuringTimed && lesson.timelimit && lesson.retake && !finishedOffline">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<!-- User left during the session with time limit and retakes allowed, ask to continue. -->
|
||||
<ion-label [innerHTML]="'addon.mod_lesson.leftduringtimed' | translate"></ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" (click)="start(false)">
|
||||
{{ 'addon.mod_lesson.continue' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="leftDuringTimed && lesson.timelimit && !lesson.retake">
|
||||
<!-- User left during the session with time limit and retakes not allowed.
|
||||
This should be handled by preventMessages. -->
|
||||
<ion-label [innerHTML]="'addon.mod_lesson.leftduringtimednoretake' | translate"></ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="!leftDuringTimed && !finishedOffline">
|
||||
<!-- User hasn't left during the session, show a start button. -->
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" *ngIf="!canManage" (click)="start(false)">
|
||||
{{ 'core.start' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" *ngIf="canManage" (click)="start(false)">
|
||||
{{ 'addon.mod_lesson.preview' | translate }}
|
||||
<ion-icon name="fas-search" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-button class="ion-text-wrap" *ngIf="finishedOffline" expand="block" (click)="start(true)">
|
||||
<!-- There's an attempt finished in offline. Let the user continue, showing the end of lesson. -->
|
||||
{{ 'addon.mod_lesson.continue' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
<ng-container *ngTemplateOutlet="buttons"></ng-container>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ng-template>
|
||||
|
@ -277,8 +208,81 @@
|
|||
</ng-template>
|
||||
</core-tab>
|
||||
</core-tabs>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="!canViewReports && ((lesson && !preventReasons.length) || retakeToReview)">
|
||||
<ng-container *ngTemplateOutlet="buttons"></ng-container>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
|
||||
<ng-template #buttons>
|
||||
<ng-container *ngIf="retakeToReview">
|
||||
<!-- A retake was finished in a synchronization, allow reviewing it. -->
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label class="ion-padding-bottom">
|
||||
{{ 'addon.mod_lesson.retakefinishedinsync' | translate }}
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" (click)="review()">
|
||||
{{ 'addon.mod_lesson.review' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="lesson && !preventReasons.length">
|
||||
<ion-item class="ion-text-wrap" *ngIf="leftDuringTimed && !lesson.timelimit && !finishedOffline">
|
||||
<!-- User left during the session and there is no time limit, ask to continue. -->
|
||||
<ion-label>
|
||||
<p [innerHTML]="'addon.mod_lesson.youhaveseen' | translate"></p>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button expand="block" fill="outline" (click)="start(false)">
|
||||
{{ 'core.no' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" (click)="start(true)">
|
||||
{{ 'core.yes' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="leftDuringTimed && lesson.timelimit && lesson.retake && !finishedOffline">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<!-- User left during the session with time limit and retakes allowed, ask to continue. -->
|
||||
<ion-label [innerHTML]="'addon.mod_lesson.leftduringtimed' | translate"></ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" (click)="start(false)">
|
||||
{{ 'addon.mod_lesson.continue' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="leftDuringTimed && lesson.timelimit && !lesson.retake">
|
||||
<!-- User left during the session with time limit and retakes not allowed.
|
||||
This should be handled by preventMessages. -->
|
||||
<ion-label [innerHTML]="'addon.mod_lesson.leftduringtimednoretake' | translate"></ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="!leftDuringTimed && !finishedOffline">
|
||||
<!-- User hasn't left during the session, show a start button. -->
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" *ngIf="!canManage" (click)="start(false)">
|
||||
{{ 'core.start' | translate }}
|
||||
</ion-button>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" fill="outline" *ngIf="canManage" (click)="start(false)">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_lesson.preview' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-button class="ion-text-wrap ion-margin" *ngIf="finishedOffline" expand="block" (click)="start(true)">
|
||||
<!-- There's an attempt finished in offline. Let the user continue, showing the end of lesson. -->
|
||||
{{ 'addon.mod_lesson.continue' | translate }}
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<core-loading [hideUntil]="loaded">
|
||||
<!-- Info messages. Only show the first one. -->
|
||||
<ion-card class="core-info-card" *ngIf="lesson && messages?.length">
|
||||
<ion-item>
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-info-circle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ messages[0].message }}</ion-label>
|
||||
</ion-item>
|
||||
|
@ -86,13 +86,12 @@
|
|||
<!-- Essay. -->
|
||||
<ng-container *ngSwitchCase="'essay'">
|
||||
<ion-item *ngIf="question.textarea">
|
||||
<ion-label>
|
||||
<ion-label class="sr-only">{{ 'core.content' | translate }}</ion-label>
|
||||
<core-rich-text-editor placeholder="{{ 'addon.mod_lesson.youranswer' | translate }}"
|
||||
[control]="question.control" [component]="component" [componentId]="lesson?.coursemodule"
|
||||
[autoSave]="true" contextLevel="module" [contextInstanceId]="lesson?.coursemodule"
|
||||
elementId="answer_editor">
|
||||
</core-rich-text-editor>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="!question.textarea && question.useranswer">
|
||||
<ion-label>
|
||||
|
@ -185,12 +184,12 @@
|
|||
</core-progress-bar>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<div class="core-info-card" *ngIf="lesson?.progressbar && canManage">
|
||||
<ion-item>
|
||||
<ion-card class="core-info-card" *ngIf="lesson?.progressbar && canManage">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-info-circle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ 'addon.mod_lesson.progressbarteacherwarning2' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-card>
|
||||
</ion-list>
|
||||
|
||||
<!-- End of lesson reached. -->
|
||||
|
@ -254,8 +253,8 @@
|
|||
<ion-label>{{ eolData.modattemptsnoteacher.message }}</ion-label>
|
||||
</ion-item>
|
||||
<!-- If activity link was successfully formatted, render the button. -->
|
||||
<ion-button *ngIf="activityLink && activityLink.formatted" expand="block" color="light" [href]="activityLink.href" core-link
|
||||
[capture]="true" class="ion-text-wrap ion-margin button-no-uppercase">
|
||||
<ion-button *ngIf="activityLink && activityLink.formatted" expand="block" fill="outline" [href]="activityLink.href"
|
||||
core-link [capture]="true" class="ion-text-wrap ion-margin button-no-uppercase">
|
||||
<core-format-text [text]="activityLink.label" contextLevel="module" [contextInstanceId]="lesson?.coursemodule"
|
||||
[courseId]="courseId">
|
||||
</core-format-text>
|
||||
|
@ -290,10 +289,10 @@
|
|||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin" color="light" *ngIf="review" (click)="changePage(LESSON_EOL)">
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin" *ngIf="review" (click)="changePage(LESSON_EOL)">
|
||||
{{ 'addon.mod_lesson.finish' | translate }}
|
||||
</ion-button>
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin" color="light" *ngFor="let button of processDataButtons"
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin" fill="outline" *ngFor="let button of processDataButtons"
|
||||
(click)="changePage(button.pageId, true)">
|
||||
{{ button.label | translate }}
|
||||
</ion-button>
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<!-- Content page, display a button and the content. -->
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button expand="block" class="ion-text-wrap" color="light" [disabled]="true">
|
||||
<ion-button expand="block" class="ion-text-wrap" fill="outline" [disabled]="true">
|
||||
{{ answer[0].buttonText }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
|
|
|
@ -557,7 +557,7 @@ export class AddonModLessonHelperProvider {
|
|||
|
||||
// Add some HTML to the answer if needed.
|
||||
if (textarea) {
|
||||
data[textarea.name] = CoreTextUtils.formatHtmlLines(<string> data[textarea.name]);
|
||||
data[textarea.name] = CoreTextUtils.formatHtmlLines(<string> data[textarea.name] || '');
|
||||
}
|
||||
} else if (question.template == 'multichoice' && (<AddonModLessonMultichoiceQuestion> question).multi) {
|
||||
// Only send the options with value set to true.
|
||||
|
|
|
@ -13,14 +13,15 @@
|
|||
[componentId]="componentId" [courseId]="courseId">
|
||||
</core-course-module-info>
|
||||
|
||||
<div class="ion-padding">
|
||||
<ion-button expand="block" (click)="launch()">
|
||||
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width">
|
||||
<ion-button expand="block" (click)="launch()" class="ion-margin ion-text-wrap">
|
||||
{{ 'addon.mod_lti.launchactivity' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<!-- Activity info. -->
|
||||
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
|
||||
[courseId]="courseId" [hasDataToSync]="buttonText && hasOffline && !showStatusSpinner">
|
||||
[courseId]="courseId" [hasDataToSync]="buttonText && hasOffline">
|
||||
</core-course-module-info>
|
||||
|
||||
<!-- Access rules description messages. -->
|
||||
|
@ -134,22 +134,26 @@
|
|||
</ion-list>
|
||||
</ion-card>
|
||||
|
||||
<!-- More data and button to start/continue. -->
|
||||
<ion-card *ngIf="quiz">
|
||||
<ion-list>
|
||||
<!-- More data. -->
|
||||
<ng-container *ngIf="quiz">
|
||||
<!-- Error messages. -->
|
||||
<ion-item class="ion-text-wrap core-danger-item addon-mod_quiz-prevent-messages" *ngFor="let message of preventMessages">
|
||||
<ion-card class="core-danger-card addon-mod_quiz-prevent-messages">
|
||||
<ion-item class="ion-text-wrap" *ngFor="let message of preventMessages">
|
||||
<ion-label>
|
||||
<p>{{ message }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap core-danger-item addon-mod_quiz-no-questions" *ngIf="quiz.hasquestions === 0">
|
||||
</ion-card>
|
||||
<ion-card class="core-danger-card addon-mod_quiz-no-questions" *ngIf="quiz.hasquestions === 0">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p>{{ 'addon.mod_quiz.noquestions' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap core-danger-item addon-mod_quiz-unsupported-questions"
|
||||
</ion-card>
|
||||
<ion-card class="core-danger-card addon-mod_quiz-unsupported-questions"
|
||||
*ngIf="!hasSupportedQuestions && unsupportedQuestions.length">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'addon.mod_quiz.errorquestionsnotsupported' | translate }}</p>
|
||||
<p *ngFor="let type of unsupportedQuestions"
|
||||
|
@ -158,7 +162,9 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap core-danger-item addon-mod_quiz-unsupported-rules" *ngIf="unsupportedRules.length">
|
||||
</ion-card>
|
||||
<ion-card class="core-danger-card addon-mod_quiz-unsupported-rules" *ngIf="unsupportedRules.length">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'addon.mod_quiz.errorrulesnotsupported' | translate }}</p>
|
||||
<p *ngFor="let name of unsupportedRules"
|
||||
|
@ -167,24 +173,34 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap core-danger-item addon-mod_quiz-unsupported-behaviours" *ngIf="behaviourSupported === false">
|
||||
</ion-card>
|
||||
<ion-card class="core-danger-card addon-mod_quiz-unsupported-behaviours" *ngIf="behaviourSupported === false">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'addon.mod_quiz.errorbehaviournotsupported' | translate }}</p>
|
||||
<p class="addon-mod_quiz-unsupported-behaviour">{{ quiz.preferredbehaviour }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<!-- Other warnings. -->
|
||||
<ion-item class="core-warning-item ion-text-wrap" *ngIf="hasSupportedQuestions && unsupportedQuestions.length">
|
||||
<ion-card class="core-warning-card" *ngIf="hasSupportedQuestions && unsupportedQuestions.length">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'addon.mod_quiz.canattemptbutnotsubmit' | translate }}</p>
|
||||
<p>{{ 'addon.mod_quiz.warningquestionsnotsupported' | translate }}</p>
|
||||
<p *ngFor="let type of unsupportedQuestions">{{ type }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="quiz">
|
||||
<!-- Button to start/continue. -->
|
||||
<ion-button *ngIf="buttonText && !showStatusSpinner" expand="block" (click)="attemptQuiz()" class="ion-margin ion-text-wrap">
|
||||
<ion-button *ngIf="buttonText" expand="block" (click)="attemptQuiz()" class="ion-margin ion-text-wrap"
|
||||
[disabled]="showStatusSpinner">
|
||||
<ion-spinner *ngIf="showStatusSpinner" slot="start" aria-hidden="true"></ion-spinner>
|
||||
{{ buttonText | translate }}
|
||||
</ion-button>
|
||||
|
||||
|
@ -195,17 +211,10 @@
|
|||
{{ 'core.openinbrowser' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<!-- Spinner shown while downloading or calculating. -->
|
||||
<ion-item class="ion-text-center" *ngIf="showStatusSpinner">
|
||||
<ion-label>
|
||||
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -13,19 +13,6 @@
|
|||
<ion-content class="addon-mod_quiz-navigation-modal">
|
||||
<nav>
|
||||
<ion-list>
|
||||
<!-- In player, show button to finish attempt. -->
|
||||
<ion-item button class="ion-text-wrap" *ngIf="!isReview" (click)="loadPage(-1)" detail="true">
|
||||
<ion-label>{{ 'addon.mod_quiz.finishattemptdots' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- In review we can toggle between all questions in same page or one page at a time. -->
|
||||
<ion-item button class="ion-text-wrap" *ngIf="isReview && numPages > 1" (click)="switchMode()" detail="true">
|
||||
<ion-label>
|
||||
<span *ngIf="!showAll">{{ 'addon.mod_quiz.showall' | translate }}</span>
|
||||
<span *ngIf="showAll">{{ 'addon.mod_quiz.showeachpage' | translate }}</span>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item button class="ion-text-wrap {{question.stateClass}}" *ngFor="let question of navigation"
|
||||
[attr.aria-current]="!summaryShown && currentPage == question.page ? 'page' : 'false'"
|
||||
(click)="loadPage(question.page, question.slot)" detail="false">
|
||||
|
@ -50,19 +37,12 @@
|
|||
slot="end">
|
||||
</ion-icon>
|
||||
</ion-item>
|
||||
|
||||
<!-- In player, show button to finish attempt. -->
|
||||
<ion-item button class="ion-text-wrap" *ngIf="!isReview" (click)="loadPage(-1)" detail="true">
|
||||
<ion-label>{{ 'addon.mod_quiz.finishattemptdots' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- In review we can toggle between all questions in same page or one page at a time. -->
|
||||
<ion-item button class="ion-text-wrap" *ngIf="isReview && numPages > 1" (click)="switchMode()" detail="true">
|
||||
<ion-label>
|
||||
<span *ngIf="!showAll">{{ 'addon.mod_quiz.showall' | translate }}</span>
|
||||
<span *ngIf="showAll">{{ 'addon.mod_quiz.showeachpage' | translate }}</span>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</nav>
|
||||
</ion-content>
|
||||
<ion-footer *ngIf="!isReview">
|
||||
<!-- In player, show button to finish attempt. -->
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" (click)="loadPage(-1)">
|
||||
{{ 'addon.mod_quiz.finishattemptdots' | translate }}
|
||||
</ion-button>
|
||||
</ion-footer>
|
||||
|
|
|
@ -26,15 +26,10 @@ import { ModalController } from '@singletons';
|
|||
})
|
||||
export class AddonModQuizNavigationModalComponent {
|
||||
|
||||
static readonly CHANGE_PAGE = 1;
|
||||
static readonly SWITCH_MODE = 2;
|
||||
|
||||
@Input() navigation?: AddonModQuizNavigationQuestion[]; // Whether the user is reviewing the attempt.
|
||||
@Input() summaryShown?: boolean; // Whether summary is currently being shown.
|
||||
@Input() currentPage?: number; // Current page.
|
||||
@Input() isReview?: boolean; // Whether the user is reviewing the attempt.
|
||||
@Input() numPages = 0; // Num of pages for review mode.
|
||||
@Input() showAll?: boolean; // Whether to show all questions in same page or not for review mode.
|
||||
|
||||
/**
|
||||
* Close modal.
|
||||
|
@ -51,21 +46,11 @@ export class AddonModQuizNavigationModalComponent {
|
|||
*/
|
||||
loadPage(page: number, slot?: number): void {
|
||||
ModalController.dismiss(<AddonModQuizNavigationModalReturn>{
|
||||
action: AddonModQuizNavigationModalComponent.CHANGE_PAGE,
|
||||
page,
|
||||
slot,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch mode in review.
|
||||
*/
|
||||
switchMode(): void {
|
||||
ModalController.dismiss(<AddonModQuizNavigationModalReturn>{
|
||||
action: AddonModQuizNavigationModalComponent.SWITCH_MODE,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +61,6 @@ export type AddonModQuizNavigationQuestion = CoreQuestionQuestionParsed & {
|
|||
};
|
||||
|
||||
export type AddonModQuizNavigationModalReturn = {
|
||||
action: number;
|
||||
page?: number;
|
||||
slot?: number;
|
||||
};
|
||||
|
|
|
@ -53,15 +53,21 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button *ngIf="showReviewColumn && attempt.finished" class="ion-margin" expand="block" (click)="reviewAttempt()">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_quiz.review' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-item class="ion-text-wrap core-danger-item" *ngIf="!showReviewColumn">
|
||||
<ion-label>
|
||||
<p>{{ 'addon.mod_quiz.noreviewattempt' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<div collapsible-footer *ngIf="loaded && attempt && showReviewColumn && attempt.finished" slot="fixed">
|
||||
<div class="list-item-limited-width">
|
||||
<ion-button class="ion-margin ion-text-wrap" expand="block" (click)="reviewAttempt()">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_quiz.review' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -28,32 +28,10 @@
|
|||
<core-timer [endTime]="endTime" (finished)="timeUp()" [timerText]="'addon.mod_quiz.timeleft' | translate" [align]="'center'">
|
||||
</core-timer>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button fill="clear" *ngIf="previousPage >= 0" (click)="changePage(previousPage)"
|
||||
[attr.aria-label]="'core.previous' | translate">
|
||||
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="nextPage >= -1" (click)="changePage(nextPage)" [attr.aria-label]="'core.next' | translate">
|
||||
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<!-- Navigation arrows if there's no timer. -->
|
||||
<ion-toolbar *ngIf="!endTime && questions.length && !quizAborted && !showSummary" color="light">
|
||||
<ion-buttons slot="end">
|
||||
<ion-button fill="clear" *ngIf="previousPage >= 0" (click)="changePage(previousPage)"
|
||||
[attr.aria-label]="'core.previous' | translate">
|
||||
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="nextPage >= -1" (click)="changePage(nextPage)" [attr.aria-label]="'core.next' | translate">
|
||||
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
|
||||
<core-loading [hideUntil]="loaded" class="has-spacer">
|
||||
<!-- Button to start attempting. -->
|
||||
<ion-button *ngIf="!attempt" expand="block" class="ion-margin" (click)="start()">
|
||||
{{ 'addon.mod_quiz.startattempt' | translate }}
|
||||
|
@ -89,22 +67,23 @@
|
|||
</form>
|
||||
|
||||
<!-- Go to next or previous page. -->
|
||||
<ion-grid class="ion-text-wrap" *ngIf="questions.length && !quizAborted && !showSummary">
|
||||
<ion-row>
|
||||
<ion-row *ngIf="questions.length && !quizAborted && !showSummary" class="spacer-top">
|
||||
<ion-col *ngIf="previousPage >= 0">
|
||||
<ion-button expand="block" color="light" (click)="changePage(previousPage)">
|
||||
<ion-button expand="block" fill="outline" (click)="changePage(previousPage)" class="ion-text-wrap">
|
||||
<ion-icon name="fas-chevron-left" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.previous' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="nextPage >= -1">
|
||||
<ion-button expand="block" (click)="changePage(nextPage)">
|
||||
<ion-button expand="block" (click)="changePage(nextPage)" class="ion-text-wrap" *ngIf="nextPage > 0">
|
||||
{{ 'core.next' | translate }}
|
||||
<ion-icon name="fas-chevron-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button expand="block" (click)="changePage(nextPage)" class="ion-text-wrap" *ngIf="nextPage == -1">
|
||||
{{ 'core.submit' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
<!-- Summary -->
|
||||
<ion-card *ngIf="!quizAborted && showSummary && summaryQuestions.length" class="addon-mod_quiz-table">
|
||||
|
@ -141,11 +120,6 @@
|
|||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Button to return to last page seen. -->
|
||||
<ion-button *ngIf="canReturn" expand="block" class="ion-margin" (click)="changePage(attempt!.currentpage!)">
|
||||
{{ 'addon.mod_quiz.returnattempt' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<!-- Due date warning. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="dueDateWarning">
|
||||
<ion-label>{{ dueDateWarning }}</ion-label>
|
||||
|
@ -162,17 +136,6 @@
|
|||
<p *ngFor="let message of preventSubmitMessages">{{message}}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-button *ngIf="preventSubmitMessages.length" expand="block" [href]="moduleUrl" core-link [showBrowserWarning]="false">
|
||||
{{ 'core.openinbrowser' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
<!-- Button to submit the quiz. -->
|
||||
<ion-button *ngIf="!attempt!.finishedOffline && !preventSubmitMessages.length" expand="block" class="ion-margin"
|
||||
(click)="finishAttempt(true)">
|
||||
{{ 'addon.mod_quiz.submitallandfinish' | translate }}
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
|
||||
<!-- Quiz aborted -->
|
||||
|
@ -180,10 +143,25 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>{{ 'addon.mod_quiz.errorparsequestions' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
<ion-button expand="block" class="ion-margin" [href]="moduleUrl" core-link [showBrowserWarning]="false">
|
||||
<ion-button *ngIf="canReturn" expand="block" class="ion-margin ion-text-wrap" fill="outline"
|
||||
(click)="changePage(attempt!.currentpage!)">
|
||||
{{ 'addon.mod_quiz.returnattempt' | translate }}
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
|
||||
<div collapsible-footer *ngIf="!quizAborted && showSummary && summaryQuestions.length && loaded" slot="fixed"
|
||||
class="list-item-limited-width">
|
||||
<ion-button *ngIf="preventSubmitMessages.length" expand="block" class="ion-margin ion-text-wrap" [href]="moduleUrl" core-link
|
||||
[showBrowserWarning]="false">
|
||||
{{ 'core.openinbrowser' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
|
||||
<!-- Button to submit the quiz. -->
|
||||
<ion-button *ngIf="!attempt!.finishedOffline && !preventSubmitMessages.length" expand="block" class="ion-margin ion-text-wrap"
|
||||
(click)="finishAttempt(true)">
|
||||
{{ 'addon.mod_quiz.submitallandfinish' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -602,9 +602,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
|||
},
|
||||
});
|
||||
|
||||
if (modalData && modalData.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) {
|
||||
this.changePage(modalData.page!, true, modalData.slot);
|
||||
if (!modalData) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.changePage(modalData.page!, true, modalData.slot);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -87,8 +87,6 @@
|
|||
|
||||
<!-- Questions -->
|
||||
<div *ngIf="attempt && questions.length">
|
||||
<!-- Arrows to go to next/previous. -->
|
||||
<ng-container *ngTemplateOutlet="navArrows"></ng-container>
|
||||
|
||||
<!-- Questions. -->
|
||||
<div *ngFor="let question of questions">
|
||||
|
@ -113,28 +111,30 @@
|
|||
</core-question>
|
||||
</ion-card>
|
||||
</div>
|
||||
|
||||
<!-- Arrows to go to next/previous. -->
|
||||
<ng-container *ngTemplateOutlet="navArrows"></ng-container>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
||||
<!-- Arrows to go to next/previous. -->
|
||||
<ng-template #navArrows>
|
||||
<ion-grid>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col class="ion-text-start">
|
||||
<ion-button color="light" *ngIf="previousPage >= 0" (click)="changePage(previousPage)"
|
||||
[title]="'core.previous' | translate">
|
||||
<div collapsible-footer *ngIf="loaded && numPages > 1" slot="fixed">
|
||||
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding ion-wrap">
|
||||
<ion-col class="ion-text-start ion-no-padding core-navigation-arrow" size="auto" *ngIf="!showAll">
|
||||
<ion-button [disabled]="previousPage < 0" fill="clear" color="dark" [attr.aria-label]="'core.previous' | translate"
|
||||
(click)="changePage(previousPage)">
|
||||
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col class="ion-text-end">
|
||||
<ion-button color="light" *ngIf="nextPage >= -1" (click)="changePage(nextPage)" [attr.aria-label]="'core.next' | translate">
|
||||
<ion-col class="ion-text-center">
|
||||
<!-- In review we can toggle between all questions in same page or one page at a time. -->
|
||||
<ion-button class="ion-text-wrap" *ngIf="numPages > 1" (click)="switchMode()" fill="outline">
|
||||
<span *ngIf="!showAll">{{ 'addon.mod_quiz.showall' | translate }}</span>
|
||||
<span *ngIf="showAll">{{ 'addon.mod_quiz.showeachpage' | translate }}</span>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col class="ion-text-end ion-no-padding core-navigation-arrow" size="auto" *ngIf="!showAll">
|
||||
<ion-button [disabled]="nextPage >= numPages" fill="clear" color="dark" [attr.aria-label]="'core.next' | translate"
|
||||
(click)="changePage(nextPage)">
|
||||
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ng-template>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -53,7 +53,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
attempt?: AddonModQuizAttemptWSData; // The attempt being reviewed.
|
||||
component = AddonModQuizProvider.COMPONENT; // Component to link the files to.
|
||||
showAll = false; // Whether to view all questions in the same page.
|
||||
numPages?: number; // Number of pages.
|
||||
numPages = 1; // Number of pages.
|
||||
showCompleted = false; // Whether to show completed time.
|
||||
additionalData?: AddonModQuizWSAdditionalData[]; // Additional data to display for the attempt.
|
||||
loaded = false; // Whether data has been loaded.
|
||||
|
@ -112,10 +112,9 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
* Change the current page. If slot is supplied, try to scroll to that question.
|
||||
*
|
||||
* @param page Page to load. -1 means all questions in same page.
|
||||
* @param fromModal Whether the page was selected using the navigation modal.
|
||||
* @param slot Slot of the question to scroll to.
|
||||
*/
|
||||
async changePage(page: number, fromModal?: boolean, slot?: number): Promise<void> {
|
||||
async changePage(page: number, slot?: number): Promise<void> {
|
||||
if (slot !== undefined && (this.attempt!.currentpage == -1 || page == this.currentPage)) {
|
||||
// Scrol to a certain question in the current page.
|
||||
this.scrollToQuestion(slot);
|
||||
|
@ -183,7 +182,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
this.setSummaryCalculatedData(data);
|
||||
|
||||
this.questions = data.questions;
|
||||
this.nextPage = page == -1 ? -2 : page + 1;
|
||||
this.nextPage = page + 1;
|
||||
this.previousPage = page - 1;
|
||||
|
||||
this.questions.forEach((question) => {
|
||||
|
@ -341,8 +340,6 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
summaryShown: false,
|
||||
currentPage: this.attempt?.currentpage,
|
||||
isReview: true,
|
||||
numPages: this.numPages,
|
||||
showAll: this.showAll,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -350,11 +347,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
if (modalData.action == AddonModQuizNavigationModalComponent.CHANGE_PAGE) {
|
||||
this.changePage(modalData.page!, true, modalData.slot);
|
||||
} else if (modalData.action == AddonModQuizNavigationModalComponent.SWITCH_MODE) {
|
||||
this.switchMode();
|
||||
}
|
||||
this.changePage(modalData.page!, modalData.slot);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</core-navbar-buttons>
|
||||
|
||||
<!-- Content. -->
|
||||
<core-loading [hideUntil]="!showLoading" class="safe-area-padding">
|
||||
<core-loading [hideUntil]="!showLoading" class="safe-area-padding core-loading-full-height">
|
||||
|
||||
<!-- Activity info. -->
|
||||
<core-course-module-info [module]="module" [courseId]="courseId" [description]="displayDescription && description"
|
||||
|
@ -76,8 +76,12 @@
|
|||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</ng-container>
|
||||
|
||||
<ion-button expand="block" class="ion-margin" (click)="open(openFileAction.OPEN)">
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="mode == 'external'">
|
||||
<ion-button expand="block" (click)="open(openFileAction.OPEN)" class="ion-margin ion-text-wrap">
|
||||
<ng-container *ngIf="isStreamedFile">
|
||||
<ion-icon name="fas-play" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.play' | translate }}
|
||||
|
@ -88,15 +92,13 @@
|
|||
</ng-container>
|
||||
</ion-button>
|
||||
|
||||
<ion-button *ngIf="isIOS && (!shouldOpenInBrowser || !isOnline)" expand="block" class="ion-margin"
|
||||
(click)="open(openFileAction.OPEN_WITH)">
|
||||
<ion-button *ngIf="isIOS && (!shouldOpenInBrowser || !isOnline)" expand="block" (click)="open(openFileAction.OPEN_WITH)"
|
||||
class="ion-margin ion-text-wrap">
|
||||
<ion-icon name="far-share-square" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.openwith' | translate }}
|
||||
</ion-button>
|
||||
</ion-list>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</div>
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ng-container *ngIf="scorm && !showLoading && !scorm.warningMessage">
|
||||
<div *ngIf="scorm && !showLoading && !scorm.warningMessage" class="list-item-limited-width">
|
||||
<!-- Attempts status. -->
|
||||
<ion-card *ngIf="(scorm.displayattemptstatus || offlineAttempts.length) && !skip">
|
||||
<ion-card-header class="ion-text-wrap">
|
||||
|
@ -31,7 +31,7 @@
|
|||
<ng-container *ngIf="scorm.displayattemptstatus">
|
||||
<ion-item class="ion-text-wrap" *ngIf="scorm.maxattempt! >= 0">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.noattemptsallowed' | translate }}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.noattemptsallowed' | translate }}</p>
|
||||
</ion-label>
|
||||
<p slot="end">
|
||||
<span *ngIf="scorm.maxattempt == 0">{{ 'core.unlimited' | translate }}</span>
|
||||
|
@ -40,13 +40,23 @@
|
|||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="numAttempts >= 0">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.noattemptsmade' | translate }}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.noattemptsmade' | translate }}</p>
|
||||
</ion-label>
|
||||
<p slot="end">{{ numAttempts }}</p>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="onlineAttempts.length > 0" button class="divider ion-text-wrap" (click)="toggleGrades()"
|
||||
[attr.aria-label]="(gradesExpanded ? 'core.collapse' : 'core.expand') | translate" detail="false">
|
||||
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
|
||||
[class.expandable-status-icon-expanded]="gradesExpanded">
|
||||
</ion-icon>
|
||||
<ion-label>
|
||||
<h3 class="item-heading">{{'core.grades.grades' | translate}}</h3>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ng-container *ngIf="gradesExpanded && onlineAttempts.length > 0">
|
||||
<ion-item class="ion-text-wrap" *ngFor="let attempt of onlineAttempts">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</p>
|
||||
</ion-label>
|
||||
<p slot="end">
|
||||
<span *ngIf="attempt.grade != -1">{{ attempt.gradeFormatted }}</span>
|
||||
|
@ -54,9 +64,10 @@
|
|||
</p>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ion-item class="ion-text-wrap" *ngFor="let attempt of offlineAttempts">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</p>
|
||||
<p *ngIf="!scorm.maxattempt || attempt.num <= scorm.maxattempt">
|
||||
{{ 'addon.mod_scorm.offlineattemptnote' | translate }}
|
||||
</p>
|
||||
|
@ -71,13 +82,13 @@
|
|||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="scorm.displayattemptstatus && gradeMethodReadable">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.grademethod' | translate }}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.grademethod' | translate }}</p>
|
||||
</ion-label>
|
||||
<p slot="end">{{ gradeMethodReadable }}</p>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="scorm.displayattemptstatus && gradeFormatted">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_scorm.gradereported' | translate }}</h3>
|
||||
<p class="item-heading">{{ 'addon.mod_scorm.gradereported' | translate }}</p>
|
||||
</ion-label>
|
||||
<p slot="end">
|
||||
<span *ngIf="grade != -1">{{ gradeFormatted }}</span>
|
||||
|
@ -86,7 +97,7 @@
|
|||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="syncTime">
|
||||
<ion-label>
|
||||
<h3>{{ 'core.lastsync' | translate }}</h3>
|
||||
<p class="item-heading">{{ 'core.lastsync' | translate }}</p>
|
||||
<p>{{ syncTime }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -144,9 +155,12 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
</div>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="scorm && !scorm.warningMessage">
|
||||
<!-- Open in browser button. -->
|
||||
<ion-card *ngIf="errorMessage">
|
||||
<ng-container *ngIf="errorMessage">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="text-danger">{{ errorMessage | translate }}</p>
|
||||
|
@ -156,20 +170,19 @@
|
|||
{{ 'core.openinbrowser' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
|
||||
<!-- Warning that user doesn't have any more attempts. -->
|
||||
<ion-card *ngIf="!errorMessage && attemptsLeft == 0">
|
||||
<ion-card *ngIf="!errorMessage && attemptsLeft == 0" class="core-danger-card">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p class="text-danger">{{ 'addon.mod_scorm.exceededmaxattempts' | translate }}</p>
|
||||
<p>{{ 'addon.mod_scorm.exceededmaxattempts' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ng-container *ngIf="!errorMessage && (!scorm.lastattemptlock || attemptsLeft > 0)">
|
||||
<!-- Open SCORM in app form -->
|
||||
<ion-card *ngIf="!errorMessage && scorm && (!scorm.lastattemptlock || attemptsLeft > 0)">
|
||||
<ion-list>
|
||||
<ng-container *ngIf="!downloading && !skip">
|
||||
<!-- Create new attempt -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="!scorm.forcenewattempt && numAttempts > 0 && !incomplete && attemptsLeft > 0">
|
||||
|
@ -178,29 +191,23 @@
|
|||
</ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="statusMessage">
|
||||
<ion-label>
|
||||
<ion-item *ngIf="statusMessage">
|
||||
<ion-label class="ion-text-wrap ion-no-margin ion-margin-top">
|
||||
<p>{{ statusMessage | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<div class="adaptable-buttons-row">
|
||||
<!-- Open mode (Preview or Normal) -->
|
||||
<ion-grid>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col *ngIf="!scorm.hidebrowse">
|
||||
<ion-button expand="block" fill="outline" (click)="open($event, true)" class="ion-text-wrap">
|
||||
<ion-button *ngIf="!scorm.hidebrowse" expand="block" fill="outline" (click)="open($event, true)"
|
||||
class="ion-text-wrap ion-margin">
|
||||
<ion-icon name="fas-search" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_scorm.browse' | translate }}
|
||||
<ion-icon name="fas-search" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-button expand="block" (click)="open($event)" class="ion-text-wrap">
|
||||
<ion-button expand="block" (click)="open($event)" class="ion-text-wrap ion-margin">
|
||||
{{ 'addon.mod_scorm.enter' | translate }}
|
||||
<ion-icon name="fas-arrow-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Download progress. -->
|
||||
|
@ -212,11 +219,9 @@
|
|||
</core-progress-bar>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</div>
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -81,6 +81,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
attemptsLeft = -1; // Number of attempts left.
|
||||
onlineAttempts: AttemptGrade[] = []; // Grades for online attempts.
|
||||
offlineAttempts: AttemptGrade[] = []; // Grades for offline attempts.
|
||||
gradesExpanded = false;
|
||||
|
||||
protected fetchContentDefaultError = 'addon.mod_scorm.errorgetscorm'; // Default error to show when loading contents.
|
||||
protected syncEventName = AddonModScormSyncProvider.AUTO_SYNCED;
|
||||
|
@ -523,6 +524,13 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle list of grades.
|
||||
*/
|
||||
toggleGrades(): void {
|
||||
this.gradesExpanded = !this.gradesExpanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a SCORM package.
|
||||
*
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
{{ 'addon.mod_survey.surveycompletednograph' | translate }}
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-margin" expand="block" [href]="module.url" core-link>
|
||||
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_survey.results' | translate }}
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
|
||||
<!-- Survey questions -->
|
||||
|
@ -119,18 +115,22 @@
|
|||
|
||||
</ng-container>
|
||||
</ion-grid>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<ion-button expand="block" (click)="submit()" [disabled]="!isValidResponse()">
|
||||
{{ 'core.submit' | translate }}
|
||||
</ion-button>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</form>
|
||||
|
||||
</core-loading>
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="survey && (survey.surveydone || (!hasOffline && questions && questions.length))">
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" [href]="module.url" core-link *ngIf="survey.surveydone">
|
||||
{{ 'addon.mod_survey.results' | translate }}
|
||||
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
<ion-button expand="block" class="ion-text-wrap ion-margin" (click)="submit()" [disabled]="!isValidResponse()"
|
||||
*ngIf="!survey.surveydone && !hasOffline && questions && questions.length">
|
||||
{{ 'core.submit' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -35,17 +35,16 @@
|
|||
<p>{{ url }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<ion-button expand="block" (click)="go()">
|
||||
</ion-list>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="url && !shouldIframe && (!shouldEmbed || !isOther)">
|
||||
<ion-button expand="block" (click)="go()" class="ion-margin ion-text-wrap">
|
||||
<ion-icon name="fas-link" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_url.accessurl' | translate }}
|
||||
</ion-button>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</div>
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -117,20 +117,6 @@
|
|||
<addon-mod-workshop-submission *ngIf="submission" [submission]="submission" [courseId]="workshop!.course" [module]="module"
|
||||
[workshop]="workshop" [access]="access">
|
||||
</addon-mod-workshop-submission>
|
||||
|
||||
<!-- Show only on current phase -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="showSubmit">
|
||||
<ion-label>
|
||||
<ion-button expand="block" *ngIf="access.creatingsubmissionallowed && !submission" (click)="gotoSubmit()">
|
||||
<ion-icon slot="start" name="fas-plus" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_workshop.createsubmission' | translate }}
|
||||
</ion-button>
|
||||
<ion-button expand="block" *ngIf="access.modifyingsubmissionallowed && submission" (click)="gotoSubmit()">
|
||||
<ion-icon slot="start" name="fas-edit" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_workshop.editsubmission' | translate }}
|
||||
</ion-button>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
|
||||
|
@ -231,8 +217,25 @@
|
|||
</ion-grid>
|
||||
</ion-card>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModule]="module"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="access && workshop?.phase >= PHASE_SUBMISSION && canSubmit && showSubmit &&
|
||||
((access.creatingsubmissionallowed && !submission) || (access.modifyingsubmissionallowed && submission))">
|
||||
<!-- Show only on current phase -->
|
||||
<ion-button expand="block" *ngIf="access.creatingsubmissionallowed && !submission" (click)="gotoSubmit()"
|
||||
class="ion-text-wrap ion-margin">
|
||||
<ion-icon slot="start" name="fas-plus" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_workshop.createsubmission' | translate }}
|
||||
</ion-button>
|
||||
<ion-button expand="block" *ngIf="access.modifyingsubmissionallowed && submission" (click)="gotoSubmit()"
|
||||
class="ion-text-wrap ion-margin">
|
||||
<ion-icon slot="start" name="fas-edit" aria-hidden="true"></ion-icon>
|
||||
{{ 'addon.mod_workshop.editsubmission' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<core-course-module-navigation [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
</div>
|
||||
</core-loading>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</ion-refresher>
|
||||
<core-loading [hideUntil]="notificationsLoaded">
|
||||
<div class="ion-padding" *ngIf="canMarkAllNotificationsAsRead">
|
||||
<ion-button [disabled]="loadingMarkAllNotificationsAsRead" expand="block" (click)="markAllNotificationsAsRead()" color="light">
|
||||
<ion-button [disabled]="loadingMarkAllNotificationsAsRead" expand="block" (click)="markAllNotificationsAsRead()" fill="outline">
|
||||
<ion-icon slot="start" name="fas-check" aria-hidden="true" *ngIf="!loadingMarkAllNotificationsAsRead"></ion-icon>
|
||||
<ion-spinner slot="start" [attr.aria-label]="'core.loading' | translate" *ngIf="loadingMarkAllNotificationsAsRead">
|
||||
</ion-spinner>
|
||||
|
|
|
@ -516,7 +516,7 @@ export class AddonQtypeEssayHandlerService implements CoreQuestionHandler {
|
|||
|
||||
if (!isPlainText) {
|
||||
// Add some HTML to the text if needed.
|
||||
answers[textarea.name] = CoreTextUtils.formatHtmlLines(<string> answers[textarea.name]);
|
||||
answers[textarea.name] = CoreTextUtils.formatHtmlLines(<string> answers[textarea.name] || '');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
<ion-icon *ngIf="option.isCorrect === 0" class="core-correct-icon" name="fas-times" color="danger"
|
||||
[attr.aria-label]="'core.question.incorrect' | translate"></ion-icon>
|
||||
</ion-item>
|
||||
<ion-button *ngIf="!multiQuestion.disabled" class="ion-text-wrap ion-margin" expand="full" color="light"
|
||||
<ion-button *ngIf="!multiQuestion.disabled" class="ion-text-wrap ion-margin-top" expand="block" fill="outline"
|
||||
[disabled]="!multiQuestion.singleChoiceModel" (click)="clear()" type="reset">
|
||||
{{ 'addon.mod_quiz.clearchoice' | translate }}
|
||||
</ion-button>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<ng-container *ngIf="!loadingMore && position != 'top'">
|
||||
<div *ngIf="enabled || error" class="ion-padding-horizontal" #bottombutton>
|
||||
<ion-button *ngIf="!error" expand="block" (click)="loadMore()" color="light">
|
||||
<ion-button *ngIf="!error" expand="block" (click)="loadMore()" fill="outline">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="error" expand="block" (click)="loadMore()" color="light">
|
||||
<ion-button *ngIf="error" expand="block" (click)="loadMore()" fill="outline">
|
||||
{{ 'core.tryagain' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
@ -15,10 +15,10 @@
|
|||
|
||||
<ng-container *ngIf="!loadingMore && position == 'top'">
|
||||
<div *ngIf="enabled || error" class="ion-padding-horizontal" #topbutton>
|
||||
<ion-button *ngIf="!error" expand="block" (click)="loadMore()" color="light">
|
||||
<ion-button *ngIf="!error" expand="block" (click)="loadMore()" fill="outline">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</ion-button>
|
||||
<ion-button *ngIf="error" expand="block" (click)="loadMore()" color="light">
|
||||
<ion-button *ngIf="error" expand="block" (click)="loadMore()" fill="outline">
|
||||
{{ 'core.tryagain' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
|
|
@ -75,6 +75,12 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
&.has-spacer .core-loading-content {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.safe-area-padding:not(.core-loading-inline) .core-loading-content,
|
||||
&.safe-area-padding-horizontal:not(.core-loading-inline) .core-loading-content {
|
||||
@include safe-area-padding-horizontal(0px, 0px);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!-- ReCAPTCHA V2 -->
|
||||
<div *ngIf="publicKey && siteUrl && model">
|
||||
<!-- A button to open the recaptcha modal. -->
|
||||
<ion-button expand="block" color="light" class="ion-margin" *ngIf="!model[modelValueName]" (click)="answerRecaptcha()">
|
||||
<ion-button expand="block" fill="outline" class="ion-margin" *ngIf="!model[modelValueName]" (click)="answerRecaptcha()">
|
||||
{{ 'core.opensecurityquestion' | translate }}
|
||||
</ion-button>
|
||||
<ion-item *ngIf="model[modelValueName]">
|
||||
|
|
|
@ -6,6 +6,7 @@ $core-timer-iterations: 15 !default;
|
|||
:host {
|
||||
.core-timer {
|
||||
--background: transparent !important;
|
||||
border-radius: var(--big-radius);
|
||||
|
||||
.core-timer-time-left, .core-timesup {
|
||||
font-weight: bold;
|
||||
|
|
|
@ -153,7 +153,11 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
this.expanded = expand;
|
||||
this.element.classList.toggle('collapsible-expanded', expand);
|
||||
this.element.classList.toggle('collapsible-collapsed', !expand);
|
||||
this.element.style.maxHeight = expand ? '' : (this.maxHeight + buttonHeight) + 'px';
|
||||
if (expand) {
|
||||
this.element.style.setProperty('--max-height', this.maxHeight + buttonHeight + 'px');
|
||||
} else {
|
||||
this.element.style.removeProperty('--max-height');
|
||||
}
|
||||
|
||||
const toggleButton = this.element.querySelector('ion-button.collapsible-toggle');
|
||||
const toggleText = toggleButton?.querySelector('.collapsible-toggle-text');
|
||||
|
|
|
@ -320,8 +320,11 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.expanded = expand;
|
||||
this.element.classList.toggle('collapsible-expanded', expand);
|
||||
this.element.classList.toggle('collapsible-collapsed', !expand);
|
||||
this.element.style.maxHeight = expand ? '' : this.maxHeight + 'px';
|
||||
|
||||
if (expand) {
|
||||
this.element.style.setProperty('--max-height', this.maxHeight + 'px');
|
||||
} else {
|
||||
this.element.style.removeProperty('--max-height');
|
||||
}
|
||||
const toggleButton = this.element.querySelector('ion-button.collapsible-toggle');
|
||||
const toggleText = toggleButton?.querySelector('.collapsible-toggle-text');
|
||||
if (!toggleButton || !toggleText) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<div class="has-spacer">
|
||||
<ion-card class="core-danger-card">
|
||||
<ion-item>
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
|
||||
|
@ -11,6 +12,7 @@
|
|||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
<div class="spacer"></div>
|
||||
<ion-item lines="none" class="ion-text-wrap" *ngIf="module?.url && module?.uservisible">
|
||||
<ion-label>
|
||||
<p>{{ 'core.course.useactivityonbrowser' | translate }}</p>
|
||||
|
@ -20,3 +22,4 @@
|
|||
</ion-button>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
:host {
|
||||
display: contents;
|
||||
}
|
|
@ -23,6 +23,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg
|
|||
@Component({
|
||||
selector: 'core-course-unsupported-module',
|
||||
templateUrl: 'core-course-unsupported-module.html',
|
||||
styleUrls: ['unsupported-module.scss'],
|
||||
})
|
||||
export class CoreCourseUnsupportedModuleComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -35,9 +35,9 @@
|
|||
"errordownloadingsection": "Error downloading section.",
|
||||
"errorgetmodule": "Error getting activity data.",
|
||||
"failed": "Failed",
|
||||
"gotonextactivity": "Continue to next activity",
|
||||
"gotonextactivity": "Go to next activity",
|
||||
"gotonextactivitynotfound": "Next activity not found. It's possible that it has been hidden or deleted.",
|
||||
"gotopreviousactivity": "Continue to previous activity",
|
||||
"gotopreviousactivity": "Go to previous activity",
|
||||
"gotopreviousactivitynotfound": "Previous activity not found. It's possible that it has been hidden or deleted.",
|
||||
"hiddenfromstudents": "Hidden from students",
|
||||
"hiddenoncoursepage": "Available but not shown on course page",
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<core-loading [hideUntil]="loaded" class="has-spacer">
|
||||
<core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname"
|
||||
[componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true">
|
||||
<div class="core-module-additional-info" title>
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
</h3>
|
||||
|
||||
<div *ngIf="step == 0" class="core-login-onboarding-step">
|
||||
<ion-button expand="block" (click)="skip($event)" class="ion-margin-bottom" color="light">
|
||||
<ion-button expand="block" (click)="skip($event)" class="ion-margin-bottom" fill="outline">
|
||||
{{'core.login.onboardingimalearner' | translate}}
|
||||
</ion-button>
|
||||
<ion-button expand="block" (click)="next($event)" class="ion-margin-bottom" color="light">
|
||||
<ion-button expand="block" (click)="next($event)" class="ion-margin-bottom" fill="outline">
|
||||
{{'core.login.onboardingimaneducator' | translate}}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<p class="core-login-onboarding-text">
|
||||
{{ 'core.login.onboardingtoconnect' | translate }}
|
||||
</p>
|
||||
<ion-button expand="block" (click)="skip($event)" class="ion-margin-bottom" color="light">
|
||||
<ion-button expand="block" (click)="skip($event)" class="ion-margin-bottom" fill="outline">
|
||||
{{ 'core.login.onboardingialreadyhaveasite' | translate }}
|
||||
</ion-button>
|
||||
<ion-button expand="block" (click)="next($event)" class="ion-margin-bottom">
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<p>{{ 'core.login.changepasswordlogoutinstructions' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" color="light" (click)="logout()">
|
||||
<ion-button class="ion-text-wrap ion-margin" expand="block" fill="outline" (click)="logout()">
|
||||
{{ logoutLabel | translate }}
|
||||
</ion-button>
|
||||
</ion-list>
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
<ng-container *ngIf="showScanQR">
|
||||
<div class="ion-text-center ion-padding">{{ 'core.login.or' | translate }}</div>
|
||||
<ion-button expand="block" color="light" class="ion-margin" (click)="showInstructionsAndScanQR()">
|
||||
<ion-button expand="block" fill="outline" class="ion-margin" (click)="showInstructionsAndScanQR()">
|
||||
<ion-icon slot="start" name="fas-qrcode" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.scanqr' | translate }}
|
||||
</ion-button>
|
||||
|
@ -93,7 +93,7 @@
|
|||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-button expand="block" class="ion-margin ion-text-wrap" color="light" (click)="openEmailSignup()">
|
||||
<ion-button expand="block" class="ion-margin ion-text-wrap" fill="outline" (click)="openEmailSignup()">
|
||||
{{ 'core.login.startsignup' | translate }}
|
||||
</ion-button>
|
||||
</ion-list>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<ion-grid class="ion-padding">
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button expand="block" color="light" (click)="cancel($event)">
|
||||
<ion-button expand="block" fill="outline" (click)="cancel($event)">
|
||||
{{ 'core.login.cancel' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
|
@ -67,7 +67,7 @@
|
|||
|
||||
<ng-container *ngIf="showScanQR">
|
||||
<div class="ion-text-center ion-padding">{{ 'core.login.or' | translate }}</div>
|
||||
<ion-button expand="block" color="light" class="ion-margin" (click)="showInstructionsAndScanQR()">
|
||||
<ion-button expand="block" fill="outline" class="ion-margin" (click)="showInstructionsAndScanQR()">
|
||||
<ion-icon slot="start" name="fas-qrcode" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.scanqr' | translate }}
|
||||
</ion-button>
|
||||
|
@ -96,7 +96,7 @@
|
|||
|
||||
<!-- If OAuth, display cancel button since the form isn't displayed. -->
|
||||
<ion-list *ngIf="isOAuth">
|
||||
<ion-button expand="block" class="ion-margin" color="light" (click)="cancel($event)">
|
||||
<ion-button expand="block" class="ion-margin" fill="outline" (click)="cancel($event)">
|
||||
{{ 'core.login.cancel' | translate }}
|
||||
</ion-button>
|
||||
</ion-list>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<ion-button class="ion-text-wrap ion-margin-horizontal" expand="block" (click)="accept()">
|
||||
{{ 'core.login.policyacceptmandatory' | translate }}
|
||||
</ion-button>
|
||||
<ion-button class="ion-text-wrap ion-margin-horizontal ion-margin-bottom" expand="block" color="light" (click)="cancel()">
|
||||
<ion-button class="ion-text-wrap ion-margin-horizontal ion-margin-bottom" expand="block" fill="outline" (click)="cancel()">
|
||||
{{ 'core.login.cancel' | translate }}
|
||||
</ion-button>
|
||||
</ion-list>
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
|
||||
<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" (click)="showInstructionsAndScanQR()" aria-haspopup="dialog">
|
||||
<ion-button expand="block" fill="outline" class="ion-margin" (click)="showInstructionsAndScanQR()" aria-haspopup="dialog">
|
||||
<ion-icon slot="start" name="fas-qrcode" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.scanqr' | translate }}
|
||||
</ion-button>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</ion-item>
|
||||
|
||||
<!-- Question behaviour buttons. -->
|
||||
<ion-button *ngFor="let button of question?.behaviourButtons" class="ion-margin ion-text-wrap" expand="block"
|
||||
<ion-button *ngFor="let button of question?.behaviourButtons" class="ion-margin-vertical ion-text-wrap" expand="block"
|
||||
(click)="buttonClicked.emit(button)" [disabled]="button.disabled">
|
||||
{{ button.value }}
|
||||
</ion-button>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</ion-card>
|
||||
</ion-content>
|
||||
<ion-footer *ngIf="displayCopyButton">
|
||||
<ion-button expand="block" color="light" (click)="copyText()">
|
||||
<ion-button expand="block" fill="outline" (click)="copyText()">
|
||||
<ion-icon name="fas-copy" aria-hidden="true" slot="start"></ion-icon>
|
||||
{{ 'core.copytoclipboard' | translate }}
|
||||
</ion-button>
|
||||
|
|
|
@ -270,6 +270,7 @@
|
|||
&.collapsible-collapsed {
|
||||
overflow: hidden;
|
||||
min-height: calc(var(--collapsible-min-button-height) + 12px);
|
||||
max-height: calc(var(--max-height), auto);
|
||||
|
||||
.collapsible-toggle-arrow {
|
||||
transform: rotate(90deg);
|
||||
|
|
|
@ -140,7 +140,7 @@ body {
|
|||
@each $color-name, $unused in $colors {
|
||||
.text-#{$color-name},
|
||||
p.text-#{$color-name} {
|
||||
color: var(--ion-color-#{$color-name});
|
||||
color: var(--ion-color-#{$color-name}) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -831,8 +831,8 @@ ion-list.core-course-module-list-wrapper,
|
|||
.list-item-limited-width,
|
||||
.core-course-module-list-wrapper {
|
||||
max-width: var(--list-item--max-width);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
ion-toolbar h1 img.core-bar-button-image,
|
||||
|
@ -1257,11 +1257,6 @@ ion-datetime.datetime-disabled {
|
|||
opacity: .8 !important;
|
||||
}
|
||||
|
||||
// Hide details on items to align badges.
|
||||
ion-item.hide-detail {
|
||||
--detail-icon-opacity: 0;
|
||||
}
|
||||
|
||||
// Make links clickable when inside radio or checkbox items. Style part.
|
||||
@media (hover: hover) {
|
||||
ion-item.item-multiple-inputs:hover::part(native) {
|
||||
|
@ -1451,13 +1446,19 @@ ion-grid.core-no-grid > ion-row {
|
|||
--core-collapsible-footer-height: auto;
|
||||
}
|
||||
|
||||
.ion-margin {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
filter: var(--scroll-shadow-top, none);
|
||||
-webkit-filter: var(--scroll-shadow-top, none);
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
height: var(--core-collapsible-footer-height, auto);
|
||||
background-color: var(--core-collapsible-footer-background);
|
||||
display: block;
|
||||
border-top: 1px solid var(--stroke);
|
||||
@include core-transition(all, 200ms);
|
||||
}
|
||||
|
||||
|
@ -1466,6 +1467,16 @@ ion-grid.core-no-grid > ion-row {
|
|||
height: 0 !important;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.adaptable-buttons-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
ion-button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-header.no-title {
|
||||
--core-header-toolbar-border-width: 0;
|
||||
--core-header-toolbar-background: transparent;
|
||||
|
@ -1580,3 +1591,18 @@ body.core-iframe-fullscreen ion-content {
|
|||
|
||||
|
||||
}
|
||||
|
||||
.has-spacer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
flex-grow: 1;
|
||||
|
||||
.spacer-top {
|
||||
flex-grow: 1;
|
||||
align-content: flex-end;
|
||||
}
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -334,7 +334,9 @@
|
|||
--addon-forum-border-color: var(--stroke);
|
||||
--addon-forum-highlight-color: var(--light);
|
||||
|
||||
--drop-shadow: 0, 0, 0, 0.18;
|
||||
--drop-shadow: 0, 0, 0, 0.5;
|
||||
--scroll-shadow-bottom: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
||||
--scroll-shadow-top: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
||||
|
||||
--core-question-correct-color: var(--success-shade);
|
||||
--core-question-correct-color-bg: var(--success-tint);
|
||||
|
|
Loading…
Reference in New Issue