// (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 { Translate } from '@singletons'; import { CoreNetwork } from '@services/network'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper'; import { CoreFileEntry } from '@services/file-helper'; import { CoreCourses } from '@features/courses/services/courses'; import { CoreUtils } from '@services/utils/utils'; /** * 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. * * * */ @Component({ selector: 'core-attachments', templateUrl: 'core-attachments.html', styleUrls: ['attachments.scss'], }) export class CoreAttachmentsComponent implements OnInit { @Input() files?: CoreFileEntry[]; // List of attachments. New attachments will be added to this array. @Input() maxSize?: number; // Max size. -1 means unlimited, 0 means course/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. @Input() courseId?: number; // Course ID. maxSizeReadable?: string; maxSubmissionsReadable?: string; unlimitedFiles?: boolean; fileTypes?: CoreFileUploaderTypeList; loaded = false; /** * @inheritdoc */ async ngOnInit(): Promise { this.files = this.files || []; this.maxSize = this.maxSize !== null ? Number(this.maxSize) : NaN; if (this.maxSize === 0) { await this.getMaxSizeOfArea(); } else if (this.maxSize > 0) { this.maxSizeReadable = CoreTextUtils.bytesToSize(this.maxSize, 2); } else if (this.maxSize === -1) { this.maxSizeReadable = Translate.instant('core.unlimited'); } else { this.maxSizeReadable = Translate.instant('core.unknown'); } if (this.maxSubmissions === undefined || this.maxSubmissions < 0) { this.maxSubmissionsReadable = this.maxSubmissions === undefined ? Translate.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.prepareFiletypeList(this.acceptedTypes); } this.loaded = true; } /** * Get max size of the area. * * @return Promise resolved when done. */ protected async getMaxSizeOfArea(): Promise { if (this.courseId) { // Check course max size. const course = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId)); if (course?.maxbytes) { this.maxSize = course.maxbytes; this.maxSizeReadable = CoreTextUtils.bytesToSize(this.maxSize, 2); return; } } // Check user max size. const currentSite = CoreSites.getCurrentSite(); const siteInfo = currentSite?.getInfo(); if (siteInfo?.usermaxuploadfilesize) { this.maxSize = siteInfo.usermaxuploadfilesize; this.maxSizeReadable = CoreTextUtils.bytesToSize(this.maxSize, 2); } else { this.maxSizeReadable = Translate.instant('core.unknown'); } } /** * Add a new attachment. */ async add(): Promise { const allowOffline = !!this.allowOffline && this.allowOffline !== 'false'; if (!allowOffline && !CoreNetwork.isOnline()) { CoreDomUtils.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true); return; } const mimetypes = this.fileTypes && this.fileTypes.mimetypes; try { const result = await CoreFileUploaderHelper.selectFile(this.maxSize, allowOffline, undefined, mimetypes); this.files?.push(result); } catch (error) { CoreDomUtils.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 { if (askConfirm) { try { await CoreDomUtils.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; } }