Directory
This commit is contained in:
1 parent
9337faab97
commit
e909e7b221
7 files changed
+120
-59
No files matched your search
+14
-25
@@ -181,7 +181,7 @@ namespace Api\File {
|
|||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$token = $this->getParam("token");
|
$token = $this->getParam("token");
|
||||||
$res = $sql->select("UserFile.uid", "valid_until", "token_type",
|
$res = $sql->select("UserFile.uid", "valid_until", "token_type",
|
||||||
"maxFiles", "maxSize", "extensions", "name", "path", "directory", "parent_id as parentId")
|
"maxFiles", "maxSize", "extensions", "name", "path", "directory", "UserFile.parent_id as parentId")
|
||||||
->from("UserFileToken")
|
->from("UserFileToken")
|
||||||
->leftJoin("UserFileTokenFile", "UserFileToken.uid", "token_id")
|
->leftJoin("UserFileTokenFile", "UserFileToken.uid", "token_id")
|
||||||
->leftJoin("UserFile", "UserFile.uid", "file_id")
|
->leftJoin("UserFile", "UserFile.uid", "file_id")
|
||||||
@@ -203,30 +203,12 @@ namespace Api\File {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$this->result["files"] = $this->createFileList($res);
|
$this->result["files"] = $this->createFileList($res);
|
||||||
/*foreach ($res as $row) {
|
|
||||||
if ($row["uid"]) {
|
|
||||||
$file = array(
|
|
||||||
"isDirectory" => $row["directory"],
|
|
||||||
"name" => $row["name"],
|
|
||||||
"uid" => $row["uid"]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($file["isDirectory"]) {
|
|
||||||
$file["items"] = array();
|
|
||||||
} else {
|
|
||||||
$file["size"] = @filesize($row["path"]);
|
|
||||||
$file["mimeType"] = @mime_content_type($row["path"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->result["files"][] = $file;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if ($row["token_type"] === "upload") {
|
if ($row["token_type"] === "upload") {
|
||||||
$this->result["restrictions"] = array(
|
$this->result["restrictions"] = array(
|
||||||
"maxFiles" => $row["maxFiles"] ?? 0,
|
"maxFiles" => $row["maxFiles"] ?? 0,
|
||||||
"maxSize" => $row["maxSize"] ?? 0,
|
"maxSize" => $row["maxSize"] ?? 0,
|
||||||
"extensions" => $row["extensions"] ?? ""
|
"extensions" => $row["extensions"] ?? "",
|
||||||
|
"parentId" => $row["parentId"] ?? 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +423,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
if (!is_null($token)) {
|
if (!is_null($token)) {
|
||||||
|
|
||||||
$res = $sql->select("uid", "token_type", "maxFiles", "maxSize", "extensions", "user_id")
|
$res = $sql->select("uid", "token_type", "maxFiles", "maxSize", "extensions", "user_id", "parent_id")
|
||||||
->from("UserFileToken")
|
->from("UserFileToken")
|
||||||
->where(new Compare("token", $token))
|
->where(new Compare("token", $token))
|
||||||
->where(new CondNull("valid_until"), new Compare("valid_until", $sql->now(), ">="))
|
->where(new CondNull("valid_until"), new Compare("valid_until", $sql->now(), ">="))
|
||||||
@@ -458,6 +440,7 @@ namespace Api\File {
|
|||||||
return $this->createError("Permission denied (token)");
|
return $this->createError("Permission denied (token)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$parentId = $res[0]["parent_id"];
|
||||||
$tokenId = $res[0]["uid"];
|
$tokenId = $res[0]["uid"];
|
||||||
$maxFiles = $res[0]["maxFiles"] ?? 0;
|
$maxFiles = $res[0]["maxFiles"] ?? 0;
|
||||||
$maxSize = $res[0]["maxSize"] ?? 0;
|
$maxSize = $res[0]["maxSize"] ?? 0;
|
||||||
@@ -675,7 +658,8 @@ namespace Api\File {
|
|||||||
"maxFiles" => new Parameter("maxFiles", Parameter::TYPE_INT, true, 1),
|
"maxFiles" => new Parameter("maxFiles", Parameter::TYPE_INT, true, 1),
|
||||||
"maxSize" => new Parameter("maxSize", Parameter::TYPE_INT, true, null),
|
"maxSize" => new Parameter("maxSize", Parameter::TYPE_INT, true, null),
|
||||||
"extensions" => new StringType("extensions", 64, true, null),
|
"extensions" => new StringType("extensions", 64, true, null),
|
||||||
"durability" => new Parameter("durability", Parameter::TYPE_INT, true, 60*24*2)
|
"durability" => new Parameter("durability", Parameter::TYPE_INT, true, 60*24*2),
|
||||||
|
"parentId" => new Parameter("parentId", Parameter::TYPE_INT, true, null)
|
||||||
));
|
));
|
||||||
$this->loginRequired = true;
|
$this->loginRequired = true;
|
||||||
$this->csrfTokenRequired = false;
|
$this->csrfTokenRequired = false;
|
||||||
@@ -690,6 +674,7 @@ namespace Api\File {
|
|||||||
$maxSize = $this->getParam("maxSize");
|
$maxSize = $this->getParam("maxSize");
|
||||||
$extensions = $this->getParam("extensions");
|
$extensions = $this->getParam("extensions");
|
||||||
$durability = $this->getParam("durability");
|
$durability = $this->getParam("durability");
|
||||||
|
$parentId = $this->getParam("parentId");
|
||||||
|
|
||||||
if (!is_null($maxFiles) && $maxFiles < 0) {
|
if (!is_null($maxFiles) && $maxFiles < 0) {
|
||||||
return $this->createError("Invalid number of maximum files.");
|
return $this->createError("Invalid number of maximum files.");
|
||||||
@@ -715,12 +700,16 @@ namespace Api\File {
|
|||||||
$extensions = implode(",", $extensions);
|
$extensions = implode(",", $extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->checkDirectory($parentId)) {
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$token = generateRandomString(36);
|
$token = generateRandomString(36);
|
||||||
$validUntil = $durability == 0 ? null : (new \DateTime())->modify("+$durability MINUTES");
|
$validUntil = $durability == 0 ? null : (new \DateTime())->modify("+$durability MINUTES");
|
||||||
$res = $sql->insert("UserFileToken",
|
$res = $sql->insert("UserFileToken",
|
||||||
array("token", "token_type", "maxSize", "maxFiles", "extensions", "valid_until", "user_id"))
|
array("token", "token_type", "maxSize", "maxFiles", "extensions", "valid_until", "user_id", "parent_id"))
|
||||||
->addRow($token, "upload", $maxSize, $maxFiles, $extensions, $validUntil, $this->user->getId())
|
->addRow($token, "upload", $maxSize, $maxFiles, $extensions, $validUntil, $this->user->getId(), $parentId)
|
||||||
->returning("uid")
|
->returning("uid")
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ class file_api extends DatabaseScript {
|
|||||||
# upload only:
|
# upload only:
|
||||||
->addInt("maxFiles", true)
|
->addInt("maxFiles", true)
|
||||||
->addInt("maxSize", true)
|
->addInt("maxSize", true)
|
||||||
|
->addInt("parent_id", true)
|
||||||
->addString("extensions", 64, true)
|
->addString("extensions", 64, true)
|
||||||
->primaryKey("uid")
|
->primaryKey("uid")
|
||||||
->foreignKey("user_id", "User", "uid", new CascadeStrategy());
|
->foreignKey("user_id", "User", "uid", new CascadeStrategy())
|
||||||
|
->foreignKey("parent_id", "UserFile", "uid", new CascadeStrategy());
|
||||||
|
|
||||||
$queries[] = $sql->createTable("UserFileTokenFile")
|
$queries[] = $sql->createTable("UserFileTokenFile")
|
||||||
->addInt("file_id")
|
->addInt("file_id")
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export default class API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
csrfToken() {
|
csrfToken() {
|
||||||
|
console.log(this.loggedIn);
|
||||||
|
console.log(this.user);
|
||||||
return this.loggedIn ? this.user.session.csrf_token : null;
|
return this.loggedIn ? this.user.session.csrf_token : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,41 +13,46 @@ export function FileBrowser(props) {
|
|||||||
let tokenObj = props.token || { valid: false };
|
let tokenObj = props.token || { valid: false };
|
||||||
let onSelectFile = props.onSelectFile || function() { };
|
let onSelectFile = props.onSelectFile || function() { };
|
||||||
let onFetchFiles = props.onFetchFiles || function() { };
|
let onFetchFiles = props.onFetchFiles || function() { };
|
||||||
|
let directories = props.directories || {};
|
||||||
|
|
||||||
let [popup, setPopup] = useState({ visible: false, directoryName: "" });
|
let [popup, setPopup] = useState({ visible: false, directoryName: "", directory: 0, type: "upload" });
|
||||||
let [alerts, setAlerts] = useState( []);
|
let [alerts, setAlerts] = useState( []);
|
||||||
let [filesToUpload, setFilesToUpload] = useState([]);
|
let [filesToUpload, setFilesToUpload] = useState([]);
|
||||||
|
|
||||||
function svgMiddle(indentation, scale=1.0) {
|
|
||||||
|
function svgMiddle(scale=1.0) {
|
||||||
let width = 48 * scale;
|
let width = 48 * scale;
|
||||||
let height = 64 * scale;
|
let height = 64 * scale;
|
||||||
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*width) + "px" } : {});
|
|
||||||
|
|
||||||
return <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" style={style}>
|
return <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg">
|
||||||
<g>
|
<g>
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={width/2}
|
<line y2="0" x2={width/2} y1={height} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
y1={height} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
<line y2={height/2} x2={width} y1={height/2} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={height/2} x2={width}
|
|
||||||
y1={height/2} x1={width/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
|
||||||
stroke="#000" fill="none"/>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>;
|
</svg>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function svgEnd(indentation, scale=1.0) {
|
function svgEnd(scale=1.0) {
|
||||||
let width = 48 * scale;
|
let width = 48 * scale;
|
||||||
let height = 64 * scale;
|
let height = 64 * scale;
|
||||||
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*width) + "px" } : {});
|
|
||||||
|
|
||||||
return <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" style={style}>
|
return <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg">
|
||||||
<g>
|
<g>
|
||||||
{ /* vertical line */}
|
{ /* vertical line */}
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={width/2}
|
<line y2="0" x2={width/2} y1={height/2} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
y1={height/2} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
|
||||||
{ /* horizontal line */}
|
{ /* horizontal line */}
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={height/2} x2={width}
|
<line y2={height/2} x2={width} y1={height/2} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
y1={height/2} x1={width/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
</g>
|
||||||
stroke="#000" fill="none"/>
|
</svg>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function svgLeft(scale=1.0) {
|
||||||
|
let width = 48 * scale;
|
||||||
|
let height = 64 * scale;
|
||||||
|
return <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" style={{}}>
|
||||||
|
<g>
|
||||||
|
{ /* vertical line */}
|
||||||
|
<line y2="0" x2={width/2} y1={height} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>;
|
</svg>;
|
||||||
}
|
}
|
||||||
@@ -140,12 +145,16 @@ export function FileBrowser(props) {
|
|||||||
let size = (fileElement.isDirectory ? "" : formatSize(fileElement.size));
|
let size = (fileElement.isDirectory ? "" : formatSize(fileElement.size));
|
||||||
let mimeType = (fileElement.isDirectory ? "application/x-directory" : fileElement.mimeType);
|
let mimeType = (fileElement.isDirectory ? "application/x-directory" : fileElement.mimeType);
|
||||||
let token = (tokenObj && tokenObj.valid ? "&token=" + tokenObj.value : "");
|
let token = (tokenObj && tokenObj.valid ? "&token=" + tokenObj.value : "");
|
||||||
let svg = <></>;
|
let svg = [];
|
||||||
if (indentation > 0) {
|
if (indentation > 0) {
|
||||||
|
for (let i = 0; i < indentation - 1; i++) {
|
||||||
|
svg.push(svgLeft(0.75));
|
||||||
|
}
|
||||||
|
|
||||||
if (i === values.length - 1) {
|
if (i === values.length - 1) {
|
||||||
svg = svgEnd(indentation, 0.75);
|
svg.push(svgEnd(0.75));
|
||||||
} else {
|
} else {
|
||||||
svg = svgMiddle(indentation, 0.75);
|
svg.push(svgMiddle(0.75));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +202,13 @@ export function FileBrowser(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let options = [];
|
||||||
|
for (const [uid, dir] of Object.entries(directories)) {
|
||||||
|
options.push(
|
||||||
|
<option key={"option-" + dir} value={uid}>{dir}</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (writePermissions) {
|
if (writePermissions) {
|
||||||
|
|
||||||
for(let i = 0; i < filesToUpload.length; i++) {
|
for(let i = 0; i < filesToUpload.length; i++) {
|
||||||
@@ -255,7 +271,7 @@ export function FileBrowser(props) {
|
|||||||
Download Selected Files ({selectedCount})
|
Download Selected Files ({selectedCount})
|
||||||
</button>
|
</button>
|
||||||
{ api.loggedIn ?
|
{ api.loggedIn ?
|
||||||
<button type={"button"} className={"btn btn-info"} onClick={onPopupOpen}>
|
<button type={"button"} className={"btn btn-info"} onClick={(e) => onPopupOpen("createDirectory")}>
|
||||||
<Icon icon={"plus"} className={"mr-1"}/>
|
<Icon icon={"plus"} className={"mr-1"}/>
|
||||||
Create Directory
|
Create Directory
|
||||||
</button> :
|
</button> :
|
||||||
@@ -265,7 +281,7 @@ export function FileBrowser(props) {
|
|||||||
writePermissions ?
|
writePermissions ?
|
||||||
<>
|
<>
|
||||||
<button type={"button"} className={"btn btn-primary"} disabled={uploadedFiles.length === 0}
|
<button type={"button"} className={"btn btn-primary"} disabled={uploadedFiles.length === 0}
|
||||||
onClick={onUpload}>
|
onClick={(e) => api.loggedIn ? onPopupOpen("upload") : onUpload()}>
|
||||||
<Icon icon={"upload"} className={"mr-1"}/>
|
<Icon icon={"upload"} className={"mr-1"}/>
|
||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
@@ -283,16 +299,25 @@ export function FileBrowser(props) {
|
|||||||
{ alertElements }
|
{ alertElements }
|
||||||
</div>
|
</div>
|
||||||
<Popup title={"Create Directory"} visible={popup.visible} buttons={["Ok","Cancel"]} onClose={onPopupClose} onClick={onPopupButton}>
|
<Popup title={"Create Directory"} visible={popup.visible} buttons={["Ok","Cancel"]} onClose={onPopupClose} onClick={onPopupButton}>
|
||||||
|
<div className={"form-group"}>
|
||||||
|
<label>Destination Directory:</label>
|
||||||
|
<select value={popup.directory} className={"form-control"}
|
||||||
|
onChange={(e) => onPopupChange(e, "directory")}>
|
||||||
|
{ options }
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{ popup.type !== "upload" ?
|
||||||
<div className={"form-group"}>
|
<div className={"form-group"}>
|
||||||
<label>Directory Name</label>
|
<label>Directory Name</label>
|
||||||
<input type={"text"} className={"form-control"} value={popup.directoryName} maxLength={32} placeholder={"Enter name…"}
|
<input type={"text"} className={"form-control"} value={popup.directoryName} maxLength={32} placeholder={"Enter name…"}
|
||||||
onChange={(e) => onPopupChange(e, "directoryName")}/>
|
onChange={(e) => onPopupChange(e, "directoryName")}/>
|
||||||
</div>
|
</div> : <></>
|
||||||
|
}
|
||||||
</Popup>
|
</Popup>
|
||||||
</>;
|
</>;
|
||||||
|
|
||||||
function onPopupOpen() {
|
function onPopupOpen(type) {
|
||||||
setPopup({ ...popup, visible: true });
|
setPopup({ ...popup, visible: true, type: type });
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPopupClose() {
|
function onPopupClose() {
|
||||||
@@ -306,13 +331,18 @@ export function FileBrowser(props) {
|
|||||||
function onPopupButton(btn) {
|
function onPopupButton(btn) {
|
||||||
|
|
||||||
if (btn === "Ok") {
|
if (btn === "Ok") {
|
||||||
api.createDirectory(popup.directoryName, null).then((res) => {
|
let parentId = popup.directory === 0 ? null : popup.directory;
|
||||||
|
if (popup.type === "createDirectory") {
|
||||||
|
api.createDirectory(popup.directoryName, parentId).then((res) => {
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
pushAlert(res, "Error creating directory");
|
pushAlert(res, "Error creating directory");
|
||||||
} else {
|
} else {
|
||||||
fetchFiles();
|
fetchFiles();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} else if (popup.type === "upload") {
|
||||||
|
onUpload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPopupClose();
|
onPopupClose();
|
||||||
@@ -374,7 +404,8 @@ export function FileBrowser(props) {
|
|||||||
|
|
||||||
function onUpload() {
|
function onUpload() {
|
||||||
let token = (api.loggedIn ? null : tokenObj.value);
|
let token = (api.loggedIn ? null : tokenObj.value);
|
||||||
api.upload(filesToUpload, token).then((res) => {
|
let parentId = ((!api.loggedIn || popup.directory === 0) ? null : popup.directory);
|
||||||
|
api.upload(filesToUpload, token, parentId).then((res) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setFilesToUpload([]);
|
setFilesToUpload([]);
|
||||||
fetchFiles();
|
fetchFiles();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export function TokenList(props) {
|
|||||||
|
|
||||||
let api = props.api;
|
let api = props.api;
|
||||||
let selectedFiles = props.selectedFiles || [];
|
let selectedFiles = props.selectedFiles || [];
|
||||||
|
let directories = props.directories || {};
|
||||||
|
|
||||||
let [tokens, setTokens] = useState(null);
|
let [tokens, setTokens] = useState(null);
|
||||||
let [alerts, setAlerts] = useState([]);
|
let [alerts, setAlerts] = useState([]);
|
||||||
@@ -19,7 +20,8 @@ export function TokenList(props) {
|
|||||||
maxSize: 0,
|
maxSize: 0,
|
||||||
extensions: "",
|
extensions: "",
|
||||||
durability: 24 * 60 * 2,
|
durability: 24 * 60 * 2,
|
||||||
visible: false
|
visible: false,
|
||||||
|
directory: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
function fetchTokens() {
|
function fetchTokens() {
|
||||||
@@ -72,6 +74,13 @@ export function TokenList(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let options = [];
|
||||||
|
for (const [uid, dir] of Object.entries(directories)) {
|
||||||
|
options.push(
|
||||||
|
<option key={"option-" + dir} value={uid}>{dir}</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<h4>
|
<h4>
|
||||||
<Icon icon={"sync"} className={"mx-3 clickable small"} onClick={fetchTokens}/>
|
<Icon icon={"sync"} className={"mx-3 clickable small"} onClick={fetchTokens}/>
|
||||||
@@ -128,6 +137,13 @@ export function TokenList(props) {
|
|||||||
</div>
|
</div>
|
||||||
{popup.tokenType === "upload" ?
|
{popup.tokenType === "upload" ?
|
||||||
<>
|
<>
|
||||||
|
<div className={"form-group"}>
|
||||||
|
<label>Destination Directory:</label>
|
||||||
|
<select value={popup.directory} className={"form-control"}
|
||||||
|
onChange={(e) => onPopupChange(e, "directory")}>
|
||||||
|
{ options }
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<b>Upload Restrictions:</b>
|
<b>Upload Restrictions:</b>
|
||||||
<div className={"form-group"}>
|
<div className={"form-group"}>
|
||||||
<label>Max. Files (0 = unlimited):</label>
|
<label>Max. Files (0 = unlimited):</label>
|
||||||
@@ -212,7 +228,8 @@ export function TokenList(props) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (popup.tokenType === "upload") {
|
} else if (popup.tokenType === "upload") {
|
||||||
api.createUploadToken(durability, null, popup.maxFiles, popup.maxSize, popup.extensions).then((res) => {
|
let parentId = popup.directory === 0 ? null : popup.directory;
|
||||||
|
api.createUploadToken(durability, parentId, popup.maxFiles, popup.maxSize, popup.extensions).then((res) => {
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
pushAlert(res, "Error creating token");
|
pushAlert(res, "Error creating token");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -24,6 +24,24 @@ class FileControlPanel extends React.Component {
|
|||||||
this.setState({ ...this.state, files: files });
|
this.setState({ ...this.state, files: files });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDirectories(prefix = "/", items = null) {
|
||||||
|
let directories = { }
|
||||||
|
items = items || this.state.files;
|
||||||
|
|
||||||
|
if (prefix === "/") {
|
||||||
|
directories[0] = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const fileItem of Object.values(items)) {
|
||||||
|
if (fileItem.isDirectory) {
|
||||||
|
let path = prefix + (prefix.length > 1 ? "/" : "") + fileItem.name;
|
||||||
|
directories[fileItem.uid] = path;
|
||||||
|
directories = Object.assign(directories, {...this.getDirectories(path, fileItem.items)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directories;
|
||||||
|
}
|
||||||
|
|
||||||
getSelectedIds(items = null, recursive = true) {
|
getSelectedIds(items = null, recursive = true) {
|
||||||
let ids = [];
|
let ids = [];
|
||||||
items = items || this.state.files;
|
items = items || this.state.files;
|
||||||
@@ -90,6 +108,7 @@ class FileControlPanel extends React.Component {
|
|||||||
this.setState({ ...this.state, validatingToken: true, errorMessage: "" });
|
this.setState({ ...this.state, validatingToken: true, errorMessage: "" });
|
||||||
token = this.state.token.value;
|
token = this.state.token.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.api.validateToken(token).then((res) => {
|
this.api.validateToken(token).then((res) => {
|
||||||
let newState = { ...this.state, loaded: true, validatingToken: false };
|
let newState = { ...this.state, loaded: true, validatingToken: false };
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
@@ -143,10 +162,11 @@ class FileControlPanel extends React.Component {
|
|||||||
return <>Loading… <Icon icon={"spinner"} /></>;
|
return <>Loading… <Icon icon={"spinner"} /></>;
|
||||||
} else if (this.api.loggedIn || this.state.token.valid) {
|
} else if (this.api.loggedIn || this.state.token.valid) {
|
||||||
let selectedIds = this.getSelectedIds();
|
let selectedIds = this.getSelectedIds();
|
||||||
|
let directories = this.getDirectories();
|
||||||
let tokenList = (this.api.loggedIn) ?
|
let tokenList = (this.api.loggedIn) ?
|
||||||
<div className={"row"}>
|
<div className={"row"}>
|
||||||
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
|
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
|
||||||
<TokenList api={this.api} selectedFiles={selectedIds} />
|
<TokenList api={this.api} selectedFiles={selectedIds} directories={directories} />
|
||||||
</div>
|
</div>
|
||||||
</div> :
|
</div> :
|
||||||
<></>;
|
<></>;
|
||||||
@@ -156,7 +176,7 @@ class FileControlPanel extends React.Component {
|
|||||||
<div className={"row"}>
|
<div className={"row"}>
|
||||||
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
|
<div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
|
||||||
<h2>File Control Panel</h2>
|
<h2>File Control Panel</h2>
|
||||||
<FileBrowser files={this.state.files} token={this.state.token} api={this.api}
|
<FileBrowser files={this.state.files} token={this.state.token} api={this.api} directories={directories}
|
||||||
onSelectFile={this.onSelectFile.bind(this)}
|
onSelectFile={this.onSelectFile.bind(this)}
|
||||||
onFetchFiles={this.onFetchFiles.bind(this)}/>
|
onFetchFiles={this.onFetchFiles.bind(this)}/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -299,4 +299,4 @@ e.defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_六月_七月
|
|||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/var r,a,i,o,s;if("undefined"==typeof window||"function"!=typeof MessageChannel){var u=null,l=null,d=function(){if(null!==u)try{var e=t.unstable_now();u(!0,e),u=null}catch(e){throw setTimeout(d,0),e}},c=Date.now();t.unstable_now=function(){return Date.now()-c},r=function(e){null!==u?setTimeout(r,0,e):(u=e,setTimeout(d,0))},a=function(e,t){l=setTimeout(e,t)},i=function(){clearTimeout(l)},o=function(){return!1},s=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,_=window.Date,m=window.setTimeout,h=window.clearTimeout;if("undefined"!=typeof console){var p=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof p&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var y=_.now();t.unstable_now=function(){return _.now()-y}}var M=!1,v=null,g=-1,L=5,k=0;o=function(){return t.unstable_now()>=k},s=function(){},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):L=0<e?Math.floor(1e3/e):5};var b=new MessageChannel,Y=b.port2;b.port1.onmessage=function(){if(null!==v){var e=t.unstable_now();k=e+L;try{v(!0,e)?Y.postMessage(null):(M=!1,v=null)}catch(e){throw Y.postMessage(null),e}}else M=!1},r=function(e){v=e,M||(M=!0,Y.postMessage(null))},a=function(e,n){g=m((function(){e(t.unstable_now())}),n)},i=function(){h(g),g=-1}}function w(e,t){var n=e.length;e.push(t);e:for(;;){var r=n-1>>>1,a=e[r];if(!(void 0!==a&&0<S(a,t)))break e;e[r]=t,e[n]=a,n=r}}function D(e){return void 0===(e=e[0])?null:e}function T(e){var t=e[0];if(void 0!==t){var n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,a=e.length;r<a;){var i=2*(r+1)-1,o=e[i],s=i+1,u=e[s];if(void 0!==o&&0>S(o,n))void 0!==u&&0>S(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[i]=n,r=i);else{if(!(void 0!==u&&0>S(u,n)))break e;e[r]=u,e[s]=n,r=s}}}return t}return null}function S(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var x=[],j=[],E=1,H=null,O=3,P=!1,F=!1,A=!1;function N(e){for(var t=D(j);null!==t;){if(null===t.callback)T(j);else{if(!(t.startTime<=e))break;T(j),t.sortIndex=t.expirationTime,w(x,t)}t=D(j)}}function C(e){if(A=!1,N(e),!F)if(null!==D(x))F=!0,r(W);else{var t=D(j);null!==t&&a(C,t.startTime-e)}}function W(e,n){F=!1,A&&(A=!1,i()),P=!0;var r=O;try{for(N(n),H=D(x);null!==H&&(!(H.expirationTime>n)||e&&!o());){var s=H.callback;if(null!==s){H.callback=null,O=H.priorityLevel;var u=s(H.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?H.callback=u:H===D(x)&&T(x),N(n)}else T(x);H=D(x)}if(null!==H)var l=!0;else{var d=D(j);null!==d&&a(C,d.startTime-n),l=!1}return l}finally{H=null,O=r,P=!1}}function z(e){switch(e){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var R=s;t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){F||P||(F=!0,r(W))},t.unstable_getCurrentPriorityLevel=function(){return O},t.unstable_getFirstCallbackNode=function(){return D(x)},t.unstable_next=function(e){switch(O){case 1:case 2:case 3:var t=3;break;default:t=O}var n=O;O=t;try{return e()}finally{O=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=R,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=O;O=e;try{return t()}finally{O=n}},t.unstable_scheduleCallback=function(e,n,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0<u?s+u:s,o="number"==typeof o.timeout?o.timeout:z(e)}else o=z(e),u=s;return e={id:E++,callback:n,priorityLevel:e,startTime:u,expirationTime:o=u+o,sortIndex:-1},u>s?(e.sortIndex=u,w(j,e),null===D(x)&&e===D(j)&&(A?i():A=!0,a(C,u-s))):(e.sortIndex=o,w(x,e),F||P||(F=!0,r(W))),e},t.unstable_shouldYield=function(){var e=t.unstable_now();N(e);var n=D(x);return n!==H&&null!==H&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime<H.expirationTime||o()},t.unstable_wrapCallback=function(e){var t=O;return function(){var n=O;O=t;try{return e.apply(this,arguments)}finally{O=n}}}},function(e,t,n){"use strict";(function(e){if(n(276),n(473),n(474),e._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");e._babelPolyfill=!0;function t(e,t,n){e[t]||Object.defineProperty(e,t,{writable:!0,configurable:!0,value:n})}t(String.prototype,"padLeft","".padStart),t(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,
Line truncated
|
*/var r,a,i,o,s;if("undefined"==typeof window||"function"!=typeof MessageChannel){var u=null,l=null,d=function(){if(null!==u)try{var e=t.unstable_now();u(!0,e),u=null}catch(e){throw setTimeout(d,0),e}},c=Date.now();t.unstable_now=function(){return Date.now()-c},r=function(e){null!==u?setTimeout(r,0,e):(u=e,setTimeout(d,0))},a=function(e,t){l=setTimeout(e,t)},i=function(){clearTimeout(l)},o=function(){return!1},s=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,_=window.Date,m=window.setTimeout,h=window.clearTimeout;if("undefined"!=typeof console){var p=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof p&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var y=_.now();t.unstable_now=function(){return _.now()-y}}var M=!1,v=null,g=-1,L=5,k=0;o=function(){return t.unstable_now()>=k},s=function(){},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):L=0<e?Math.floor(1e3/e):5};var b=new MessageChannel,Y=b.port2;b.port1.onmessage=function(){if(null!==v){var e=t.unstable_now();k=e+L;try{v(!0,e)?Y.postMessage(null):(M=!1,v=null)}catch(e){throw Y.postMessage(null),e}}else M=!1},r=function(e){v=e,M||(M=!0,Y.postMessage(null))},a=function(e,n){g=m((function(){e(t.unstable_now())}),n)},i=function(){h(g),g=-1}}function w(e,t){var n=e.length;e.push(t);e:for(;;){var r=n-1>>>1,a=e[r];if(!(void 0!==a&&0<S(a,t)))break e;e[r]=t,e[n]=a,n=r}}function D(e){return void 0===(e=e[0])?null:e}function T(e){var t=e[0];if(void 0!==t){var n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,a=e.length;r<a;){var i=2*(r+1)-1,o=e[i],s=i+1,u=e[s];if(void 0!==o&&0>S(o,n))void 0!==u&&0>S(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[i]=n,r=i);else{if(!(void 0!==u&&0>S(u,n)))break e;e[r]=u,e[s]=n,r=s}}}return t}return null}function S(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var x=[],j=[],E=1,H=null,O=3,P=!1,F=!1,A=!1;function N(e){for(var t=D(j);null!==t;){if(null===t.callback)T(j);else{if(!(t.startTime<=e))break;T(j),t.sortIndex=t.expirationTime,w(x,t)}t=D(j)}}function C(e){if(A=!1,N(e),!F)if(null!==D(x))F=!0,r(W);else{var t=D(j);null!==t&&a(C,t.startTime-e)}}function W(e,n){F=!1,A&&(A=!1,i()),P=!0;var r=O;try{for(N(n),H=D(x);null!==H&&(!(H.expirationTime>n)||e&&!o());){var s=H.callback;if(null!==s){H.callback=null,O=H.priorityLevel;var u=s(H.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?H.callback=u:H===D(x)&&T(x),N(n)}else T(x);H=D(x)}if(null!==H)var l=!0;else{var d=D(j);null!==d&&a(C,d.startTime-n),l=!1}return l}finally{H=null,O=r,P=!1}}function z(e){switch(e){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var R=s;t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){F||P||(F=!0,r(W))},t.unstable_getCurrentPriorityLevel=function(){return O},t.unstable_getFirstCallbackNode=function(){return D(x)},t.unstable_next=function(e){switch(O){case 1:case 2:case 3:var t=3;break;default:t=O}var n=O;O=t;try{return e()}finally{O=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=R,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=O;O=e;try{return t()}finally{O=n}},t.unstable_scheduleCallback=function(e,n,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0<u?s+u:s,o="number"==typeof o.timeout?o.timeout:z(e)}else o=z(e),u=s;return e={id:E++,callback:n,priorityLevel:e,startTime:u,expirationTime:o=u+o,sortIndex:-1},u>s?(e.sortIndex=u,w(j,e),null===D(x)&&e===D(j)&&(A?i():A=!0,a(C,u-s))):(e.sortIndex=o,w(x,e),F||P||(F=!0,r(W))),e},t.unstable_shouldYield=function(){var e=t.unstable_now();N(e);var n=D(x);return n!==H&&null!==H&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime<H.expirationTime||o()},t.unstable_wrapCallback=function(e){var t=O;return function(){var n=O;O=t;try{return e.apply(this,arguments)}finally{O=n}}}},function(e,t,n){"use strict";(function(e){if(n(276),n(473),n(474),e._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");e._babelPolyfill=!0;function t(e,t,n){e[t]||Object.defineProperty(e,t,{writable:!0,configurable:!0,value:n})}t(String.prototype,"padLeft","".padStart),t(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,
Line truncated
|
||||||
Reference in new issue
Block a user