ZipStream

This commit is contained in:
2021-01-13 01:36:04 +01:00
parent 8da94bafdf
commit e19b4d1a46
15 changed files with 614 additions and 43 deletions

View File

@@ -7751,6 +7751,11 @@
"object.assign": "^4.1.0"
}
},
"keymaster": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/keymaster/-/keymaster-1.6.2.tgz",
"integrity": "sha1-4a5U0OqUiPn2C2a2aPAumhlGxus="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",

View File

@@ -285,7 +285,8 @@ export class FileBrowser extends React.Component {
</tbody>
</table>
<div className={"file-control-buttons"}>
<button type={"button"} className={"btn btn-success"} disabled={selectedCount === 0}>
<button type={"button"} className={"btn btn-success"} disabled={selectedCount === 0}
onClick={() => this.onDownload(selectedIds)}>
<Icon icon={"download"} className={"mr-1"}/>
Download Selected Files ({selectedCount})
</button>
@@ -305,7 +306,7 @@ export class FileBrowser extends React.Component {
Upload
</button>
<button type={"button"} className={"btn btn-danger"} disabled={selectedCount === 0}
onClick={(e) => this.deleteFiles(selectedIds)}>
onClick={() => this.deleteFiles(selectedIds)}>
<Icon icon={"trash"} className={"mr-1"}/>
Delete Selected Files ({selectedCount})
</button>
@@ -354,14 +355,16 @@ export class FileBrowser extends React.Component {
}
deleteFiles(selectedIds) {
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);
}
});
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() {
@@ -375,4 +378,31 @@ export class FileBrowser extends React.Component {
}
});
}
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();
});
});
}
}
}

View File

@@ -0,0 +1,37 @@
import React from 'react';
class Popup extends React.Component {
constructor(props) {
super(props);
this.state = {
title: props.title || "Title",
content: props.content || "Content",
buttons: props.buttons || ["Ok", "Cancel"]
}
}
render() {
return <div className="modal" tabIndex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">{this.state.title}</h5>
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<p>Modal body text goes here.</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>;
}
}

View File

@@ -1,6 +1,7 @@
import * as React from "react";
import Icon from "./icon";
import moment from "moment";
import Popup from "react-popup";
export class TokenList extends React.Component {
@@ -23,12 +24,15 @@ export class TokenList extends React.Component {
});
} else {
for (const token of this.state.tokens) {
const revoked = moment(token.valid_until).isSameOrBefore(new Date());
const validUntil = token.valid_until;
const revoked = validUntil !== null && moment(validUntil).isSameOrBefore(new Date());
const timeStr = (validUntil === null ? "Forever" : moment(validUntil).format("Do MMM YYYY, HH:mm"));
rows.push(
<tr key={"token-" + token.uid} className={revoked ? "token-revoked" : ""}>
<td>{token.token}</td>
<td>{token.type}</td>
<td>{moment(token.valid_until).format("Do MMM YYYY, HH:mm")}</td>
<td>{timeStr}</td>
<td>
<Icon icon={"times"} className={"clickable text-" + (revoked ? "secondary" : "danger")}
onClick={() => (revoked ? null : this.onRevokeToken(token.token) )}
@@ -65,7 +69,7 @@ export class TokenList extends React.Component {
</tbody>
</table>
<div>
<button type={"button"} className={"btn btn-success m-2"}>
<button type={"button"} className={"btn btn-success m-2"} onClick={this.onCreateToken.bind(this)}>
<Icon icon={"plus"} className={"mr-1"}/>
Create Token
</button>
@@ -94,4 +98,8 @@ export class TokenList extends React.Component {
}
});
}
onCreateToken() {
Popup.alert('I am alert, nice to meet you');
}
}

View File

@@ -86,15 +86,17 @@ class FileControlPanel extends React.Component {
</div> :
<></>;
return <div className={"container mt-4"}>
<div className={"row"}>
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
<h2>File Control Panel</h2>
<FileBrowser files={this.state.files} token={this.state.token} api={this.api} />
return <>
<div className={"container mt-4"}>
<div className={"row"}>
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
<h2>File Control Panel</h2>
<FileBrowser files={this.state.files} token={this.state.token} api={this.api} />
</div>
</div>
{ tokenList }
</div>
{ tokenList }
</div>;
</>;
} else {
return <div className={"container mt-4"}>
<div className={"row"}>