File Control Panel

This commit is contained in:
Roman Hergenreder 2021-01-10 23:41:00 +01:00
parent 191afa06fb
commit bf4301f4be
9 changed files with 439 additions and 125 deletions

@ -215,7 +215,7 @@ namespace Api\File {
foreach ($files as &$dir) { foreach ($files as &$dir) {
if ($dir["isDirectory"]) { if ($dir["isDirectory"]) {
$target =& $this->findDirectory($dir["items"], $id); $target =& $this->findDirectory($dir["items"], $id);
if ($target !== $dir) { if ($target !== $dir["items"]) {
return $target; return $target;
} }
} }
@ -476,6 +476,7 @@ namespace Api\File {
$sha1Hash = @hash_file('sha1', $tmpPath); $sha1Hash = @hash_file('sha1', $tmpPath);
$filePath = $uploadDir . "/" . $md5Hash . $sha1Hash; $filePath = $uploadDir . "/" . $md5Hash . $sha1Hash;
if (move_uploaded_file($tmpPath, $filePath)) { if (move_uploaded_file($tmpPath, $filePath)) {
$res = $sql->insert("UserFile", array("name", "directory", "path", "user_id", "parent_id")) $res = $sql->insert("UserFile", array("name", "directory", "path", "user_id", "parent_id"))
->addRow($fileName, false, $filePath, $userId, $parentId) ->addRow($fileName, false, $filePath, $userId, $parentId)
->returning("uid") ->returning("uid")
@ -582,6 +583,7 @@ namespace Api\File {
return false; return false;
} }
// TODO:
return $this->success; return $this->success;
} }

@ -1642,177 +1642,178 @@
} }
}, },
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
"integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/helper-module-context": "1.9.0", "@webassemblyjs/helper-module-context": "1.8.5",
"@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
"@webassemblyjs/wast-parser": "1.9.0" "@webassemblyjs/wast-parser": "1.8.5"
} }
}, },
"@webassemblyjs/floating-point-hex-parser": { "@webassemblyjs/floating-point-hex-parser": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
"integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-api-error": { "@webassemblyjs/helper-api-error": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
"integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-buffer": { "@webassemblyjs/helper-buffer": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
"integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-code-frame": { "@webassemblyjs/helper-code-frame": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
"integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/wast-printer": "1.9.0" "@webassemblyjs/wast-printer": "1.8.5"
} }
}, },
"@webassemblyjs/helper-fsm": { "@webassemblyjs/helper-fsm": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
"integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-module-context": { "@webassemblyjs/helper-module-context": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
"integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0" "@webassemblyjs/ast": "1.8.5",
"mamacro": "^0.0.3"
} }
}, },
"@webassemblyjs/helper-wasm-bytecode": { "@webassemblyjs/helper-wasm-bytecode": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
"integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-wasm-section": { "@webassemblyjs/helper-wasm-section": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
"integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-buffer": "1.9.0", "@webassemblyjs/helper-buffer": "1.8.5",
"@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
"@webassemblyjs/wasm-gen": "1.9.0" "@webassemblyjs/wasm-gen": "1.8.5"
} }
}, },
"@webassemblyjs/ieee754": { "@webassemblyjs/ieee754": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
"integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@xtuc/ieee754": "^1.2.0" "@xtuc/ieee754": "^1.2.0"
} }
}, },
"@webassemblyjs/leb128": { "@webassemblyjs/leb128": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
"integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"@webassemblyjs/utf8": { "@webassemblyjs/utf8": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
"integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
"dev": true "dev": true
}, },
"@webassemblyjs/wasm-edit": { "@webassemblyjs/wasm-edit": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
"integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-buffer": "1.9.0", "@webassemblyjs/helper-buffer": "1.8.5",
"@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
"@webassemblyjs/helper-wasm-section": "1.9.0", "@webassemblyjs/helper-wasm-section": "1.8.5",
"@webassemblyjs/wasm-gen": "1.9.0", "@webassemblyjs/wasm-gen": "1.8.5",
"@webassemblyjs/wasm-opt": "1.9.0", "@webassemblyjs/wasm-opt": "1.8.5",
"@webassemblyjs/wasm-parser": "1.9.0", "@webassemblyjs/wasm-parser": "1.8.5",
"@webassemblyjs/wast-printer": "1.9.0" "@webassemblyjs/wast-printer": "1.8.5"
} }
}, },
"@webassemblyjs/wasm-gen": { "@webassemblyjs/wasm-gen": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
"integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
"@webassemblyjs/ieee754": "1.9.0", "@webassemblyjs/ieee754": "1.8.5",
"@webassemblyjs/leb128": "1.9.0", "@webassemblyjs/leb128": "1.8.5",
"@webassemblyjs/utf8": "1.9.0" "@webassemblyjs/utf8": "1.8.5"
} }
}, },
"@webassemblyjs/wasm-opt": { "@webassemblyjs/wasm-opt": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
"integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-buffer": "1.9.0", "@webassemblyjs/helper-buffer": "1.8.5",
"@webassemblyjs/wasm-gen": "1.9.0", "@webassemblyjs/wasm-gen": "1.8.5",
"@webassemblyjs/wasm-parser": "1.9.0" "@webassemblyjs/wasm-parser": "1.8.5"
} }
}, },
"@webassemblyjs/wasm-parser": { "@webassemblyjs/wasm-parser": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
"integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-api-error": "1.9.0", "@webassemblyjs/helper-api-error": "1.8.5",
"@webassemblyjs/helper-wasm-bytecode": "1.9.0", "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
"@webassemblyjs/ieee754": "1.9.0", "@webassemblyjs/ieee754": "1.8.5",
"@webassemblyjs/leb128": "1.9.0", "@webassemblyjs/leb128": "1.8.5",
"@webassemblyjs/utf8": "1.9.0" "@webassemblyjs/utf8": "1.8.5"
} }
}, },
"@webassemblyjs/wast-parser": { "@webassemblyjs/wast-parser": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
"integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/floating-point-hex-parser": "1.9.0", "@webassemblyjs/floating-point-hex-parser": "1.8.5",
"@webassemblyjs/helper-api-error": "1.9.0", "@webassemblyjs/helper-api-error": "1.8.5",
"@webassemblyjs/helper-code-frame": "1.9.0", "@webassemblyjs/helper-code-frame": "1.8.5",
"@webassemblyjs/helper-fsm": "1.9.0", "@webassemblyjs/helper-fsm": "1.8.5",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"@webassemblyjs/wast-printer": { "@webassemblyjs/wast-printer": {
"version": "1.9.0", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
"integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/wast-parser": "1.9.0", "@webassemblyjs/wast-parser": "1.8.5",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
@ -2194,6 +2195,11 @@
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
}, },
"attr-accept": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
},
"autoprefixer": { "autoprefixer": {
"version": "9.8.6", "version": "9.8.6",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
@ -5617,6 +5623,21 @@
"schema-utils": "^2.5.0" "schema-utils": "^2.5.0"
} }
}, },
"file-selector": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
"integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
"requires": {
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
}
}
},
"filesize": { "filesize": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz",
@ -10589,6 +10610,16 @@
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"react-dropzone": {
"version": "11.2.4",
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.2.4.tgz",
"integrity": "sha512-EGSvK2CxFTuc28WxwuJCICyuYFX8b+sRumwU6Bs6sTbElV2HtQkT0d6C+HEee6XfbjiLIZ+Th9uji27rvo2wGw==",
"requires": {
"attr-accept": "^2.2.1",
"file-selector": "^0.2.2",
"prop-types": "^15.7.2"
}
},
"react-error-overlay": { "react-error-overlay": {
"version": "6.0.8", "version": "6.0.8",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz",
@ -13442,33 +13473,33 @@
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
}, },
"webpack": { "webpack": {
"version": "4.45.0", "version": "4.42.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.45.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz",
"integrity": "sha512-JhDaVi4CbRcwLLAoqC7eugMSMJnZbIfE2AyjaZ19pnOIh/R2O/lXOiXA2tQFN0iXEcxgpPJsPJHW2wOWqiTLcw==", "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.8.5",
"@webassemblyjs/helper-module-context": "1.9.0", "@webassemblyjs/helper-module-context": "1.8.5",
"@webassemblyjs/wasm-edit": "1.9.0", "@webassemblyjs/wasm-edit": "1.8.5",
"@webassemblyjs/wasm-parser": "1.9.0", "@webassemblyjs/wasm-parser": "1.8.5",
"acorn": "^6.4.1", "acorn": "^6.2.1",
"ajv": "^6.10.2", "ajv": "^6.10.2",
"ajv-keywords": "^3.4.1", "ajv-keywords": "^3.4.1",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^4.3.0", "enhanced-resolve": "^4.1.0",
"eslint-scope": "^4.0.3", "eslint-scope": "^4.0.3",
"json-parse-better-errors": "^1.0.2", "json-parse-better-errors": "^1.0.2",
"loader-runner": "^2.4.0", "loader-runner": "^2.4.0",
"loader-utils": "^1.2.3", "loader-utils": "^1.2.3",
"memory-fs": "^0.4.1", "memory-fs": "^0.4.1",
"micromatch": "^3.1.10", "micromatch": "^3.1.10",
"mkdirp": "^0.5.3", "mkdirp": "^0.5.1",
"neo-async": "^2.6.1", "neo-async": "^2.6.1",
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"tapable": "^1.1.3", "tapable": "^1.1.3",
"terser-webpack-plugin": "^1.4.3", "terser-webpack-plugin": "^1.4.3",
"watchpack": "^1.7.4", "watchpack": "^1.6.0",
"webpack-sources": "^1.4.1" "webpack-sources": "^1.4.1"
}, },
"dependencies": { "dependencies": {

@ -8,6 +8,7 @@
"react-collapse": "^5.0.1", "react-collapse": "^5.0.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-draft-wysiwyg": "^1.14.5", "react-draft-wysiwyg": "^1.14.5",
"react-dropzone": "^11.2.4",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "^3.4.4" "react-scripts": "^3.4.4"
}, },

@ -48,4 +48,8 @@ export default class API {
listTokens() { listTokens() {
return this.apiCall("file/listTokens"); return this.apiCall("file/listTokens");
} }
delete(id) {
return this.apiCall("file/delete", { id: id })
}
}; };

