MOBILE-3651 core: Implement attachments, files and local-file components
parent
f9ea74067d
commit
b30a654dfa
|
@ -0,0 +1,155 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { FileEntry } from '@ionic-native/file/ngx';
|
||||||
|
|
||||||
|
import { CoreFileUploader, CoreFileUploaderTypeList } from '@features/fileuploader/services/fileuploader';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreWSExternalFile } from '@services/ws';
|
||||||
|
import { Translate } from '@singletons';
|
||||||
|
import { CoreApp } from '@services/app';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render attachments, allow adding more and delete the current ones.
|
||||||
|
*
|
||||||
|
* All the changes done will be applied to the "files" input array, no file will be uploaded. The component using this
|
||||||
|
* component should be the one uploading and moving the files.
|
||||||
|
*
|
||||||
|
* All the files added will be copied to the app temporary folder, so they should be deleted after uploading them
|
||||||
|
* or if the user cancels the action.
|
||||||
|
*
|
||||||
|
* <core-attachments [files]="files" [maxSize]="configs.maxsubmissionsizebytes" [maxSubmissions]="configs.maxfilesubmissions"
|
||||||
|
* [component]="component" [componentId]="assign.cmid" [acceptedTypes]="configs.filetypeslist" [allowOffline]="allowOffline">
|
||||||
|
* </core-attachments>
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-attachments',
|
||||||
|
templateUrl: 'core-attachments.html',
|
||||||
|
})
|
||||||
|
export class CoreAttachmentsComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() files?: (CoreWSExternalFile | FileEntry)[]; // List of attachments. New attachments will be added to this array.
|
||||||
|
@Input() maxSize?: number; // Max size for attachments. -1 means unlimited, 0 means user max size, not defined means unknown.
|
||||||
|
@Input() maxSubmissions?: number; // Max number of attachments. -1 means unlimited, not defined means unknown limit.
|
||||||
|
@Input() component?: string; // Component the downloaded files will be linked to.
|
||||||
|
@Input() componentId?: string | number; // Component ID.
|
||||||
|
@Input() allowOffline?: boolean | string; // Whether to allow selecting files in offline.
|
||||||
|
@Input() acceptedTypes?: string; // List of supported filetypes. If undefined, all types supported.
|
||||||
|
@Input() required?: boolean; // Whether to display the required mark.
|
||||||
|
|
||||||
|
maxSizeReadable?: string;
|
||||||
|
maxSubmissionsReadable?: string;
|
||||||
|
unlimitedFiles?: boolean;
|
||||||
|
fileTypes?: CoreFileUploaderTypeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.files = this.files || [];
|
||||||
|
this.maxSize = this.maxSize !== null ? Number(this.maxSize) : NaN;
|
||||||
|
|
||||||
|
if (this.maxSize === 0) {
|
||||||
|
const currentSite = CoreSites.instance.getCurrentSite();
|
||||||
|
const siteInfo = currentSite?.getInfo();
|
||||||
|
|
||||||
|
if (siteInfo?.usermaxuploadfilesize) {
|
||||||
|
this.maxSize = siteInfo.usermaxuploadfilesize;
|
||||||
|
this.maxSizeReadable = CoreTextUtils.instance.bytesToSize(this.maxSize, 2);
|
||||||
|
} else {
|
||||||
|
this.maxSizeReadable = Translate.instance.instant('core.unknown');
|
||||||
|
}
|
||||||
|
} else if (this.maxSize > 0) {
|
||||||
|
this.maxSizeReadable = CoreTextUtils.instance.bytesToSize(this.maxSize, 2);
|
||||||
|
} else if (this.maxSize === -1) {
|
||||||
|
this.maxSizeReadable = Translate.instance.instant('core.unlimited');
|
||||||
|
} else {
|
||||||
|
this.maxSizeReadable = Translate.instance.instant('core.unknown');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.maxSubmissions === undefined || this.maxSubmissions < 0) {
|
||||||
|
this.maxSubmissionsReadable = this.maxSubmissions === undefined ?
|
||||||
|
Translate.instance.instant('core.unknown') : undefined;
|
||||||
|
this.unlimitedFiles = true;
|
||||||
|
} else {
|
||||||
|
this.maxSubmissionsReadable = String(this.maxSubmissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.acceptedTypes = this.acceptedTypes?.trim();
|
||||||
|
|
||||||
|
if (this.acceptedTypes && this.acceptedTypes != '*') {
|
||||||
|
this.fileTypes = CoreFileUploader.instance.prepareFiletypeList(this.acceptedTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new attachment.
|
||||||
|
*/
|
||||||
|
async add(): Promise<void> {
|
||||||
|
const allowOffline = !!this.allowOffline && this.allowOffline !== 'false';
|
||||||
|
|
||||||
|
if (!allowOffline && !CoreApp.instance.isOnline()) {
|
||||||
|
CoreDomUtils.instance.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mimetypes = this.fileTypes && this.fileTypes.mimetypes;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await CoreFileUploaderHelper.instance.selectFile(this.maxSize, allowOffline, undefined, mimetypes);
|
||||||
|
|
||||||
|
this.files?.push(result);
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModalDefault(error, 'Error selecting file.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a file from the list.
|
||||||
|
*
|
||||||
|
* @param index The index of the file.
|
||||||
|
* @param askConfirm Whether to ask confirm.
|
||||||
|
*/
|
||||||
|
async delete(index: number, askConfirm?: boolean): Promise<void> {
|
||||||
|
|
||||||
|
|
||||||
|
if (askConfirm) {
|
||||||
|
try {
|
||||||
|
await CoreDomUtils.instance.showDeleteConfirm('core.confirmdeletefile');
|
||||||
|
} catch {
|
||||||
|
// User cancelled.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the file from the list.
|
||||||
|
this.files?.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file was renamed.
|
||||||
|
*
|
||||||
|
* @param index Index of the file.
|
||||||
|
* @param data The data received.
|
||||||
|
*/
|
||||||
|
renamed(index: number, data: { file: FileEntry }): void {
|
||||||
|
this.files![index] = data.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<span *ngIf="maxSubmissionsReadable">
|
||||||
|
{{ 'core.maxsizeandattachments' | translate:{$a: {size: maxSizeReadable, attachments: maxSubmissionsReadable} } }}
|
||||||
|
</span>
|
||||||
|
<span *ngIf="!maxSubmissionsReadable">{{ 'core.maxfilesize' | translate:{$a: maxSizeReadable} }}</span>
|
||||||
|
<span [core-mark-required]="required" class="core-mark-required"></span>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="fileTypes && fileTypes.mimetypes && fileTypes.mimetypes.length">
|
||||||
|
<ion-label>
|
||||||
|
<p>{{ 'core.fileuploader.filesofthesetypes' | translate }}</p>
|
||||||
|
<ul class="list-with-style">
|
||||||
|
<li *ngFor="let typeInfo of fileTypes.info">
|
||||||
|
<strong *ngIf="typeInfo.name">{{typeInfo.name}} </strong>{{typeInfo.extlist}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<div *ngFor="let file of files; let index=index">
|
||||||
|
<!-- Files already attached to the submission, either in online or in offline. -->
|
||||||
|
<core-file *ngIf="!file.name" [file]="file" [component]="component" [componentId]="componentId"
|
||||||
|
[canDelete]="true" (onDelete)="delete(index, true)" [canDownload]="!file.offline">
|
||||||
|
</core-file>
|
||||||
|
|
||||||
|
<!-- Files added to draft but not attached to submission yet. -->
|
||||||
|
<core-local-file *ngIf="file.name" [file]="file" [manage]="true" (onDelete)="delete(index, false)"
|
||||||
|
(onRename)="renamed(index, $event)">
|
||||||
|
</core-local-file>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Button to add more files. -->
|
||||||
|
<ion-button expand="block"
|
||||||
|
*ngIf="unlimitedFiles || (maxSubmissions !== undefined && maxSubmissions >= 0 && files && files.length < maxSubmissions)"
|
||||||
|
class="ion-text-wrap ion-margin" (click)="add()">
|
||||||
|
<ion-icon name="fas-plus" slot="start"></ion-icon>
|
||||||
|
{{ 'core.fileuploader.addfiletext' | translate }}
|
||||||
|
</ion-button>
|
|
@ -48,6 +48,9 @@ import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar';
|
||||||
|
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { CoreAttachmentsComponent } from './attachments/attachments';
|
||||||
|
import { CoreFilesComponent } from './files/files';
|
||||||
|
import { CoreLocalFileComponent } from './local-file/local-file';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -78,6 +81,9 @@ import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
CoreSendMessageFormComponent,
|
CoreSendMessageFormComponent,
|
||||||
CoreTimerComponent,
|
CoreTimerComponent,
|
||||||
CoreNavigationBarComponent,
|
CoreNavigationBarComponent,
|
||||||
|
CoreAttachmentsComponent,
|
||||||
|
CoreFilesComponent,
|
||||||
|
CoreLocalFileComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -115,6 +121,9 @@ import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
CoreSendMessageFormComponent,
|
CoreSendMessageFormComponent,
|
||||||
CoreTimerComponent,
|
CoreTimerComponent,
|
||||||
CoreNavigationBarComponent,
|
CoreNavigationBarComponent,
|
||||||
|
CoreAttachmentsComponent,
|
||||||
|
CoreFilesComponent,
|
||||||
|
CoreLocalFileComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreComponentsModule {}
|
export class CoreComponentsModule {}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<ng-container *ngIf="showInline && contentText">
|
||||||
|
<core-format-text [text]="contentText" [filter]="false"></core-format-text>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngFor="let file of files">
|
||||||
|
<!-- Files already attached to the filearea. -->
|
||||||
|
<core-file *ngIf="!file.name && !file.embedType" [file]="file" [component]="component" [componentId]="componentId"
|
||||||
|
[alwaysDownload]="alwaysDownload" [canDownload]="canDownload" [showSize]="showSize" [showTime]="showTime">
|
||||||
|
</core-file>
|
||||||
|
|
||||||
|
<!-- Files stored in offline to be sent later. -->
|
||||||
|
<core-local-file *ngIf="file.name && !file.embedType" [file]="file"></core-local-file>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,85 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, Input, OnInit, DoCheck, KeyValueDiffers } from '@angular/core';
|
||||||
|
import { FileEntry } from '@ionic-native/file/ngx';
|
||||||
|
|
||||||
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreWSExternalFile } from '@services/ws';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render a file list.
|
||||||
|
*
|
||||||
|
* <core-files [files]="files" [component]="component" [componentId]="assign.cmid">
|
||||||
|
* </core-files>
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-files',
|
||||||
|
templateUrl: 'core-files.html',
|
||||||
|
})
|
||||||
|
export class CoreFilesComponent implements OnInit, DoCheck {
|
||||||
|
|
||||||
|
@Input() files?: (CoreWSExternalFile | FileEntry)[]; // List of files.
|
||||||
|
@Input() component?: string; // Component the downloaded files will be linked to.
|
||||||
|
@Input() componentId?: string | number; // Component ID.
|
||||||
|
@Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded.
|
||||||
|
@Input() canDownload?: boolean | string = true; // Whether file can be downloaded.
|
||||||
|
@Input() showSize?: boolean | string = true; // Whether show filesize.
|
||||||
|
@Input() showTime?: boolean | string = true; // Whether show file time modified.
|
||||||
|
@Input() showInline = false; // If true, it will reorder and try to show inline files first.
|
||||||
|
|
||||||
|
contentText?: string;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
protected differ: any; // To detect changes in the data input.
|
||||||
|
|
||||||
|
constructor(differs: KeyValueDiffers) {
|
||||||
|
this.differ = differs.find([]).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (CoreUtils.instance.isTrueOrOne(this.showInline) && this.files) {
|
||||||
|
this.renderInlineFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
|
||||||
|
*/
|
||||||
|
ngDoCheck(): void {
|
||||||
|
if (CoreUtils.instance.isTrueOrOne(this.showInline) && this.files) {
|
||||||
|
// Check if there's any change in the files array.
|
||||||
|
const changes = this.differ.diff(this.files);
|
||||||
|
if (changes) {
|
||||||
|
this.renderInlineFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate contentText based on fils that can be rendered inline.
|
||||||
|
*/
|
||||||
|
protected renderInlineFiles(): void {
|
||||||
|
this.contentText = this.files!.reduce((previous, file) => {
|
||||||
|
const text = CoreMimetypeUtils.instance.getEmbeddedHtml(file);
|
||||||
|
|
||||||
|
return text ? previous + '<br>' + text : previous;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<form (ngSubmit)="changeName(newFileName, $event)" #nameForm>
|
||||||
|
<ion-item class="ion-text-wrap item-media" (click)="fileClicked($event)"> <!-- [class.item-input]="editMode" -->
|
||||||
|
<img [src]="fileIcon" alt="{{fileExtension}}" role="presentation" slot="start" />
|
||||||
|
|
||||||
|
<ion-label>
|
||||||
|
<!-- File name and edit button (if editable). -->
|
||||||
|
<h3 *ngIf="!editMode">{{fileName}}</h3>
|
||||||
|
<!-- More data about the file. -->
|
||||||
|
<p *ngIf="size && !editMode">{{ size }}</p>
|
||||||
|
<p *ngIf="timemodified && !editMode">{{ timemodified }}</p>
|
||||||
|
</ion-label>
|
||||||
|
|
||||||
|
<!-- Form to edit the file's name. -->
|
||||||
|
<ion-input type="text" name="filename" [placeholder]="'core.filename' | translate" autocapitalize="none" autocorrect="off"
|
||||||
|
(click)="$event.stopPropagation()" [core-auto-focus] [(ngModel)]="newFileName" *ngIf="editMode">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<div class="buttons" slot="end" *ngIf="manage">
|
||||||
|
<ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)"
|
||||||
|
[attr.aria-label]="'core.edit' | translate" color="dark">
|
||||||
|
<ion-icon name="fas-pen" slot="icon-only"></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"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
|
||||||
|
<ion-button fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger">
|
||||||
|
<ion-icon name="fas-trash" slot="icon-only"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
</ion-item>
|
||||||
|
</form>
|
|
@ -0,0 +1,213 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, Input, Output, OnInit, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
import { FileEntry } from '@ionic-native/file/ngx';
|
||||||
|
|
||||||
|
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
||||||
|
import { CoreFile } from '@services/file';
|
||||||
|
import { CoreFileHelper } from '@services/file-helper';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to handle a local file. Only files inside the app folder can be managed.
|
||||||
|
*
|
||||||
|
* Shows the file name, icon (depending on extension), size and time modified.
|
||||||
|
* Also, if managing is enabled it will also show buttons to rename and delete the file.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-local-file',
|
||||||
|
templateUrl: 'core-local-file.html',
|
||||||
|
})
|
||||||
|
export class CoreLocalFileComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() file?: FileEntry; // A fileEntry retrieved using CoreFileProvider.getFile or similar.
|
||||||
|
@Input() manage?: boolean | string; // Whether the user can manage the file (edit and delete).
|
||||||
|
@Input() overrideClick?: boolean | string; // Whether the default item click should be overridden.
|
||||||
|
@Output() onDelete = new EventEmitter<void>(); // Will notify when the file is deleted.
|
||||||
|
@Output() onRename = new EventEmitter<{ file: FileEntry }>(); // Will notify when the file is renamed.
|
||||||
|
@Output() onClick = new EventEmitter<void>(); // Will notify when the file is clicked. Only if overrideClick is true.
|
||||||
|
|
||||||
|
@ViewChild('nameForm') formElement?: ElementRef;
|
||||||
|
|
||||||
|
fileName?: string;
|
||||||
|
fileIcon?: string;
|
||||||
|
fileExtension?: string;
|
||||||
|
size?: string;
|
||||||
|
timemodified?: string;
|
||||||
|
newFileName = '';
|
||||||
|
editMode = false;
|
||||||
|
relativePath?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.manage = CoreUtils.instance.isTrueOrOne(this.manage);
|
||||||
|
|
||||||
|
if (!this.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadFileBasicData(this.file);
|
||||||
|
|
||||||
|
// Get the size and timemodified.
|
||||||
|
const metadata = await CoreFile.instance.getMetadata(this.file);
|
||||||
|
if (metadata.size >= 0) {
|
||||||
|
this.size = CoreTextUtils.instance.bytesToSize(metadata.size, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timemodified = CoreTimeUtils.instance.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the basic data for the file.
|
||||||
|
*/
|
||||||
|
protected loadFileBasicData(file: FileEntry): void {
|
||||||
|
this.fileName = file.name;
|
||||||
|
this.fileIcon = CoreMimetypeUtils.instance.getFileIcon(file.name);
|
||||||
|
this.fileExtension = CoreMimetypeUtils.instance.getFileExtension(file.name);
|
||||||
|
|
||||||
|
// Let's calculate the relative path for the file.
|
||||||
|
this.relativePath = CoreFile.instance.removeBasePath(file.toURL());
|
||||||
|
if (!this.relativePath) {
|
||||||
|
// Didn't find basePath, use fullPath but if the user tries to manage the file it'll probably fail.
|
||||||
|
this.relativePath = file.fullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File clicked.
|
||||||
|
*
|
||||||
|
* @param e Click event.
|
||||||
|
*/
|
||||||
|
async fileClicked(e: Event): Promise<void> {
|
||||||
|
if (this.editMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (CoreUtils.instance.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) {
|
||||||
|
this.onClick.emit();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CoreFileHelper.instance.isOpenableInApp(this.file!)) {
|
||||||
|
try {
|
||||||
|
await CoreFileHelper.instance.showConfirmOpenUnsupportedFile();
|
||||||
|
} catch (error) {
|
||||||
|
return; // Cancelled, stop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreUtils.instance.openFile(this.file!.toURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate the edit mode.
|
||||||
|
*
|
||||||
|
* @param e Click event.
|
||||||
|
*/
|
||||||
|
activateEdit(e: Event): void {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.editMode = true;
|
||||||
|
this.newFileName = this.file!.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename the file.
|
||||||
|
*
|
||||||
|
* @param newName New name.
|
||||||
|
* @param e Click event.
|
||||||
|
*/
|
||||||
|
async changeName(newName: string, e: Event): Promise<void> {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (newName == this.file!.name) {
|
||||||
|
// Name hasn't changed, stop.
|
||||||
|
this.editMode = false;
|
||||||
|
CoreDomUtils.instance.triggerFormCancelledEvent(this.formElement, CoreSites.instance.getCurrentSiteId());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = await CoreDomUtils.instance.showModalLoading();
|
||||||
|
const fileAndDir = CoreFile.instance.getFileAndDirectoryFromPath(this.relativePath!);
|
||||||
|
const newPath = CoreTextUtils.instance.concatenatePaths(fileAndDir.directory, newName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if there's a file with this name.
|
||||||
|
await CoreFile.instance.getFile(newPath);
|
||||||
|
|
||||||
|
// There's a file with this name, show error and stop.
|
||||||
|
CoreDomUtils.instance.showErrorModal('core.errorfileexistssamename', true);
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
// File doesn't exist, move it.
|
||||||
|
const fileEntry = await CoreFile.instance.moveFile(this.relativePath!, newPath);
|
||||||
|
|
||||||
|
CoreDomUtils.instance.triggerFormSubmittedEvent(this.formElement, false, CoreSites.instance.getCurrentSiteId());
|
||||||
|
|
||||||
|
this.editMode = false;
|
||||||
|
this.file = fileEntry;
|
||||||
|
this.loadFileBasicData(this.file);
|
||||||
|
this.onRename.emit({ file: this.file });
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModalDefault(error, 'core.errorrenamefile', true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the file.
|
||||||
|
*
|
||||||
|
* @param e Click event.
|
||||||
|
*/
|
||||||
|
async deleteFile(e: Event): Promise<void> {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let modal: CoreIonLoadingElement | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ask confirmation.
|
||||||
|
await CoreDomUtils.instance.showDeleteConfirm('core.confirmdeletefile');
|
||||||
|
|
||||||
|
modal = await CoreDomUtils.instance.showModalLoading('core.deleting', true);
|
||||||
|
|
||||||
|
await CoreFile.instance.removeFile(this.relativePath!);
|
||||||
|
|
||||||
|
this.onDelete.emit();
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModalDefault(error, 'core.errordeletefile', true);
|
||||||
|
} finally {
|
||||||
|
modal?.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue