mailtrain/client/src/lib/files.js

176 lines
6.5 KiB
JavaScript

'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import {withTranslation} from './i18n';
import {requiresAuthenticatedUser, Title, withPageHelpers} from "./page";
import {withErrorHandling} from "./error-handling";
import {Table} from "./table";
import Dropzone from "react-dropzone";
import {Icon, ModalDialog} from "./bootstrap-components";
import axios from './axios';
import styles from "./styles.scss";
import {getPublicUrl, getUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Files extends Component {
constructor(props) {
super(props);
this.state = {
fileToDeleteName: null,
fileToDeleteId: null
};
const t = props.t;
}
static propTypes = {
title: PropTypes.string,
help: PropTypes.string,
entity: PropTypes.object.isRequired,
entityTypeId: PropTypes.string.isRequired,
entitySubTypeId: PropTypes.string.isRequired,
managePermission: PropTypes.string.isRequired,
usePublicDownloadUrls: PropTypes.bool
}
static defaultProps = {
usePublicDownloadUrls: true
}
getFilesUploadedMessage(response){
const t = this.props.t;
const details = [];
if (response.data.added) {
details.push(t('countFileAdded', {count: response.data.added}));
}
if (response.data.replaced) {
details.push(t('countFileReplaced', {count: response.data.replaced}));
}
if (response.data.ignored) {
details.push(t('countFileIgnored', {count: response.data.ignored}));
}
const detailsMessage = details ? ' (' + details.join(', ') + ')' : '';
return t('countFileUploaded', {count: response.data.uploaded}) + detailsMessage;
}
onDrop(files){
const t = this.props.t;
if (files.length > 0) {
this.setFlashMessage('info', t('uploadingCountFile', {count: files.length}));
const data = new FormData();
for (const file of files) {
data.append('files[]', file)
}
axios.post(getUrl(`rest/files/${this.props.entityTypeId}/${this.props.entitySubTypeId}/${this.props.entity.id}`), data)
.then(res => {
this.filesTable.refresh();
const message = this.getFilesUploadedMessage(res);
this.setFlashMessage('info', message);
})
.catch(res => this.setFlashMessage('danger', t('fileUploadFailed') + ' ' + res.message));
}
else{
this.setFlashMessage('info', t('noFilesToUpload'));
}
}
deleteFile(fileId, fileName){
this.setState({fileToDeleteId: fileId, fileToDeleteName: fileName})
}
async hideDeleteFile(){
this.setState({fileToDeleteId: null, fileToDeleteName: null})
}
async performDeleteFile() {
const t = this.props.t;
const fileToDeleteId = this.state.fileToDeleteId;
await this.hideDeleteFile();
try {
this.setFlashMessage('info', t('deletingFile'));
await axios.delete(getUrl(`rest/files/${this.props.entityTypeId}/${this.props.entitySubTypeId}/${fileToDeleteId}`));
this.filesTable.refresh();
this.setFlashMessage('info', t('fileDeleted'));
} catch (err) {
this.filesTable.refresh();
this.setFlashMessage('danger', t('deleteFileFailed') + ' ' + err.message);
}
}
render() {
const t = this.props.t;
const columns = [
{ data: 1, title: t('name') },
{ data: 3, title: t('size') },
{
actions: data => {
const actions = [];
let downloadUrl;
if (this.props.usePublicDownloadUrls) {
downloadUrl = getPublicUrl(`files/${this.props.entityTypeId}/${this.props.entitySubTypeId}/${this.props.entity.id}/${data[2]}`);
} else {
downloadUrl = getUrl(`rest/files/${this.props.entityTypeId}/${this.props.entitySubTypeId}/${data[0]}`);
}
actions.push({
label: <Icon icon="download" title={t('download')}/>,
href: downloadUrl
});
if (this.props.entity.permissions.includes(this.props.managePermission)) {
actions.push({
label: <Icon icon="trash-alt" title={t('delete')}/>,
action: () => this.deleteFile(data[0], data[1])
});
}
return actions;
}
}
];
return (
<div>
<ModalDialog
hidden={this.state.fileToDeleteId === null}
title={t('confirmFileDeletion')}
onCloseAsync={::this.hideDeleteFile}
buttons={[
{ label: t('no'), className: 'btn-primary', onClickAsync: ::this.hideDeleteFile },
{ label: t('yes'), className: 'btn-danger', onClickAsync: ::this.performDeleteFile }
]}>
{t('areYouSureYouWantToDeleteTheFile?', {name: this.state.fileToDeleteName})}
</ModalDialog>
{this.props.title && <Title>{this.props.title}</Title>}
{this.props.help && <p>{this.props.help}</p>}
{
this.props.entity.permissions.includes(this.props.managePermission) &&
<Dropzone onDrop={::this.onDrop}>
{({getRootProps, getInputProps, isDragActive, draggedFiles}) => (
<div {...getRootProps()} className={styles.dropZone + (isDragActive ? ' ' + styles.dropZoneActive : '')}>
<input {...getInputProps()} />
<p>{isDragActive ? t('dropCountFile', {count: draggedFiles.length}) : t('dropFilesHere')}</p>
</div>
)}
</Dropzone>
}
<Table withHeader ref={node => this.filesTable = node} dataUrl={`rest/files-table/${this.props.entityTypeId}/${this.props.entitySubTypeId}/${this.props.entity.id}`} columns={columns} />
</div>
);
}
}