mailtrain/client/src/lib/files.js
Tomas Bures b5cdf57f72 Files can be added to templates and managed in a dedicated "Files" view.
Mosaico integration in progress. The files seem to be working for Mosaico.
2018-03-24 23:55:50 +01:00

155 lines
5.5 KiB
JavaScript

'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import {translate} from "react-i18next";
import {requiresAuthenticatedUser} from "./page";
import {withErrorHandling} from "./error-handling";
import {Table} from "./table";
import Dropzone from "react-dropzone";
import {ModalDialog} from "./modals";
import {Icon} from "./bootstrap-components";
import axios from './axios';
import styles from "./styles.scss";
import {withPageHelpers} from "./page";
@translate()
@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,
entity: PropTypes.object,
entityTypeId: PropTypes.string,
usePublicDownloadUrls: PropTypes.bool
}
static defaultProps = {
usePublicDownloadUrls: true
}
getFilesUploadedMessage(response){
const t = this.props.t;
const details = [];
if (response.data.added) {
details.push(t('{{count}} file(s) added', {count: response.data.added}));
}
if (response.data.replaced) {
details.push(t('{{count}} file(s) replaced', {count: response.data.replaced}));
}
if (response.data.ignored) {
details.push(t('{{count}} file(s) ignored', {count: response.data.ignored}));
}
const detailsMessage = details ? ' (' + details.join(', ') + ')' : '';
return t('{{count}} file(s) uploaded', {count: response.data.uploaded}) + detailsMessage;
}
onDrop(files){
const t = this.props.t;
if (files.length > 0) {
this.setFlashMessage('info', t('Uploading {{count}} file(s)', files.length));
const data = new FormData();
for (const file of files) {
data.append('files[]', file)
}
axios.post(`/rest/files/${this.props.entityTypeId}/${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('File upload failed: ') + res.message));
}
else{
this.setFlashMessage('info', t('No files to upload'));
}
}
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('Deleting file ...'));
await axios.delete(`/rest/files/${this.props.entityTypeId}/${fileToDeleteId}`);
this.filesTable.refresh();
this.setFlashMessage('info', t('File deleted'));
} catch (err) {
this.filesTable.refresh();
this.setFlashMessage('danger', t('Delete file failed: ') + err.message);
}
}
render() {
const t = this.props.t;
const columns = [
{ data: 1, title: "Name" },
{ data: 3, title: "Size" },
{
actions: data => {
let downloadUrl;
if (this.props.usePublicDownloadUrls) {
downloadUrl =`/files/${this.props.entityTypeId}/${this.props.entity.id}/${data[2]}`;
} else {
downloadUrl =`/rest/files/${this.props.entityTypeId}/${data[0]}`;
}
const actions = [
{
label: <Icon icon="download" title={t('Download')}/>,
href: downloadUrl
},
{
label: <Icon icon="remove" title={t('Delete')}/>,
action: () => this.deleteFile(data[0], data[1])
}
];
return actions;
}
}
];
return (
<div>
<ModalDialog
hidden={this.state.fileToDeleteId === null}
title={t('Confirm file deletion')}
onCloseAsync={::this.hideDeleteFile}
buttons={[
{ label: t('No'), className: 'btn-primary', onClickAsync: ::this.hideDeleteFile },
{ label: t('Yes'), className: 'btn-danger', onClickAsync: ::this.performDeleteFile }
]}>
{t('Are you sure you want to delete file "{{name}}"?', {name: this.state.fileToDeleteName})}
</ModalDialog>
<Dropzone onDrop={::this.onDrop} className={styles.dropZone} activeClassName="dropZoneActive">
{state => state.isDragActive ? t('Drop {{count}} file(s)', {count:state.draggedFiles.length}) : t('Drop files here')}
</Dropzone>
<Table withHeader ref={node => this.filesTable = node} dataUrl={`/rest/files-table/${this.props.entityTypeId}/${this.props.entity.id}`} columns={columns} />
</div>
);
}
}