File Frontend + Array type Backend
This commit is contained in:
parent
bf4301f4be
commit
9e02f64275
@ -3,6 +3,9 @@
|
|||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
use Driver\SQL\Condition\Compare;
|
use Driver\SQL\Condition\Compare;
|
||||||
|
use Driver\SQL\Condition\CondIn;
|
||||||
|
use Driver\SQL\Query\Query;
|
||||||
|
use Driver\SQL\SQL;
|
||||||
|
|
||||||
abstract class FileAPI extends Request {
|
abstract class FileAPI extends Request {
|
||||||
|
|
||||||
@ -62,12 +65,66 @@ namespace Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isInDirectory($fileId, $directoryId) {
|
protected function &findDirectory(&$files, $id) {
|
||||||
|
|
||||||
$sql = $this->user->getSQL();
|
if ($id !== null) {
|
||||||
|
$id = (string)$id;
|
||||||
|
if (isset($files[$id])) {
|
||||||
|
return $files[$id]["items"];
|
||||||
|
} else {
|
||||||
|
foreach ($files as &$dir) {
|
||||||
|
if ($dir["isDirectory"]) {
|
||||||
|
$target =& $this->findDirectory($dir["items"], $id);
|
||||||
|
if ($target !== $dir["items"]) {
|
||||||
|
return $target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createFileList($res) {
|
||||||
|
$files = array();
|
||||||
|
foreach ($res as $row) {
|
||||||
|
if ($row["uid"] === null) continue;
|
||||||
|
$fileId = (string)$row["uid"];
|
||||||
|
$parentId = $row["parentId"];
|
||||||
|
$fileName = $row["name"];
|
||||||
|
$isDirectory = $row["directory"];
|
||||||
|
$fileElement = array("uid" => $fileId, "name" => $fileName, "isDirectory" => $isDirectory);
|
||||||
|
if ($isDirectory) {
|
||||||
|
$fileElement["items"] = array();
|
||||||
|
} else {
|
||||||
|
$fileElement["size"] = @filesize($row["path"]);
|
||||||
|
$fileElement["mimeType"] = @mime_content_type($row["path"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dir =& $this->findDirectory($files, $parentId);
|
||||||
|
$dir[$fileId] = $fileElement;
|
||||||
|
unset($dir);
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function filterFiles(SQL $sql, $query, $id, $token = null) {
|
||||||
|
if (is_array($id)) {
|
||||||
|
$query->where(new CondIn("UserFile.uid", $id));
|
||||||
|
} else {
|
||||||
|
$query->where(new Compare("UserFile.uid", $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($token)) {
|
||||||
|
$query->where(new Compare("user_id", $this->user->getId()));
|
||||||
|
} else {
|
||||||
|
$query->innerJoin("UserFileTokenFile", "UserFile.uid", "file_id")
|
||||||
|
->innerJoin("UserFileToken", "UserFileToken.uid", "token_id")
|
||||||
|
->where(new Compare("token", $token))
|
||||||
|
->where(new Compare("valid_until", $sql->now(), ">="));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +132,7 @@ namespace Api {
|
|||||||
namespace Api\File {
|
namespace Api\File {
|
||||||
|
|
||||||
use Api\FileAPI;
|
use Api\FileAPI;
|
||||||
|
use Api\Parameter\ArrayType;
|
||||||
use Api\Parameter\Parameter;
|
use Api\Parameter\Parameter;
|
||||||
use Api\Parameter\StringType;
|
use Api\Parameter\StringType;
|
||||||
use Driver\SQL\Condition\Compare;
|
use Driver\SQL\Condition\Compare;
|
||||||
@ -97,7 +155,8 @@ 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", "maxFiles", "maxSize", "extensions", "name", "path", "directory")
|
$res = $sql->select("UserFile.uid", "valid_until", "token_type",
|
||||||
|
"maxFiles", "maxSize", "extensions", "name", "path", "directory", "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")
|
||||||
@ -118,8 +177,8 @@ namespace Api\File {
|
|||||||
"valid_until" => $row["valid_until"]
|
"valid_until" => $row["valid_until"]
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->result["files"] = array();
|
$this->result["files"] = $this->createFileList($res);
|
||||||
foreach ($res as $row) {
|
/*foreach ($res as $row) {
|
||||||
if ($row["uid"]) {
|
if ($row["uid"]) {
|
||||||
$file = array(
|
$file = array(
|
||||||
"isDirectory" => $row["directory"],
|
"isDirectory" => $row["directory"],
|
||||||
@ -136,7 +195,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
$this->result["files"][] = $file;
|
$this->result["files"][] = $file;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if ($row["token_type"] === "upload") {
|
if ($row["token_type"] === "upload") {
|
||||||
$this->result["restrictions"] = array(
|
$this->result["restrictions"] = array(
|
||||||
@ -169,7 +228,7 @@ namespace Api\File {
|
|||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$token = $this->getParam("token");
|
$token = $this->getParam("token");
|
||||||
$res = $sql->select($sql->count())
|
$res = $sql->select($sql->count())
|
||||||
->from("UserToken")
|
->from("UserFileToken")
|
||||||
->where(new Compare("user_id", $this->user->getId()))
|
->where(new Compare("user_id", $this->user->getId()))
|
||||||
->where(new Compare("token", $token))
|
->where(new Compare("token", $token))
|
||||||
->execute();
|
->execute();
|
||||||
@ -179,9 +238,9 @@ namespace Api\File {
|
|||||||
|
|
||||||
if ($this->success) {
|
if ($this->success) {
|
||||||
if(empty($res)) {
|
if(empty($res)) {
|
||||||
return $this->getParam("Invalid token");
|
return $this->createError("Invalid token");
|
||||||
} else {
|
} else {
|
||||||
$res = $sql->update("UserToken")
|
$res = $sql->update("UserFileToken")
|
||||||
->set("valid_until", new \DateTime())
|
->set("valid_until", new \DateTime())
|
||||||
->where(new Compare("user_id", $this->user->getId()))
|
->where(new Compare("user_id", $this->user->getId()))
|
||||||
->where(new Compare("token", $token))
|
->where(new Compare("token", $token))
|
||||||
@ -205,28 +264,6 @@ namespace Api\File {
|
|||||||
$this->csrfTokenRequired = false;
|
$this->csrfTokenRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function &findDirectory(&$files, $id) {
|
|
||||||
|
|
||||||
if ($id !== null) {
|
|
||||||
$id = (string)$id;
|
|
||||||
if (isset($files[$id])) {
|
|
||||||
return $files[$id]["items"];
|
|
||||||
} else {
|
|
||||||
foreach ($files as &$dir) {
|
|
||||||
if ($dir["isDirectory"]) {
|
|
||||||
$target =& $this->findDirectory($dir["items"], $id);
|
|
||||||
if ($target !== $dir["items"]) {
|
|
||||||
return $target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $files;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return $files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute($values = array()) {
|
public function execute($values = array()) {
|
||||||
if (!parent::execute($values)) {
|
if (!parent::execute($values)) {
|
||||||
return false;
|
return false;
|
||||||
@ -247,25 +284,7 @@ namespace Api\File {
|
|||||||
$this->lastError = $sql->getLastError();
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
if ($this->success) {
|
if ($this->success) {
|
||||||
$files = array();
|
$files = $this->createFileList($res);
|
||||||
|
|
||||||
foreach ($res as $row) {
|
|
||||||
$fileId = (string)$row["uid"];
|
|
||||||
$parentId = $row["parentId"];
|
|
||||||
$fileName = $row["name"];
|
|
||||||
$isDirectory = $row["directory"];
|
|
||||||
$fileElement = array("uid" => $fileId, "name" => $fileName, "isDirectory" => $isDirectory);
|
|
||||||
if ($isDirectory) {
|
|
||||||
$fileElement["items"] = array();
|
|
||||||
} else {
|
|
||||||
$fileElement["size"] = @filesize($row["path"]);
|
|
||||||
$fileElement["mimeType"] = @mime_content_type($row["path"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dir =& $this->findDirectory($files, $parentId);
|
|
||||||
$dir[$fileId] = $fileElement;
|
|
||||||
unset($dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
$directoryId = $this->getParam("directory");
|
$directoryId = $this->getParam("directory");
|
||||||
if (!is_null($directoryId)) {
|
if (!is_null($directoryId)) {
|
||||||
@ -419,11 +438,12 @@ namespace Api\File {
|
|||||||
$maxFiles = $res[0]["maxFiles"] ?? 0;
|
$maxFiles = $res[0]["maxFiles"] ?? 0;
|
||||||
$maxSize = $res[0]["maxSize"] ?? 0;
|
$maxSize = $res[0]["maxSize"] ?? 0;
|
||||||
$userId = $res[0]["user_id"];
|
$userId = $res[0]["user_id"];
|
||||||
$extensions = explode(",", strtolower($res[0]["extensions"] ?? ""));
|
$extensions = explode(",", trim(strtolower($res[0]["extensions"] ?? "")));
|
||||||
|
$extensions = array_filter($extensions);
|
||||||
|
|
||||||
$res = $sql->select($sql->count())
|
$res = $sql->select($sql->count())
|
||||||
->from("UserFileTokenFile")
|
->from("UserFileTokenFile")
|
||||||
->innerJoin("UserFileToken", "token_id", "UserFileToken.uid")
|
->where(new Compare("token_id", $tokenId))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$this->success = ($res !== false);
|
$this->success = ($res !== false);
|
||||||
@ -434,7 +454,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
$count = $res[0]["count"];
|
$count = $res[0]["count"];
|
||||||
if ($maxFiles > 0 && $numFilesUploaded > 0 && $numFilesUploaded + $count > $maxFiles) {
|
if ($maxFiles > 0 && $numFilesUploaded > 0 && $numFilesUploaded + $count > $maxFiles) {
|
||||||
return $this->createError("File limit exceeded. Currently uploaded $count / $maxFiles count");
|
return $this->createError("File limit exceeded. Currently uploaded $count / $maxFiles files");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($maxSize > 0 || !empty($extensions)) {
|
if ($maxSize > 0 || !empty($extensions)) {
|
||||||
@ -513,7 +533,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
public function __construct(User $user, bool $externalCall = false) {
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
parent::__construct($user, $externalCall, array(
|
parent::__construct($user, $externalCall, array(
|
||||||
"id" => new Parameter("id", Parameter::TYPE_INT),
|
"id" => new ArrayType("id", Parameter::TYPE_INT, true),
|
||||||
"token" => new StringType("token", 36, true, null)
|
"token" => new StringType("token", 36, true, null)
|
||||||
));
|
));
|
||||||
$this->csrfTokenRequired = false;
|
$this->csrfTokenRequired = false;
|
||||||
@ -530,18 +550,9 @@ namespace Api\File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$fileId = $this->getParam("id");
|
$fileIds = $this->getParam("id");
|
||||||
$query = $sql->select("path", "name")
|
$query = $sql->select("uid", "path", "name")->from("UserFile");
|
||||||
->from("UserFile")
|
$this->filterFiles($sql, $query, $fileIds, $token);
|
||||||
->where(new Compare("UserFile.uid", $fileId));
|
|
||||||
|
|
||||||
if (is_null($token)) {
|
|
||||||
$query->where(new Compare("user_id", $this->user->getId()));
|
|
||||||
} else {
|
|
||||||
$query->innerJoin("UserFileTokenFile", "UserFile.uid", "file_id")
|
|
||||||
->innerJoin("UserFileToken", "UserFileToken.uid", "token_id")
|
|
||||||
->where(new Compare("token", $token));
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = $query->execute();
|
$res = $query->execute();
|
||||||
$this->success = ($res !== false);
|
$this->success = ($res !== false);
|
||||||
@ -573,9 +584,10 @@ namespace Api\File {
|
|||||||
|
|
||||||
public function __construct(User $user, bool $externalCall = false) {
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
parent::__construct($user, $externalCall, array(
|
parent::__construct($user, $externalCall, array(
|
||||||
"id" => new Parameter("id", Parameter::TYPE_INT),
|
"id" => new ArrayType("id", Parameter::TYPE_INT, true),
|
||||||
"token" => new StringType("token", 36, true, null)
|
"token" => new StringType("token", 36, true, null)
|
||||||
));
|
));
|
||||||
|
$this->csrfTokenRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute($values = array()) {
|
public function execute($values = array()) {
|
||||||
@ -583,7 +595,38 @@ namespace Api\File {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
$token = $this->getParam("token");
|
||||||
|
if (!$this->user->isLoggedIn() && is_null($token)) {
|
||||||
|
return $this->createError("Permission denied (expected token)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$fileIds = array_unique($this->getParam("id"));
|
||||||
|
|
||||||
|
$query = $sql->select("UserFile.uid", "path", "name")->from("UserFile");
|
||||||
|
$this->filterFiles($sql, $query, $fileIds, $token);
|
||||||
|
$res = $query->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
if (count($res) !== count($fileIds)) {
|
||||||
|
$foundFiles = array_map(function ($row) { return $row["uid"]; }, $res);
|
||||||
|
foreach($fileIds as $fileId) {
|
||||||
|
if (!in_array($fileId, $foundFiles)) {
|
||||||
|
return $this->createError("File not found: $fileId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$res = $sql->delete("UserFile")
|
||||||
|
->where(new CondIn("uid", $fileIds))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->success;
|
return $this->success;
|
||||||
}
|
}
|
||||||
@ -637,7 +680,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$token = generateRandomString(36);
|
$token = generateRandomString(36);
|
||||||
$validUntil = (new \DateTime())->modify("+$durability HOURS");
|
$validUntil = (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"))
|
||||||
@ -715,7 +758,7 @@ namespace Api\File {
|
|||||||
|
|
||||||
// Insert
|
// Insert
|
||||||
$token = generateRandomString(36);
|
$token = generateRandomString(36);
|
||||||
$validUntil = (new \DateTime())->modify("+$durability HOURS");
|
$validUntil = (new \DateTime())->modify("+$durability MINUTES");
|
||||||
$res = $sql->insert("UserFileToken", array("token_type", "valid_until", "user_id", "token"))
|
$res = $sql->insert("UserFileToken", array("token_type", "valid_until", "user_id", "token"))
|
||||||
->addRow("download", $validUntil, $this->user->getId(), $token)
|
->addRow("download", $validUntil, $this->user->getId(), $token)
|
||||||
->returning("uid")
|
->returning("uid")
|
||||||
|
56
core/Api/Parameter/ArrayType.class.php
Normal file
56
core/Api/Parameter/ArrayType.class.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Api\Parameter;
|
||||||
|
|
||||||
|
class ArrayType extends Parameter {
|
||||||
|
|
||||||
|
private Parameter $elementParameter;
|
||||||
|
public int $elementType;
|
||||||
|
public int $canBeOne;
|
||||||
|
|
||||||
|
public function __construct($name, $elementType = Parameter::TYPE_MIXED, $canBeOne=false, $optional = FALSE, $defaultValue = NULL) {
|
||||||
|
$this->elementType = $elementType;
|
||||||
|
$this->elementParameter = new Parameter('', $elementType);
|
||||||
|
$this->canBeOne = $canBeOne;
|
||||||
|
parent::__construct($name, Parameter::TYPE_ARRAY, $optional, $defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseParam($value) {
|
||||||
|
if(!is_array($value)) {
|
||||||
|
if (!$this->canBeOne) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$value = array($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->elementType != Parameter::TYPE_MIXED) {
|
||||||
|
foreach ($value as &$element) {
|
||||||
|
if ($this->elementParameter->parseParam($element)) {
|
||||||
|
$element = $this->elementParameter->value;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->value = $value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTypeName() {
|
||||||
|
$elementType = $this->elementParameter->getTypeName();
|
||||||
|
return parent::getTypeName() . "($elementType)";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString() {
|
||||||
|
$typeName = $this->getTypeName();
|
||||||
|
$str = "$typeName $this->name";
|
||||||
|
$defaultValue = (is_null($this->value) ? 'NULL' : (is_array($this->value) ? '[' . implode(",", $this->value) . ']' : $this->value));
|
||||||
|
if($this->optional) {
|
||||||
|
$str = "[$str = $defaultValue]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
}
|
@ -18,9 +18,11 @@ class Parameter {
|
|||||||
const TYPE_RAW = 8;
|
const TYPE_RAW = 8;
|
||||||
|
|
||||||
// only json will work here i guess
|
// only json will work here i guess
|
||||||
|
// nope. also name[]=value
|
||||||
const TYPE_ARRAY = 9;
|
const TYPE_ARRAY = 9;
|
||||||
|
const TYPE_MIXED = 10;
|
||||||
|
|
||||||
const names = array('Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array');
|
const names = array('Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array', 'Mixed');
|
||||||
|
|
||||||
public string $name;
|
public string $name;
|
||||||
public $value;
|
public $value;
|
||||||
|
@ -58,14 +58,14 @@ class file_api extends DatabaseScript {
|
|||||||
->addInt("maxSize", true)
|
->addInt("maxSize", true)
|
||||||
->addString("extensions", 64, true)
|
->addString("extensions", 64, true)
|
||||||
->primaryKey("uid")
|
->primaryKey("uid")
|
||||||
->foreignKey("user_id", "User", "uid");
|
->foreignKey("user_id", "User", "uid", new CascadeStrategy());
|
||||||
|
|
||||||
$queries[] = $sql->createTable("UserFileTokenFile")
|
$queries[] = $sql->createTable("UserFileTokenFile")
|
||||||
->addInt("file_id")
|
->addInt("file_id")
|
||||||
->addInt("token_id")
|
->addInt("token_id")
|
||||||
->unique("file_id", "token_id")
|
->unique("file_id", "token_id")
|
||||||
->foreignKey("file_id", "UserFile", "uid")
|
->foreignKey("file_id", "UserFile", "uid", new CascadeStrategy())
|
||||||
->foreignKey("token_id", "UserFileToken", "uid");
|
->foreignKey("token_id", "UserFileToken", "uid", new CascadeStrategy());
|
||||||
|
|
||||||
return $queries;
|
return $queries;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,32 @@ export default class API {
|
|||||||
return this.apiCall("file/listTokens");
|
return this.apiCall("file/listTokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id) {
|
delete(id, token=null) {
|
||||||
return this.apiCall("file/delete", { id: id })
|
return this.apiCall("file/delete", { id: id, token: token });
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeToken(token) {
|
||||||
|
return this.apiCall("file/revokeToken", { token: token });
|
||||||
|
}
|
||||||
|
|
||||||
|
async upload(files, token = null, parentId = null) {
|
||||||
|
const csrf_token = this.csrfToken();
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
fd.append('file' + i, files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (csrf_token) fd.append("csrf_token", csrf_token);
|
||||||
|
if (token) fd.append("token", token);
|
||||||
|
if (parentId) fd.append("parentId", parentId);
|
||||||
|
|
||||||
|
// send `POST` request
|
||||||
|
let response = await fetch('/api/file/upload', {
|
||||||
|
method: 'POST',
|
||||||
|
body: fd
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.json();
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -31,6 +31,7 @@
|
|||||||
.uploaded-file {
|
.uploaded-file {
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploaded-file > img {
|
.uploaded-file > img {
|
||||||
@ -44,10 +45,25 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploaded-file > i {
|
.uploaded-file > i:nth-child(3) {
|
||||||
color: red;
|
color: red;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -9px;
|
top: -9px;
|
||||||
right: 25px;
|
right: 25px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable { cursor: pointer; }
|
||||||
|
.token-revoked td { text-decoration: line-through; }
|
||||||
|
|
||||||
|
.token-table td:not(:first-child), .token-table th:not(:first-child) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-table td:nth-child(n+3), .file-table th:nth-child(n+3) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-table tr, .file-table td {
|
||||||
|
height: 40px;
|
||||||
|
}
|
@ -13,37 +13,82 @@ export class FileBrowser extends React.Component {
|
|||||||
files: props.files,
|
files: props.files,
|
||||||
token: props.token,
|
token: props.token,
|
||||||
filesToUpload: [],
|
filesToUpload: [],
|
||||||
|
alerts: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svgMiddle(indentation, size=64) {
|
svgMiddle(indentation, scale=1.0) {
|
||||||
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*size) + "px" } : {});
|
let width = 48 * scale;
|
||||||
return <svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" style={style}>
|
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}>
|
||||||
<g>
|
<g>
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={size/2}
|
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={width/2}
|
||||||
y1={size} x1={size/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
y1={height} x1={width/2} strokeWidth="1.5" stroke="#000" fill="none"/>
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={size/2} x2={size}
|
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={height/2} x2={width}
|
||||||
y1={size/2} x1={size/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
y1={height/2} x1={width/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
||||||
stroke="#000" fill="none"/>
|
stroke="#000" fill="none"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>;
|
</svg>;
|
||||||
}
|
}
|
||||||
|
|
||||||
svgEnd(indentation, size=64) {
|
svgEnd(indentation, scale=1.0) {
|
||||||
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*size) + "px" } : {});
|
let width = 48 * scale;
|
||||||
return <svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" style={style}>
|
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}>
|
||||||
<g>
|
<g>
|
||||||
{ /* vertical line */}
|
{ /* vertical line */}
|
||||||
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={size/2}
|
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={width/2}
|
||||||
y1={size/2} x1={size/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={size/2} x2={size}
|
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={height/2} x2={width}
|
||||||
y1={size/2} x1={size/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
y1={height/2} x1={width/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
|
||||||
stroke="#000" fill="none"/>
|
stroke="#000" fill="none"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>;
|
</svg>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 <Icon icon={icon} type={"far"} className={"p-1 align-middle fa-" + size + "x"} />
|
||||||
|
}
|
||||||
|
|
||||||
formatSize(size) {
|
formatSize(size) {
|
||||||
const suffixes = ["B","KiB","MiB","GiB","TiB"];
|
const suffixes = ["B","KiB","MiB","GiB","TiB"];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
@ -134,15 +179,14 @@ export class FileBrowser extends React.Component {
|
|||||||
let uid = fileElement.uid;
|
let uid = fileElement.uid;
|
||||||
let type = (fileElement.isDirectory ? "Directory" : fileElement.mimeType);
|
let type = (fileElement.isDirectory ? "Directory" : fileElement.mimeType);
|
||||||
let size = (fileElement.isDirectory ? "" : this.formatSize(fileElement.size));
|
let size = (fileElement.isDirectory ? "" : this.formatSize(fileElement.size));
|
||||||
// let iconUrl = (fileElement.directory ? "/img/icon/")
|
let mimeType = (fileElement.isDirectory ? "application/x-directory" : fileElement.mimeType);
|
||||||
let iconUrl = "";
|
let token = (this.state.token && this.state.token.valid ? "&token=" + this.state.token.value : "");
|
||||||
let token = (this.state.token && this.state.token.valid ? "&token=" + this.token.state.value : "");
|
|
||||||
let svg = <></>;
|
let svg = <></>;
|
||||||
if (indentation > 0) {
|
if (indentation > 0) {
|
||||||
if (i === values.length - 1) {
|
if (i === values.length - 1) {
|
||||||
svg = this.svgEnd(indentation, 48);
|
svg = this.svgEnd(indentation, 0.75);
|
||||||
} else {
|
} else {
|
||||||
svg = this.svgMiddle(indentation, 48);
|
svg = this.svgMiddle(indentation, 0.75);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +194,7 @@ export class FileBrowser extends React.Component {
|
|||||||
<tr key={"file-" + uid} data-id={uid} className={"file-row"}>
|
<tr key={"file-" + uid} data-id={uid} className={"file-row"}>
|
||||||
<td>
|
<td>
|
||||||
{ svg }
|
{ svg }
|
||||||
<img src={iconUrl} alt={"[Icon]"} />
|
{ this.createFileIcon(mimeType) }
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{fileElement.isDirectory ? name :
|
{fileElement.isDirectory ? name :
|
||||||
@ -183,6 +227,16 @@ export class FileBrowser extends React.Component {
|
|||||||
let uploadZone = <></>;
|
let uploadZone = <></>;
|
||||||
let writePermissions = this.canUpload();
|
let writePermissions = this.canUpload();
|
||||||
let uploadedFiles = [];
|
let uploadedFiles = [];
|
||||||
|
let alerts = [];
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (const alert of this.state.alerts) {
|
||||||
|
alerts.push(
|
||||||
|
<div key={"alert-" + i++} className={"alert alert-" + alert.type}>
|
||||||
|
{ alert.text }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (writePermissions) {
|
if (writePermissions) {
|
||||||
|
|
||||||
@ -190,7 +244,7 @@ export class FileBrowser extends React.Component {
|
|||||||
const file = this.state.filesToUpload[i];
|
const file = this.state.filesToUpload[i];
|
||||||
uploadedFiles.push(
|
uploadedFiles.push(
|
||||||
<span className={"uploaded-file"} key={i}>
|
<span className={"uploaded-file"} key={i}>
|
||||||
<img />
|
{ this.createFileIcon(file.type, 3) }
|
||||||
<span>{file.name}</span>
|
<span>{file.name}</span>
|
||||||
<Icon icon={"times"} onClick={(e) => this.onRemoveUploadedFile(e, i)}/>
|
<Icon icon={"times"} onClick={(e) => this.onRemoveUploadedFile(e, i)}/>
|
||||||
</span>
|
</span>
|
||||||
@ -203,7 +257,10 @@ export class FileBrowser extends React.Component {
|
|||||||
<div {...getRootProps()}>
|
<div {...getRootProps()}>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||||
{ uploadedFiles.length === 0 ? <Icon className={"mx-auto fa-3x text-black-50"} icon={"upload"}/> : <div>{uploadedFiles}</div> }
|
{ uploadedFiles.length === 0 ?
|
||||||
|
<Icon className={"mx-auto fa-3x text-black-50"} icon={"upload"}/> :
|
||||||
|
<div>{uploadedFiles}</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
@ -213,7 +270,7 @@ export class FileBrowser extends React.Component {
|
|||||||
|
|
||||||
return <>
|
return <>
|
||||||
<h4>File Browser</h4>
|
<h4>File Browser</h4>
|
||||||
<table className={"table"}>
|
<table className={"table data-table file-table"}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th/>
|
<th/>
|
||||||
@ -242,11 +299,13 @@ export class FileBrowser extends React.Component {
|
|||||||
{
|
{
|
||||||
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={this.onUpload.bind(this)}>
|
||||||
<Icon icon={"upload"} className={"mr-1"}/>
|
<Icon icon={"upload"} className={"mr-1"}/>
|
||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
<button type={"button"} className={"btn btn-danger"} disabled={selectedCount === 0} onClick={(e) => this.deleteFiles(selectedIds)}>
|
<button type={"button"} className={"btn btn-danger"} disabled={selectedCount === 0}
|
||||||
|
onClick={(e) => this.deleteFiles(selectedIds)}>
|
||||||
<Icon icon={"trash"} className={"mr-1"}/>
|
<Icon icon={"trash"} className={"mr-1"}/>
|
||||||
Delete Selected Files ({selectedCount})
|
Delete Selected Files ({selectedCount})
|
||||||
</button>
|
</button>
|
||||||
@ -254,11 +313,33 @@ export class FileBrowser extends React.Component {
|
|||||||
: <></>
|
: <></>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ uploadZone }
|
{ uploadZone }
|
||||||
|
<div>
|
||||||
|
{ alerts }
|
||||||
|
</div>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
onRemoveUploadedFile(e, i) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
let files = this.state.filesToUpload.slice();
|
let files = this.state.filesToUpload.slice();
|
||||||
@ -266,10 +347,31 @@ export class FileBrowser extends React.Component {
|
|||||||
this.setState({ ...this.state, filesToUpload: files });
|
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) {
|
deleteFiles(selectedIds) {
|
||||||
// TODO: delete files
|
let token = (this.state.api.loggedIn ? null : this.state.token.value);
|
||||||
this.state.api.delete(selectedIds).then((res) => {
|
this.state.api.delete(selectedIds, token).then((res) => {
|
||||||
if (res.success) {
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Icon from "./icon";
|
import Icon from "./icon";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
export class TokenList extends React.Component {
|
export class TokenList extends React.Component {
|
||||||
|
|
||||||
@ -8,7 +9,8 @@ export class TokenList extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
api: props.api,
|
api: props.api,
|
||||||
tokens: null
|
tokens: null,
|
||||||
|
alerts: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,20 +23,35 @@ export class TokenList extends React.Component {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
for (const token of this.state.tokens) {
|
for (const token of this.state.tokens) {
|
||||||
|
const revoked = moment(token.valid_until).isSameOrBefore(new Date());
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key={"token-" + token.uid}>
|
<tr key={"token-" + token.uid} className={revoked ? "token-revoked" : ""}>
|
||||||
<td>{token.token}</td>
|
<td>{token.token}</td>
|
||||||
<td>{token.type}</td>
|
<td>{token.type}</td>
|
||||||
<td>{token.valid_until}</td>
|
<td>{moment(token.valid_until).format("Do MMM YYYY, HH:mm")}</td>
|
||||||
<td><Icon icon={"times"} className={"text-danger"}/></td>
|
<td>
|
||||||
|
<Icon icon={"times"} className={"clickable text-" + (revoked ? "secondary" : "danger")}
|
||||||
|
onClick={() => (revoked ? null : this.onRevokeToken(token.token) )}
|
||||||
|
disabled={revoked} />
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let alerts = [];
|
||||||
|
let i = 0;
|
||||||
|
for (const alert of this.state.alerts) {
|
||||||
|
alerts.push(
|
||||||
|
<div key={"alert-" + i++} className={"alert alert-" + alert.type}>
|
||||||
|
{ alert.text }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<h4>Tokens</h4>
|
<h4>Tokens</h4>
|
||||||
<table className={"table"}>
|
<table className={"table token-table"}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Token</th>
|
<th>Token</th>
|
||||||
@ -53,6 +70,28 @@ export class TokenList extends React.Component {
|
|||||||
Create Token
|
Create Token
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{ alerts }
|
||||||
|
</div>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRevokeToken(token) {
|
||||||
|
this.state.api.revokeToken(token).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
let newTokens = this.state.tokens.slice();
|
||||||
|
for (const tokenObj of newTokens) {
|
||||||
|
if (tokenObj.token === token) {
|
||||||
|
tokenObj.valid_until = moment();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({ ...this.state, tokens: newTokens });
|
||||||
|
} else {
|
||||||
|
let newAlerts = this.state.alerts.slice();
|
||||||
|
newAlerts.push({ type: "danger", text: res.msg });
|
||||||
|
this.setState({ ...this.state, alerts: newAlerts });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,22 +20,24 @@ class FileControlPanel extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onValidateToken() {
|
onValidateToken(token = null) {
|
||||||
this.setState({ ...this.state, validatingToken: true, errorMessage: "" })
|
if (token === null) {
|
||||||
this.api.validateToken(this.state.token.value).then((res) => {
|
this.setState({ ...this.state, validatingToken: true, errorMessage: "" });
|
||||||
if (res.success) {
|
token = this.state.token.value;
|
||||||
this.setState({ ...this.state, validatingToken: false,
|
|
||||||
token: {
|
|
||||||
...this.state.token,
|
|
||||||
valid: true,
|
|
||||||
validUntil: res.token.valid_util,
|
|
||||||
type: res.token.type
|
|
||||||
},
|
|
||||||
files: res.files
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({ ...this.state, validatingToken: false, errorMessage: res.msg });
|
|
||||||
}
|
}
|
||||||
|
this.api.validateToken(token).then((res) => {
|
||||||
|
let newState = { ...this.state, loaded: true, validatingToken: false };
|
||||||
|
if (res.success) {
|
||||||
|
newState.token = { ...this.state.token, valid: true, validUntil: res.token.valid_until, type: res.token.type };
|
||||||
|
if (!newState.token.value) {
|
||||||
|
newState.token.value = token;
|
||||||
|
}
|
||||||
|
newState.files = res.files;
|
||||||
|
} else {
|
||||||
|
newState.errorMessage = res.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(newState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +50,21 @@ class FileControlPanel extends React.Component {
|
|||||||
const errorMessageShown = !!this.state.errorMessage;
|
const errorMessageShown = !!this.state.errorMessage;
|
||||||
|
|
||||||
if (!this.state.loaded) {
|
if (!this.state.loaded) {
|
||||||
|
|
||||||
|
let checkUser = true;
|
||||||
|
let pathName = window.location.pathname;
|
||||||
|
if (pathName.length > 1) {
|
||||||
|
let end = (pathName.endsWith("/") ? pathName.length - 2 : pathName.length - 1);
|
||||||
|
let start = (pathName.startsWith("/files/") ? ("/files/").length : 1);
|
||||||
|
let token = pathName.substr(start, end);
|
||||||
|
if (token) {
|
||||||
|
// this.setState({ ...this.state, loaded: true, token: { ...this.state.token, value: token } });
|
||||||
|
this.onValidateToken(token);
|
||||||
|
checkUser = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkUser) {
|
||||||
this.api.fetchUser().then((isLoggedIn) => {
|
this.api.fetchUser().then((isLoggedIn) => {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
this.api.listFiles().then((res) => {
|
this.api.listFiles().then((res) => {
|
||||||
@ -57,6 +74,8 @@ class FileControlPanel extends React.Component {
|
|||||||
this.setState({ ...this.state, loaded: true, user: this.api.user });
|
this.setState({ ...this.state, loaded: true, user: this.api.user });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
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 tokenList = (this.api.loggedIn) ?
|
let tokenList = (this.api.loggedIn) ?
|
||||||
@ -85,7 +104,7 @@ class FileControlPanel extends React.Component {
|
|||||||
<label htmlFor={"token"}>Enter a file token to download or upload files</label>
|
<label htmlFor={"token"}>Enter a file token to download or upload files</label>
|
||||||
<input type={"text"} className={"form-control"} name={"token"} placeholder={"Enter token…"} maxLength={36}
|
<input type={"text"} className={"form-control"} name={"token"} placeholder={"Enter token…"} maxLength={36}
|
||||||
value={this.state.token.value} onChange={(e) => self.onUpdateToken(e)}/>
|
value={this.state.token.value} onChange={(e) => self.onUpdateToken(e)}/>
|
||||||
<button className={"btn btn-success mt-2"} onClick={this.onValidateToken.bind(this)} disabled={this.state.validatingToken}>
|
<button className={"btn btn-success mt-2"} onClick={() => this.onValidateToken()} disabled={this.state.validatingToken}>
|
||||||
{ this.state.validatingToken ? <>Validating… <Icon icon={"spinner"}/></> : "Submit" }
|
{ this.state.validatingToken ? <>Validating… <Icon icon={"spinner"}/></> : "Submit" }
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
282
js/files.min.js
vendored
282
js/files.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user