commit
						ba0f71dcb0
					
				@ -49,7 +49,7 @@
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.issuercontact">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.contact' | translate}}</p>
 | 
			
		||||
                        <p><a href="mailto:{{badge.issuercontact}}" core-link auto-login="no" [showBrowserWarning]="false">
 | 
			
		||||
                        <p><a href="mailto:{{badge.issuercontact}}" core-link [autoLogin]="false" [showBrowserWarning]="false">
 | 
			
		||||
                                {{ badge.issuercontact }}
 | 
			
		||||
                            </a></p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
@ -95,7 +95,7 @@
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.imageauthoremail">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.imageauthoremail' | translate}}</p>
 | 
			
		||||
                        <p><a href="mailto:{{badge.imageauthoremail}}" core-link auto-login="no" [showBrowserWarning]="false">
 | 
			
		||||
                        <p><a href="mailto:{{badge.imageauthoremail}}" core-link [autoLogin]="false" [showBrowserWarning]="false">
 | 
			
		||||
                                {{ badge.imageauthoremail }}
 | 
			
		||||
                            </a></p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
@ -103,7 +103,7 @@
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.imageauthorurl">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.imageauthorurl' | translate}}</p>
 | 
			
		||||
                        <p><a [href]="badge.imageauthorurl" core-link auto-login="no"> {{ badge.imageauthorurl }} </a></p>
 | 
			
		||||
                        <p><a [href]="badge.imageauthorurl" core-link [autoLogin]="false"> {{ badge.imageauthorurl }} </a></p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.imagecaption">
 | 
			
		||||
@ -166,7 +166,7 @@
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.issueremail' | translate}}</p>
 | 
			
		||||
                        <p>
 | 
			
		||||
                            <a href="mailto:{{badge.endorsement.issueremail}}" core-link auto-login="no" [showBrowserWarning]="false">
 | 
			
		||||
                            <a href="mailto:{{badge.endorsement.issueremail}}" core-link [autoLogin]="false" [showBrowserWarning]="false">
 | 
			
		||||
                                {{ badge.endorsement.issueremail }}
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </p>
 | 
			
		||||
@ -175,7 +175,7 @@
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.endorsement.issuerurl">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.issuerurl' | translate}}</p>
 | 
			
		||||
                        <p><a [href]="badge.endorsement.issuerurl" core-link auto-login="no"> {{ badge.endorsement.issuerurl }} </a></p>
 | 
			
		||||
                        <p><a [href]="badge.endorsement.issuerurl" core-link [autoLogin]="false"> {{ badge.endorsement.issuerurl }} </a></p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.endorsement.dateissued">
 | 
			
		||||
@ -187,7 +187,7 @@
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.endorsement.claimid">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ 'addon.badges.claimid' | translate}}</p>
 | 
			
		||||
                        <p><a [href]="badge.endorsement.claimid" core-link auto-login="no"> {{ badge.endorsement.claimid }} </a></p>
 | 
			
		||||
                        <p><a [href]="badge.endorsement.claimid" core-link [autoLogin]="false"> {{ badge.endorsement.claimid }} </a></p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngIf="badge.endorsement.claimcomment">
 | 
			
		||||
@ -225,7 +225,7 @@
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item-divider>
 | 
			
		||||
                <ion-item class="ion-text-wrap" *ngFor="let alignment of badge.alignment" [href]="alignment.targeturl" core-link
 | 
			
		||||
                    auto-login="no">
 | 
			
		||||
                    [autoLogin]="false">
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <p class="item-heading">{{ alignment.targetname }}</p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper';
 | 
			
		||||
import { AddonBlockTimelineDayEvents } from '@addons/block/timeline/classes/section';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to render a list of events in course overview.
 | 
			
		||||
@ -36,9 +37,9 @@ export class AddonBlockTimelineEventsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() events: AddonBlockTimelineDayEvents[] = []; // The events to render.
 | 
			
		||||
    @Input() course?: CoreEnrolledCourseDataWithOptions; // Whether to show the course name.
 | 
			
		||||
    @Input() showInlineCourse = true; // Whether to show the course name within event items.
 | 
			
		||||
    @Input() canLoadMore = false; // Whether more events can be loaded.
 | 
			
		||||
    @Input() loadingMore = false; // Whether loading is ongoing.
 | 
			
		||||
    @Input({ transform: toBoolean }) showInlineCourse = true; // Whether to show the course name within event items.
 | 
			
		||||
    @Input({ transform: toBoolean }) canLoadMore = false; // Whether more events can be loaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) loadingMore = false; // Whether loading is ongoing.
 | 
			
		||||
    @Output() loadMore = new EventEmitter(); // Notify that more events should be loaded.
 | 
			
		||||
 | 
			
		||||
    colorizeIcons = false;
 | 
			
		||||
 | 
			
		||||
@ -53,6 +53,7 @@ import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
 | 
			
		||||
import { CoreUrl } from '@singletons/url';
 | 
			
		||||
import { CoreTime } from '@singletons/time';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays a calendar.
 | 
			
		||||
@ -69,9 +70,9 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
 | 
			
		||||
    @Input() initialYear?: number; // Initial year to load.
 | 
			
		||||
    @Input() initialMonth?: number; // Initial month to load.
 | 
			
		||||
    @Input() filter?: AddonCalendarFilter; // Filter to apply.
 | 
			
		||||
    @Input() hidden?: boolean; // Whether the component is hidden.
 | 
			
		||||
    @Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true.
 | 
			
		||||
    @Input() displayNavButtons?: string | boolean; // Whether to display nav buttons created by this component. Defaults to true.
 | 
			
		||||
    @Input({ transform: toBoolean }) hidden = false; // Whether the component is hidden.
 | 
			
		||||
    @Input({ transform: toBoolean }) canNavigate = true; // Whether to include arrows to change the month
 | 
			
		||||
    @Input({ transform: toBoolean }) displayNavButtons = true; // Whether to display nav buttons created by this component.
 | 
			
		||||
    @Output() onEventClicked = new EventEmitter<number>();
 | 
			
		||||
    @Output() onDayClicked = new EventEmitter<{day: number; month: number; year: number}>();
 | 
			
		||||
 | 
			
		||||
@ -145,10 +146,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.canNavigate = typeof this.canNavigate == 'undefined' ? true : CoreUtils.isTrueOrOne(this.canNavigate);
 | 
			
		||||
        this.displayNavButtons = typeof this.displayNavButtons == 'undefined' ? true :
 | 
			
		||||
            CoreUtils.isTrueOrOne(this.displayNavButtons);
 | 
			
		||||
 | 
			
		||||
        const source = new AddonCalendarMonthSlidesItemsManagerSource(this, moment({
 | 
			
		||||
            year: this.initialYear,
 | 
			
		||||
            month: this.initialMonth ? this.initialMonth - 1 : undefined,
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <p class="item-heading">{{ 'core.location' | translate}}</p>
 | 
			
		||||
                    <p>
 | 
			
		||||
                        <a [href]="event.encodedLocation" core-link auto-login="no">
 | 
			
		||||
                        <a [href]="event.encodedLocation" core-link [autoLogin]="false">
 | 
			
		||||
                            <core-format-text [text]="event.location" [contextLevel]="event.contextLevel"
 | 
			
		||||
                                [contextInstanceId]="event.contextInstanceId" />
 | 
			
		||||
                        </a>
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreModals } from '@services/modals';
 | 
			
		||||
import { AddonModAssignFeedbackCommentsTextData } from '../feedback/comments/services/handler';
 | 
			
		||||
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../services/assign';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for component to render a feedback plugin.
 | 
			
		||||
@ -27,13 +28,13 @@ import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission }
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignFeedbackPluginBaseComponent implements IAddonModAssignFeedbackPluginComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input() submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input() plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input() userId!: number; // The user ID of the submission.
 | 
			
		||||
    @Input({ required: true }) assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input({ required: true }) plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input({ required: true }) userId!: number; // The user ID of the submission.
 | 
			
		||||
    @Input() configs?: Record<string,string>; // The configs for the plugin.
 | 
			
		||||
    @Input() canEdit = false; // Whether the user can edit.
 | 
			
		||||
    @Input() edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input({ transform: toBoolean }) canEdit = false; // Whether the user can edit.
 | 
			
		||||
    @Input({ transform: toBoolean }) edit = false; // Whether the user is editing.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open a modal to edit the feedback plugin.
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../services/assign';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for component to render a submission plugin.
 | 
			
		||||
@ -23,12 +24,12 @@ import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission }
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignSubmissionPluginBaseComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input() submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input() plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input({ required: true }) assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input({ required: true }) plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input() configs?: Record<string, string>; // The configs for the plugin.
 | 
			
		||||
    @Input() edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input() allowOffline = false; // Whether to allow offline.
 | 
			
		||||
    @Input({ transform: toBoolean }) edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input({ transform: toBoolean }) allowOffline = false; // Whether to allow offline.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate the data.
 | 
			
		||||
 | 
			
		||||
@ -37,10 +37,10 @@ import { AddonModAssignComponentsModule } from '../components.module';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModAssignEditFeedbackModalComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input() submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input() plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input() userId!: number; // The user ID of the submission.
 | 
			
		||||
    @Input({ required: true }) assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input({ required: true }) plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input({ required: true }) userId!: number; // The user ID of the submission.
 | 
			
		||||
 | 
			
		||||
    @ViewChild('editFeedbackForm') formElement?: ElementRef;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import {
 | 
			
		||||
import { AddonModAssignHelper, AddonModAssignPluginConfig } from '../../services/assign-helper';
 | 
			
		||||
import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate';
 | 
			
		||||
import { ADDON_MOD_ASSIGN_COMPONENT } from '../../constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays an assignment feedback plugin.
 | 
			
		||||
@ -37,12 +38,12 @@ export class AddonModAssignFeedbackPluginComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreDynamicComponent) dynamicComponent!: CoreDynamicComponent<IAddonModAssignFeedbackPluginComponent>;
 | 
			
		||||
 | 
			
		||||
    @Input() assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input() submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input() plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input() userId!: number; // The user ID of the submission.
 | 
			
		||||
    @Input() canEdit = false; // Whether the user can edit.
 | 
			
		||||
    @Input() edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input({ required: true }) assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input({ required: true }) plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input({ required: true }) userId!: number; // The user ID of the submission.
 | 
			
		||||
    @Input({ transform: toBoolean }) canEdit = false; // Whether the user can edit.
 | 
			
		||||
    @Input({ transform: toBoolean }) edit = false; // Whether the user is editing.
 | 
			
		||||
 | 
			
		||||
    pluginComponent?: Type<IAddonModAssignFeedbackPluginComponent>; // Component to render the plugin.
 | 
			
		||||
    data?: AddonModAssignFeedbackPluginData; // Data to pass to the component.
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import { AddonModAssignSubmissionDelegate } from '../../services/submission-dele
 | 
			
		||||
import { CoreFileEntry } from '@services/file-helper';
 | 
			
		||||
import type { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
 | 
			
		||||
import { ADDON_MOD_ASSIGN_COMPONENT } from '../../constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays an assignment submission plugin.
 | 
			
		||||
@ -37,11 +38,11 @@ export class AddonModAssignSubmissionPluginComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreDynamicComponent) dynamicComponent!: CoreDynamicComponent<AddonModAssignSubmissionPluginBaseComponent>;
 | 
			
		||||
 | 
			
		||||
    @Input() assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input() submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input() plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input() edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input() allowOffline = false; // Whether to allow offline.
 | 
			
		||||
    @Input({ required: true }) assign!: AddonModAssignAssign; // The assignment.
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModAssignSubmission; // The submission.
 | 
			
		||||
    @Input({ required: true }) plugin!: AddonModAssignPlugin; // The plugin object.
 | 
			
		||||
    @Input({ transform: toBoolean }) edit = false; // Whether the user is editing.
 | 
			
		||||
    @Input({ transform: toBoolean }) allowOffline = false; // Whether to allow offline.
 | 
			
		||||
 | 
			
		||||
    pluginComponent?: Type<AddonModAssignSubmissionPluginBaseComponent>; // Component to render the plugin.
 | 
			
		||||
    data?: AddonModAssignSubmissionPluginData; // Data to pass to the component.
 | 
			
		||||
 | 
			
		||||
@ -82,9 +82,9 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
 | 
			
		||||
    @ViewChildren(AddonModAssignSubmissionPluginComponent) submissionComponents!:
 | 
			
		||||
        QueryList<AddonModAssignSubmissionPluginComponent>;
 | 
			
		||||
 | 
			
		||||
    @Input() courseId!: number; // Course ID the submission belongs to.
 | 
			
		||||
    @Input() moduleId!: number; // Module ID the submission belongs to.
 | 
			
		||||
    @Input() submitId!: number; // User that did the submission.
 | 
			
		||||
    @Input({ required: true }) courseId!: number; // Course ID the submission belongs to.
 | 
			
		||||
    @Input({ required: true }) moduleId!: number; // Module ID the submission belongs to.
 | 
			
		||||
    @Input() submitId!: number; // User that did the submission. Defaults to current user
 | 
			
		||||
    @Input() blindId?: number; // Blinded user ID (if it's blinded).
 | 
			
		||||
 | 
			
		||||
    loaded = false; // Whether data has been loaded.
 | 
			
		||||
 | 
			
		||||
@ -33,8 +33,8 @@ import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModChatUsersModalComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() sessionId!: string;
 | 
			
		||||
    @Input() cmId!: number;
 | 
			
		||||
    @Input({ required: true }) sessionId!: string;
 | 
			
		||||
    @Input({ required: true }) cmId!: number;
 | 
			
		||||
 | 
			
		||||
    users: AddonModChatUser[] = [];
 | 
			
		||||
    usersLoaded = false;
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,8 @@ import { AddonModDataTemplateMode } from '../constants';
 | 
			
		||||
})
 | 
			
		||||
export abstract class AddonModDataFieldPluginBaseComponent implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input() field!: AddonModDataField; // The field to render.
 | 
			
		||||
    @Input({ required: true }) mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input({ required: true }) field!: AddonModDataField; // The field to render.
 | 
			
		||||
    @Input() value?: Partial<AddonModDataEntryField>; // The value of the field.
 | 
			
		||||
    @Input() database?: AddonModDataData; // Database object.
 | 
			
		||||
    @Input() error?: string; // Error when editing.
 | 
			
		||||
 | 
			
		||||
