import * as React from "react";
import "./file-browser.css";
import Dropzone from "react-dropzone";
import Icon from "./icon";
export class FileBrowser extends React.Component {
constructor(props) {
super(props);
this.state = {
api: props.api,
files: props.files,
token: props.token,
filesToUpload: [],
alerts: []
}
}
svgMiddle(indentation, scale=1.0) {
let width = 48 * scale;
let height = 64 * scale;
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*width) + "px" } : {});
return
;
}
svgEnd(indentation, scale=1.0) {
let width = 48 * scale;
let height = 64 * scale;
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*width) + "px" } : {});
return
{ /* vertical line */}
{ /* horizontal line */}
;
}
createFileIcon(mimeType, size=2) {
let icon = "";
if (mimeType !== null) {
mimeType = mimeType.toLowerCase().trim();
let types = ["image", "text", "audio", "video"];
let languages = ["php", "java", "python", "cpp"];
let archives = ["zip", "tar", "archive"];
let [mainType, subType] = mimeType.split("/");
if (mainType === "text" && languages.find(a => subType.includes(a))) {
icon = "code";
} else if (mainType === "application" && archives.find(a => subType.includes(a))) {
icon = "archive";
} else if (mainType === "application" && subType === "pdf") {
icon = "pdf";
} else if (mainType === "application" && (subType.indexOf("powerpoint") > -1 || subType.indexOf("presentation") > -1)) {
icon = "powerpoint";
} else if (mainType === "application" && (subType.indexOf("word") > -1 || subType.indexOf("opendocument") > -1)) {
icon = "word";
} else if (mainType === "application" && (subType.indexOf("excel") > -1 || subType.indexOf("sheet") > -1)) {
icon = "excel";
} else if (mainType === "application" && subType.indexOf("directory") > -1) {
icon = "folder";
} else if (types.indexOf(mainType) > -1) {
if (mainType === "text") {
icon = "alt";
} else {
icon = mainType;
}
}
}
if (icon !== "folder") {
icon = "file" + (icon ? ("-" + icon) : icon);
}
return
}
formatSize(size) {
const suffixes = ["B","KiB","MiB","GiB","TiB"];
let i = 0;
for (; i < suffixes.length && size >= 1024; i++) {
size /= 1024.0;
}
return size.toFixed(1) + " " + suffixes[i];
}
canUpload() {
return this.state.api.loggedIn || (this.state.token.valid && this.state.token.type === "upload");
}
onAddUploadFiles(acceptedFiles) {
let files = this.state.filesToUpload.slice();
files.push(...acceptedFiles);
this.setState({ ...this.state, filesToUpload: files });
}
getSelectedIds(items = null, recursive = true) {
let ids = [];
items = items || this.state.files;
for (const fileItem of Object.values(items)) {
if (fileItem.selected) {
ids.push(fileItem.uid);
}
if (recursive && fileItem.isDirectory) {
ids.push(...this.getSelectedIds(fileItem.items));
}
}
return ids;
}
onSelectAll(selected, items) {
for (const fileElement of Object.values(items)) {
fileElement.selected = selected;
if (fileElement.isDirectory) {
this.onSelectAll(selected, fileElement.items);
}
}
}
onSelectFile(e, uid, items=null) {
let found = false;
let updatedFiles = (items === null) ? {...this.state.files} : items;
if (updatedFiles.hasOwnProperty(uid)) {
let fileElement = updatedFiles[uid];
found = true;
fileElement.selected = e.target.checked;
if (fileElement.isDirectory) {
this.onSelectAll(fileElement.selected, fileElement.items);
}
} else {
for (const fileElement of Object.values(updatedFiles)) {
if (fileElement.isDirectory) {
if (this.onSelectFile(e, uid, fileElement.items)) {
if (!e.target.checked) {
fileElement.selected = false;
} else if (this.getSelectedIds(fileElement.items, false).length === Object.values(fileElement.items).length) {
fileElement.selected = true;
}
found = true;
break;
}
}
}
}
if (items === null) {
this.setState({
...this.state,
files: updatedFiles
});
}
return found;
}
createFileList(elements, indentation=0) {
let rows = [];
let i = 0;
const values = Object.values(elements);
for (const fileElement of values) {
let name = fileElement.name;
let uid = fileElement.uid;
let type = (fileElement.isDirectory ? "Directory" : fileElement.mimeType);
let size = (fileElement.isDirectory ? "" : this.formatSize(fileElement.size));
let mimeType = (fileElement.isDirectory ? "application/x-directory" : fileElement.mimeType);
let token = (this.state.token && this.state.token.valid ? "&token=" + this.state.token.value : "");
let svg = <>>;
if (indentation > 0) {
if (i === values.length - 1) {
svg = this.svgEnd(indentation, 0.75);
} else {
svg = this.svgMiddle(indentation, 0.75);
}
}
rows.push(
{ svg }
{ this.createFileIcon(mimeType) }
{fileElement.isDirectory ? name :
{name}
}
{type}
{size}
this.onSelectFile(e, uid)}
/>
);
if (fileElement.isDirectory) {
rows.push(...this.createFileList(fileElement.items, indentation + 1));
}
i++;
}
return rows;
}
render() {
let rows = this.createFileList(this.state.files);
let selectedIds = this.getSelectedIds();
let selectedCount = selectedIds.length;
let uploadZone = <>>;
let writePermissions = this.canUpload();
let uploadedFiles = [];
let alerts = [];
let i = 0;
for (const alert of this.state.alerts) {
alerts.push(
{ alert.text }
);
}
if (writePermissions) {
for(let i = 0; i < this.state.filesToUpload.length; i++) {
const file = this.state.filesToUpload[i];
uploadedFiles.push(
{ this.createFileIcon(file.type, 3) }
{file.name}
this.onRemoveUploadedFile(e, i)}/>
);
}
uploadZone = <>
{({getRootProps, getInputProps}) => (
Drag 'n' drop some files here, or click to select files
{ uploadedFiles.length === 0 ?
:
{uploadedFiles}
}
)}
>;
}
return <>
File Browser
this.onDownload(selectedIds)}>
Download Selected Files ({selectedCount})
{ this.state.api.loggedIn ?
Create Directory
:
<>>
}
{
writePermissions ?
<>
Upload
this.deleteFiles(selectedIds)}>
Delete Selected Files ({selectedCount})
>
: <>>
}
{ uploadZone }
{ alerts }
>;
}
fetchFiles() {
if (this.state.token.valid) {
this.state.api.validateToken(this.state.token.value).then((res) => {
if (res) {
this.setState({ ...this.state, files: res.files });
} else {
this.pushAlert(res);
}
});
} else if (this.state.api.loggedIn) {
this.state.api.listFiles().then((res) => {
if (res) {
this.setState({ ...this.state, files: res.files });
} else {
this.pushAlert(res);
}
});
}
}
onRemoveUploadedFile(e, i) {
e.stopPropagation();
let files = this.state.filesToUpload.slice();
files.splice(i, 1);
this.setState({ ...this.state, filesToUpload: files });
}
pushAlert(res) {
let newAlerts = this.state.alerts.slice();
newAlerts.push({ type: "danger", text: res.msg });
this.setState({ ...this.state, alerts: newAlerts });
}
deleteFiles(selectedIds) {
if (selectedIds && selectedIds.length > 0) {
let token = (this.state.api.loggedIn ? null : this.state.token.value);
this.state.api.delete(selectedIds, token).then((res) => {
if (res.success) {
this.fetchFiles();
} else {
this.pushAlert(res);
}
});
}
}
onUpload() {
let token = (this.state.api.loggedIn ? null : this.state.token.value);
this.state.api.upload(this.state.filesToUpload, token).then((res) => {
if (res.success) {
this.setState({ ...this.state, filesToUpload: [] })
this.fetchFiles();
} else {
this.pushAlert(res);
}
});
}
onDownload(selectedIds) {
if (selectedIds && selectedIds.length > 0) {
let token = (this.state.api.loggedIn ? "" : "&token=" + this.state.token.value);
let ids = selectedIds.map(id => "id[]=" + id).join("&");
let downloadUrl = "/api/file/download?" + ids + token;
fetch(downloadUrl)
.then(response => {
let header = response.headers.get("Content-Disposition") || "";
let fileNameFields = header.split(";").filter(c => c.trim().toLowerCase().startsWith("filename="));
let fileName = null;
if (fileNameFields.length > 0) {
fileName = fileNameFields[0].trim().substr("filename=".length);
} else {
fileName = null;
}
response.blob().then(blob => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
if (fileName !== null) a.download = fileName;
a.click();
});
});
}
}
}