forked from CIT/Vmeda.Online
		
	MOBILE-3954 course: Remove prefetch module from course and blocks
This commit is contained in:
		
							parent
							
								
									255e987412
								
							
						
					
					
						commit
						018ebd6bb2
					
				@ -12,7 +12,6 @@
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [courseId]="siteHomeId"
 | 
			
		||||
            [downloadEnabled]="downloadEnabled" [section]="mainMenuBlock"></core-course-module>
 | 
			
		||||
        <core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [section]="mainMenuBlock"></core-course-module>
 | 
			
		||||
    </ng-container>
 | 
			
		||||
</core-loading>
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,6 @@ import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
 | 
			
		||||
export class CoreBlockSideBlocksButtonComponent {
 | 
			
		||||
 | 
			
		||||
    @Input() courseId!: number;
 | 
			
		||||
    @Input() downloadEnabled = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open side blocks.
 | 
			
		||||
@ -37,7 +36,6 @@ export class CoreBlockSideBlocksButtonComponent {
 | 
			
		||||
            component: CoreBlockSideBlocksComponent,
 | 
			
		||||
            componentProps: {
 | 
			
		||||
                courseId: this.courseId,
 | 
			
		||||
                downloadEnabled: this.downloadEnabled,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <ion-list *ngIf="blocks.length > 0">
 | 
			
		||||
            <ng-container *ngFor="let block of blocks">
 | 
			
		||||
                <core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId"
 | 
			
		||||
                    [extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
 | 
			
		||||
                <core-block *ngIf="block.visible" [block]="block" contextLevel="course" [instanceId]="courseId"></core-block>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,6 @@ import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
 | 
			
		||||
export class CoreBlockSideBlocksComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() courseId?: number;
 | 
			
		||||
    @Input() downloadEnabled = false;
 | 
			
		||||
 | 
			
		||||
    @ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,10 +11,8 @@
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <!-- Section selector. -->
 | 
			
		||||
        <core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
 | 
			
		||||
 | 
			
		||||
            <div *ngIf="displaySectionSelector && sections && hasSeveralSections"
 | 
			
		||||
                class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row"
 | 
			
		||||
                [class.core-section-download]="downloadEnabled">
 | 
			
		||||
                class="ion-text-wrap ion-justify-content-between ion-align-items-center core-button-selector-row">
 | 
			
		||||
                <core-combobox [modalOptions]="sectionSelectorModalOptions" interface="modal" listboxId="core-course-section-button"
 | 
			
		||||
                    icon="fas-folder" [label]="'core.course.section' | translate"
 | 
			
		||||
                    [selection]="selectedSection ? selectedSection.name : 'core.course.sections' | translate"
 | 
			
		||||
@ -26,16 +24,14 @@
 | 
			
		||||
                        <ng-container *ngIf="!selectedSection">{{ 'core.course.sections' | translate }}</ng-container>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </core-combobox>
 | 
			
		||||
                <!-- Section download. -->
 | 
			
		||||
                <ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: selectedSection}"></ng-container>
 | 
			
		||||
            </div>
 | 
			
		||||
        </core-dynamic-component>
 | 
			
		||||
 | 
			
		||||
        <!-- Course summary. By default we only display the course progress. -->
 | 
			
		||||
        <core-dynamic-component [component]="courseSummaryComponent" [data]="data">
 | 
			
		||||
            <ion-list lines="none" class="core-format-progress-list" *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) ||
 | 
			
		||||
            <ion-list *ngIf="imageThumb || (selectedSection?.id == allSectionsId && progress !== undefined) ||
 | 
			
		||||
                    (selectedSection && selectedSection.id != allSectionsId &&
 | 
			
		||||
                    (selectedSection.availabilityinfo || selectedSection.visible === 0))">
 | 
			
		||||
                    (selectedSection.availabilityinfo || selectedSection.visible === 0))" lines="none" class="core-format-progress-list">
 | 
			
		||||
                <div *ngIf="imageThumb" class="core-course-thumb">
 | 
			
		||||
                    <img [src]="imageThumb" core-external-content alt="" />
 | 
			
		||||
                </div>
 | 
			
		||||
@ -103,8 +99,7 @@
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id"
 | 
			
		||||
            [downloadEnabled]="downloadEnabled">
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="course && displayBlocks && hasBlocks" [courseId]="course.id">
 | 
			
		||||
        </core-block-side-blocks-button>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</core-dynamic-component>
 | 
			
		||||
@ -114,7 +109,7 @@
 | 
			
		||||
    <section *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId">
 | 
			
		||||
        <!-- Title is only displayed when viewing all sections. -->
 | 
			
		||||
        <ion-item-divider *ngIf="selectedSection?.id == allSectionsId && section.name" class="ion-text-wrap" color="light"
 | 
			