@ -45,10 +45,10 @@ import {
 | 
			
		||||
export class AddonModDataActionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() access?: AddonModDataGetDataAccessInformationWSResponse; // Access info.
 | 
			
		||||
    @Input() mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input() action!: AddonModDataAction; // The field to render.
 | 
			
		||||
    @Input() entry!: AddonModDataEntry; // The value of the field.
 | 
			
		||||
    @Input() database!: AddonModDataData; // Database object.
 | 
			
		||||
    @Input({ required: true }) mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input({ required: true }) action!: AddonModDataAction; // The field to render.
 | 
			
		||||
    @Input({ required: true }) entry!: AddonModDataEntry; // The value of the field.
 | 
			
		||||
    @Input({ required: true }) database!: AddonModDataData; // Database object.
 | 
			
		||||
    @Input() title = ''; // Name of the module.
 | 
			
		||||
    @Input() group = 0; // Module group.
 | 
			
		||||
    @Input() offset?: number; // Offset of the entry.
 | 
			
		||||
 | 
			
		||||
@ -32,8 +32,8 @@ export class AddonModDataFieldPluginComponent implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreDynamicComponent) dynamicComponent?: CoreDynamicComponent<AddonModDataFieldPluginBaseComponent>;
 | 
			
		||||
 | 
			
		||||
    @Input() mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input() field!: AddonModDataField; // The field to render.
 | 
			
		||||
    @Input({ required: true }) mode!: AddonModDataTemplateMode; // The render mode.
 | 
			
		||||
    @Input({ required: true }) field!: AddonModDataField; // The field to render.
 | 
			
		||||
    @Input() value?: unknown; // The value of the field.
 | 
			
		||||
    @Input() database?: AddonModDataData; // Database object.
 | 
			
		||||
    @Input() error?: string; // Error when editing.
 | 
			
		||||
 | 
			
		||||
@ -50,9 +50,9 @@ export class AddonModDataSearchModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild('searchFormEl') formElement!: ElementRef;
 | 
			
		||||
 | 
			
		||||
    @Input() search!: AddonModDataSearchDataParams;
 | 
			
		||||
    @Input() fields!: Record<number, AddonModDataField>;
 | 
			
		||||
    @Input() database!: AddonModDataData;
 | 
			
		||||
    @Input({ required: true }) search!: AddonModDataSearchDataParams;
 | 
			
		||||
    @Input({ required: true }) fields!: Record<number, AddonModDataField>;
 | 
			
		||||
    @Input({ required: true }) database!: AddonModDataData;
 | 
			
		||||
 | 
			
		||||
    advancedSearch = '';
 | 
			
		||||
    advancedIndexed: CoreFormFields = {};
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,9 @@ import { CoreToasts } from '@services/toasts';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModForumDiscussionOptionsMenuComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() discussion!: AddonModForumDiscussion; // The discussion.
 | 
			
		||||
    @Input() forumId!: number; // The forum Id.
 | 
			
		||||
    @Input() cmId!: number; // The component module Id.
 | 
			
		||||
    @Input({ required: true }) discussion!: AddonModForumDiscussion; // The discussion.
 | 
			
		||||
    @Input({ required: true }) forumId!: number; // The forum Id.
 | 
			
		||||
    @Input({ required: true }) cmId!: number; // The component module Id.
 | 
			
		||||
 | 
			
		||||
    canPin = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,9 @@ import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModForumPostOptionsMenuComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() post!: AddonModForumPost; // The post.
 | 
			
		||||
    @Input() cmId!: number;
 | 
			
		||||
    @Input() forumId!: number; // The forum Id.
 | 
			
		||||
    @Input({ required: true }) post!: AddonModForumPost; // The post.
 | 
			
		||||
    @Input({ required: true }) cmId!: number;
 | 
			
		||||
    @Input({ required: true }) forumId!: number; // The forum Id.
 | 
			
		||||
 | 
			
		||||
    canEdit = false;
 | 
			
		||||
    canDelete = false;
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,7 @@ import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
 | 
			
		||||
import { ADDON_MOD_FORUM_CHANGE_DISCUSSION_EVENT, ADDON_MOD_FORUM_COMPONENT } from '../../constants';
 | 
			
		||||
import { CoreToasts } from '@services/toasts';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
 | 
			
		||||
@ -65,21 +66,21 @@ import { CoreToasts } from '@services/toasts';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() post!: AddonModForumPost; // Post.
 | 
			
		||||
    @Input() courseId!: number; // Post's course ID.
 | 
			
		||||
    @Input() discussionId!: number; // Post's' discussion ID.
 | 
			
		||||
    @Input({ required: true }) post!: AddonModForumPost; // Post.
 | 
			
		||||
    @Input({ required: true }) courseId!: number; // Post's course ID.
 | 
			
		||||
    @Input({ required: true }) discussionId!: number; // Post's' discussion ID.
 | 
			
		||||
    @Input() discussion?: AddonModForumDiscussion; // Post's' discussion, only for starting posts.
 | 
			
		||||
    @Input() component!: string; // Component this post belong to.
 | 
			
		||||
    @Input() componentId!: number; // Component ID.
 | 
			
		||||
    @Input() formData!: AddonModForumSharedPostFormData; // Object with the new post data. Usually shared between posts.
 | 
			
		||||
    @Input() originalData!: Omit<AddonModForumPostFormData, 'id'>; // Original post data. Usually shared between posts.
 | 
			
		||||
    @Input() trackPosts!: boolean; // True if post is being tracked.
 | 
			
		||||
    @Input() forum!: AddonModForumData; // The forum the post belongs to. Required for attachments and offline posts.
 | 
			
		||||
    @Input() accessInfo!: AddonModForumAccessInformation; // Forum access information.
 | 
			
		||||
    @Input({ required: true }) component!: string; // Component this post belong to.
 | 
			
		||||
    @Input({ required: true }) componentId!: number; // Component ID.
 | 
			
		||||
    @Input({ required: true }) formData!: AddonModForumSharedPostFormData; // New post data. Usually shared between posts.
 | 
			
		||||
    @Input({ required: true }) originalData!: Omit<AddonModForumPostFormData, 'id'>; // Original data. Usually shared between posts.
 | 
			
		||||
    @Input({ required: true, transform: toBoolean }) trackPosts = false; // True if post is being tracked.
 | 
			
		||||
    @Input({ required: true }) forum!: AddonModForumData; // The forum the post belongs to.
 | 
			
		||||
    @Input({ required: true }) accessInfo!: AddonModForumAccessInformation; // Forum access information.
 | 
			
		||||
    @Input() parentSubject?: string; // Subject of parent post.
 | 
			
		||||
    @Input() ratingInfo?: CoreRatingInfo; // Rating info item.
 | 
			
		||||
    @Input() leavingPage?: boolean; // Whether the page that contains this post is being left and will be destroyed.
 | 
			
		||||
    @Input() highlight = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) leavingPage = false; // Whether the page that contains this post is being left.
 | 
			
		||||
    @Input({ transform: toBoolean }) highlight = false;
 | 
			
		||||
    @Output() onPostChange: EventEmitter<void> = new EventEmitter<void>(); // Event emitted when a reply is posted or modified.
 | 
			
		||||
 | 
			
		||||
    @ViewChild('replyFormEl') formElement!: ElementRef;
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
 | 
			
		||||
import { AddonModQuizSync } from '@addons/mod/quiz/services/quiz-sync';
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
@ -29,7 +30,7 @@ export class AddonModQuizAccessOfflineAttemptsComponent implements OnInit {
 | 
			
		||||
    @Input() rule?: string; // The name of the rule.
 | 
			
		||||
    @Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
 | 
			
		||||
    @Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
 | 
			
		||||
    @Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input({ transform: toBoolean }) prefetch = false; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input() siteId?: string; // Site ID.
 | 
			
		||||
    @Input() form?: FormGroup; // Form where to add the form control.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { FormGroup, FormBuilder } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render the preflight for password.
 | 
			
		||||
@ -29,7 +30,7 @@ export class AddonModQuizAccessPasswordComponent implements OnInit {
 | 
			
		||||
    @Input() rule?: string; // The name of the rule.
 | 
			
		||||
    @Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
 | 
			
		||||
    @Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
 | 
			
		||||
    @Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input({ transform: toBoolean }) prefetch = false; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input() siteId?: string; // Site ID.
 | 
			
		||||
    @Input() form?: FormGroup; // Form where to add the form control.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import { FormGroup } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
 | 
			
		||||
import { CoreTime } from '@singletons/time';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render the preflight for time limit.
 | 
			
		||||
@ -30,7 +31,7 @@ export class AddonModQuizAccessTimeLimitComponent implements OnInit {
 | 
			
		||||
    @Input() rule?: string; // The name of the rule.
 | 
			
		||||
    @Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
 | 
			
		||||
    @Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
 | 
			
		||||
    @Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input({ transform: toBoolean }) prefetch = false; // Whether the user is prefetching the quiz.
 | 
			
		||||
    @Input() siteId?: string; // Site ID.
 | 
			
		||||
    @Input() form?: FormGroup; // Form where to add the form control.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,8 +30,8 @@ import { isSafeNumber } from '@/core/utils/types';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModQuizAttemptInfoComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() quiz!: AddonModQuizQuizData;
 | 
			
		||||
    @Input() attempt!: AddonModQuizAttempt;
 | 
			
		||||
    @Input({ required: true }) quiz!: AddonModQuizQuizData;
 | 
			
		||||
    @Input({ required: true }) attempt!: AddonModQuizAttempt;
 | 
			
		||||
    @Input() additionalData?: AddonModQuizWSAdditionalData[]; // Additional data to display for the attempt.
 | 
			
		||||
 | 
			
		||||
    isFinished = false;
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { Component, Input, OnChanges } from '@angular/core';
 | 
			
		||||
import { AddonModQuiz } from '../../services/quiz';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays an attempt state.
 | 
			
		||||
@ -26,7 +27,7 @@ import { AddonModQuiz } from '../../services/quiz';
 | 
			
		||||
export class AddonModQuizAttemptStateComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() state = '';
 | 
			
		||||
    @Input() finishedOffline = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) finishedOffline = false;
 | 
			
		||||
 | 
			
		||||
    readableState = '';
 | 
			
		||||
    color = '';
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreQuestionQuestionParsed } from '@features/question/services/question';
 | 
			
		||||
@ -32,11 +33,11 @@ import { ModalController } from '@singletons';
 | 
			
		||||
export class AddonModQuizNavigationModalComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() navigation?: AddonModQuizNavigationQuestion[]; // Whether the user is reviewing the attempt.
 | 
			
		||||
    @Input() summaryShown?: boolean; // Whether summary is currently being shown.
 | 
			
		||||
    @Input({ transform: toBoolean }) summaryShown = false; // Whether summary is currently being shown.
 | 
			
		||||
    @Input() nextPage?: number; // Next page.
 | 
			
		||||
    @Input() currentPage?: number; // Current page.
 | 
			
		||||
    @Input() isReview?: boolean; // Whether the user is reviewing the attempt.
 | 
			
		||||
    @Input() isSequential?: boolean; // Whether quiz navigation is sequential.
 | 
			
		||||
    @Input({ transform: toBoolean }) isReview = false; // Whether the user is reviewing the attempt.
 | 
			
		||||
    @Input({ transform: toBoolean }) isSequential = false; // Whether quiz navigation is sequential.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-dele
 | 
			
		||||
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '../../services/quiz';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modal that renders the access rules for a quiz.
 | 
			
		||||
@ -39,12 +40,12 @@ export class AddonModQuizPreflightModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @ViewChild('preflightFormEl') formElement?: ElementRef;
 | 
			
		||||
 | 
			
		||||
    @Input() title!: string;
 | 
			
		||||
    @Input({ required: true }) title!: string;
 | 
			
		||||
    @Input() quiz?: AddonModQuizQuizWSData;
 | 
			
		||||
    @Input() attempt?: AddonModQuizAttemptWSData;
 | 
			
		||||
    @Input() prefetch?: boolean;
 | 
			
		||||
    @Input() siteId!: string;
 | 
			
		||||
    @Input() rules!: string[];
 | 
			
		||||
    @Input({ transform: toBoolean }) prefetch = false;
 | 
			
		||||
    @Input({ required: true }) siteId!: string;
 | 
			
		||||
    @Input({ required: true }) rules!: string[];
 | 
			
		||||
 | 
			
		||||
    preflightForm: FormGroup;
 | 
			
		||||
    accessRulesData: { component: Type<unknown>; data: Record<string, unknown>}[] = []; // Component and data for each access rule.
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,6 @@ import { CoreQuestionQuestionForView } from '@features/question/services/questio
 | 
			
		||||
})
 | 
			
		||||
export class AddonModQuizQuestionCardComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() question!: CoreQuestionQuestionForView;
 | 
			
		||||
    @Input({ required: true }) question!: CoreQuestionQuestionForView;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -35,9 +35,9 @@ export class AddonModScormTocComponent implements OnInit {
 | 
			
		||||
    @Input() toc: AddonModScormTOCScoWithIcon[] = [];
 | 
			
		||||
    @Input() attemptToContinue?: number;
 | 
			
		||||
    @Input() selected?: number;
 | 
			
		||||
    @Input() moduleId!: number;
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input() accessInfo!: AddonModScormGetScormAccessInformationWSResponse;
 | 
			
		||||
    @Input({ required: true }) moduleId!: number;
 | 
			
		||||
    @Input({ required: true }) courseId!: number;
 | 
			
		||||
    @Input({ required: true }) accessInfo!: AddonModScormGetScormAccessInformationWSResponse;
 | 
			
		||||
    @Input() mode = '';
 | 
			
		||||
 | 
			
		||||
    isBrowse = false;
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ export class AddonModWikiSubwikiPickerComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() subwikis: AddonModWikiSubwikiListGrouping[] = [];
 | 
			
		||||
    @Input() currentSubwiki!: AddonModWikiSubwiki;
 | 
			
		||||
    @Input({ required: true }) currentSubwiki!: AddonModWikiSubwiki;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the given subwiki is the one currently selected.
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
import { AddonModWorkshopGetAssessmentFormFieldsParsedData } from '../services/workshop';
 | 
			
		||||
import { AddonModWorkshopSubmissionAssessmentWithFormData } from '../services/workshop-helper';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for component to render an assessment strategy.
 | 
			
		||||
@ -24,13 +25,13 @@ import { AddonModWorkshopSubmissionAssessmentWithFormData } from '../services/wo
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopAssessmentStrategyBaseComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() workshopId!: number;
 | 
			
		||||
    @Input() assessment!: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input() edit!: boolean;
 | 
			
		||||
    @Input() selectedValues!: AddonModWorkshopGetAssessmentFormFieldsParsedData[];
 | 
			
		||||
    @Input() fieldErrors!: Record<string, string>;
 | 
			
		||||
    @Input() strategy!: string;
 | 
			
		||||
    @Input() moduleId!: number;
 | 
			
		||||
    @Input({ required: true }) workshopId!: number;
 | 
			
		||||
    @Input({ required: true }) assessment!: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input({ required: true, transform: toBoolean }) edit = false;
 | 
			
		||||
    @Input({ required: true }) selectedValues!: AddonModWorkshopGetAssessmentFormFieldsParsedData[];
 | 
			
		||||
    @Input({ required: true }) fieldErrors!: Record<string, string>;
 | 
			
		||||
    @Input({ required: true }) strategy!: string;
 | 
			
		||||
    @Input({ required: true }) moduleId!: number;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,7 @@ import {
 | 
			
		||||
    ADDON_MOD_WORKSHOP_COMPONENT,
 | 
			
		||||
    AddonModWorkshopOverallFeedbackMode,
 | 
			
		||||
} from '@addons/mod/workshop/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays workshop assessment strategy form.
 | 
			
		||||
@ -52,12 +53,12 @@ import {
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input() access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input() assessmentId!: number;
 | 
			
		||||
    @Input() userId!: number;
 | 
			
		||||
    @Input() strategy!: string;
 | 
			
		||||
    @Input() edit = false;
 | 
			
		||||
    @Input({ required: true }) workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input({ required: true }) access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input({ required: true }) assessmentId!: number;
 | 
			
		||||
    @Input({ required: true }) userId!: number;
 | 
			
		||||
    @Input({ required: true }) strategy!: string;
 | 
			
		||||
    @Input({ transform: toBoolean }) edit = false;
 | 
			
		||||
 | 
			
		||||
    @ViewChild('assessmentForm') formElement!: ElementRef;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,12 +36,12 @@ import { AddonModWorkshopOffline } from '../../services/workshop-offline';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopAssessmentComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() assessment!: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input() workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input() access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input() submission!: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    @Input() module!: CoreCourseModuleData;
 | 
			
		||||
    @Input({ required: true }) assessment!: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input({ required: true }) courseId!: number;
 | 
			
		||||
    @Input({ required: true }) workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input({ required: true }) access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    @Input({ required: true }) module!: CoreCourseModuleData;
 | 
			
		||||
 | 
			
		||||
    canViewAssessment = false;
 | 
			
		||||
    canSelfAssess = false;
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { ModalController } from '@singletons';
 | 
			
		||||
import { AddonModWorkshopPhaseData, AddonModWorkshopPhaseTaskData } from '../../services/workshop';
 | 
			
		||||
import { AddonModWorkshopPhase } from '../../constants';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the phase info modal.
 | 
			
		||||
@ -31,10 +32,10 @@ import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopPhaseInfoModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() phases!: AddonModWorkshopPhaseDataWithSwitch[];
 | 
			
		||||
    @Input() workshopPhase!: AddonModWorkshopPhase;
 | 
			
		||||
    @Input() showSubmit = false;
 | 
			
		||||
    @Input() externalUrl!: string;
 | 
			
		||||
    @Input({ required: true }) phases!: AddonModWorkshopPhaseDataWithSwitch[];
 | 
			
		||||
    @Input({ required: true }) workshopPhase!: AddonModWorkshopPhase;
 | 
			
		||||
    @Input({ transform: toBoolean }) showSubmit = false;
 | 
			
		||||
    @Input({ required: true }) externalUrl!: string;
 | 
			
		||||
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import {
 | 
			
		||||
} from '../../services/workshop-helper';
 | 
			
		||||
import { AddonModWorkshopOffline } from '../../services/workshop-offline';
 | 
			
		||||
import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME, AddonModWorkshopPhase } from '@addons/mod/workshop/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays workshop submission.
 | 
			
		||||
@ -41,13 +42,13 @@ import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME, AddonModWor
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopSubmissionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() submission!: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    @Input() module!: CoreCourseModuleData;
 | 
			
		||||
    @Input() workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input() access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input({ required: true }) submission!: AddonModWorkshopSubmissionDataWithOfflineData;
 | 
			
		||||
    @Input({ required: true }) module!: CoreCourseModuleData;
 | 
			
		||||
    @Input({ required: true }) workshop!: AddonModWorkshopData;
 | 
			
		||||
    @Input({ required: true }) access!: AddonModWorkshopGetWorkshopAccessInformationWSResponse;
 | 
			
		||||
    @Input({ required: true }) courseId!: number;
 | 
			
		||||
    @Input() assessment?: AddonModWorkshopSubmissionAssessmentWithFormData;
 | 
			
		||||
    @Input() summary = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) summary = false;
 | 
			
		||||
 | 
			
		||||
    component = ADDON_MOD_WORKSHOP_COMPONENT;
 | 
			
		||||
    componentId?: number;
 | 
			
		||||
 | 
			
		||||