@ -0,0 +1,53 @@
.file-row td {
padding: 0;
border: none;
vertical-align: middle;
}
.file-control-buttons {
display: grid;
grid-template-rows: auto auto;
grid-template-columns: auto auto;
}
.file-control-buttons > button {
margin: 15px;
}
.file-upload-container {
border: dotted;
margin: 18px;
padding: 15px;
min-height: 150px;
text-align: center;
cursor: pointer;
}
.file-upload-container > div > div {
display: grid;
grid-template-columns: auto auto auto auto;
}
.uploaded-file {
max-width: 120px;
position: relative;
}
.uploaded-file > img {
width: 50px;
height: 56px;
border: 1px solid black;
}
.uploaded-file > span {
display: block;
word-wrap: break-word;
}
.uploaded-file > i {
color: red;
position: absolute;
top: -9px;
right: 25px;
cursor: pointer;
}

@ -1,4 +1,7 @@
import * as React from "react"; import * as React from "react";
import "./file-browser.css";
import Dropzone from "react-dropzone";
import Icon from "./icon";
export class FileBrowser extends React.Component { export class FileBrowser extends React.Component {
@ -6,10 +9,41 @@ export class FileBrowser extends React.Component {
super(props); super(props);
this.state = { this.state = {
api: props.api,
files: props.files, files: props.files,
token: props.token,
filesToUpload: [],
} }
} }
svgMiddle(indentation, size=64) {
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*size) + "px" } : {});
return <svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" style={style}>
<g>
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={size/2}
y1={size} x1={size/2} strokeWidth="1.5" stroke="#000" fill="none"/>
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={size/2} x2={size}
y1={size/2} x1={size/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
stroke="#000" fill="none"/>
</g>
</svg>;
}
svgEnd(indentation, size=64) {
let style = (indentation > 1 ? { marginLeft: ((indentation-1)*size) + "px" } : {});
return <svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" style={style}>
<g>
{ /* vertical line */}
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2="0" x2={size/2}
y1={size/2} x1={size/2} strokeWidth="1.5" stroke="#000" fill="none"/>
{ /* horizontal line */}
<line strokeLinecap="undefined" strokeLinejoin="undefined" y2={size/2} x2={size}
y1={size/2} x1={size/2} fillOpacity="null" strokeOpacity="null" strokeWidth="1.5"
stroke="#000" fill="none"/>
</g>
</svg>;
}
formatSize(size) { formatSize(size) {
const suffixes = ["B","KiB","MiB","GiB","TiB"]; const suffixes = ["B","KiB","MiB","GiB","TiB"];
let i = 0; let i = 0;
@ -20,24 +54,161 @@ export class FileBrowser extends React.Component {
return size.toFixed(1) + " " + suffixes[i]; return size.toFixed(1) + " " + suffixes[i];
} }
render() { 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 rows = [];
for (const [uid, fileElement] of Object.entries(this.state.files)) { let i = 0;
const values = Object.values(elements);
for (const fileElement of values) {
let name = fileElement.name; let name = fileElement.name;
let type = (fileElement.directory ? "Directory" : fileElement.mimeType); let uid = fileElement.uid;
let size = (fileElement.directory ? "" : fileElement.size) let type = (fileElement.isDirectory ? "Directory" : fileElement.mimeType);
let size = (fileElement.isDirectory ? "" : this.formatSize(fileElement.size));
// let iconUrl = (fileElement.directory ? "/img/icon/") // let iconUrl = (fileElement.directory ? "/img/icon/")
let iconUrl = ""; let iconUrl = "";
let token = (this.state.token && this.state.token.valid ? "&token=" + this.token.state.value : "");
let svg = <></>;
if (indentation > 0) {
if (i === values.length - 1) {
svg = this.svgEnd(indentation, 48);
} else {
svg = this.svgMiddle(indentation, 48);
}
}
rows.push( rows.push(
<tr key={"file-" + uid}> <tr key={"file-" + uid} data-id={uid} className={"file-row"}>
<td><img src={iconUrl} alt={"[Icon]"} /></td> <td>
<td>{name}</td> { svg }
<img src={iconUrl} alt={"[Icon]"} />
</td>
<td>
{fileElement.isDirectory ? name :
<a href={"/api/file/download?id=" + uid + token} download={true}>{name}</a>
}
</td>
<td>{type}</td> <td>{type}</td>
<td>{this.formatSize(size)}</td> <td>{size}</td>
<td>
<input type={"checkbox"} checked={!!fileElement.selected}
onChange={(e) => this.onSelectFile(e, uid)}
/>
</td>
</tr> </tr>
); );
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 = [];
if (writePermissions) {
for(let i = 0; i < this.state.filesToUpload.length; i++) {
const file = this.state.filesToUpload[i];
uploadedFiles.push(
<span className={"uploaded-file"} key={i}>
<img />
<span>{file.name}</span>
<Icon icon={"times"} onClick={(e) => this.onRemoveUploadedFile(e, i)}/>
</span>
);
}
uploadZone = <><Dropzone onDrop={this.onAddUploadFiles.bind(this)}>
{({getRootProps, getInputProps}) => (
<section className={"file-upload-container"}>
<div {...getRootProps()}>
<input {...getInputProps()} />
<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> }
</div>
</section>
)}
</Dropzone>
</>;
} }
return <> return <>
@ -49,12 +220,57 @@ export class FileBrowser extends React.Component {
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Size</th> <th>Size</th>
<th/>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{ rows } { rows }
</tbody> </tbody>
</table> </table>
<div className={"file-control-buttons"}>
<button type={"button"} className={"btn btn-success"} disabled={selectedCount === 0}>
<Icon icon={"download"} className={"mr-1"}/>
Download Selected Files ({selectedCount})
</button>
{ this.state.api.loggedIn ?
<button type={"button"} className={"btn btn-info"}>
<Icon icon={"plus"} className={"mr-1"}/>
Create Directory
</button> :
<></>
}
{
writePermissions ?
<>
<button type={"button"} className={"btn btn-primary"} disabled={uploadedFiles.length === 0}>
<Icon icon={"upload"} className={"mr-1"}/>
Upload
</button>
<button type={"button"} className={"btn btn-danger"} disabled={selectedCount === 0} onClick={(e) => this.deleteFiles(selectedIds)}>
<Icon icon={"trash"} className={"mr-1"}/>
Delete Selected Files ({selectedCount})
</button>
</>
: <></>
}
</div>
{ uploadZone }
</>; </>;
} }
onRemoveUploadedFile(e, i) {
e.stopPropagation();
let files = this.state.filesToUpload.slice();
files.splice(i, 1);
this.setState({ ...this.state, filesToUpload: files });
}
deleteFiles(selectedIds) {
// TODO: delete files
this.state.api.delete(selectedIds).then((res) => {
if (res.success) {
}
});
}
} }

