ZipStream
This commit is contained in:
5
fileControlPanel/package-lock.json
generated
5
fileControlPanel/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
fileControlPanel/src/elements/popup.js
Normal file
37
fileControlPanel/src/elements/popup.js
Normal 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">×</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>;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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"}>
|
||||
|
||||
Reference in New Issue
Block a user