@ -88,8 +88,8 @@
 | 
			
		||||
                    <h2>{{ 'addon.mod_workshop.givengrades' | translate }}</h2>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item-divider>
 | 
			
		||||
            <addon-mod-workshop-assessment *ngFor="let reviewer of submissionInfo.reviewerof" [assessment]="reviewer" [courseId]="courseId"
 | 
			
		||||
                [module]="module" [workshop]="workshop" [access]="access" />
 | 
			
		||||
            <addon-mod-workshop-assessment *ngFor="let reviewer of submissionInfo.reviewerof" [submission]="submission"
 | 
			
		||||
                [assessment]="reviewer" [courseId]="courseId" [module]="module" [workshop]="workshop" [access]="access" />
 | 
			
		||||
        </ion-list>
 | 
			
		||||
 | 
			
		||||
        <form [formGroup]="feedbackForm" *ngIf="canAddFeedback && submission" #feedbackFormEl>
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ export class AddonNotesAddComponent {
 | 
			
		||||
 | 
			
		||||
    @ViewChild('itemEdit') formElement?: ElementRef;
 | 
			
		||||
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input({ required: true }) courseId!: number;
 | 
			
		||||
    @Input() userId?: number;
 | 
			
		||||
    @Input() type: AddonNotesPublishState = 'personal';
 | 
			
		||||
    text = '';
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper';
 | 
			
		||||
@ -30,11 +31,11 @@ export class AddonQbehaviourDeferredCBMComponent {
 | 
			
		||||
    @Input() component?: string; // The component the question belongs to.
 | 
			
		||||
    @Input() componentId?: number; // ID of the component the question belongs to.
 | 
			
		||||
    @Input() attemptId?: number; // Attempt ID.
 | 
			
		||||
    @Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
 | 
			
		||||
    @Input({ transform: toBoolean }) offlineEnabled = false; // Whether the question can be answered in offline.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
 | 
			
		||||
    @Input() review?: boolean; // Whether the user is in review mode.
 | 
			
		||||
    @Input({ transform: toBoolean }) review = false; // Whether the user is in review mode.
 | 
			
		||||
    @Input() preferredBehaviour?: string; // Preferred behaviour.
 | 
			
		||||
    @Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
 | 
			
		||||
    @Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper';
 | 
			
		||||
@ -30,11 +31,11 @@ export class AddonQbehaviourInformationItemComponent {
 | 
			
		||||
    @Input() component?: string; // The component the question belongs to.
 | 
			
		||||
    @Input() componentId?: number; // ID of the component the question belongs to.
 | 
			
		||||
    @Input() attemptId?: number; // Attempt ID.
 | 
			
		||||
    @Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
 | 
			
		||||
    @Input({ transform: toBoolean }) offlineEnabled = false; // Whether the question can be answered in offline.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
 | 
			
		||||
    @Input() review?: boolean; // Whether the user is in review mode.
 | 
			
		||||
    @Input({ transform: toBoolean }) review = false; // Whether the user is in review mode.
 | 
			
		||||
    @Input() preferredBehaviour?: string; // Preferred behaviour.
 | 
			
		||||
    @Output() buttonClicked = new EventEmitter<CoreQuestionBehaviourButton>(); // Will emit when a behaviour button is clicked.
 | 
			
		||||
    @Output() onAbort = new EventEmitter<void>(); // Should emit an event if the question should be aborted.
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,7 @@ import { CoreDirectivesRegistry } from '@singletons/directives-registry';
 | 
			
		||||
import { Swiper } from 'swiper';
 | 
			
		||||
import { SwiperOptions } from 'swiper/types';
 | 
			
		||||
import { CoreSwiper } from '@singletons/swiper';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class to abstract some common code for tabs.
 | 
			
		||||
@ -52,7 +53,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
 | 
			
		||||
    protected static readonly MIN_TAB_WIDTH = 107;
 | 
			
		||||
 | 
			
		||||
    @Input() selectedIndex = 0; // Index of the tab to select.
 | 
			
		||||
    @Input() hideUntil = false; // Determine when should the contents be shown.
 | 
			
		||||
    @Input({ transform: toBoolean }) hideUntil = false; // Determine when should the contents be shown.
 | 
			
		||||
    @Output() protected ionChange = new EventEmitter<T>(); // Emitted when the tab changes.
 | 
			
		||||
 | 
			
		||||
    protected swiper?: Swiper;
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuplo
 | 
			
		||||
import { CoreFileEntry } from '@services/file-helper';
 | 
			
		||||
import { CoreCourses } from '@features/courses/services/courses';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render attachments, allow adding more and delete the current ones.
 | 
			
		||||
@ -51,9 +52,9 @@ export class CoreAttachmentsComponent implements OnInit {
 | 
			
		||||
    @Input() maxSubmissions?: number; // Max number of attachments. -1 means unlimited, not defined means unknown limit.
 | 
			
		||||
    @Input() component?: string; // Component the downloaded files will be linked to.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID.
 | 
			
		||||
    @Input() allowOffline?: boolean | string; // Whether to allow selecting files in offline.
 | 
			
		||||
    @Input({ transform: toBoolean }) allowOffline = false; // Whether to allow selecting files in offline.
 | 
			
		||||
    @Input() acceptedTypes?: string; // List of supported filetypes. If undefined, all types supported.
 | 
			
		||||
    @Input() required?: boolean; // Whether to display the required mark.
 | 
			
		||||
    @Input({ transform: toBoolean }) required = false; // Whether to display the required mark.
 | 
			
		||||
    @Input() courseId?: number; // Course ID.
 | 
			
		||||
    @Input() title = Translate.instant('core.fileuploader.attachedfiles'); // Title to display.
 | 
			
		||||
 | 
			
		||||
@ -131,9 +132,7 @@ export class CoreAttachmentsComponent implements OnInit {
 | 
			
		||||
     * Add a new attachment.
 | 
			
		||||
     */
 | 
			
		||||
    async add(): Promise<void> {
 | 
			
		||||
        const allowOffline = !!this.allowOffline && this.allowOffline !== 'false';
 | 
			
		||||
 | 
			
		||||
        if (!allowOffline && !CoreNetwork.isOnline()) {
 | 
			
		||||
        if (!this.allowOffline && !CoreNetwork.isOnline()) {
 | 
			
		||||
            CoreDomUtils.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
@ -142,7 +141,7 @@ export class CoreAttachmentsComponent implements OnInit {
 | 
			
		||||
        const mimetypes = this.fileTypes && this.fileTypes.mimetypes;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const result = await CoreFileUploaderHelper.selectFile(this.maxSize, allowOffline, undefined, mimetypes);
 | 
			
		||||
            const result = await CoreFileUploaderHelper.selectFile(this.maxSize, this.allowOffline, undefined, mimetypes);
 | 
			
		||||
 | 
			
		||||
            this.files?.push(result);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -24,6 +25,6 @@ import { Component, Input } from '@angular/core';
 | 
			
		||||
export class CoreBSTooltipComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() content = '';
 | 
			
		||||
    @Input() html?: boolean;
 | 
			
		||||
    @Input({ transform: toBoolean }) html = false;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
import { CoreAnimations } from '@components/animations';
 | 
			
		||||
 | 
			
		||||
@ -31,7 +32,7 @@ import { CoreAnimations } from '@components/animations';
 | 
			
		||||
})
 | 
			
		||||
export class CoreButtonWithSpinnerComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() loading = true;
 | 
			
		||||
    @Input({ transform: toBoolean }) loading = true;
 | 
			
		||||
    @Input() loadingLabel = 'core.loading';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,10 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, OnDestroy, OnInit, ElementRef, OnChanges, ViewChild, SimpleChange } from '@angular/core';
 | 
			
		||||
import { CoreFilter } from '@features/filter/services/filter';
 | 
			
		||||
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { ChartLegendLabelItem, ChartLegendOptions } from 'chart.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -50,11 +50,12 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
 | 
			
		||||
    @Input() type?: string; // Type of chart.
 | 
			
		||||
    @Input() legend?: ChartLegendOptions; // Legend options.
 | 
			
		||||
    @Input() height = 300; // Height of the chart element.
 | 
			
		||||
    @Input() filter?: boolean | string; // Whether to filter labels. If not defined, true if contextLevel and instanceId are set.
 | 
			
		||||
    @Input({ transform: toBoolean }) filter?: boolean; // Whether to filter labels.
 | 
			
		||||
                                                       // If not defined, true if contextLevel and instanceId are set.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level of the text.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
 | 
			
		||||
    @Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the labels for some reason.
 | 
			
		||||
    @Input({ transform: toBoolean }) wsNotFiltered = false; // If true it means the WS didn't filter the labels for some reason.
 | 
			
		||||
    @ViewChild('canvas') canvas?: ElementRef<HTMLCanvasElement>;
 | 
			
		||||
 | 
			
		||||
    chart?: ChartWithLegend;
 | 
			
		||||
@ -158,7 +159,7 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
 | 
			
		||||
            clean: true,
 | 
			
		||||
            singleLine: true,
 | 
			
		||||
            courseId: this.courseId,
 | 
			
		||||
            wsNotFiltered: CoreUtils.isTrueOrOne(this.wsNotFiltered),
 | 
			
		||||
            wsNotFiltered: this.wsNotFiltered,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const filters = await CoreFilterHelper.getFilters(this.contextLevel, this.contextInstanceId, options);
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import {
 | 
			
		||||
    Component,
 | 
			
		||||
    Input,
 | 
			
		||||
@ -42,11 +43,11 @@ import {
 | 
			
		||||
})
 | 
			
		||||
export class CoreChronoComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() running?: boolean; // Set it to true to start the chrono. Set it to false to stop it.
 | 
			
		||||
    @Input({ transform: toBoolean }) running = false; // Set it to true to start the chrono. Set it to false to stop it.
 | 
			
		||||
    @Input() startTime = 0; // Number of milliseconds to put in the chrono before starting.
 | 
			
		||||
    @Input() endTime?: number; // Number of milliseconds to stop the chrono.
 | 
			
		||||
    @Input() reset?: boolean; // Set it to true to reset the chrono.
 | 
			
		||||
    @Input() hours = true;
 | 
			
		||||
    @Input({ transform: toBoolean }) reset = false; // Set it to true to reset the chrono.
 | 
			
		||||
    @Input({ transform: toBoolean }) hours = true;
 | 
			
		||||
    @Output() onEnd: EventEmitter<void>; // Will emit an event when the endTime is reached.
 | 
			
		||||
 | 
			
		||||
    time = 0;
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import { Translate } from '@singletons';
 | 
			
		||||
import { ModalOptions } from '@ionic/core';
 | 
			
		||||
import { CoreModals } from '@services/modals';
 | 
			
		||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that show a combo select button (combobox).
 | 
			
		||||
@ -52,7 +53,7 @@ export class CoreComboboxComponent implements ControlValueAccessor {
 | 
			
		||||
 | 
			
		||||
    @Input() interface: 'popover' | 'modal' = 'popover';
 | 
			
		||||
    @Input() label = Translate.instant('core.show'); // Aria label.
 | 
			
		||||
    @Input() disabled = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) disabled = false;
 | 
			
		||||
    @Input() selection = '';
 | 
			
		||||
    @Output() onChange = new EventEmitter<unknown>(); // Will emit an event the value changed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
 | 
			
		||||
import { CoreContextMenuComponent } from '../context-menu/context-menu';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This directive adds a item to the Context Menu popover.
 | 
			
		||||
@ -40,19 +41,19 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
 | 
			
		||||
    // If is "toggle" a toggle switch will be shown.
 | 
			
		||||
    // If no icon or spinner is selected, no action or link will work.
 | 
			
		||||
    // If href but no iconAction is provided arrow-right will be used.
 | 
			
		||||
    @Input() iconSlash?: boolean; // Display a red slash over the icon.
 | 
			
		||||
    @Input({ transform: toBoolean }) iconSlash = false; // Display a red slash over the icon.
 | 
			
		||||
    @Input() ariaAction?: string; // Aria label to add to iconAction. If not set, it will be equal to content.
 | 
			
		||||
    @Input() href?: string; // Link to go if no action provided.
 | 
			
		||||
    @Input() captureLink?: boolean | string; // Whether the link needs to be captured by the app.
 | 
			
		||||
    @Input() autoLogin: boolean | string = true; // Whether the link needs to be opened using auto-login.
 | 
			
		||||
    @Input() closeOnClick = true; // Whether to close the popover when the item is clicked.
 | 
			
		||||
    @Input({ transform: toBoolean }) captureLink = false; // Whether the link needs to be captured by the app.
 | 
			
		||||
    @Input({ transform: toBoolean }) autoLogin = true; // Whether the link needs to be opened using auto-login.
 | 
			
		||||
    @Input({ transform: toBoolean }) closeOnClick = true; // Whether to close the popover when the item is clicked.
 | 
			
		||||
    @Input() priority?: number; // Used to sort items. The highest priority, the highest position.
 | 
			
		||||
    @Input() badge?: string; // A badge to show in the item.
 | 
			
		||||
    @Input() badgeClass?: number; // A class to set in the badge.
 | 
			
		||||
    @Input() badgeA11yText?: string; // Description for the badge, if needed.
 | 
			
		||||
    @Input() hidden?: boolean; // Whether the item should be hidden.
 | 
			
		||||
    @Input() showBrowserWarning = true; // Whether to show a warning before opening browser (for links). Defaults to true.
 | 
			
		||||
    @Input() toggle = false; // Whether the toggle is on or off.
 | 
			
		||||
    @Input({ transform: toBoolean }) hidden = false; // Whether the item should be hidden.
 | 
			
		||||
    @Input({ transform: toBoolean }) showBrowserWarning = true; // Whether to show a warning before opening browser (for links).
 | 
			
		||||
    @Input({ transform: toBoolean }) toggle = false; // Whether the toggle is on or off.
 | 
			
		||||
    @Output() action?: EventEmitter<() => void>; // Will emit an event when the item clicked.
 | 
			
		||||
    @Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked.
 | 
			
		||||
    @Output() toggleChange = new EventEmitter<boolean>();// Will emit an event when toggle changes to enable 2-way data binding.
 | 
			
		||||
@ -94,10 +95,6 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
 | 
			
		||||
     * @param event Event.
 | 
			
		||||
     */
 | 
			
		||||
    toggleChanged(event: Event): void {
 | 
			
		||||
        if (this.toggle === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
        this.toggleChange.emit(this.toggle);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
<ion-icon *ngIf="!course.courseimage" name="fas-graduation-cap" slot="start" aria-hidden="true" />
 | 
			
		||||
<ion-avatar *ngIf="course.courseimage" slot="start">
 | 
			
		||||
    <img [url]="course.courseimage" core-external-content alt="" (error)="loadFallbackCourseIcon()" />
 | 
			
		||||
<ion-icon *ngIf="!course().courseimage" name="fas-graduation-cap" slot="start" aria-hidden="true" />
 | 
			
		||||
<ion-avatar *ngIf="course().courseimage" slot="start">
 | 
			
		||||
    <img [url]="course().courseimage" core-external-content alt="" (error)="loadFallbackCourseIcon()" />
 | 
			
		||||
</ion-avatar>
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, Input, ElementRef, OnInit, OnChanges, HostBinding } from '@angular/core';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, ElementRef, HostBinding, input, effect } from '@angular/core';
 | 
			
		||||
import { CoreCourseListItem } from '@features/courses/services/courses';
 | 
			
		||||
import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
 | 
			
		||||
import { CoreColors } from '@singletons/colors';
 | 
			
		||||
@ -22,41 +23,31 @@ import { CoreColors } from '@singletons/colors';
 | 
			
		||||
    templateUrl: 'course-image.html',
 | 
			
		||||
    styleUrls: ['./course-image.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseImageComponent implements OnInit, OnChanges {
 | 
			
		||||
export class CoreCourseImageComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() course!: CoreCourseListItem;
 | 
			
		||||
    @Input() fill = false;
 | 
			
		||||
    course = input.required<CoreCourseListItem>();
 | 
			
		||||
    fill = input(false, { transform: toBoolean });
 | 
			
		||||
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    constructor(element: ElementRef) {
 | 
			
		||||
        this.element = element.nativeElement;
 | 
			
		||||
 | 
			
		||||
        effect(() => {
 | 
			
		||||
            this.setCourseColor();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @HostBinding('class.fill-container')
 | 
			
		||||
    get fillContainer(): boolean {
 | 
			
		||||
        return this.fill;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.setCourseColor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnChanges(): void {
 | 
			
		||||
        this.setCourseColor();
 | 
			
		||||
        return this.fill();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes the course image set because it cannot be loaded and set the fallback icon color.
 | 
			
		||||
     */
 | 
			
		||||
    loadFallbackCourseIcon(): void {
 | 
			
		||||
        this.course.courseimage = undefined;
 | 
			
		||||
        this.course().courseimage = undefined;
 | 
			
		||||
 | 
			
		||||
        // Set the color because it won't be set at this point.
 | 
			
		||||
        this.setCourseColor();
 | 
			
		||||
@ -66,15 +57,17 @@ export class CoreCourseImageComponent implements OnInit, OnChanges {
 | 
			
		||||
     * Set course color.
 | 
			
		||||
     */
 | 
			
		||||
    protected async setCourseColor(): Promise<void> {
 | 
			
		||||
        await CoreCoursesHelper.loadCourseColorAndImage(this.course);
 | 
			
		||||
        const course = this.course();
 | 
			
		||||
 | 
			
		||||
        if (this.course.color) {
 | 
			
		||||
            this.element.style.setProperty('--course-color', this.course.color);
 | 
			
		||||
        await CoreCoursesHelper.loadCourseColorAndImage(course);
 | 
			
		||||
 | 
			
		||||
            const tint = CoreColors.lighter(this.course.color, 50);
 | 
			
		||||
        if (course.color) {
 | 
			
		||||
            this.element.style.setProperty('--course-color', course.color);
 | 
			
		||||
 | 
			
		||||
            const tint = CoreColors.lighter(course.color, 50);
 | 
			
		||||
            this.element.style.setProperty('--course-color-tint', tint);
 | 
			
		||||
        } else if(this.course.colorNumber !== undefined) {
 | 
			
		||||
            this.element.classList.add('course-color-' + this.course.colorNumber);
 | 
			
		||||
        } else if(course.colorNumber !== undefined) {
 | 
			
		||||
            this.element.classList.add('course-color-' + course.colorNumber);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
 | 
			
		||||
import { DownloadStatus } from '@/core/constants';
 | 
			
		||||
import { CoreAnimations } from '@components/animations';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to show a download button with refresh option, the spinner and the status of it.
 | 
			
		||||
@ -34,9 +35,9 @@ export class CoreDownloadRefreshComponent implements OnInit {
 | 
			
		||||
    @Input() status?: DownloadStatus; // Download status.
 | 
			
		||||
    @Input() statusesTranslatable?: Partial<CoreDownloadStatusTranslatable>; // Download statuses translatable strings.
 | 
			
		||||
    @Input() statusSubject = ''; // Status subject to use on name filed in the translatable string.
 | 
			
		||||
    @Input() enabled = false; // Whether the download is enabled.
 | 
			
		||||
    @Input() loading = true; // Force loading status when is not downloading.
 | 
			
		||||
    @Input() canTrustDownload = false; // If false, refresh will be shown if downloaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) enabled = false; // Whether the download is enabled.
 | 
			
		||||
    @Input({ transform: toBoolean }) loading = true; // Force loading status when is not downloading.
 | 
			
		||||
    @Input({ transform: toBoolean }) canTrustDownload = false; // If false, refresh will be shown if downloaded.
 | 
			
		||||
    @Output() action: EventEmitter<boolean>; // Will emit an event when the item clicked.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, HostBinding, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -30,13 +31,13 @@ import { Component, HostBinding, Input } from '@angular/core';
 | 
			
		||||
export class CoreEmptyBoxComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() message = ''; // Message to display.
 | 
			
		||||
    @Input() dimmed = false; // Wether the box is dimmed or not.
 | 
			
		||||
    @Input({ transform: toBoolean }) dimmed = false; // Wether the box is dimmed or not.
 | 
			
		||||
    @Input() icon?: string; // Name of the icon to use.
 | 
			
		||||
    @Input() image?: string; // Image source. If an icon is provided, image won't be used.
 | 
			
		||||
    /**
 | 
			
		||||
     * @deprecated since 4.4. Not used anymore.
 | 
			
		||||
     */
 | 
			
		||||
    @Input() flipIconRtl = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) flipIconRtl = false;
 | 
			
		||||
 | 
			
		||||
    @HostBinding('class.dimmed')
 | 
			
		||||
    get isDimmed(): boolean {
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import { DownloadStatus } from '@/core/constants';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreWSFile } from '@services/ws';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to handle a remote file. Shows the file name, icon (depending on mimetype) and a button
 | 
			
		||||
@ -41,11 +42,11 @@ export class CoreFileComponent implements OnInit, OnDestroy {
 | 
			
		||||
    @Input() file?: CoreWSFile; // The file.
 | 
			
		||||
    @Input() component?: string; // Component the file belongs to.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID.
 | 
			
		||||
    @Input() canDelete?: boolean | string; // Whether file can be deleted.
 | 
			
		||||
    @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded.
 | 
			
		||||
    @Input() canDownload?: boolean | string = true; // Whether file can be downloaded.
 | 
			
		||||
    @Input() showSize?: boolean | string = true; // Whether show filesize.
 | 
			
		||||
    @Input() showTime?: boolean | string = true; // Whether show file time modified.
 | 
			
		||||
    @Input({ transform: toBoolean }) canDelete = false; // Whether file can be deleted.
 | 
			
		||||
    @Input({ transform: toBoolean }) alwaysDownload = false; // True to always display the refresh button when file is downloaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) canDownload = true; // Whether file can be downloaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) showSize = true; // Whether show filesize.
 | 
			
		||||
    @Input({ transform: toBoolean }) showTime = true; // Whether show file time modified.
 | 
			
		||||
    @Output() onDelete: EventEmitter<void>; // Will notify when the delete button is clicked.
 | 
			
		||||
 | 
			
		||||
    isDownloading?: boolean;
 | 
			
		||||
@ -77,10 +78,6 @@ export class CoreFileComponent implements OnInit, OnDestroy {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.canDelete = CoreUtils.isTrueOrOne(this.canDelete);
 | 
			
		||||
        this.alwaysDownload = CoreUtils.isTrueOrOne(this.alwaysDownload);
 | 
			
		||||
        this.canDownload = CoreUtils.isTrueOrOne(this.canDownload);
 | 
			
		||||
 | 
			
		||||
        this.fileUrl = CoreFileHelper.getFileUrl(this.file);
 | 
			
		||||
        this.timemodified = this.file.timemodified || 0;
 | 
			
		||||
        this.siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
@ -92,11 +89,11 @@ export class CoreFileComponent implements OnInit, OnDestroy {
 | 
			
		||||
        this.openButtonIcon = this.defaultIsOpenWithPicker ? 'fas-file' : 'fas-share-from-square';
 | 
			
		||||
        this.openButtonLabel = this.defaultIsOpenWithPicker ? 'core.openfile' : 'core.openwith';
 | 
			
		||||
 | 
			
		||||
        if (CoreUtils.isTrueOrOne(this.showSize) && this.fileSize && this.fileSize >= 0) {
 | 
			
		||||
        if (this.showSize && this.fileSize && this.fileSize >= 0) {
 | 
			
		||||
            this.fileSizeReadable = CoreTextUtils.bytesToSize(this.fileSize, 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.showTime = CoreUtils.isTrueOrOne(this.showTime) && this.timemodified > 0;
 | 
			
		||||
        this.showTime = this.showTime && this.timemodified > 0;
 | 
			
		||||
 | 
			
		||||
        if ('isexternalfile' in this.file && this.file.isexternalfile) {
 | 
			
		||||
            this.alwaysDownload = true; // Always show the download button in external files.
 | 
			
		||||
 | 
			
		||||
@ -12,11 +12,11 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, OnInit, DoCheck, KeyValueDiffers } from '@angular/core';
 | 
			
		||||
import { CoreFileEntry } from '@services/file-helper';
 | 
			
		||||
 | 
			
		||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render a file list.
 | 
			
		||||
@ -33,11 +33,11 @@ export class CoreFilesComponent implements OnInit, DoCheck {
 | 
			
		||||
    @Input() files: CoreFileEntry[] = []; // List of files.
 | 
			
		||||
    @Input() component?: string; // Component the downloaded files will be linked to.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID.
 | 
			
		||||
    @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded.
 | 
			
		||||
    @Input() canDownload?: boolean | string = true; // Whether file can be downloaded.
 | 
			
		||||
    @Input() showSize?: boolean | string = true; // Whether show filesize.
 | 
			
		||||
    @Input() showTime?: boolean | string = true; // Whether show file time modified.
 | 
			
		||||
    @Input() showInline = false; // If true, it will reorder and try to show inline files first.
 | 
			
		||||
    @Input({ transform: toBoolean }) alwaysDownload = false; // True to always display the refresh button when file is downloaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) canDownload = true; // Whether file can be downloaded.
 | 
			
		||||
    @Input({ transform: toBoolean }) showSize = true; // Whether show filesize.
 | 
			
		||||
    @Input({ transform: toBoolean }) showTime = true; // Whether show file time modified.
 | 
			
		||||
    @Input({ transform: toBoolean }) showInline = false; // If true, it will reorder and try to show inline files first.
 | 
			
		||||
    @Input() extraHtml?: string[]; // Extra HTML for each attachment. Each HTML should be at the same position as the attachment.
 | 
			
		||||
 | 
			
		||||
    contentText?: string;
 | 
			
		||||
@ -53,7 +53,7 @@ export class CoreFilesComponent implements OnInit, DoCheck {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        if (CoreUtils.isTrueOrOne(this.showInline) && this.files) {
 | 
			
		||||
        if (this.showInline && this.files) {
 | 
			
		||||
            this.renderInlineFiles();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -62,7 +62,7 @@ export class CoreFilesComponent implements OnInit, DoCheck {
 | 
			
		||||
     * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
 | 
			
		||||
     */
 | 
			
		||||
    ngDoCheck(): void {
 | 
			
		||||
        if (CoreUtils.isTrueOrOne(this.showInline) && this.files) {
 | 
			
		||||
        if (this.showInline && this.files) {
 | 
			
		||||
            // Check if there's any change in the files array.
 | 
			
		||||
            const changes = this.differ.diff(this.files);
 | 
			
		||||
            if (changes) {
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ export class CoreGroupSelectorComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() groupInfo?: CoreGroupInfo;
 | 
			
		||||
    @Input() multipleGroupsMessage?: string;
 | 
			
		||||
    @Input() selected!: number;
 | 
			
		||||
    @Input({ required: true }) selected!: number;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Output() selectedChange = new EventEmitter<number>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@ import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUrl } from '@singletons/url';
 | 
			
		||||
import { CoreIframeUtils } from '@services/utils/iframe';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { DomSanitizer, Router, StatusBar } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreScreen, CoreScreenOrientation } from '@services/screen';
 | 
			
		||||
@ -29,6 +28,7 @@ import { Subscription } from 'rxjs';
 | 
			
		||||
import { filter } from 'rxjs/operators';
 | 
			
		||||
import { NavigationStart } from '@angular/router';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-iframe',
 | 
			
		||||
@ -49,10 +49,10 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
 | 
			
		||||
    @Input() id: string | null = null;
 | 
			
		||||
    @Input() iframeWidth = '100%';
 | 
			
		||||
    @Input() iframeHeight = '100%';
 | 
			
		||||
    @Input() allowFullscreen?: boolean | string;
 | 
			
		||||
    @Input() showFullscreenOnToolbar?: boolean | string;
 | 
			
		||||
    @Input() autoFullscreenOnRotate?: boolean | string;
 | 
			
		||||
    @Input() allowAutoLogin = true;
 | 
			
		||||
    @Input({ transform: toBoolean }) allowFullscreen = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) showFullscreenOnToolbar = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) autoFullscreenOnRotate = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) allowAutoLogin = true;
 | 
			
		||||
    @Output() loaded: EventEmitter<HTMLIFrameElement> = new EventEmitter<HTMLIFrameElement>();
 | 
			
		||||
 | 
			
		||||
    loading?: boolean;
 | 
			
		||||
@ -167,15 +167,6 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
 | 
			
		||||
        if (changes.iframeHeight) {
 | 
			
		||||
            this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
 | 
			
		||||
        }
 | 
			
		||||
        if (changes.allowFullscreen) {
 | 
			
		||||
            this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
 | 
			
		||||
        }
 | 
			
		||||
        if (changes.showFullscreenOnToolbar) {
 | 
			
		||||
            this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
 | 
			
		||||
        }
 | 
			
		||||
        if (changes.autoFullscreenOnRotate) {
 | 
			
		||||
            this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!changes.src) {
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core';
 | 
			
		||||
import { IonInfiniteScroll } from '@ionic/angular';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
@ -30,8 +31,8 @@ const THRESHOLD = .15; // % of the scroll element height that must be close to t
 | 
			
		||||
})
 | 
			
		||||
export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() enabled!: boolean;
 | 
			
		||||
    @Input() error = false;
 | 
			
		||||
    @Input({ required: true, transform: toBoolean }) enabled = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) error = false;
 | 
			
		||||
    @Input() position: 'top' | 'bottom' = 'bottom';
 | 
			
		||||
    @Output() action: EventEmitter<() => void>; // Will emit an event when triggered.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
import { AsyncDirective } from '@classes/async-directive';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to show a loading spinner and message while data is being loaded.
 | 
			
		||||
@ -51,9 +52,9 @@ import { CoreWait } from '@singletons/wait';
 | 
			
		||||
})
 | 
			
		||||
export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, AsyncDirective, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() hideUntil: unknown = false; // Determine when should the contents be shown.
 | 
			
		||||
    @Input({ transform: toBoolean }) hideUntil = false; // Determine when should the contents be shown.
 | 
			
		||||
    @Input() message?: string; // Message to show while loading.
 | 
			
		||||
    @Input() fullscreen = true; // Use the whole screen.
 | 
			
		||||
    @Input({ transform: toBoolean }) fullscreen = true; // Use the whole screen.
 | 
			
		||||
 | 
			
		||||
    uniqueId: string;
 | 
			
		||||
    loaded = false;
 | 
			
		||||
@ -108,7 +109,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngAfterViewInit(): void {
 | 
			
		||||
        this.changeState(!!this.hideUntil);
 | 
			
		||||
        this.changeState(this.hideUntil);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -116,7 +117,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
 | 
			
		||||
     */
 | 
			
		||||
    ngOnChanges(changes: { [name: string]: SimpleChange }): void {
 | 
			
		||||
        if (changes.hideUntil) {
 | 
			
		||||
            this.changeState(!!this.hideUntil);
 | 
			
		||||
            this.changeState(this.hideUntil);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/u
 | 
			
		||||
import { CoreForms } from '@singletons/form';
 | 
			
		||||
import { CorePath } from '@singletons/path';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to handle a local file. Only files inside the app folder can be managed.
 | 
			
		||||
@ -41,8 +42,8 @@ import { CorePlatform } from '@services/platform';
 | 
			
		||||
export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() file?: FileEntry; // A fileEntry retrieved using CoreFileProvider.getFile or similar.
 | 
			
		||||
    @Input() manage?: boolean | string; // Whether the user can manage the file (edit and delete).
 | 
			
		||||
    @Input() overrideClick?: boolean | string; // Whether the default item click should be overridden.
 | 
			
		||||
    @Input({ transform: toBoolean }) manage = false; // Whether the user can manage the file (edit and delete).
 | 
			
		||||
    @Input({ transform: toBoolean }) overrideClick = false; // Whether the default item click should be overridden.
 | 
			
		||||
    @Output() onDelete = new EventEmitter<void>(); // Will notify when the file is deleted.
 | 
			
		||||
    @Output() onRename = new EventEmitter<{ file: FileEntry }>(); // Will notify when the file is renamed.
 | 
			
		||||
    @Output() onClick = new EventEmitter<void>(); // Will notify when the file is clicked. Only if overrideClick is true.
 | 
			
		||||
@ -67,8 +68,6 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        this.manage = CoreUtils.isTrueOrOne(this.manage);
 | 
			
		||||
 | 
			
		||||
        if (!this.file) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -119,7 +118,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
 | 
			
		||||
        if (!isOpenButton && CoreUtils.isTrueOrOne(this.overrideClick) && this.onClick.observed) {
 | 
			
		||||
        if (!isOpenButton && this.overrideClick && this.onClick.observed) {
 | 
			
		||||
            this.onClick.emit();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,10 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, Input, OnInit, AfterViewInit, ElementRef } from '@angular/core';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, AfterViewInit, ElementRef } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -33,9 +33,9 @@ import { Translate } from '@singletons';
 | 
			
		||||
    templateUrl: 'core-mark-required.html',
 | 
			
		||||
    styleUrls: ['mark-required.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
 | 
			
		||||
export class CoreMarkRequiredComponent implements AfterViewInit {
 | 
			
		||||
 | 
			
		||||
    @Input('core-mark-required') coreMarkRequired: boolean | string = true;
 | 
			
		||||
    @Input({ alias: 'core-mark-required', transform: toBoolean }) coreMarkRequired = true;
 | 
			
		||||
 | 
			
		||||
    protected hostElement: HTMLElement;
 | 
			
		||||
    requiredLabel = Translate.instant('core.required');
 | 
			
		||||
@ -46,13 +46,6 @@ export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
 | 
			
		||||
        this.hostElement = element.nativeElement;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.coreMarkRequired = CoreUtils.isTrueOrOne(this.coreMarkRequired);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreText } from '@singletons/text';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUserWithAvatar } from '@components/user-avatar/user-avatar';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to handle a message in a conversation.
 | 
			
		||||
@ -39,7 +40,7 @@ export class CoreMessageComponent implements OnInit {
 | 
			
		||||
    @Input() instanceId = 0;
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() contextLevel: ContextLevel = ContextLevel.SYSTEM;
 | 
			
		||||
    @Input() showDelete = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) showDelete = false;
 | 
			
		||||
    @Output() onDeleteMessage = new EventEmitter<void>();
 | 
			
		||||
    @Output() onUndoDeleteMessage = new EventEmitter<void>();
 | 
			
		||||
    @Output() afterRender = new EventEmitter<void>();
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CoreConstants, ModPurpose } from '@/core/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import {
 | 
			
		||||
    ChangeDetectionStrategy,
 | 
			
		||||
    Component,
 | 
			
		||||
@ -54,10 +55,10 @@ export class CoreModIconComponent implements OnInit, OnChanges {
 | 
			
		||||
    @Input() fallbackTranslation = ''; // Fallback translation string if cannot auto translate.
 | 
			
		||||
    @Input() componentId?: number; // Component Id for external icons.
 | 
			
		||||
    @Input() modicon?: string; // Module icon url or local url.
 | 
			
		||||
    @Input() showAlt = true; // Show alt otherwise it's only presentation icon.
 | 
			
		||||
    @Input({ transform: toBoolean }) showAlt = true; // Show alt otherwise it's only presentation icon.
 | 
			
		||||
    @Input() purpose: ModPurpose = ModPurpose.MOD_PURPOSE_OTHER; // Purpose of the module.
 | 
			
		||||
    @Input() @HostBinding('class.colorize') colorize = true; // Colorize the icon. Only applies on 4.0 onwards.
 | 
			
		||||
    @Input() isBranded?: boolean; // If icon is branded and no colorize will be applied.
 | 
			
		||||
    @Input({ transform: toBoolean }) @HostBinding('class.colorize') colorize = true; // Colorize the icon. Only applies on 4.0+.
 | 
			
		||||
    @Input({ transform: toBoolean }) isBranded = false; // If icon is branded and no colorize will be applied.
 | 
			
		||||
 | 
			
		||||
    @HostBinding('class.branded') brandedClass?: boolean;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ import { DomSanitizer, Translate } from '@singletons';
 | 
			
		||||
})
 | 
			
		||||
export class CoreProgressBarComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() progress!: number | string; // Percentage from 0 to 100. Negative number will show an indeterminate progress bar.
 | 
			
		||||
    @Input({ required: true }) progress!: number | string; // Percentage (0 to 100). Negative number will show an indeterminate bar.
 | 
			
		||||
    @Input() text?: string; // Percentage in text to be shown at the right. If not defined, progress will be used.
 | 
			
		||||
    @Input() a11yText?: string; // Accessibility text to read before the percentage.
 | 
			
		||||
    @Input() ariaDescribedBy?: string; // ID of the element that described the progress, if any.
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreLang, CoreLangFormat } from '@services/lang';
 | 
			
		||||
@ -32,7 +33,7 @@ export class CoreRecaptchaComponent implements OnInit {
 | 
			
		||||
    @Input() publicKey?: string; // The site public key.
 | 
			
		||||
    @Input() modelValueName = 'recaptcharesponse'; // Name of the model property where to store the response.
 | 
			
		||||
    @Input() siteUrl = ''; // The site URL. If not defined, current site.
 | 
			
		||||
    @Input() showRequiredError = false; // Whether to display the required error if recaptcha hasn't been answered.
 | 
			
		||||
    @Input({ transform: toBoolean }) showRequiredError = false; // Whether to display the required error if recaptcha not answered.
 | 
			
		||||
 | 
			
		||||
    expired = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,15 +12,15 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Component, Input, Output, EventEmitter, OnInit, ViewChild, ElementRef } from '@angular/core';
 | 
			
		||||
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreForms } from '@singletons/form';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a "send message form".
 | 
			
		||||
@ -37,12 +37,12 @@ import { CorePlatform } from '@services/platform';
 | 
			
		||||
    templateUrl: 'core-send-message-form.html',
 | 
			
		||||
    styleUrls: ['send-message-form.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreSendMessageFormComponent implements OnInit {
 | 
			
		||||
export class CoreSendMessageFormComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() message = ''; // Input text.
 | 
			
		||||
    @Input() placeholder = ''; // Placeholder for the input area.
 | 
			
		||||
    @Input() showKeyboard = false; // If keyboard is shown or not.
 | 
			
		||||
    @Input() sendDisabled = false; // If send is disabled.
 | 
			
		||||
    @Input({ transform: toBoolean }) showKeyboard = false; // If keyboard is shown or not.
 | 
			
		||||
    @Input({ transform: toBoolean }) sendDisabled = false; // If send is disabled.
 | 
			
		||||
    @Output() onSubmit: EventEmitter<string>; // Send data when submitting the message form.
 | 
			
		||||
    @Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
 | 
			
		||||
 | 
			
		||||
@ -68,10 +68,6 @@ export class CoreSendMessageFormComponent implements OnInit {
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.showKeyboard = CoreUtils.isTrueOrOne(this.showKeyboard);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Form submitted.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ import { CoreDirectivesRegistry } from '@singletons/directives-registry';
 | 
			
		||||
})
 | 
			
		||||
export class CoreSheetModalComponent<T extends CoreModalComponent> implements AfterViewInit {
 | 
			
		||||
 | 
			
		||||
    @Input() component!: Constructor<T>;
 | 
			
		||||
    @Input({ required: true }) component!: Constructor<T>;
 | 
			
		||||
    @Input() componentProps?: Record<string, unknown>;
 | 
			
		||||
    @ViewChild('wrapper') wrapper?: ElementRef<HTMLElement>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
                <core-format-text [text]="site.siteName" clean="true" [siteId]="site.id" />
 | 
			
		||||
            </h2>
 | 
			
		||||
            <p *ngIf="displaySiteUrl(site)">
 | 
			
		||||
                <a [href]="site.siteUrl" core-link [autoLogin]="isCurrentSite ? 'yes' : 'no'">
 | 
			
		||||
                <a [href]="site.siteUrl" core-link [autoLogin]="!!isCurrentSite">
 | 
			
		||||
                    {{ site.siteUrlWithoutProtocol }}
 | 
			
		||||
                </a>
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter } fro
 | 
			
		||||
import { CoreSiteBasicInfo } from '@services/sites';
 | 
			
		||||
import { CoreAccountsList } from '@features/login/services/login-helper';
 | 
			
		||||
import { CoreSitesFactory } from '@services/sites-factory';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a list of sites (accounts).
 | 
			
		||||
@ -42,9 +43,9 @@ import { CoreSitesFactory } from '@services/sites-factory';
 | 
			
		||||
})
 | 
			
		||||
export class CoreSitesListComponent<T extends CoreSiteBasicInfo> {
 | 
			
		||||
 | 
			
		||||
    @Input() accountsList!: CoreAccountsList<T>;
 | 
			
		||||
    @Input() sitesClickable = false; // Whether the sites are clickable.
 | 
			
		||||
    @Input() currentSiteClickable?: boolean; // If set, specify a different clickable value for current site.
 | 
			
		||||
    @Input({ required: true }) accountsList!: CoreAccountsList<T>;
 | 
			
		||||
    @Input({ transform: toBoolean }) sitesClickable = false; // Whether the sites are clickable.
 | 
			
		||||
    @Input({ transform: toBoolean }) currentSiteClickable = false; // If set, specify a different clickable value for current site.
 | 
			
		||||
    @Output() onSiteClicked = new EventEmitter<T>();
 | 
			
		||||
 | 
			
		||||
    @ContentChild('siteItem') siteItemTemplate?: TemplateRef<{site: T; isCurrentSite: boolean}>;
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ import { CoreTabsComponent } from './tabs';
 | 
			
		||||
})
 | 
			
		||||
export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
 | 
			
		||||
 | 
			
		||||
    @Input() title!: string; // The tab title.
 | 
			
		||||
    @Input({ required: true }) title!: string; // The tab title.
 | 
			
		||||
    @Input() icon?: string; // The tab icon.
 | 
			
		||||
    @Input() badge?: string; // A badge to add in the tab.
 | 
			
		||||
    @Input() badgeStyle?: string; // The badge color.
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import {
 | 
			
		||||
 | 
			
		||||
import { CoreTabsBaseComponent } from '@classes/tabs';
 | 
			
		||||
import { CoreTabComponent } from './tab';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This component displays some top scrollable tabs that will autohide on vertical scroll.
 | 
			
		||||
@ -44,7 +45,7 @@ import { CoreTabComponent } from './tab';
 | 
			
		||||
})
 | 
			
		||||
export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> implements AfterViewInit {
 | 
			
		||||
 | 
			
		||||
    @Input() parentScrollable = false; // Determine if the scroll should be in the parent content or the tab itself.
 | 
			
		||||
    @Input({ transform: toBoolean }) parentScrollable = false; // Determine if scroll should be in the parent content or the tab.
 | 
			
		||||
    @Input() layout: 'icon-top' | 'icon-start' | 'icon-end' | 'icon-bottom' | 'icon-hide' | 'label-hide' = 'icon-hide';
 | 
			
		||||
 | 
			
		||||
    @ViewChild('originalTabs')
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ElementRef } from '@angular/core';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
 | 
			
		||||
@ -35,7 +36,7 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
 | 
			
		||||
    @Input() timeLeftClass?: string; // Name of the class to apply with each second. By default, 'core-timer-timeleft-'.
 | 
			
		||||
    @Input() timeLeftClassThreshold = 100; // Number of seconds to start adding the timeLeftClass. Set it to -1 to not add it.
 | 
			
		||||
    @Input() align = 'start'; // Where to align the time and text. Defaults to 'start'. Other values: 'center', 'end'.
 | 
			
		||||
    @Input() hidable = false; // Whether the user can hide the time left.
 | 
			
		||||
    @Input({ transform: toBoolean }) hidable = false; // Whether the user can hide the time left.
 | 
			
		||||
    @Input() timeUpText?: string; // Text to show when the timer reaches 0. If not defined, 'core.timesup'.
 | 
			
		||||
    @Input() mode: CoreTimerMode = CoreTimerMode.ITEM; // How to display data.
 | 
			
		||||
    @Input() underTimeClassThresholds = []; // Number of seconds to add the class 'core-timer-under-'.
 | 
			
		||||
@ -45,7 +46,7 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
 | 
			
		||||
    /**
 | 
			
		||||
     * @deprecated since 4.4. Use hidable instead.
 | 
			
		||||
     */
 | 
			
		||||
    @Input() hiddable?: boolean; // Whether the user can hide the time left.
 | 
			
		||||
    @Input({ transform: toBoolean }) hiddable = false; // Whether the user can hide the time left.
 | 
			
		||||
 | 
			
		||||
    timeLeft?: number; // Seconds left to end.
 | 
			
		||||
    modeBasic = CoreTimerMode.BASIC;
 | 
			
		||||
@ -63,9 +64,9 @@ export class CoreTimerComponent implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        // eslint-disable-next-line deprecation/deprecation
 | 
			
		||||
        if (this.hiddable !== undefined && this.hidable === undefined) {
 | 
			
		||||
            // eslint-disable-next-line deprecation/deprecation
 | 
			
		||||
            this.hidable = this.hiddable;
 | 
			
		||||
        if (this.hiddable && !this.hidable) {
 | 
			
		||||
 | 
			
		||||
            this.hidable = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const timeLeftClass = this.timeLeftClass || 'core-timer-timeleft-';
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import { CoreNetwork } from '@services/network';
 | 
			
		||||
import { CoreUserHelper } from '@features/user/services/user-helper';
 | 
			
		||||
import { CoreUrl } from '@singletons/url';
 | 
			
		||||
import { CoreSiteInfo } from '@classes/sites/unauthenticated-site';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a "user avatar".
 | 
			
		||||
@ -40,11 +41,11 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
    @Input() site?: CoreSiteBasicInfo | CoreSiteInfo; // Site info contains user info.
 | 
			
		||||
    // The following params will override the ones in user object.
 | 
			
		||||
    @Input() profileUrl?: string;
 | 
			
		||||
    @Input() linkProfile = true; // Avoid linking to the profile if wanted.
 | 
			
		||||
    @Input({ transform: toBoolean }) linkProfile = true; // Avoid linking to the profile if wanted.
 | 
			
		||||
    @Input() fullname?: string;
 | 
			
		||||
    @Input() userId?: number; // If provided or found it will be used to link the image to the profile.
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() checkOnline = false; // If want to check and show online status.
 | 
			
		||||
    @Input({ transform: toBoolean }) checkOnline = false; // If want to check and show online status.
 | 
			
		||||
    @Input() siteId?: string;
 | 
			
		||||
 | 
			
		||||
    avatarUrl?: string;
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { Directive, ElementRef, OnInit, Output, EventEmitter, OnChanges, SimpleChanges, Input } from '@angular/core';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to emulate click and key actions following aria role button.
 | 
			
		||||
@ -25,7 +26,7 @@ export class CoreAriaButtonClickDirective implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    @Input() disabled = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) disabled = false;
 | 
			
		||||
    @Output() ariaButtonClick = new EventEmitter();
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,9 @@
 | 
			
		||||
import { Directive, Input, ElementRef, AfterViewInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to auto focus an element when a view is loaded.
 | 
			
		||||
@ -32,7 +32,7 @@ import { CoreWait } from '@singletons/wait';
 | 
			
		||||
})
 | 
			
		||||
export class CoreAutoFocusDirective implements AfterViewInit {
 | 
			
		||||
 | 
			
		||||
    @Input('core-auto-focus') autoFocus: boolean | string = true;
 | 
			
		||||
    @Input({ alias: 'core-auto-focus', transform: toBoolean }) autoFocus = true;
 | 
			
		||||
 | 
			
		||||
    protected element: HTMLIonInputElement | HTMLIonTextareaElement | HTMLIonSearchbarElement | HTMLElement;
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ export class CoreAutoFocusDirective implements AfterViewInit {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngAfterViewInit(): Promise<void> {
 | 
			
		||||
        if (CoreUtils.isFalseOrZero(this.autoFocus)) {
 | 
			
		||||
        if (!this.autoFocus) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import { CoreLoadingComponent } from '@components/loading/loading';
 | 
			
		||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to make an element fixed at the bottom collapsible when scrolling.
 | 
			
		||||
@ -37,7 +38,7 @@ import { CoreWait } from '@singletons/wait';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() appearOnBottom = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) appearOnBottom = false; // Whether footer should re-appear when reaching the bottom.
 | 
			
		||||
 | 
			
		||||
    protected id = '0';
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
@ -67,9 +68,6 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        this.id = String(CoreUtils.getUniqueId('CoreCollapsibleFooterDirective'));
 | 
			
		||||
 | 
			
		||||
        // Only if not present or explicitly falsy it will be false.
 | 
			
		||||
        this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
 | 
			
		||||
        this.slotPromise = CoreDom.slotOnContent(this.element);
 | 
			
		||||
 | 
			
		||||
        await this.slotPromise;
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet';
 | 
			
		||||
import { CoreTabsComponent } from '@components/tabs/tabs';
 | 
			
		||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
 | 
			
		||||
import { ScrollDetail } from '@ionic/core';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
@ -28,6 +27,7 @@ import { CoreMath } from '@singletons/math';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
import { CoreFormatTextDirective } from './format-text';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
declare module '@singletons/events' {
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@ export const COLLAPSIBLE_HEADER_UPDATED = 'collapsible_header_updated';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() collapsible = true;
 | 
			
		||||
    @Input({ transform: toBoolean }) collapsible = true;
 | 
			
		||||
 | 
			
		||||
    protected page?: HTMLElement;
 | 
			
		||||
    protected collapsedHeader: HTMLIonHeaderElement;
 | 
			
		||||
@ -106,8 +106,6 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.collapsible = !CoreUtils.isFalseOrZero(this.collapsible);
 | 
			
		||||
 | 
			
		||||
        if (CoreDom.closest(this.collapsedHeader, 'core-tabs-outlet')) {
 | 
			
		||||
            this.collapsible = false;
 | 
			
		||||
        }
 | 
			
		||||
@ -142,7 +140,6 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnChanges(changes: {[name: string]: SimpleChange}): Promise<void> {
 | 
			
		||||
        if (changes.collapsible && !changes.collapsible.firstChange) {
 | 
			
		||||
            this.collapsible = !CoreUtils.isFalseOrZero(changes.collapsible.currentValue);
 | 
			
		||||
            this.enabled = this.collapsible;
 | 
			
		||||
 | 
			
		||||
            await this.init();
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,7 @@ import { CoreUrl } from '@singletons/url';
 | 
			
		||||
import { CoreIcons } from '@singletons/icons';
 | 
			
		||||
import { ContextLevel } from '../constants';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
 | 
			
		||||
@ -77,19 +78,20 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
 | 
			
		||||
    @Input() siteId?: string; // Site ID to use.
 | 
			
		||||
    @Input() component?: string; // Component for CoreExternalContentDirective.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID to use in conjunction with the component.
 | 
			
		||||
    @Input() adaptImg?: boolean | string = true; // Whether to adapt images to screen width.
 | 
			
		||||
    @Input() clean?: boolean | string; // Whether all the HTML tags should be removed.
 | 
			
		||||
    @Input() singleLine?: boolean | string; // Whether new lines should be removed (all text in single line). Only if clean=true.
 | 
			
		||||
    @Input({ transform: toBoolean }) adaptImg = true; // Whether to adapt images to screen width.
 | 
			
		||||
    @Input({ transform: toBoolean }) clean = false; // Whether all the HTML tags should be removed.
 | 
			
		||||
    @Input({ transform: toBoolean }) singleLine = false; // Whether new lines should be removed. Only if clean=true.
 | 
			
		||||
    @Input() highlight?: string; // Text to highlight.
 | 
			
		||||
    @Input() filter?: boolean | string; // Whether to filter the text. If not defined, true if contextLevel and instanceId are set.
 | 
			
		||||
    @Input({ transform: toBoolean }) filter?: boolean; // Whether to filter the text.
 | 
			
		||||
                                                       // If not defined, true if contextLevel and instanceId are set.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level of the text.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
 | 
			
		||||
    @Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
 | 
			
		||||
    @Input() captureLinks?: boolean; // Whether links should tried to be opened inside the app. Defaults to true.
 | 
			
		||||
    @Input() openLinksInApp?: boolean; // Whether links should be opened in InAppBrowser.
 | 
			
		||||
    @Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
 | 
			
		||||
    @Input() disabled?: boolean; // If disabled, autoplay elements will be disabled.
 | 
			
		||||
    @Input({ transform: toBoolean }) wsNotFiltered = false; // If true it means the WS didn't filter the text for some reason.
 | 
			
		||||
    @Input({ transform: toBoolean }) captureLinks = true; // Whether links should tried to be opened inside the app.
 | 
			
		||||
    @Input({ transform: toBoolean }) openLinksInApp = false; // Whether links should be opened in InAppBrowser.
 | 
			
		||||
    @Input({ transform: toBoolean }) hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
 | 
			
		||||
    @Input({ transform: toBoolean }) disabled = false; // If disabled, autoplay elements will be disabled.
 | 
			
		||||
 | 
			
		||||
    @Output() afterRender: EventEmitter<void>; // Called when the data is rendered.
 | 
			
		||||
    @Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
 | 
			
		||||
@ -361,7 +363,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.element.getAttribute('singleLine')) {
 | 
			
		||||
            this.element.setAttribute('singleLine', String(CoreUtils.isTrueOrOne(this.singleLine)));
 | 
			
		||||
            this.element.setAttribute('singleLine', String(this.singleLine));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.text = this.text ? this.text.trim() : '';
 | 
			
		||||
@ -423,15 +425,14 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
 | 
			
		||||
            this.contextInstanceId = this.courseId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const filter = this.filter === undefined ?
 | 
			
		||||
            !!(this.contextLevel && this.contextInstanceId !== undefined) : CoreUtils.isTrueOrOne(this.filter);
 | 
			
		||||
        const filter = this.filter ?? !!(this.contextLevel && this.contextInstanceId !== undefined);
 | 
			
		||||
 | 
			
		||||
        const options: CoreFilterFormatTextOptions = {
 | 
			
		||||
            clean: CoreUtils.isTrueOrOne(this.clean),
 | 
			
		||||
            singleLine: CoreUtils.isTrueOrOne(this.singleLine),
 | 
			
		||||
            clean: this.clean,
 | 
			
		||||
            singleLine: this.singleLine,
 | 
			
		||||
            highlight: this.highlight,
 | 
			
		||||
            courseId: this.courseId,
 | 
			
		||||
            wsNotFiltered: CoreUtils.isTrueOrOne(this.wsNotFiltered),
 | 
			
		||||
            wsNotFiltered: this.wsNotFiltered,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let formatted: string;
 | 
			
		||||
@ -521,7 +522,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
 | 
			
		||||
                    externalImages.push(externalImage);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (CoreUtils.isTrueOrOne(this.adaptImg) && !img.classList.contains('icon')) {
 | 
			
		||||
                if (this.adaptImg && !img.classList.contains('icon')) {
 | 
			
		||||
                    this.adaptImage(img);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import { CoreCustomURLSchemes } from '@services/urlschemes';
 | 
			
		||||
import { DomSanitizer } from '@singletons';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { toBoolean } from '../transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directive to open a link in external browser or in the app.
 | 
			
		||||
@ -37,10 +38,10 @@ import { CoreDom } from '@singletons/dom';
 | 
			
		||||
export class CoreLinkDirective implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() href?: string | SafeUrl; // Link URL.
 | 
			
		||||
    @Input() capture?: boolean | string; // If the link needs to be captured by the app.
 | 
			
		||||
    @Input() inApp?: boolean | string; // True to open in embedded browser, false to open in system browser.
 | 
			
		||||
    @Input() autoLogin: boolean | string = true; // Whether to try to use auto-login. Values yes/no/check are deprecated.
 | 
			
		||||
    @Input() showBrowserWarning = true; // Whether to show a warning before opening browser. Defaults to true.
 | 
			
		||||
    @Input({ transform: toBoolean }) capture = false; // If the link needs to be captured by the app.
 | 
			
		||||
    @Input({ transform: toBoolean }) inApp = false; // True to open in embedded browser, false to open in system browser.
 | 
			
		||||
    @Input({ transform: toBoolean }) autoLogin = true; // Whether to try to use auto-login.
 | 
			
		||||
    @Input({ transform: toBoolean }) showBrowserWarning = true; // Whether to show a warning before opening browser.
 | 
			
		||||
 | 
			
		||||
    protected element: HTMLElement | HTMLIonFabButtonElement | HTMLIonButtonElement | HTMLIonItemElement;
 | 
			
		||||
 | 
			
		||||
@ -93,7 +94,7 @@ export class CoreLinkDirective implements OnInit {
 | 
			
		||||
 | 
			
		||||
        const openIn = this.element.getAttribute('data-open-in');
 | 
			
		||||
 | 
			
		||||
        if (CoreUtils.isTrueOrOne(this.capture)) {
 | 
			
		||||
        if (this.capture) {
 | 
			
		||||
            const treated = await CoreContentLinksHelper.handleLink(CoreTextUtils.decodeURI(href), undefined, true, true);
 | 
			
		||||
 | 
			
		||||
            if (!treated) {
 | 
			
		||||
@ -177,8 +178,7 @@ export class CoreLinkDirective implements OnInit {
 | 
			
		||||
     */
 | 
			
		||||
    protected async openExternalLink(href: string, openIn?: string | null): Promise<void> {
 | 
			
		||||
        // Priority order is: core-link inApp attribute > forceOpenLinksIn setting > data-open-in HTML attribute.
 | 
			
		||||
        const openInApp = this.inApp !== undefined ?
 | 
			
		||||
            CoreUtils.isTrueOrOne(this.inApp) :
 | 
			
		||||
        const openInApp = this.inApp ??
 | 
			
		||||
            (CoreConstants.CONFIG.forceOpenLinksIn !== 'browser' &&
 | 
			
		||||
                (CoreConstants.CONFIG.forceOpenLinksIn === 'app' || openIn === 'app'));
 | 
			
		||||
 | 
			
		||||
@ -219,11 +219,7 @@ export class CoreLinkDirective implements OnInit {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const autoLogin = typeof this.autoLogin === 'boolean' ?
 | 
			
		||||
            this.autoLogin :
 | 
			
		||||
            !CoreUtils.isFalseOrZero(this.autoLogin) && this.autoLogin !== 'no'; // Support deprecated values yes/no/check.
 | 
			
		||||
 | 
			
		||||
        if (autoLogin) {
 | 
			
		||||
        if (this.autoLogin) {
 | 
			
		||||
            if (openInApp) {
 | 
			
		||||
                await currentSite.openInAppWithAutoLogin(href);
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ import { CoreDom } from '@singletons/dom';
 | 
			
		||||
})
 | 
			
		||||
export class CoreUserTourDirective implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() userTour!: CoreUserTourDirectiveOptions;
 | 
			
		||||
    @Input({ required: true }) userTour!: CoreUserTourDirectiveOptions;
 | 
			
		||||
 | 
			
		||||
    private tour?: CoreUserToursUserTour | null;
 | 
			
		||||
    private element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,10 @@ import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
})
 | 
			
		||||
export abstract class CoreBlockBaseComponent implements OnInit, OnChanges, ICoreBlockComponent, AsyncDirective {
 | 
			
		||||
 | 
			
		||||
    @Input() title!: string; // The block title.
 | 
			
		||||
    @Input() block!: CoreCourseBlock; // The block to render.
 | 
			
		||||
    @Input() contextLevel!: ContextLevel; // The context where the block will be used.
 | 
			
		||||
    @Input() instanceId!: number; // The instance ID associated with the context level.
 | 
			
		||||
    @Input({ required: true }) title!: string; // The block title.
 | 
			
		||||
    @Input({ required: true }) block!: CoreCourseBlock; // The block to render.
 | 
			
		||||
    @Input({ required: true }) contextLevel!: ContextLevel; // The context where the block will be used.
 | 
			
		||||
    @Input({ required: true }) instanceId!: number; // The instance ID associated with the context level.
 | 
			
		||||
    @Input() link?: string; // Link to go when clicked.
 | 
			
		||||
    @Input() linkParams?: Params; // Link params to go when clicked.
 | 
			
		||||
    @Input() navOptions?: CoreNavigationOptions; // Navigation options.
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,10 @@ export class CoreBlockComponent implements OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @ViewChild(CoreDynamicComponent) dynamicComponent?: CoreDynamicComponent<ICoreBlockComponent>;
 | 
			
		||||
 | 
			
		||||
    @Input() block!: CoreCourseBlock; // The block to render.
 | 
			
		||||
    @Input() contextLevel!: ContextLevel; // The context where the block will be used.
 | 
			
		||||
    @Input() instanceId!: number; // The instance ID associated with the context level.
 | 
			
		||||
    @Input() extraData!: Record<string, unknown>; // Any extra data to be passed to the block.
 | 
			
		||||
    @Input({ required: true }) block!: CoreCourseBlock; // The block to render.
 | 
			
		||||
    @Input({ required: true }) contextLevel!: ContextLevel; // The context where the block will be used.
 | 
			
		||||
    @Input({ required: true }) instanceId!: number; // The instance ID associated with the context level.
 | 
			
		||||
    @Input() extraData?: Record<string, unknown>; // Any extra data to be passed to the block.
 | 
			
		||||
    @Input() labelledBy?: string;
 | 
			
		||||
 | 
			
		||||
    componentClass?: Type<ICoreBlockComponent>; // The class of the component to render.
 | 
			
		||||
 | 
			
		||||
@ -31,8 +31,8 @@ import { ContextLevel } from '@/core/constants';
 | 
			
		||||
})
 | 
			
		||||
export class CoreBlockSideBlocksButtonComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() contextLevel!: ContextLevel;
 | 
			
		||||
    @Input() instanceId!: number;
 | 
			
		||||
    @Input({ required: true }) contextLevel!: ContextLevel;
 | 
			
		||||
    @Input({ required: true }) instanceId!: number;
 | 
			
		||||
    @Input() myDashboardPage?: string;
 | 
			
		||||
 | 
			
		||||
    userTour: CoreUserTourDirectiveOptions = {
 | 
			
		||||
 | 
			
		||||
@ -42,8 +42,8 @@ import { CoreBlockComponentsModule } from '../components.module';
 | 
			
		||||
})
 | 
			
		||||
export class CoreBlockSideBlocksComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() contextLevel!: ContextLevel;
 | 
			
		||||
    @Input() instanceId!: number;
 | 
			
		||||
    @Input({ required: true }) contextLevel!: ContextLevel;
 | 
			
		||||
    @Input({ required: true }) instanceId!: number;
 | 
			
		||||
    @Input() initialBlockInstanceId?: number;
 | 
			
		||||
    @Input() myDashboardPage?: string;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays the count of comments.
 | 
			
		||||
@ -33,15 +34,15 @@ import { ContextLevel } from '@/core/constants';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCommentsCommentsComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() contextLevel!: ContextLevel;
 | 
			
		||||
    @Input() instanceId!: number;
 | 
			
		||||
    @Input() component!: string;
 | 
			
		||||
    @Input() itemId!: number;
 | 
			
		||||
    @Input({ required: true }) contextLevel!: ContextLevel;
 | 
			
		||||
    @Input({ required: true }) instanceId!: number;
 | 
			
		||||
    @Input({ required: true }) component!: string;
 | 
			
		||||
    @Input({ required: true }) itemId!: number;
 | 
			
		||||
    @Input() area = '';
 | 
			
		||||
    @Input() title?: string;
 | 
			
		||||
    @Output() onLoading = new EventEmitter<boolean>();  // Event that indicates whether the component is loading data.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the comments belong to. It can be used to improve performance with filters.
 | 
			
		||||
    @Input() showItem = false; // Show button as an item.
 | 
			
		||||
    @Input({ transform: toBoolean }) showItem = false; // Show button as an item.
 | 
			
		||||
 | 
			
		||||
    commentsLoaded = false;
 | 
			
		||||
    commentsCount = '';
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import {
 | 
			
		||||
    Component,
 | 
			
		||||
    Input,
 | 
			
		||||
@ -32,6 +33,7 @@ import {
 | 
			
		||||
    AfterViewInit,
 | 
			
		||||
    Type,
 | 
			
		||||
    KeyValueDiffer,
 | 
			
		||||
    Injector,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
 | 
			
		||||
@ -63,14 +65,14 @@ import { CoreDom } from '@singletons/dom';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
 | 
			
		||||
    @Input() text!: string; // The HTML text to display.
 | 
			
		||||
    @Input({ required: true }) text!: string; // The HTML text to display.
 | 
			
		||||
    @Input() javascript?: string; // The Javascript to execute in the component.
 | 
			
		||||
    @Input() jsData?: Record<string, unknown>; // Data to pass to the fake component.
 | 
			
		||||
    @Input() cssCode?: string; // The styles to apply.
 | 
			
		||||
    @Input() stylesPath?: string; // The styles URL to apply (only if cssCode is not set).
 | 
			
		||||
    @Input() extraImports: unknown[] = []; // Extra import modules.
 | 
			
		||||
    @Input() extraProviders: Type<unknown>[] = []; // Extra providers.
 | 
			
		||||
    @Input() forceCompile = false; // Set it to true to force compile even if the text/javascript hasn't changed.
 | 
			
		||||
    @Input({ transform: toBoolean }) forceCompile = false; // True to force compile even if the text/javascript hasn't changed.
 | 
			
		||||
    @Output() created = new EventEmitter<unknown>(); // Will emit an event when the component is instantiated.
 | 
			
		||||
    @Output() compiling = new EventEmitter<boolean>(); // Event that indicates whether the template is being compiled.
 | 
			
		||||
 | 
			
		||||
@ -88,6 +90,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        protected changeDetector: ChangeDetectorRef,
 | 
			
		||||
        protected injector: Injector,
 | 
			
		||||
        element: ElementRef,
 | 
			
		||||
        differs: KeyValueDiffers,
 | 
			
		||||
    ) {
 | 
			
		||||
@ -122,7 +125,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
        // Only compile if text/javascript has changed or the forceCompile flag has been set to true.
 | 
			
		||||
        if (this.text === undefined ||
 | 
			
		||||
            !(changes.text || changes.javascript || changes.cssCode || changes.stylesPath ||
 | 
			
		||||
                (changes.forceCompile && CoreUtils.isTrueOrOne(this.forceCompile)))) {
 | 
			
		||||
                (changes.forceCompile && this.forceCompile))) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -222,6 +225,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
                CoreCompile.injectLibraries(
 | 
			
		||||
                    this,
 | 
			
		||||
                    compileInstance.extraProviders,
 | 
			
		||||
                    compileInstance.injector,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                // Always add these elements, they could be needed on component init (componentObservable).
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,10 @@ import {
 | 
			
		||||
    ViewContainerRef,
 | 
			
		||||
    signal,
 | 
			
		||||
    computed,
 | 
			
		||||
    effect,
 | 
			
		||||
    EffectCleanupRegisterFn,
 | 
			
		||||
    CreateEffectOptions,
 | 
			
		||||
    EffectRef,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
    ActionSheetController,
 | 
			
		||||
@ -260,9 +264,10 @@ export class CoreCompileProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @param instance The instance where to inject the libraries.
 | 
			
		||||
     * @param extraLibraries Extra imported providers if needed and not imported by this class.
 | 
			
		||||
     * @param injector Injector of the injection context. E.g. for a component, use the component's injector.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
    injectLibraries(instance: any, extraLibraries: Type<unknown>[] = []): void {
 | 
			
		||||
    injectLibraries(instance: any, extraLibraries: Type<unknown>[] = [], injector?: Injector): void {
 | 
			
		||||
        if (!this.libraries || !this.exportedObjects) {
 | 
			
		||||
            throw new CoreError('Libraries not loaded. You need to call loadLibraries before calling injectLibraries.');
 | 
			
		||||
        }
 | 
			
		||||
@ -271,6 +276,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...this.libraries,
 | 
			
		||||
            ...extraLibraries,
 | 
			
		||||
        ];
 | 
			
		||||
        injector = injector ?? this.injector;
 | 
			
		||||
 | 
			
		||||
        // We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
 | 
			
		||||
        for (const i in libraries) {
 | 
			
		||||
@ -278,7 +284,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            if (typeof libraryDef === 'function' && libraryDef.name) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Inject the provider to the instance. We use the class name as the property name.
 | 
			
		||||
                    instance[libraryDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get<Provider>(libraryDef);
 | 
			
		||||
                    instance[libraryDef.name.replace(/DelegateService$/, 'Delegate')] = injector.get<Provider>(libraryDef);
 | 
			
		||||
                } catch (ex) {
 | 
			
		||||
                    this.logger.error('Error injecting provider', libraryDef.name, ex);
 | 
			
		||||
                }
 | 
			
		||||
@ -289,17 +295,26 @@ export class CoreCompileProvider {
 | 
			
		||||
        instance['CoreCompileProvider'] = this;
 | 
			
		||||
 | 
			
		||||
        // Add some final classes.
 | 
			
		||||
        instance['injector'] = this.injector;
 | 
			
		||||
        instance['injector'] = injector;
 | 
			
		||||
        instance['Validators'] = Validators;
 | 
			
		||||
        instance['CoreConstants'] = CoreConstants;
 | 
			
		||||
        instance['DownloadStatus'] = DownloadStatus;
 | 
			
		||||
        instance['CoreConfigConstants'] = CoreConstants.CONFIG;
 | 
			
		||||
        instance['CoreEventsProvider'] = CoreEvents;
 | 
			
		||||
        instance['CoreLoggerProvider'] = CoreLogger;
 | 
			
		||||
        instance['signal'] = signal;
 | 
			
		||||
        instance['computed'] = computed;
 | 
			
		||||
        instance['moment'] = moment;
 | 
			
		||||
        instance['Md5'] = Md5;
 | 
			
		||||
        instance['signal'] = signal;
 | 
			
		||||
        instance['computed'] = computed;
 | 
			
		||||
        // Create a wrapper to call effect with the proper injection context.
 | 
			
		||||
        instance['effect'] = (
 | 
			
		||||
            effectFn: (onCleanup: EffectCleanupRegisterFn) => void,
 | 
			
		||||
            options?: Omit<CreateEffectOptions, 'injector'>,
 | 
			
		||||
        ): EffectRef =>
 | 
			
		||||
            effect(effectFn, {
 | 
			
		||||
                ...options,
 | 
			
		||||
                injector,
 | 
			
		||||
            });
 | 
			
		||||
        /**
 | 
			
		||||
         * @deprecated since 4.1, plugins should use CoreNetwork instead.
 | 
			
		||||
         * Keeping this a bit more to avoid plugins breaking.
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
})
 | 
			
		||||
export class CoreContentLinksChooseSiteModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() url!: string;
 | 
			
		||||
    @Input({ required: true }) url!: string;
 | 
			
		||||
 | 
			
		||||
    sites: CoreSiteBasicInfo[] = [];
 | 
			
		||||
    loaded = false;
 | 
			
		||||
 | 
			
		||||
@ -52,8 +52,8 @@ export type CoreCourseResourceDownloadResult = {
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() module!: CoreCourseModuleData; // The module of the component.
 | 
			
		||||
    @Input() courseId!: number; // Course ID the component belongs to.
 | 
			
		||||
    @Input({ required: true }) module!: CoreCourseModuleData; // The module of the component.
 | 
			
		||||
    @Input({ required: true }) courseId!: number; // Course ID the component belongs to.
 | 
			
		||||
    @Output() dataRetrieved = new EventEmitter<unknown>(); // Called to notify changes the index page from the main component.
 | 
			
		||||
 | 
			
		||||
    showLoading = true; // Whether to show loading.
 | 
			
		||||
 | 
			
		||||
@ -58,6 +58,7 @@ import { CoreBlockComponentsModule } from '@features/block/components/components
 | 
			
		||||
import { CoreCourseComponentsModule } from '../components.module';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { COURSE_ALL_SECTIONS_PREFERRED_PREFIX } from '@features/course/constants';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display course contents using a certain format. If the format isn't found, use default one.
 | 
			
		||||
@ -84,13 +85,13 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    static readonly LOAD_MORE_ACTIVITIES = 10; // How many activities should load each time showMoreActivities is called.
 | 
			
		||||
 | 
			
		||||
    @Input() course!: CoreCourseAnyCourseData; // The course to render.
 | 
			
		||||
    @Input({ required: true }) course!: CoreCourseAnyCourseData; // The course to render.
 | 
			
		||||
    @Input() sections: CoreCourseSectionToDisplay[] = []; // List of course sections.
 | 
			
		||||
    @Input() initialSectionId?: number; // The section to load first (by ID).
 | 
			
		||||
    @Input() initialSectionNumber?: number; // The section to load first (by number).
 | 
			
		||||
    @Input() initialBlockInstanceId?: number; // The instance to focus.
 | 
			
		||||
    @Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
 | 
			
		||||
    @Input() isGuest?: boolean; // If user is accessing using an ACCESS_GUEST enrolment method.
 | 
			
		||||
    @Input({ transform: toBoolean }) isGuest = false; // If user is accessing using an ACCESS_GUEST enrolment method.
 | 
			
		||||
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
    @ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to handle activity completion. It shows a checkbox with the current status, and allows manually changing
 | 
			
		||||
@ -43,8 +44,8 @@ export class CoreCourseModuleCompletionComponent
 | 
			
		||||
    extends CoreCourseModuleCompletionBaseComponent
 | 
			
		||||
    implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() showCompletionConditions = false; // Whether to show activity completion conditions.
 | 
			
		||||
    @Input() showManualCompletion = false; // Whether to show manual completion.
 | 
			
		||||
    @Input({ transform: toBoolean }) showCompletionConditions = false; // Whether to show activity completion conditions.
 | 
			
		||||
    @Input({ transform: toBoolean }) showManualCompletion = false; // Whether to show manual completion.
 | 
			
		||||
 | 
			
		||||
    completed = false;
 | 
			
		||||
    accessibleDescription: string | null = null;
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, HostBinding, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -49,7 +50,7 @@ export class CoreCourseModuleDescriptionComponent {
 | 
			
		||||
    @Input() note?: string; // A note to display along with the description.
 | 
			
		||||
    @Input() component?: string; // Component for format text directive.
 | 
			
		||||
    @Input() componentId?: string | number; // Component ID to use in conjunction with the component.
 | 
			
		||||
    @Input() showFull?: string | boolean; // Whether to always display the full description.
 | 
			
		||||
    @Input({ transform: toBoolean }) showFull = false; // Whether to always display the full description.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModuleCompletionData, CoreCourseModuleData } from '@features/course/services/course-helper';
 | 
			
		||||
@ -35,20 +36,20 @@ import { CoreSites } from '@services/sites';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseModuleInfoComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() module!: CoreCourseModuleData; // The module to render.
 | 
			
		||||
    @Input() courseId!: number; // The courseId the module belongs to.
 | 
			
		||||
    @Input({ required: true }) module!: CoreCourseModuleData; // The module to render.
 | 
			
		||||
    @Input({ required: true }) courseId!: number; // The courseId the module belongs to.
 | 
			
		||||
 | 
			
		||||
    @Input() component!: string; // Component for format text directive.
 | 
			
		||||
    @Input() componentId!: string | number; // Component ID to use in conjunction with the component.
 | 
			
		||||
    @Input({ required: true }) component!: string; // Component for format text directive.
 | 
			
		||||
    @Input({ required: true }) componentId!: string | number; // Component ID to use in conjunction with the component.
 | 
			
		||||
 | 
			
		||||
    @Input() description?: string | false; // The description to display. If false, no description will be shown.
 | 
			
		||||
    @Input() expandDescription = false; // If the description should be expanded by default.
 | 
			
		||||
    @Input({ transform: toBoolean }) expandDescription = false; // If the description should be expanded by default.
 | 
			
		||||
 | 
			
		||||
    @Input() showAvailabilityInfo = false; // If show availability info on the box.
 | 
			
		||||
    @Input({ transform: toBoolean }) showAvailabilityInfo = false; // If show availability info on the box.
 | 
			
		||||
 | 
			
		||||
    @Input() hasDataToSync = false; // If the activity has any data to be synced.
 | 
			
		||||
    @Input({ transform: toBoolean }) hasDataToSync = false; // If the activity has any data to be synced.
 | 
			
		||||
 | 
			
		||||
    @Input() showManualCompletion = true; // Whether to show manual completion, true by default.
 | 
			
		||||
    @Input({ transform: toBoolean }) showManualCompletion = true; // Whether to show manual completion, true by default.
 | 
			
		||||
    @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when completion changes.
 | 
			
		||||
 | 
			
		||||
    modicon = '';
 | 
			
		||||
 | 
			
		||||
@ -36,8 +36,8 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() courseId!: number; // Course ID.
 | 
			
		||||
    @Input() currentModuleId!: number; // Current module Id.
 | 
			
		||||
    @Input({ required: true }) courseId!: number; // Course ID.
 | 
			
		||||
    @Input({ required: true }) currentModuleId!: number; // Current module Id.
 | 
			
		||||
 | 
			
		||||
    nextModule?: CoreCourseModuleData;
 | 
			
		||||
    previousModule?: CoreCourseModuleData;
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,7 @@ import { ModalController, NgZone } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a module summary modal.
 | 
			
		||||
@ -54,7 +55,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy {
 | 
			
		||||
    @Input() moduleId = 0; // Module ID the component belongs to.
 | 
			
		||||
    @Input() component = ''; // Component name.
 | 
			
		||||
    @Input() description = ''; // Module description.
 | 
			
		||||
    @Input() hasOffline = false; // If it has offline data to be synced.
 | 
			
		||||
    @Input({ transform: toBoolean }) hasOffline = false; // If it has offline data to be synced.
 | 
			
		||||
    @Input() displayOptions: CoreCourseModuleSummaryDisplayOptions = {};
 | 
			
		||||
 | 
			
		||||
    loaded = false; // If the component has been loaded.
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import {
 | 
			
		||||
import { CoreConstants, DownloadStatus } from '@/core/constants';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { BehaviorSubject } from 'rxjs';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a module entry in a list of modules.
 | 
			
		||||
@ -45,17 +46,17 @@ import { BehaviorSubject } from 'rxjs';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() module!: CoreCourseModuleData; // The module to render.
 | 
			
		||||
    @Input({ required: true }) module!: CoreCourseModuleData; // The module to render.
 | 
			
		||||
    @Input() section?: CoreCourseSection; // The section the module belongs to.
 | 
			
		||||
    @Input() showActivityDates = false; // Whether to show activity dates.
 | 
			
		||||
    @Input() showCompletionConditions = false; // Whether to show activity completion conditions.
 | 
			
		||||
    @Input() showLegacyCompletion?: boolean; // Whether to show module completion in the old format.
 | 
			
		||||
    @Input() showCompletion = true; // Whether to show module completion.
 | 
			
		||||
    @Input() showAvailability = true; // Whether to show module availability.
 | 
			
		||||
    @Input() showExtra = true; // Whether to show extra badges.
 | 
			
		||||
    @Input() showDownloadStatus = true; // Whether to show download status.
 | 
			
		||||
    @Input() showIndentation = true; // Whether to show indentation
 | 
			
		||||
    @Input() isLastViewed = false; // Whether it's the last module viewed in a course.
 | 
			
		||||
    @Input({ transform: toBoolean }) showActivityDates = false; // Whether to show activity dates.
 | 
			
		||||
    @Input({ transform: toBoolean }) showCompletionConditions = false; // Whether to show activity completion conditions.
 | 
			
		||||
    @Input({ transform: toBoolean }) showLegacyCompletion?: boolean; // Whether to show module completion in the old format.
 | 
			
		||||
    @Input({ transform: toBoolean }) showCompletion = true; // Whether to show module completion.
 | 
			
		||||
    @Input({ transform: toBoolean }) showAvailability = true; // Whether to show module availability.
 | 
			
		||||
    @Input({ transform: toBoolean }) showExtra = true; // Whether to show extra badges.
 | 
			
		||||
    @Input({ transform: toBoolean }) showDownloadStatus = true; // Whether to show download status.
 | 
			
		||||
    @Input({ transform: toBoolean }) showIndentation = true; // Whether to show indentation
 | 
			
		||||
    @Input({ transform: toBoolean }) isLastViewed = false; // Whether it's the last module viewed in a course.
 | 
			
		||||
    @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes.
 | 
			
		||||
    @HostBinding('class.indented') indented = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from
 | 
			
		||||
import { CoreCoursesCourseOptionsMenuComponent } from '../course-options-menu/course-options-menu';
 | 
			
		||||
import { CoreEnrolHelper } from '@features/enrol/services/enrol-helper';
 | 
			
		||||
import { CoreDownloadStatusTranslatable } from '@components/download-refresh/download-refresh';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This directive is meant to display an item for a list of courses.
 | 
			
		||||
@ -43,8 +44,8 @@ import { CoreDownloadStatusTranslatable } from '@components/download-refresh/dow
 | 
			
		||||
})
 | 
			
		||||
export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() course!: CoreCourseListItem; // The course to render.
 | 
			
		||||
    @Input() showDownload = false; // If true, will show download button.
 | 
			
		||||
    @Input({ required: true }) course!: CoreCourseListItem; // The course to render.
 | 
			
		||||
    @Input({ transform: toBoolean }) showDownload = false; // If true, will show download button.
 | 
			
		||||
    @Input() layout: 'listwithenrol'|'summarycard'|'list'|'card' = 'listwithenrol';
 | 
			
		||||
 | 
			
		||||
    enrolmentIcons: CoreCoursesEnrolmentIcons[] = [];
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,8 @@ import { PopoverController } from '@singletons';
 | 
			
		||||
})
 | 
			
		||||
export class CoreCoursesCourseOptionsMenuComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() course!: CoreEnrolledCourseDataWithExtraInfoAndOptions; // The course.
 | 
			
		||||
    @Input() prefetch!: CorePrefetchStatusInfo; // The prefecth info.
 | 
			
		||||
    @Input({ required: true }) course!: CoreEnrolledCourseDataWithExtraInfoAndOptions; // The course.
 | 
			
		||||
    @Input({ required: true }) prefetch!: CorePrefetchStatusInfo; // The prefecth info.
 | 
			
		||||
 | 
			
		||||
    downloadCourseEnabled = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,7 @@ import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { CoreSwiper } from '@singletons/swiper';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreWait } from '@singletons/wait';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a rich text editor if enabled.
 | 
			
		||||
@ -73,7 +74,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
			
		||||
    @Input() name = 'core-rich-text-editor'; // Name to set to the textarea.
 | 
			
		||||
    @Input() component?: string; // The component to link the files to.
 | 
			
		||||
    @Input() componentId?: number; // An ID to use in conjunction with the component.
 | 
			
		||||
    @Input() autoSave?: boolean | string; // Whether to auto-save the contents in a draft. Defaults to true.
 | 
			
		||||
    @Input({ transform: toBoolean }) autoSave = true; // Whether to auto-save the contents in a draft.
 | 
			
		||||
    @Input() contextLevel?: ContextLevel; // The context level of the text.
 | 
			
		||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
			
		||||
    @Input() elementId?: string; // An ID to set to the element.
 | 
			
		||||
@ -887,7 +888,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
			
		||||
     */
 | 
			
		||||
    protected shouldAutoSaveDrafts(): boolean {
 | 
			
		||||
        return !!CoreSites.getCurrentSite() &&
 | 
			
		||||
                (this.autoSave === undefined || CoreUtils.isTrueOrOne(this.autoSave)) &&
 | 
			
		||||
                this.autoSave &&
 | 
			
		||||
                this.contextLevel !== undefined &&
 | 
			
		||||
                this.contextInstanceId !== undefined &&
 | 
			
		||||
                this.elementId !== undefined;
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreCaptureError } from '@classes/errors/captureerror';
 | 
			
		||||
import { CoreCanceledError } from '@classes/errors/cancelederror';
 | 
			
		||||
import { CorePath } from '@singletons/path';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page to capture media in browser.
 | 
			
		||||
@ -41,7 +42,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
 | 
			
		||||
    @Input() mimetype?: string;
 | 
			
		||||
    @Input() extension?: string;
 | 
			
		||||
    @Input() quality?: number; // Only for images.
 | 
			
		||||
    @Input() returnDataUrl?: boolean; // Whether it should return a data img. Only for images.
 | 
			
		||||
    @Input({ transform: toBoolean }) returnDataUrl = false; // Whether it should return a data img. Only for images.
 | 
			
		||||
 | 
			
		||||
    @ViewChild('streamVideo') streamVideo?: ElementRef;
 | 
			
		||||
    @ViewChild('previewVideo') previewVideo?: ElementRef;
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -31,8 +32,8 @@ export class CoreFileUploaderAudioHistogramComponent implements AfterViewInit, O
 | 
			
		||||
    private static readonly BARS_MIN_HEIGHT = 4;
 | 
			
		||||
    private static readonly BARS_GUTTER = 4;
 | 
			
		||||
 | 
			
		||||
    @Input() analyser!: AnalyserNode;
 | 
			
		||||
    @Input() paused?: boolean;
 | 
			
		||||
    @Input({ required: true }) analyser!: AnalyserNode;
 | 
			
		||||
    @Input({ transform: toBoolean }) paused = false;
 | 
			
		||||
    @ViewChild('canvas') canvasRef?: ElementRef<HTMLCanvasElement>;
 | 
			
		||||
 | 
			
		||||
    private element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreH5PCore, CoreH5PDisplayOptions } from '../../classes/core';
 | 
			
		||||
import { CoreH5PHelper } from '../../classes/helper';
 | 
			
		||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to render an iframe with an H5P package.
 | 
			
		||||
@ -45,7 +46,7 @@ export class CoreH5PIframeComponent implements OnChanges, OnDestroy {
 | 
			
		||||
    @Input() onlinePlayerUrl?: string; // The URL of the online player to display the H5P package.
 | 
			
		||||
    @Input() trackComponent?: string; // Component to send xAPI events to.
 | 
			
		||||
    @Input() contextId?: number; // Context ID. Required for tracking.
 | 
			
		||||
    @Input() enableInAppFullscreen?: boolean; // Whether to enable our custom in-app fullscreen feature.
 | 
			
		||||
    @Input({ transform: toBoolean }) enableInAppFullscreen = false; // Whether to enable our custom in-app fullscreen feature.
 | 
			
		||||
    @Input() saveFreq?: number; // Save frequency (in seconds) if enabled.
 | 
			
		||||
    @Input() state?: string; // Initial content state.
 | 
			
		||||
    @Output() onIframeUrlSet = new EventEmitter<{src: string; online: boolean}>();
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import { CoreUserSupport } from '@features/user/services/support';
 | 
			
		||||
})
 | 
			
		||||
export class CoreLoginExceededAttemptsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() supportConfig!: CoreUserSupportConfig;
 | 
			
		||||
    @Input({ required: true }) supportConfig!: CoreUserSupportConfig;
 | 
			
		||||
    @Input() supportSubject?: string;
 | 
			
		||||
 | 
			
		||||
    canContactSupport = false;
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { toBoolean } from '@/core/transforms/boolean';
 | 
			
		||||
import { Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site';
 | 
			
		||||
import { CoreLoginHelper, CoreLoginMethod } from '@features/login/services/login-helper';
 | 
			
		||||
@ -27,7 +28,7 @@ import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
})
 | 
			
		||||
export class CoreLoginMethodsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() reconnect = false;
 | 
			
		||||
    @Input({ transform: toBoolean }) reconnect = false;
 | 
			
		||||
    @Input() siteUrl = '';
 | 
			
		||||
    @Input() siteConfig?: CoreSitePublicConfigResponse;
 | 
			
		||||
    @Input() redirectData?: CoreRedirectPayload;
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user