		||||
            [class.core-section-download]="downloadEnabled" [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
 | 
			
		||||
            [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <h2>
 | 
			
		||||
                    <core-format-text [text]="section.name" contextLevel="course" [contextInstanceId]="course?.id">
 | 
			
		||||
@ -133,8 +128,6 @@
 | 
			
		||||
                    </ion-badge>
 | 
			
		||||
                </p>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
            <!-- Section download. -->
 | 
			
		||||
            <ng-container *ngTemplateOutlet="sectionDownloadTemplate; context: {section: section}"></ng-container>
 | 
			
		||||
        </ion-item-divider>
 | 
			
		||||
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="section.summary">
 | 
			
		||||
@ -145,28 +138,10 @@
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngFor="let module of section.modules">
 | 
			
		||||
            <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [courseId]="course?.id"
 | 
			
		||||
                [downloadEnabled]="downloadEnabled" [section]="section" (completionChanged)="onCompletionChange($event)"
 | 
			
		||||
                (statusChanged)="onModuleStatusChange()" [showActivityDates]="course?.showactivitydates"
 | 
			
		||||
            <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section"
 | 
			
		||||
                (completionChanged)="onCompletionChange($event)" [showActivityDates]="course?.showactivitydates"
 | 
			
		||||
                [showCompletionConditions]="course?.showcompletionconditions">
 | 
			
		||||
            </core-course-module>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </section>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<!-- Template to render a section download button/progress. -->
 | 
			
		||||
<ng-template #sectionDownloadTemplate let-section="section">
 | 
			
		||||
    <div *ngIf="section && downloadEnabled" slot="end" class="core-button-spinner">
 | 
			
		||||
        <!-- Download progress. -->
 | 
			
		||||
        <ion-badge class="core-course-download-section-progress"
 | 
			
		||||
            *ngIf="section.isDownloading && section.total > 0 && section.count < section.total" role="progressbar"
 | 
			
		||||
            [attr.aria-valuemax]="section.total" [attr.aria-valuenow]="section.count"
 | 
			
		||||
            [attr.aria-valuetext]="'core.course.downloadsectionprogressdescription' | translate:section">
 | 
			
		||||
            {{section.count}} / {{section.total}}
 | 
			
		||||
        </ion-badge>
 | 
			
		||||
 | 
			
		||||
        <core-download-refresh [status]="section.downloadStatus" [enabled]="downloadEnabled" (action)="prefetch(section)"
 | 
			
		||||
            [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="true" size="small">
 | 
			
		||||
        </core-download-refresh>
 | 
			
		||||
    </div>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
@ -55,21 +55,6 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-section-download {
 | 
			
		||||
        core-combobox {
 | 
			
		||||
            max-width: calc(100% - 64px);
 | 
			
		||||
        }
 | 
			
		||||
        .core-button-spinner {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            align-items: center;
 | 
			
		||||
            @include margin-horizontal(10px);
 | 
			
		||||
 | 
			
		||||
            ion-badge.core-course-download-courses-progress {
 | 
			
		||||
                @include margin(null, 12px, null, null);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-course-section-nav-buttons {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: flex-end;
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,6 @@ import {
 | 
			
		||||
    ElementRef,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { ModalOptions } from '@ionic/core';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
 | 
			
		||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
 | 
			
		||||
@ -37,17 +36,14 @@ import {
 | 
			
		||||
    CoreCourseProvider,
 | 
			
		||||
} from '@features/course/services/course';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourseHelper,
 | 
			
		||||
    CoreCourseModuleData,
 | 
			
		||||
    CoreCourseModuleCompletionData,
 | 
			
		||||
    CoreCourseSection,
 | 
			
		||||
    CoreCourseSectionWithStatus,
 | 
			
		||||
} from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { IonContent, IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { CoreCourseSectionSelectorComponent } from '../section-selector/section-selector';
 | 
			
		||||
import { CoreBlockHelper } from '@features/block/services/block-helper';
 | 
			
		||||
 | 
			
		||||
@ -71,8 +67,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
    static readonly LOAD_MORE_ACTIVITIES = 20; // How many activities should load each time showMoreActivities is called.
 | 
			
		||||
 | 
			
		||||
    @Input() course?: CoreCourseAnyCourseData; // The course to render.
 | 
			
		||||
    @Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections. The status will be calculated in this component.
 | 
			
		||||
    @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 | 
			
		||||
    @Input() sections?: CoreCourseSection[]; // 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() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
 | 
			
		||||
@ -108,7 +103,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
        componentProps: {},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    protected sectionStatusObserver?: CoreEventObserver;
 | 
			
		||||
    protected selectTabObserver?: CoreEventObserver;
 | 
			
		||||
    protected lastCourseFormat?: string;
 | 
			
		||||
    protected sectionSelectorExpanded = false;
 | 
			
		||||
@ -125,38 +119,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        // Listen for section status changes.
 | 
			
		||||
        this.sectionStatusObserver = CoreEvents.on(
 | 
			
		||||
            CoreEvents.SECTION_STATUS_CHANGED,
 | 
			
		||||
            async (data) => {
 | 
			
		||||
                if (!this.downloadEnabled || !this.sections?.length || !data.sectionId || data.courseId != this.course?.id) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Check if the affected section is being downloaded.
 | 
			
		||||
                // If so, we don't update section status because it'll already be updated when the download finishes.
 | 
			
		||||
                const downloadId = CoreCourseHelper.getSectionDownloadId({ id: data.sectionId });
 | 
			
		||||
                if (CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Get the affected section.
 | 
			
		||||
                const section = this.sections.find(section => section.id == data.sectionId);
 | 
			
		||||
                if (!section) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Recalculate the status.
 | 
			
		||||
                await CoreCourseHelper.calculateSectionStatus(section, this.course.id, false);
 | 
			
		||||
 | 
			
		||||
                if (section.isDownloading && !CoreCourseModulePrefetchDelegate.isBeingDownloaded(downloadId)) {
 | 
			
		||||
                    // All the modules are now downloading, set a download all promise.
 | 
			
		||||
                    this.prefetch(section);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            CoreSites.getCurrentSiteId(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Listen for select course tab events to select the right section if needed.
 | 
			
		||||
        this.selectTabObserver = CoreEvents.on(CoreEvents.SELECT_COURSE_TAB, (data) => {
 | 
			
		||||
            if (data.name) {
 | 
			
		||||
@ -205,10 +167,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
            this.sectionSelectorModalOptions.componentProps!.sections = this.sections;
 | 
			
		||||
            this.treatSections(this.sections);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.downloadEnabled && (changes.downloadEnabled || changes.sections)) {
 | 
			
		||||
            this.calculateSectionsStatus(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -219,7 +177,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
        this.data.sections = this.sections;
 | 
			
		||||
        this.data.initialSectionId = this.initialSectionId;
 | 
			
		||||
        this.data.initialSectionNumber = this.initialSectionNumber;
 | 
			
		||||
        this.data.downloadEnabled = this.downloadEnabled;
 | 
			
		||||
        this.data.moduleId = this.moduleId;
 | 
			
		||||
        this.data.completionChanged = this.completionChanged;
 | 
			
		||||
    }
 | 
			
		||||
@ -394,9 +351,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
            this.canLoadMore = false;
 | 
			
		||||
            this.showSectionId = 0;
 | 
			
		||||
            this.showMoreActivities();
 | 
			
		||||
            if (this.downloadEnabled) {
 | 
			
		||||
                this.calculateSectionsStatus(false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.moduleId && previousValue === undefined) {
 | 
			
		||||
@ -432,62 +386,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
        return section1 && section2 ? section1.id === section2.id : section1 === section2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the status of sections.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh If refresh or not.
 | 
			
		||||
     */
 | 
			
		||||
    protected calculateSectionsStatus(refresh?: boolean): void {
 | 
			
		||||
        if (!this.sections || !this.course) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreUtils.ignoreErrors(CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Confirm and prefetch a section. If the section is "all sections", prefetch all the sections.
 | 
			
		||||
     *
 | 
			
		||||
     * @param section Section to download.
 | 
			
		||||
     * @param refresh Refresh clicked (not used).
 | 
			
		||||
     */
 | 
			
		||||
    async prefetch(section: CoreCourseSectionWithStatus): Promise<void> {
 | 
			
		||||
        section.isCalculating = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreCourseHelper.confirmDownloadSizeSection(this.course!.id, section, this.sections);
 | 
			
		||||
 | 
			
		||||
            await this.prefetchSection(section, true);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // User cancelled or there was an error calculating the size.
 | 
			
		||||
            if (error) {
 | 
			
		||||
                CoreDomUtils.showErrorModal(error);
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            section.isCalculating = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a section.
 | 
			
		||||
     *
 | 
			
		||||
     * @param section The section to download.
 | 
			
		||||
     * @param manual Whether the prefetch was started manually or it was automatically started because all modules
 | 
			
		||||
     *               are being downloaded.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchSection(section: CoreCourseSectionWithStatus, manual?: boolean): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreCourseHelper.prefetchSection(section, this.course!.id, this.sections);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Don't show error message if it's an automatic download.
 | 
			
		||||
            if (!manual) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingsection', true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the data.
 | 
			
		||||
     *
 | 
			
		||||
@ -580,7 +478,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
     * Component destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.sectionStatusObserver && this.sectionStatusObserver.off();
 | 
			
		||||
        this.selectTabObserver && this.selectTabObserver.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -591,17 +488,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
        this.dynamicComponents?.forEach((component) => {
 | 
			
		||||
            component.callComponentFunction('ionViewDidEnter');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!this.downloadEnabled || !this.course || !this.sections) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The download status of a section might have been changed from within a module page.
 | 
			
		||||
        if (this.selectedSection && this.selectedSection.id !== CoreCourseProvider.ALL_SECTIONS_ID) {
 | 
			
		||||
            CoreCourseHelper.calculateSectionStatus(this.selectedSection, this.course.id, false, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, false, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -653,17 +539,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
 | 
			
		||||
        this.updateProgress();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Recalculate the download status of each section, in response to a module being downloaded.
 | 
			
		||||
     */
 | 
			
		||||
    onModuleStatusChange(): void {
 | 
			
		||||
        if (!this.downloadEnabled || !this.sections || !this.course) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreCourseHelper.calculateSectionsStatus(this.sections, this.course.id, false, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update course progress.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
            <ion-label class="core-module-title">
 | 
			
		||||
                <p class="item-heading">
 | 
			
		||||
                    <core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                        [courseId]="courseId" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
 | 
			
		||||
                        [courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </p>
 | 
			
		||||
                <ion-badge *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor"
 | 
			
		||||
@ -31,7 +31,7 @@
 | 
			
		||||
                <div class="core-module-availabilityinfo" *ngIf="module.availabilityinfo">
 | 
			
		||||
                    <ion-badge class="ion-text-wrap">{{ 'core.restricted' | translate }}</ion-badge>
 | 
			
		||||
                    <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id"
 | 
			
		||||
                        [courseId]="courseId" class="ion-text-wrap">
 | 
			
		||||
                        [courseId]="module.course" class="ion-text-wrap">
 | 
			
		||||
                    </core-format-text>
 | 
			
		||||
                </div>
 | 
			
		||||
                <ion-badge *ngIf="module.completiondata?.offline" color="warning" class="ion-text-wrap">
 | 
			
		||||
@ -48,13 +48,9 @@
 | 
			
		||||
                </core-course-module-completion-legacy>
 | 
			
		||||
 | 
			
		||||
                <div class="core-module-buttons-more">
 | 
			
		||||
                    <core-download-refresh [status]="downloadStatus" [enabled]="downloadEnabled" [canTrustDownload]="true"
 | 
			
		||||
                        [loading]="spinner || module.handlerData.spinner" (action)="download($event)" size="small">
 | 
			
		||||
                    </core-download-refresh>
 | 
			
		||||
 | 
			
		||||
                    <!-- Buttons defined by the module handler. -->
 | 
			
		||||
                    <ion-button fill="clear" *ngFor="let button of module.handlerData.buttons" color="dark" size="small"
 | 
			
		||||
                        [hidden]="button.hidden || spinner || module.handlerData.spinner" class="core-animate-show-hide"
 | 
			
		||||
                        [hidden]="button.hidden || module.handlerData.spinner" class="core-animate-show-hide"
 | 
			
		||||
                        (click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
 | 
			
		||||
                        <ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
@ -81,7 +77,7 @@
 | 
			
		||||
                </core-course-module-completion>
 | 
			
		||||
 | 
			
		||||
                <core-format-text class="core-module-description" *ngIf="module.description" [maxHeight]="80" [text]="module.description"
 | 
			
		||||
                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
			
		||||
                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
@ -15,20 +15,13 @@
 | 
			
		||||
import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourseHelper,
 | 
			
		||||
    CoreCourseModuleData,
 | 
			
		||||
    CoreCourseModuleCompletionData,
 | 
			
		||||
    CoreCourseSection,
 | 
			
		||||
} from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourseModulePrefetchDelegate,
 | 
			
		||||
    CoreCourseModulePrefetchHandler,
 | 
			
		||||
} from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a module entry in a list of modules.
 | 
			
		||||
@ -45,47 +38,20 @@ import {
 | 
			
		||||
export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() module!: CoreCourseModuleData; // The module to render.
 | 
			
		||||
    @Input() courseId?: number; // The course the module belongs to.
 | 
			
		||||
    @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.
 | 
			
		||||
    // eslint-disable-next-line @angular-eslint/no-input-rename
 | 
			
		||||
    @Input('downloadEnabled') set enabled(value: boolean) {
 | 
			
		||||
        this.downloadEnabled = value;
 | 
			
		||||
 | 
			
		||||
        if (!this.module.handlerData?.showDownloadButton || !this.downloadEnabled || this.statusCalculated) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // First time that the download is enabled. Initialize the data.
 | 
			
		||||
        this.statusCalculated = true;
 | 
			
		||||
        this.spinner = true; // Show spinner while calculating the status.
 | 
			
		||||
 | 
			
		||||
        // Get current status to decide which icon should be shown.
 | 
			
		||||
        this.calculateAndShowStatus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when module completion changes.
 | 
			
		||||
    @Output() statusChanged = new EventEmitter<CoreCourseModuleStatusChangedData>(); // Notify when the download status changes.
 | 
			
		||||
 | 
			
		||||
    downloadStatus?: string;
 | 
			
		||||
    spinner?: boolean; // Whether to display a loading spinner.
 | 
			
		||||
    downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 | 
			
		||||
    modNameTranslated = '';
 | 
			
		||||
    hasInfo = false;
 | 
			
		||||
    showLegacyCompletion = false; // Whether to show module completion in the old format.
 | 
			
		||||
    showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
 | 
			
		||||
 | 
			
		||||
    protected prefetchHandler?: CoreCourseModulePrefetchHandler;
 | 
			
		||||
    protected statusObserver?: CoreEventObserver;
 | 
			
		||||
    protected statusCalculated = false;
 | 
			
		||||
    protected isDestroyed = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.courseId = this.courseId || this.module.course;
 | 
			
		||||
        this.modNameTranslated = CoreCourse.translateModuleName(this.module.modname) || '';
 | 
			
		||||
        this.showLegacyCompletion = !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.11');
 | 
			
		||||
        this.checkShowManualCompletion();
 | 
			
		||||
@ -103,29 +69,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
			
		||||
                    (this.showCompletionConditions && this.module.completiondata.isautomatic))
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (this.module.handlerData.showDownloadButton) {
 | 
			
		||||
            // Listen for changes on this module status, even if download isn't enabled.
 | 
			
		||||
            this.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module.modname);
 | 
			
		||||
 | 
			
		||||
            this.statusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
 | 
			
		||||
                if (!this.module || data.componentId != this.module.id || !this.prefetchHandler ||
 | 
			
		||||
                        data.component != this.prefetchHandler.component) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Call determineModuleStatus to get the right status to display.
 | 
			
		||||
                const status = CoreCourseModulePrefetchDelegate.determineModuleStatus(this.module, data.status);
 | 
			
		||||
 | 
			
		||||
                if (this.downloadEnabled) {
 | 
			
		||||
                    // Download is enabled, show the status.
 | 
			
		||||
                    this.showStatus(status);
 | 
			
		||||
                } else if (this.module.handlerData?.updateStatus) {
 | 
			
		||||
                    // Download isn't enabled but the handler defines a updateStatus function, call it anyway.
 | 
			
		||||
                    this.module.handlerData.updateStatus(status);
 | 
			
		||||
                }
 | 
			
		||||
            }, CoreSites.getCurrentSiteId());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -143,7 +86,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    moduleClicked(event: Event): void {
 | 
			
		||||
        if (this.module.uservisible !== false && this.module.handlerData?.action) {
 | 
			
		||||
            this.module.handlerData.action(event, this.module, this.courseId!);
 | 
			
		||||
            this.module.handlerData.action(event, this.module, this.module.course);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -161,91 +104,14 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
        button.action(event, this.module, this.courseId!);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Download the module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh Whether it's refreshing.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async download(refresh: boolean): Promise<void> {
 | 
			
		||||
        if (!this.prefetchHandler || !this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Show spinner since this operation might take a while.
 | 
			
		||||
        this.spinner = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Get download size to ask for confirm if it's high.
 | 
			
		||||
            const size = await this.prefetchHandler.getDownloadSize(this.module, this.module.course, true);
 | 
			
		||||
 | 
			
		||||
            await CoreCourseHelper.prefetchModule(this.prefetchHandler, this.module, size, this.module.course, refresh);
 | 
			
		||||
 | 
			
		||||
            const eventData = {
 | 
			
		||||
                sectionId: this.section?.id,
 | 
			
		||||
                moduleId: this.module.id,
 | 
			
		||||
                courseId: this.module.course,
 | 
			
		||||
            };
 | 
			
		||||
            this.statusChanged.emit(eventData);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Error, hide spinner.
 | 
			
		||||
            this.spinner = false;
 | 
			
		||||
            if (!this.isDestroyed) {
 | 
			
		||||
                CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show download buttons according to module status.
 | 
			
		||||
     *
 | 
			
		||||
     * @param status Module status.
 | 
			
		||||
     */
 | 
			
		||||
    protected showStatus(status: string): void {
 | 
			
		||||
        if (!status) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.spinner = false;
 | 
			
		||||
        this.downloadStatus = status;
 | 
			
		||||
 | 
			
		||||
        this.module.handlerData?.updateStatus?.(status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate and show module status.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async calculateAndShowStatus(): Promise<void> {
 | 
			
		||||
        if (!this.module || !this.courseId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.courseId);
 | 
			
		||||
 | 
			
		||||
        this.showStatus(status);
 | 
			
		||||
        button.action(event, this.module, this.module.course);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        // this.statusObserver?.off();
 | 
			
		||||
        this.module.handlerData?.onDestroy?.();
 | 
			
		||||
        this.isDestroyed = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data sent to the status changed output.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreCourseModuleStatusChangedData = {
 | 
			
		||||
    moduleId: number;
 | 
			
		||||
    courseId: number;
 | 
			
		||||
    sectionId?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
<core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component>
 | 
			
		||||
 | 
			
		||||
<core-block-side-blocks-button *ngIf="course && hasBlocks" [courseId]="course.id" [downloadEnabled]="downloadEnabled">
 | 
			
		||||
<core-block-side-blocks-button *ngIf="course && hasBlocks" [courseId]="course.id">
 | 
			
		||||
</core-block-side-blocks-button>
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,6 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
    @Input() course?: CoreCourseAnyCourseData; // The course to render.
 | 
			
		||||
    @Input() sections?: CoreCourseSectionWithStatus[]; // List of course sections.
 | 
			
		||||
    @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
 | 
			
		||||
    @Input() initialSectionId?: number; // The section to load first (by ID).
 | 
			
		||||
    @Input() initialSectionNumber?: number; // The section to load first (by number).
 | 
			
		||||
    @Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
 | 
			
		||||
 | 
			
		||||
@ -68,13 +68,6 @@ export class CoreCourseFormatSingleActivityHandlerService implements CoreCourseF
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    displayEnableDownload(): boolean {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,5 @@
 | 
			
		||||
<core-navbar-buttons slot="end">
 | 
			
		||||
    <core-context-menu>
 | 
			
		||||
        <core-context-menu-item [hidden]="!displayEnableDownload" [priority]="2000" iconAction="toggle" [(toggle)]="downloadEnabled"
 | 
			
		||||
            [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item [hidden]="!downloadCourseEnabled" [priority]="1900"
 | 
			
		||||
            [content]="prefetchCourseData.statusTranslatable | translate" (action)="prefetchCourse()" [iconAction]="prefetchCourseData.icon"
 | 
			
		||||
            [closeOnClick]="false">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item [priority]="1800" [content]="'core.course.coursesummary' | translate" (action)="openCourseSummary()"
 | 
			
		||||
            iconAction="fas-graduation-cap">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
@ -22,8 +15,7 @@
 | 
			
		||||
 | 
			
		||||
    <core-loading [hideUntil]="dataLoaded">
 | 
			
		||||
        <core-course-format [course]="course" [sections]="sections" [initialSectionId]="sectionId" [initialSectionNumber]="sectionNumber"
 | 
			
		||||
            [downloadEnabled]="downloadEnabled" [moduleId]="moduleId" (completionChanged)="onCompletionChange($event)"
 | 
			
		||||
            class="core-course-format-{{course.format}}">
 | 
			
		||||
            [moduleId]="moduleId" (completionChanged)="onCompletionChange($event)" class="core-course-format-{{course.format}}">
 | 
			
		||||
        </core-course-format>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
 | 
			
		||||
@ -15,20 +15,17 @@
 | 
			
		||||
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
import { IonContent, IonRefresher } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreCourses, CoreCourseAnyCourseData, CoreCoursesProvider } from '@features/courses/services/courses';
 | 
			
		||||
import { CoreCourses, CoreCourseAnyCourseData } from '@features/courses/services/courses';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourse,
 | 
			
		||||
    CoreCourseCompletionActivityStatus,
 | 
			
		||||
    CoreCourseProvider,
 | 
			
		||||
} from '@features/course/services/course';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourseHelper,
 | 
			
		||||
    CoreCourseModuleCompletionData,
 | 
			
		||||
    CoreCourseSection,
 | 
			
		||||
    CorePrefetchStatusInfo,
 | 
			
		||||
} from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
@ -43,7 +40,6 @@ import {
 | 
			
		||||
    CoreEventObserver,
 | 
			
		||||
} from '@singletons/events';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the contents of a course.
 | 
			
		||||
@ -63,47 +59,19 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
    sectionNumber?: number;
 | 
			
		||||
    courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
 | 
			
		||||
    dataLoaded = false;
 | 
			
		||||
    downloadEnabled = false;
 | 
			
		||||
    downloadCourseEnabled = false;
 | 
			
		||||
    moduleId?: number;
 | 
			
		||||
    displayEnableDownload = false;
 | 
			
		||||
    displayRefresher = false;
 | 
			
		||||
    prefetchCourseData: CorePrefetchStatusInfo = {
 | 
			
		||||
        icon: CoreConstants.ICON_LOADING,
 | 
			
		||||
        statusTranslatable: 'core.course.downloadcourse',
 | 
			
		||||
        status: '',
 | 
			
		||||
        loading: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    protected formatOptions?: Record<string, unknown>;
 | 
			
		||||
    protected completionObserver?: CoreEventObserver;
 | 
			
		||||
    protected courseStatusObserver?: CoreEventObserver;
 | 
			
		||||
    protected siteUpdatedObserver?: CoreEventObserver;
 | 
			
		||||
    protected downloadEnabledObserver?: CoreEventObserver;
 | 
			
		||||
    protected syncObserver?: CoreEventObserver;
 | 
			
		||||
    protected isDestroyed = false;
 | 
			
		||||
    protected modulesHaveCompletion = false;
 | 
			
		||||
    protected isGuest?: boolean;
 | 
			
		||||
    protected isGuest = false;
 | 
			
		||||
    protected debouncedUpdateCachedCompletion?: () => void; // Update the cached completion after a certain time.
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        // Refresh the enabled flags if site is updated.
 | 
			
		||||
        this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
 | 
			
		||||
            this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
 | 
			
		||||
 | 
			
		||||
            this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled() &&
 | 
			
		||||
                CoreCourseFormatDelegate.displayEnableDownload(this.course);
 | 
			
		||||
 | 
			
		||||
            this.downloadEnabled = this.displayEnableDownload && this.downloadEnabled;
 | 
			
		||||
 | 
			
		||||
            this.initListeners();
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => {
 | 
			
		||||
            this.downloadEnabled = this.displayEnableDownload && data.enabled;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
@ -121,13 +89,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
        this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
 | 
			
		||||
        this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
 | 
			
		||||
        this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
 | 
			
		||||
        this.isGuest = CoreNavigator.getRouteBooleanParam('isGuest');
 | 
			
		||||
 | 
			
		||||
        this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled() &&
 | 
			
		||||
            CoreCourseFormatDelegate.displayEnableDownload(this.course);
 | 
			
		||||
        this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
 | 
			
		||||
 | 
			
		||||
        this.downloadEnabled = this.displayEnableDownload && CoreCourses.getCourseDownloadOptionsEnabled();
 | 
			
		||||
        this.isGuest = !!CoreNavigator.getRouteBooleanParam('isGuest');
 | 
			
		||||
 | 
			
		||||
        this.debouncedUpdateCachedCompletion = CoreUtils.debounce(() => {
 | 
			
		||||
            if (this.modulesHaveCompletion) {
 | 
			
		||||
@ -149,8 +111,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
        await this.loadData(false, true);
 | 
			
		||||
 | 
			
		||||
        this.dataLoaded = true;
 | 
			
		||||
 | 
			
		||||
        this.initPrefetch();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -159,15 +119,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async initListeners(): Promise<void> {
 | 
			
		||||
        if (this.downloadCourseEnabled && !this.courseStatusObserver) {
 | 
			
		||||
            // Listen for changes in course status.
 | 
			
		||||
            this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data) => {
 | 
			
		||||
                if (data.courseId == this.course.id || data.courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
 | 
			
		||||
                    this.updateCourseStatus(data.status);
 | 
			
		||||
                }
 | 
			
		||||
            }, CoreSites.getCurrentSiteId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if the course format requires the view to be refreshed when completion changes.
 | 
			
		||||
        const shouldRefresh = await CoreCourseFormatDelegate.shouldRefreshWhenCompletionChanges(this.course);
 | 
			
		||||
        if (!shouldRefresh) {
 | 
			
		||||
@ -200,41 +151,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init prefetch data if needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async initPrefetch(): Promise<void> {
 | 
			
		||||
        if (!this.downloadCourseEnabled) {
 | 
			
		||||
            // Cannot download the whole course, stop.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Determine the course prefetch status.
 | 
			
		||||
        await this.determineCoursePrefetchIcon();
 | 
			
		||||
 | 
			
		||||
        if (this.prefetchCourseData.icon != CoreConstants.ICON_LOADING) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Course is being downloaded. Get the download promise.
 | 
			
		||||
        const promise = CoreCourseHelper.getCourseDownloadPromise(this.course.id);
 | 
			
		||||
        if (promise) {
 | 
			
		||||
            // There is a download promise. Show an error if it fails.
 | 
			
		||||
            promise.catch((error) => {
 | 
			
		||||
                if (!this.isDestroyed) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            // No download, this probably means that the app was closed while downloading. Set previous status.
 | 
			
		||||
            const status = await CoreCourse.setCoursePreviousStatus(this.course.id);
 | 
			
		||||
 | 
			
		||||
            this.updateCourseStatus(status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch and load all the data required for the view.
 | 
			
		||||
     *
 | 
			
		||||
@ -463,59 +379,6 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines the prefetch icon of the course.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async determineCoursePrefetchIcon(): Promise<void> {
 | 
			
		||||
        this.prefetchCourseData = await CoreCourseHelper.getCourseStatusIconAndTitle(this.course.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch the whole course.
 | 
			
		||||
     */
 | 
			
		||||
    async prefetchCourse(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreCourseHelper.confirmAndPrefetchCourse(
 | 
			
		||||
                this.prefetchCourseData,
 | 
			
		||||
                this.course,
 | 
			
		||||
                {
 | 
			
		||||
                    sections: this.sections,
 | 
			
		||||
                    menuHandlers: this.courseMenuHandlers,
 | 
			
		||||
                    isGuest: this.isGuest,
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (this.isDestroyed) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Toggle download enabled.
 | 
			
		||||
     */
 | 
			
		||||
    toggleDownload(): void {
 | 
			
		||||
        CoreCourses.setCourseDownloadOptionsEnabled(this.downloadEnabled);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the course status icon and title.
 | 
			
		||||
     *
 | 
			
		||||
     * @param status Status to show.
 | 
			
		||||
     */
 | 
			
		||||
    protected updateCourseStatus(status: string): void {
 | 
			
		||||
        const statusData = CoreCourseHelper.getCoursePrefetchStatusInfo(status);
 | 
			
		||||
 | 
			
		||||
        this.prefetchCourseData.status = statusData.status;
 | 
			
		||||
        this.prefetchCourseData.icon = statusData.icon;
 | 
			
		||||
        this.prefetchCourseData.statusTranslatable = statusData.statusTranslatable;
 | 
			
		||||
        this.prefetchCourseData.loading = statusData.loading;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the course summary
 | 
			
		||||
     */
 | 
			
		||||
@ -542,10 +405,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.isDestroyed = true;
 | 
			
		||||
        this.completionObserver?.off();
 | 
			
		||||
        this.courseStatusObserver?.off();
 | 
			
		||||
        this.syncObserver?.off();
 | 
			
		||||
        this.siteUpdatedObserver?.off();
 | 
			
		||||
        this.downloadEnabledObserver?.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,7 @@
 | 
			
		||||
        <ion-list>
 | 
			
		||||
            <ng-container *ngFor="let section of sections">
 | 
			
		||||
                <ng-container *ngFor="let module of section.modules">
 | 
			
		||||
                    <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section" [courseId]="courseId"
 | 
			
		||||
                        [downloadEnabled]="downloadEnabled">
 | 
			
		||||
                    <core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [section]="section">
 | 
			
		||||
                    </core-course-module>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,6 @@
 | 
			
		||||
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
			
		||||
@ -36,7 +35,6 @@ export class CoreCourseListModTypePage implements OnInit {
 | 
			
		||||
    sections: CoreCourseSection[] = [];
 | 
			
		||||
    title = '';
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    downloadEnabled = false;
 | 
			
		||||
    courseId?: number;
 | 
			
		||||
 | 
			
		||||
    protected modName?: string;
 | 
			
		||||
@ -49,7 +47,6 @@ export class CoreCourseListModTypePage implements OnInit {
 | 
			
		||||
        this.title = CoreNavigator.getRouteParam('title') || '';
 | 
			
		||||
        this.courseId = CoreNavigator.getRouteNumberParam('courseId');
 | 
			
		||||
        this.modName = CoreNavigator.getRouteParam('modName');
 | 
			
		||||
        this.downloadEnabled = !CoreSites.getCurrentSite()?.isOfflineDisabled();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.fetchData();
 | 
			
		||||
 | 
			
		||||
@ -58,8 +58,9 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
 | 
			
		||||
    displayBlocks?(course: CoreCourseAnyCourseData): boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the option to enable section/module download should be displayed. Defaults to true.
 | 
			
		||||
     * Whether the option to enable section/module download should be displayed.
 | 
			
		||||
     *
 | 
			
		||||
     * @deprecated on 4.0 Not used anymore because prefetch has been moved to storage manager.
 | 
			
		||||
     * @param course The course to check.
 | 
			
		||||
     * @return Whether the option to enable section/module download should be displayed.
 | 
			
		||||
     */
 | 
			
		||||
@ -204,16 +205,6 @@ export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseForm
 | 
			
		||||
        return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayBlocks', [course]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the option to enable section/module download should be displayed. Defaults to true.
 | 
			
		||||
     *
 | 
			
		||||
     * @param course The course to check.
 | 
			
		||||
     * @return Whether the option to enable section/module download should be displayed
 | 
			
		||||
     */
 | 
			
		||||
    displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
 | 
			
		||||
        return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayEnableDownload', [course]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
 | 
			
		||||
     * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
 | 
			
		||||
 | 
			
		||||
@ -78,17 +78,6 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the option to enable section/module download should be displayed. Defaults to true.
 | 
			
		||||
     *
 | 
			
		||||
     * @param course The course to check.
 | 
			
		||||
     * @return Whether the option to enable section/module download should be displayed
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
    displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the default section selector should be displayed. Defaults to true.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="hasSideBlocks" [downloadEnabled]="downloadEnabled"></core-block-side-blocks-button>
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="hasSideBlocks"></core-block-side-blocks-button>
 | 
			
		||||
 | 
			
		||||
        <core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate">
 | 
			
		||||
        </core-empty-box>
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,6 @@
 | 
			
		||||
        <ion-icon name="fas-search" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </ion-button>
 | 
			
		||||
    <core-context-menu>
 | 
			
		||||
        <core-context-menu-item [priority]="1000" *ngIf="displayEnableDownload" [content]="'core.settings.showdownloadoptions' | translate"
 | 
			
		||||
            (action)="switchDownload()" iconAction="toggle" [(toggle)]="downloadEnabled"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item [priority]="500" [content]="'addon.storagemanager.managestorage' | translate"
 | 
			
		||||
            (action)="manageCoursesStorage()" iconAction="fas-archive"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item [priority]="400" [content]="'addon.storagemanager.managecoursestorage' | translate"
 | 
			
		||||
@ -26,8 +24,7 @@
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
 | 
			
		||||
                <core-course-module *ngFor="let module of section.modules" [module]="module" [courseId]="siteHomeId"
 | 
			
		||||
                    [downloadEnabled]="downloadEnabled" [section]="section"></core-course-module>
 | 
			
		||||
                <core-course-module *ngFor="let module of section.modules" [module]="module" [section]="section"></core-course-module>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <!-- Site home items: news, categories, courses, etc. -->
 | 
			
		||||
@ -54,7 +51,7 @@
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled">
 | 
			
		||||
        <core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId">
 | 
			
		||||
        </core-block-side-blocks-button>
 | 
			
		||||
 | 
			
		||||
        <core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate">
 | 
			
		||||
@ -73,8 +70,7 @@
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<ng-template #news>
 | 
			
		||||
    <core-course-module class="core-sitehome-news" *ngIf="newsForumModule" [module]="newsForumModule" [courseId]="siteHomeId"
 | 
			
		||||
        [downloadEnabled]="downloadEnabled">
 | 
			
		||||
    <core-course-module class="core-sitehome-news" *ngIf="newsForumModule" [module]="newsForumModule">
 | 
			
		||||
    </core-course-module>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { CoreCourse, CoreCourseWSSection } from '@features/course/services/cours
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreSiteHome } from '@features/sitehome/services/sitehome';
 | 
			
		||||
import { CoreCourses, CoreCoursesProvider } from '@features//courses/services/courses';
 | 
			
		||||
import { CoreCourses } from '@features//courses/services/courses';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
			
		||||
@ -50,24 +50,15 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
 | 
			
		||||
    siteHomeId = 1;
 | 
			
		||||
    currentSite!: CoreSite;
 | 
			
		||||
    searchEnabled = false;
 | 
			
		||||
    displayEnableDownload = false;
 | 
			
		||||
    downloadEnabled = false;
 | 
			
		||||
    newsForumModule?: CoreCourseModuleData;
 | 
			
		||||
 | 
			
		||||
    protected updateSiteObserver: CoreEventObserver;
 | 
			
		||||
    protected downloadEnabledObserver: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        // Refresh the enabled flags if site is updated.
 | 
			
		||||
        this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
 | 
			
		||||
            this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
 | 
			
		||||
 | 
			
		||||
            this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled();
 | 
			
		||||
        }, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        this.downloadEnabledObserver = CoreEvents.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED, (data) => {
 | 
			
		||||
            this.downloadEnabled = data.enabled;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -85,9 +76,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
 | 
			
		||||
            CoreCourseHelper.openModule(module, this.siteHomeId, undefined, modParams);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.displayEnableDownload = !CoreSites.getRequiredCurrentSite().isOfflineDisabled();
 | 
			
		||||
        this.downloadEnabled = CoreCourses.getCourseDownloadOptionsEnabled();
 | 
			
		||||
 | 
			
		||||
        this.loadContent().finally(() => {
 | 
			
		||||
            this.dataLoaded = true;
 | 
			
		||||
        });
 | 
			
		||||
@ -186,13 +174,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Switch download enabled.
 | 
			
		||||
     */
 | 
			
		||||
    switchDownload(): void {
 | 
			
		||||
        CoreCourses.setCourseDownloadOptionsEnabled(this.downloadEnabled);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open page to manage courses storage.
 | 
			
		||||
     */
 | 
			
		||||
@ -240,7 +221,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.updateSiteObserver.off();
 | 
			
		||||
        this.downloadEnabledObserver.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -35,13 +35,6 @@ export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandl
 | 
			
		||||
        return this.handlerSchema.canviewallsections ?? true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    displayEnableDownload(): boolean {
 | 
			
		||||
        return this.handlerSchema.displayenabledownload ?? true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ information provided here is intended especially for developers.
 | 
			
		||||
- CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor now admits module name instead of full module object.
 | 
			
		||||
- CoreCourse.getModuleBasicInfoByInstance and CoreCourse.getModuleBasicInfo have been modified to accept an "options" parameter instead of only siteId.
 | 
			
		||||
- The function CoreFilepool.isFileDownloadingByUrl now returns Promise<boolean> instead of relying on resolve/reject.
 | 
			
		||||
- downloadEnabled input has been removed from CoreBlockSideBlocksComponent, CoreCourseFormatComponent, CoreCourseFormatSingleActivityComponent and CoreCourseModuleComponent.
 | 
			
		||||
 | 
			
		||||
=== 3.9.5 ===
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user