@ -1,4 +1,5 @@
import * as React from "react"; import * as React from "react";
import Icon from "./icon";
export class TokenList extends React.Component { export class TokenList extends React.Component {
@ -25,6 +26,7 @@ export class TokenList extends React.Component {
<td>{token.token}</td> <td>{token.token}</td>
<td>{token.type}</td> <td>{token.type}</td>
<td>{token.valid_until}</td> <td>{token.valid_until}</td>
<td><Icon icon={"times"} className={"text-danger"}/></td>
</tr> </tr>
); );
} }
@ -38,12 +40,19 @@ export class TokenList extends React.Component {
<th>Token</th> <th>Token</th>
<th>Type</th> <th>Type</th>
<th>Valid Until</th> <th>Valid Until</th>
<th/>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{ rows } { rows }
</tbody> </tbody>
</table> </table>
<div>
<button type={"button"} className={"btn btn-success m-2"}>
<Icon icon={"plus"} className={"mr-1"}/>
Create Token
</button>
</div>
</>; </>;
} }
} }

@ -49,10 +49,8 @@ class FileControlPanel extends React.Component {
if (!this.state.loaded) { if (!this.state.loaded) {
this.api.fetchUser().then((isLoggedIn) => { this.api.fetchUser().then((isLoggedIn) => {
console.log(`api.fetchUser => ${isLoggedIn}`);
if (isLoggedIn) { if (isLoggedIn) {
this.api.listFiles().then((res) => { this.api.listFiles().then((res) => {
console.log(`api.listFiles => ${res.success}`);
this.setState({ ...this.state, loaded: true, user: this.api.user, files: res.files }); this.setState({ ...this.state, loaded: true, user: this.api.user, files: res.files });
}); });
} else { } else {
@ -63,7 +61,7 @@ class FileControlPanel extends React.Component {
} 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) ?
<div className={"row"}> <div className={"row"}>
<div className={"col-lg-6 col-md-8 col-sm-10 col-xs-12 mx-auto"}> <div className={"col-lg-8 col-md-10 col-sm-12 mx-auto"}>
<TokenList api={this.api} /> <TokenList api={this.api} />
</div> </div>
</div> : </div> :
@ -71,9 +69,9 @@ class FileControlPanel extends React.Component {
return <div className={"container mt-4"}> return <div className={"container mt-4"}>
<div className={"row"}> <div className={"row"}>
<div className={"col-lg-6 col-md-8 col-sm-10 col-xs-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}/> <FileBrowser files={this.state.files} token={this.state.token} api={this.api} />
</div> </div>
</div> </div>
{ tokenList } { tokenList }
@ -81,7 +79,7 @@ class FileControlPanel extends React.Component {
} else { } else {
return <div className={"container mt-4"}> return <div className={"container mt-4"}>
<div className={"row"}> <div className={"row"}>
<div className={"col-lg-6 col-md-8 col-sm-10 col-xs-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>
<form onSubmit={(e) => e.preventDefault()}> <form onSubmit={(e) => e.preventDefault()}>
<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>

10
js/files.min.js vendored

File diff suppressed because one or more lines are too long