@ -11,10 +11,10 @@
 | 
			
		||||
    <!-- Activity info. -->
 | 
			
		||||
    <core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
 | 
			
		||||
        [courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()">
 | 
			
		||||
        <ion-list inset="true" description *ngIf="assign && assign.introattachments?.length && !assign.submissionattachments">
 | 
			
		||||
        <div description *ngIf="assign && assign.introattachments?.length && !assign.submissionattachments">
 | 
			
		||||
            <core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId">
 | 
			
		||||
            </core-file>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
        </div>
 | 
			
		||||
    </core-course-module-info>
 | 
			
		||||
 | 
			
		||||
    <!-- User can view all submissions (teacher). -->
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
 | 
			
		||||
    <ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)">
 | 
			
		||||
        <ng-container *ngFor="let folder of contents.folders">
 | 
			
		||||
            <ion-item class="item-file" (click)="openFolder(folder)" detail="true" button>
 | 
			
		||||
            <ion-item class="ion-text-wrap item-file item-directory" (click)="openFolder(folder)" detail="true" button>
 | 
			
		||||
                <ion-icon name="fas-folder" slot="start" [attr.aria-label]="'core.folder' | translate"></ion-icon>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <p class="item-heading">{{folder.filename}}</p>
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
 | 
			
		||||
        if (this.subfolder) {
 | 
			
		||||
            this.description = this.folderInstance ? this.folderInstance.intro : this.module.description;
 | 
			
		||||
            this.contents = this.subfolder;
 | 
			
		||||
            this.sortFilesAndFolders();
 | 
			
		||||
 | 
			
		||||
            this.showLoading = false;
 | 
			
		||||
 | 
			
		||||
@ -88,6 +89,30 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
 | 
			
		||||
 | 
			
		||||
        this.description = this.folderInstance ? this.folderInstance.intro : this.module.description;
 | 
			
		||||
        this.contents = AddonModFolderHelper.formatContents(contents);
 | 
			
		||||
        this.sortFilesAndFolders();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sort files and folders alphabetically.
 | 
			
		||||
     */
 | 
			
		||||
    protected sortFilesAndFolders(): void {
 | 
			
		||||
        if (!this.contents) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.contents.folders.sort((a, b) => {
 | 
			
		||||
            const compareA = a.filename.toLowerCase();
 | 
			
		||||
            const compareB = b.filename.toLowerCase();
 | 
			
		||||
 | 
			
		||||
            return compareA.localeCompare(compareB);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.contents.files.sort((a, b) => {
 | 
			
		||||
            const compareA = a.filename.toLowerCase();
 | 
			
		||||
            const compareB = b.filename.toLowerCase();
 | 
			
		||||
 | 
			
		||||
            return compareA.localeCompare(compareB);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,8 @@ ion-item {
 | 
			
		||||
    div.core-notification-icon,
 | 
			
		||||
    core-mod-icon.core-notification-icon {
 | 
			
		||||
        padding: 8px;
 | 
			
		||||
        max-width: var(--core-avatar-size);
 | 
			
		||||
        max-height:  var(--core-avatar-size);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,10 +37,8 @@
 | 
			
		||||
        <!-- List of files. -->
 | 
			
		||||
        <ion-list *ngIf="files && files.length > 0">
 | 
			
		||||
            <ng-container *ngFor="let file of files">
 | 
			
		||||
                <ion-item button *ngIf="file.isdir" class="item-file" (click)="openFolder(file)" detail="true">
 | 
			
		||||
                    <ion-thumbnail slot="start">
 | 
			
		||||
                        <img [src]="file.imgPath" alt="" role="presentation">
 | 
			
		||||
                    </ion-thumbnail>
 | 
			
		||||
                <ion-item button *ngIf="file.isdir" class="ion-text-wrap item-file item-directory" (click)="openFolder(file)" detail="true">
 | 
			
		||||
                    <ion-icon name="fas-folder" slot="start" [attr.aria-label]="'core.folder' | translate"></ion-icon>
 | 
			
		||||
                    <ion-label>{{file.filename}}</ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <core-file *ngIf="!file.isdir" [file]="file" [component]="component" [componentId]="file.contextid"></core-file>
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/archive.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 4.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/audio.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/avi.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 4.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/base.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/bmp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/calc.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 3.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/chart.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 4.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/database.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/document.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 3.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/draw.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/eps.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 3.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/epub.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/flash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/folder-open.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 539 B  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/folder.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 543 B  | 
| 
		 Before Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/gif.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/h5p.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/html.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 888 B  | 
| 
		 Before Width: | Height: | Size: 5.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/image.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 3.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/impress.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 3.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/isf.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/jpeg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 4.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/markup.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/math.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/moodle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/mp3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 5.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/mpeg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/oth.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 4.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/pdf.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/png.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 4.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/powerpoint.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/psd.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 4.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/publisher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/quicktime.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/sourcecode.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 5.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/spreadsheet.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/tiff.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/unknown.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 888 B  | 
| 
		 Before Width: | Height: | Size: 4.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/video.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 5.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/wav.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/wmv.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/files/writer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
@ -1,11 +1,15 @@
 | 
			
		||||
<ion-card class="card-file">
 | 
			
		||||
    <ion-item *ngIf="file" button class="ion-text-wrap item-file" (click)="download($event, true)" detail="false">
 | 
			
		||||
        <ion-thumbnail slot="start">
 | 
			
		||||
            <img [src]="fileIcon" alt="" role="presentation" />
 | 
			
		||||
        </ion-thumbnail>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{fileName}}</p>
 | 
			
		||||
        <p *ngIf="fileSizeReadable">{{ fileSizeReadable }}</p>
 | 
			
		||||
        <p *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</p>
 | 
			
		||||
            <p *ngIf="fileSizeReadable || showTime">
 | 
			
		||||
                <ng-container *ngIf="fileSizeReadable">{{ fileSizeReadable }}</ng-container>
 | 
			
		||||
                <ng-container *ngIf="fileSizeReadable && showTime"> · </ng-container>
 | 
			
		||||
                <ng-container *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</ng-container>
 | 
			
		||||
            </p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
        <div slot="end" class="flex-row">
 | 
			
		||||
            <core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" [canTrustDownload]="!alwaysDownload"
 | 
			
		||||
@ -16,9 +20,10 @@
 | 
			
		||||
                <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
 | 
			
		||||
        <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)" [attr.aria-label]="'core.delete' | translate"
 | 
			
		||||
            color="danger">
 | 
			
		||||
            <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)"
 | 
			
		||||
                [attr.aria-label]="'core.delete' | translate" color="danger">
 | 
			
		||||
                <ion-icon slot="icon-only" name="fas-trash" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
</ion-card>
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,19 @@
 | 
			
		||||
<form (ngSubmit)="changeName(newFileName, $event)" #nameForm>
 | 
			
		||||
    <ion-card class="card-file">
 | 
			
		||||
        <ion-item class="ion-text-wrap item-file" (click)="openFile($event)" button detail="false">
 | 
			
		||||
            <ion-thumbnail slot="start">
 | 
			
		||||
                <img [src]="fileIcon" [alt]="fileExtension" role="presentation" />
 | 
			
		||||
            </ion-thumbnail>
 | 
			
		||||
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <ion-label *ngIf="!editMode">
 | 
			
		||||
                <!-- File name and edit button (if editable). -->
 | 
			
		||||
            <p class="item-heading" *ngIf="!editMode">{{fileName}}</p>
 | 
			
		||||
                <p class="item-heading">{{fileName}}</p>
 | 
			
		||||
                <!-- More data about the file. -->
 | 
			
		||||
            <p *ngIf="size && !editMode">{{ size }}</p>
 | 
			
		||||
            <p *ngIf="timemodified && !editMode">{{ timemodified }}</p>
 | 
			
		||||
                <p *ngIf="size || timemodified">
 | 
			
		||||
                    <ng-container *ngIf="size">{{ size }}</ng-container>
 | 
			
		||||
                    <ng-container *ngIf="size && timemodified"> · </ng-container>
 | 
			
		||||
                    <ng-container *ngIf="timemodified">{{ timemodified }}</ng-container>
 | 
			
		||||
                </p>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
 | 
			
		||||
            <!-- Form to edit the file's name. -->
 | 
			
		||||
@ -23,19 +27,22 @@
 | 
			
		||||
                </ion-button>
 | 
			
		||||
 | 
			
		||||
                <ng-container *ngIf="manage">
 | 
			
		||||
                    <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit"
 | 
			
		||||
                        (click)="changeName(newFileName, $event)">
 | 
			
		||||
                        <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
 | 
			
		||||
                    <ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)"
 | 
			
		||||
                        [attr.aria-label]="'core.edit' | translate">
 | 
			
		||||
                        <ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
 | 
			
		||||
                <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit">
 | 
			
		||||
                    <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                </ion-button>
 | 
			
		||||
 | 
			
		||||
                <ion-button fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger">
 | 
			
		||||
                    <ion-button *ngIf="!editMode" fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate"
 | 
			
		||||
                        color="danger">
 | 
			
		||||
                        <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
            </div>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
    </ion-card>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
    timemodified?: string;
 | 
			
		||||
    newFileName = '';
 | 
			
		||||
    editMode = false;
 | 
			
		||||
    relativePath?: string;
 | 
			
		||||
    relativePath = '';
 | 
			
		||||
    isIOS = false;
 | 
			
		||||
    openButtonIcon = '';
 | 
			
		||||
    openButtonLabel = '';
 | 
			
		||||
@ -112,7 +112,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
     * @param isOpenButton Whether the open button was clicked.
 | 
			
		||||
     */
 | 
			
		||||
    async openFile(e: Event, isOpenButton = false): Promise<void> {
 | 
			
		||||
        if (this.editMode) {
 | 
			
		||||
        if (this.editMode || !this.file) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -125,7 +125,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CoreFileHelper.isOpenableInApp(this.file!)) {
 | 
			
		||||
        if (!CoreFileHelper.isOpenableInApp(this.file)) {
 | 
			
		||||
            try {
 | 
			
		||||
                await CoreFileHelper.showConfirmOpenUnsupportedFile();
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
@ -139,7 +139,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
            options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreUtils.openFile(this.file!.toURL(), options);
 | 
			
		||||
        CoreUtils.openFile(this.file.toURL(), options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -148,11 +148,15 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
     * @param e Click event.
 | 
			
		||||
     */
 | 
			
		||||
    activateEdit(e: Event): void {
 | 
			
		||||
        if (!this.file) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
 | 
			
		||||
        this.editMode = true;
 | 
			
		||||
        this.newFileName = this.file!.name;
 | 
			
		||||
        this.newFileName = this.file.name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -162,10 +166,14 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
     * @param e Click event.
 | 
			
		||||
     */
 | 
			
		||||
    async changeName(newName: string, e: Event): Promise<void> {
 | 
			
		||||
        if (!this.file) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
 | 
			
		||||
        if (newName == this.file!.name) {
 | 
			
		||||
        if (newName == this.file.name) {
 | 
			
		||||
            // Name hasn't changed, stop.
 | 
			
		||||
            this.editMode = false;
 | 
			
		||||
            CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
 | 
			
		||||
@ -174,7 +182,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
        const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath!);
 | 
			
		||||
        const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath);
 | 
			
		||||
        const newPath = CoreText.concatenatePaths(fileAndDir.directory, newName);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@ -186,7 +194,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
        } catch {
 | 
			
		||||
            try {
 | 
			
		||||
                // File doesn't exist, move it.
 | 
			
		||||
                const fileEntry = await CoreFile.moveFile(this.relativePath!, newPath);
 | 
			
		||||
                const fileEntry = await CoreFile.moveFile(this.relativePath, newPath);
 | 
			
		||||
 | 
			
		||||
                CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
@ -219,7 +227,7 @@ export class CoreLocalFileComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
            modal = await CoreDomUtils.showModalLoading('core.deleting', true);
 | 
			
		||||
 | 
			
		||||
            await CoreFile.removeFile(this.relativePath!);
 | 
			
		||||
            await CoreFile.removeFile(this.relativePath);
 | 
			
		||||
 | 
			
		||||
            this.onDelete.emit();
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
 | 
			
		||||
@ -208,7 +208,11 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
 | 
			
		||||
        this.expandedHeader = this.page?.querySelector('ion-item[collapsible]') ?? undefined;
 | 
			
		||||
 | 
			
		||||
        if (!this.expandedHeader) {
 | 
			
		||||
            this.enabled = false;
 | 
			
		||||
            this.setEnabled(this.enabled);
 | 
			
		||||
 | 
			
		||||
            throw new Error('[collapsible-header] Couldn\'t initialize expanded header');
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        this.expandedHeader.classList.add('collapsible-header-expanded');
 | 
			
		||||
 | 
			
		||||
@ -384,11 +388,11 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
 | 
			
		||||
     * @param enable True to enable, false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    async setEnabled(enable: boolean): Promise<void> {
 | 
			
		||||
        if (!this.page || !this.content) {
 | 
			
		||||
        if (!this.page) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (enable) {
 | 
			
		||||
        if (enable && this.content) {
 | 
			
		||||
            const contentScroll = await this.content.getScrollElement();
 | 
			
		||||
 | 
			
		||||
            // Do nothing, since scroll has already started on the page.
 | 
			
		||||
 | 
			
		||||
@ -15,11 +15,14 @@
 | 
			
		||||
import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
 | 
			
		||||
import { CoreLoadingComponent } from '@components/loading/loading';
 | 
			
		||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreColors } from '@singletons/colors';
 | 
			
		||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CoreEventObserver } from '@singletons/events';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
import { CoreFormatTextDirective } from './format-text';
 | 
			
		||||
 | 
			
		||||
const defaultMaxHeight = 80;
 | 
			
		||||
@ -50,6 +53,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
 | 
			
		||||
    protected maxHeight = defaultMaxHeight;
 | 
			
		||||
    protected expandedHeight = 0;
 | 
			
		||||
    protected resizeListener?: CoreEventObserver;
 | 
			
		||||
    protected darkModeListener?: Subscription;
 | 
			
		||||
    protected domPromise?: CoreCancellablePromise<void>;
 | 
			
		||||
    protected uniqueId: string;
 | 
			
		||||
 | 
			
		||||
@ -92,6 +96,10 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
 | 
			
		||||
        this.resizeListener = CoreDom.onWindowResize(() => {
 | 
			
		||||
            this.calculateHeight();
 | 
			
		||||
        }, 50);
 | 
			
		||||
 | 
			
		||||
        this.darkModeListener = CoreSettingsHelper.onDarkModeChange().subscribe(() => {
 | 
			
		||||
            this.setGradientColor();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -135,7 +143,34 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
 | 
			
		||||
        this.element.classList.remove('collapsible-loading-height');
 | 
			
		||||
 | 
			
		||||
        // If cannot calculate height, shorten always.
 | 
			
		||||
        this.setExpandButtonEnabled(!this.expandedHeight || this.expandedHeight >= this.maxHeight);
 | 
			
		||||
        const enable = !this.expandedHeight || this.expandedHeight >= this.maxHeight;
 | 
			
		||||
        this.setExpandButtonEnabled(enable);
 | 
			
		||||
        this.setGradientColor();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the gradient color based on the background.
 | 
			
		||||
     */
 | 
			
		||||
    protected setGradientColor(): void {
 | 
			
		||||
        if (!this.toggleExpandEnabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let coloredElement: HTMLElement | null = this.element;
 | 
			
		||||
        let backgroundColor = [0, 0, 0, 0];
 | 
			
		||||
        let background = '';
 | 
			
		||||
        while (coloredElement && backgroundColor[3] === 0) {
 | 
			
		||||
            background = getComputedStyle(coloredElement).backgroundColor;
 | 
			
		||||
            backgroundColor = CoreColors.getColorRGBA(background);
 | 
			
		||||
            coloredElement = coloredElement.parentElement;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (backgroundColor[3] !== 0) {
 | 
			
		||||
            delete(backgroundColor[3]);
 | 
			
		||||
            const bgList = backgroundColor.join(',');
 | 
			
		||||
            this.element.style.setProperty('--background-gradient-rgb', `${bgList}`);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -241,6 +276,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.resizeListener?.off();
 | 
			
		||||
        this.darkModeListener?.unsubscribe();
 | 
			
		||||
        this.domPromise?.cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
</core-dynamic-component>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<core-block-side-blocks-button slot="fixed" *ngIf="loaded && course && displayBlocks && hasBlocks" contextlevel="course"
 | 
			
		||||
<core-block-side-blocks-button slot="fixed" *ngIf="loaded && course && displayBlocks && hasBlocks" contextLevel="course"
 | 
			
		||||
    [instanceId]="course.id">
 | 
			
		||||
</core-block-side-blocks-button>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,43 +14,39 @@
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
 | 
			
		||||
<div class="core-module-info-box">
 | 
			
		||||
    <!-- Module completion. -->
 | 
			
		||||
<ion-item class="ion-text-wrap"
 | 
			
		||||
    *ngIf="showCompletion && module.completiondata && (module.completiondata.isautomatic || (showManualCompletion && module.uservisible))">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
    <div class="core-module-info-completion core-module-info-box-section" *ngIf="showCompletion &&
 | 
			
		||||
            module.completiondata && (module.completiondata.isautomatic || (showManualCompletion && module.uservisible))">
 | 
			
		||||
        <core-course-module-completion [completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
 | 
			
		||||
            [showCompletionConditions]="true" [showManualCompletion]="showManualCompletion && module.uservisible"
 | 
			
		||||
            (completionChanged)="completionChanged.emit($event)">
 | 
			
		||||
        </core-course-module-completion>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
<div class="core-module-dates-availabilityinfo"
 | 
			
		||||
    *ngIf="(module.dates && module.dates.length) || (showAvailabilityInfo && module.availabilityinfo)">
 | 
			
		||||
    <!-- Activity dates. -->
 | 
			
		||||
    <div *ngIf="module.dates && module.dates.length" class="core-module-dates">
 | 
			
		||||
    <div *ngIf="module.dates && module.dates.length" class="core-module-dates core-module-info-box-section">
 | 
			
		||||
        <p *ngFor="let date of module.dates">
 | 
			
		||||
            <ion-icon name="fas-calendar" aria-hidden="true"></ion-icon><strong>{{ date.label }}</strong> {{ date.timestamp
 | 
			
		||||
            *
 | 
			
		||||
            1000 | coreFormatDate:'strftimedatetime' }}
 | 
			
		||||
        </p>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Availability info space. -->
 | 
			
		||||
    <div class="core-module-availabilityinfo" *ngIf="showAvailabilityInfo">
 | 
			
		||||
    <div class="core-module-availabilityinfo core-module-info-box-section" *ngIf="showAvailabilityInfo">
 | 
			
		||||
        <ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate"></ion-icon>
 | 
			
		||||
        <core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
 | 
			
		||||
        </core-format-text>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<ion-item class="ion-text-wrap" *ngIf="description">
 | 
			
		||||
    <ion-label>
 | 
			
		||||
    <div class="core-module-info-description core-module-info-box-section" *ngIf="description">
 | 
			
		||||
        <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
 | 
			
		||||
            [contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="expandDescription ? null : ''">
 | 
			
		||||
        </core-format-text>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
    </div>
 | 
			
		||||
    <ng-content select="[description]"></ng-content>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<ng-content></ng-content>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,11 +23,34 @@
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-module-dates-availabilityinfo {
 | 
			
		||||
    .core-module-info-box {
 | 
			
		||||
        background: var(--light);
 | 
			
		||||
        border-radius: var(--small-radius);
 | 
			
		||||
        padding: 8px;
 | 
			
		||||
        margin: 8px;
 | 
			
		||||
        padding: 8px;
 | 
			
		||||
 | 
			
		||||
        ::ng-deep ion-item {
 | 
			
		||||
            --ion-item-background: var(--light);
 | 
			
		||||
            --background: var(--light);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ::ng-deep ion-card.card-file {
 | 
			
		||||
            --ion-card-horizontal-margin: 0px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .core-module-info-box-section + .core-module-info-box-section {
 | 
			
		||||
            border-top: 1px solid var(--stroke);
 | 
			
		||||
            margin-top: 8px;
 | 
			
		||||
            padding-top: 8px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .core-module-dates ion-icon {
 | 
			
		||||
            margin-left: 4px;
 | 
			
		||||
            margin-right: 4px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .core-module-dates,
 | 
			
		||||
        .core-module-availabilityinfo {
 | 
			
		||||
            font-size: 90%;
 | 
			
		||||
            ion-icon {
 | 
			
		||||
                position: static;
 | 
			
		||||
@ -40,10 +63,6 @@
 | 
			
		||||
                margin-bottom: 4px;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    .core-module-dates + .core-module-availabilityinfo {
 | 
			
		||||
        border-top: 1px solid var(--stroke);
 | 
			
		||||
        padding-top: 8px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core-course-module-completion ::ng-deep ion-button {
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,10 @@
 | 
			
		||||
        margin-top: var(--button-vertical-margin);
 | 
			
		||||
        margin-bottom: var(--button-vertical-margin);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.empty {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:host-context(core-course-format.core-course-format-singleactivity) {
 | 
			
		||||
 | 
			
		||||