FileApi start
This commit is contained in:
parent
fae8a71bac
commit
8747812a56
@ -162,8 +162,8 @@ namespace Api\ApiKey {
|
|||||||
$this->loginRequired = true;
|
$this->loginRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute($aValues = array()) {
|
public function execute($values = array()) {
|
||||||
if(!parent::execute($aValues)) {
|
if(!parent::execute($values)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ namespace Api\Contact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->createNotification();
|
$this->createNotification();
|
||||||
|
$this->sendMail();
|
||||||
|
|
||||||
if (!$this->success) {
|
if (!$this->success) {
|
||||||
return $this->createError("The contact request was saved, but the server was unable to create a notification.");
|
return $this->createError("The contact request was saved, but the server was unable to create a notification.");
|
||||||
@ -110,6 +111,17 @@ namespace Api\Contact {
|
|||||||
|
|
||||||
return $this->success;
|
return $this->success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendMail() {
|
||||||
|
/*$email = $this->getParam("fromEmail");
|
||||||
|
$settings = $this->user->getConfiguration()->getSettings();
|
||||||
|
$request = new \Api\Mail\Send($this->user);
|
||||||
|
$this->success = $request->execute(array(
|
||||||
|
"to" => $settings->get,
|
||||||
|
"subject" => "[$siteName] Account Invitation",
|
||||||
|
"body" => $messageBody
|
||||||
|
));*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
737
core/Api/FileAPI.class.php
Normal file
737
core/Api/FileAPI.class.php
Normal file
@ -0,0 +1,737 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
|
||||||
|
use Driver\SQL\Condition\Compare;
|
||||||
|
|
||||||
|
abstract class FileAPI extends Request {
|
||||||
|
|
||||||
|
protected function checkDirectory($parentId) {
|
||||||
|
if ($parentId === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->select("directory")
|
||||||
|
->from("UserFile")
|
||||||
|
->where(new Compare("uid", $parentId))
|
||||||
|
->limit(1)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
if (empty($res)) {
|
||||||
|
return $this->createError("Parent directory not found");
|
||||||
|
} else if(!$res[0]["directory"]) {
|
||||||
|
return $this->createError("Parent file is not a directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function downloadFile($name, $path) {
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
http_response_code(404);
|
||||||
|
die("404 - File does not exist anymore");
|
||||||
|
} else if(!is_file($path) || !is_readable($path)) {
|
||||||
|
die("403 - Unable to download file.");
|
||||||
|
} else {
|
||||||
|
$mimeType = @mime_content_type($path);
|
||||||
|
if ($mimeType) {
|
||||||
|
header("Content-Type: $mimeType");
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = trim(preg_replace('/\s\s+/', ' ', $name));
|
||||||
|
header("Content-Disposition: attachment; filename=$name");
|
||||||
|
|
||||||
|
//No cache
|
||||||
|
header('Expires: 0');
|
||||||
|
header('Cache-Control: must-revalidate');
|
||||||
|
header('Pragma: public');
|
||||||
|
|
||||||
|
//Define file size
|
||||||
|
header('Content-Length: ' . filesize($path));
|
||||||
|
|
||||||
|
ob_clean();
|
||||||
|
flush();
|
||||||
|
readfile($path);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isInDirectory($fileId, $directoryId) {
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Api\File {
|
||||||
|
|
||||||
|
use Api\FileAPI;
|
||||||
|
use Api\Parameter\Parameter;
|
||||||
|
use Api\Parameter\StringType;
|
||||||
|
use Driver\SQL\Condition\Compare;
|
||||||
|
use Driver\SQL\Condition\CondIn;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
|
class ValidateToken extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
'token' => new StringType('token', 36, false)
|
||||||
|
));
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$token = $this->getParam("token");
|
||||||
|
$res = $sql->select("UserFile.uid", "valid_until", "token_type", "maxFiles", "maxSize", "extensions", "name", "path", "directory")
|
||||||
|
->from("UserFileToken")
|
||||||
|
->leftJoin("UserFileTokenFile", "UserFileToken.uid", "token_id")
|
||||||
|
->innerJoin("UserFile", "UserFile.uid", "file_id")
|
||||||
|
->where(new Compare("token", $token))
|
||||||
|
->where(new Compare("valid_until", $sql->now(), ">"))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
if (empty($res)) {
|
||||||
|
return $this->createError("Invalid Token");
|
||||||
|
} else {
|
||||||
|
$row = $res[0];
|
||||||
|
$this->result["token"] = array(
|
||||||
|
"type" => $row["token_type"],
|
||||||
|
"valid_until" => $row["valid_until"]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->result["files"] = array();
|
||||||
|
foreach ($res as $row) {
|
||||||
|
$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") {
|
||||||
|
$this->result["restrictions"] = array(
|
||||||
|
"maxFiles" => $row["maxFiles"] ?? 0,
|
||||||
|
"maxSize" => $row["maxSize"] ?? 0,
|
||||||
|
"extensions" => $row["extensions"] ?? ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RevokeToken extends FileAPI {
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"token" => new StringType("token", 36)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$token = $this->getParam("token");
|
||||||
|
$res = $sql->select($sql->count())
|
||||||
|
->from("UserToken")
|
||||||
|
->where(new Compare("user_id", $this->user->getId()))
|
||||||
|
->where(new Compare("token", $token))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
if(empty($res)) {
|
||||||
|
return $this->getParam("Invalid token");
|
||||||
|
} else {
|
||||||
|
$res = $sql->update("UserToken")
|
||||||
|
->set("valid_until", new \DateTime())
|
||||||
|
->where(new Compare("user_id", $this->user->getId()))
|
||||||
|
->where(new Compare("token", $token))
|
||||||
|
->execute();
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListFiles extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"directory" => new Parameter("directory", Parameter::TYPE_INT, true, null)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$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) {
|
||||||
|
return $target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->select(
|
||||||
|
"UserFile.uid", "UserFile.directory", "UserFile.path", "UserFile.name",
|
||||||
|
"UserFile.user_id", "parentTable.uid as parentId", "parentTable.name as parentName")
|
||||||
|
->from("UserFile")
|
||||||
|
->leftJoin("UserFile", "UserFile.parent_id", "parentTable.uid", "parentTable")
|
||||||
|
->where(new Compare("UserFile.user_id", $this->user->getId()))
|
||||||
|
->orderBy("UserFile.parent_id")
|
||||||
|
->ascending()
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$files = array();
|
||||||
|
|
||||||
|
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");
|
||||||
|
if (!is_null($directoryId)) {
|
||||||
|
$wantedDir =& $this->findDirectory($files, $directoryId);
|
||||||
|
if ($files === $wantedDir) {
|
||||||
|
$files = array();
|
||||||
|
} else {
|
||||||
|
$files = $wantedDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->result["files"] = $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListTokens extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array());
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->select("uid","token","valid_until","token_type", "maxFiles", "maxSize", "extensions")
|
||||||
|
->from("UserFileToken")
|
||||||
|
->orderBy("valid_until")
|
||||||
|
->descending()
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->result["tokens"] = array();
|
||||||
|
foreach ($res as $row) {
|
||||||
|
$tokenType = $row["token_type"];
|
||||||
|
if ($tokenType !== "upload") {
|
||||||
|
unset($row["maxFiles"]);
|
||||||
|
unset($row["maxSize"]);
|
||||||
|
unset($row["extensions"]);
|
||||||
|
}
|
||||||
|
$this->result["tokens"][] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateDirectory extends FileAPI {
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
'name' => new StringType('name', 32),
|
||||||
|
'parentId' => new Parameter('parentId', Parameter::TYPE_INT, true, null)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$name = $this->getParam('name');
|
||||||
|
$parentId = $this->getParam("parentId");
|
||||||
|
if (!$this->checkDirectory($parentId)) {
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $sql->insert("UserFile", array("directory", "name", "user_id", "parent_id"))
|
||||||
|
->addRow(true, $name, $this->user->getId(), $parentId)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rename extends FileAPI { }
|
||||||
|
|
||||||
|
class Move extends FileAPI { }
|
||||||
|
|
||||||
|
class Upload extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
'parentId' => new Parameter('parentId', Parameter::TYPE_INT, true, null),
|
||||||
|
'token' => new StringType('token', 36, true, null)
|
||||||
|
));
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $this->getParam("token");
|
||||||
|
if (!$this->user->isLoggedIn() && is_null($token)) {
|
||||||
|
return $this->createError("Permission denied (expected token)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$parentId = $this->getParam("parentId");
|
||||||
|
if (!is_null($token) && !is_null($parentId)) {
|
||||||
|
return $this->createError("Cannot upload to parent directory using token");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($parentId) && !$this->checkDirectory($parentId)) {
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileKeys = array_keys($_FILES);
|
||||||
|
$numFilesUploaded = count($fileKeys);
|
||||||
|
|
||||||
|
if (!is_null($token)) {
|
||||||
|
|
||||||
|
$res = $sql->select("uid", "token_type", "maxFiles", "maxSize", "extensions", "user_id")
|
||||||
|
->from("UserFileToken")
|
||||||
|
->where(new Compare("token", $token))
|
||||||
|
->where(new Compare("valid_until", $sql->now(), ">="))
|
||||||
|
->limit(1)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
if (!$this->success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($res) || $res[0]["token_type"] !== "upload") {
|
||||||
|
return $this->createError("Permission denied (token)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenId = $res[0]["uid"];
|
||||||
|
$maxFiles = $res[0]["maxFiles"] ?? 0;
|
||||||
|
$maxSize = $res[0]["maxSize"] ?? 0;
|
||||||
|
$userId = $res[0]["user_id"];
|
||||||
|
$extensions = explode(",", strtolower($res[0]["extensions"] ?? ""));
|
||||||
|
|
||||||
|
$res = $sql->select($sql->count())
|
||||||
|
->from("UserFileTokenFile")
|
||||||
|
->innerJoin("UserFileToken", "token_id", "UserFileToken.uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
if (!$this->success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = $res[0]["count"];
|
||||||
|
if ($maxFiles > 0 && $numFilesUploaded > 0 && $numFilesUploaded + $count > $maxFiles) {
|
||||||
|
return $this->createError("File limit exceeded. Currently uploaded $count / $maxFiles count");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($maxSize > 0 || !empty($extensions)) {
|
||||||
|
foreach ($_FILES as $file) {
|
||||||
|
$name = $file["name"];
|
||||||
|
if ($maxSize > 0 && $file["size"] > $maxSize) {
|
||||||
|
return $this->createError("File Size limit of $maxSize bytes exceeded for file $name");
|
||||||
|
}
|
||||||
|
|
||||||
|
$dotPos = strrpos($name, ".");
|
||||||
|
$ext = ($dotPos !== false ? strtolower(substr($name, $dotPos + 1)) : false);
|
||||||
|
if (!empty($extensions) && $ext !== false && !in_array($ext, $extensions)) {
|
||||||
|
return $this->createError("File '$name' has prohibited extension. Allowed extensions: " . implode(",", $extensions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$userId = $this->user->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($numFilesUploaded === 0) {
|
||||||
|
return $this->createError("No file uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadDir = realpath($_SERVER["DOCUMENT_ROOT"] . "/files/uploaded/");
|
||||||
|
if (!is_writable($uploadDir)) {
|
||||||
|
return $this->createError("Upload directory is not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileIds = array();
|
||||||
|
foreach ($_FILES as $key => $file) {
|
||||||
|
$fileName = $file["name"];
|
||||||
|
$tmpPath = $file["tmp_name"];
|
||||||
|
$md5Hash = hash_file('md5', $tmpPath);
|
||||||
|
$sha1Hash = hash_file('sha1', $tmpPath);
|
||||||
|
$filePath = $uploadDir . "/" . $md5Hash . $sha1Hash;
|
||||||
|
if (move_uploaded_file($tmpPath, $filePath)) {
|
||||||
|
$res = $sql->insert("UserFile", array("name", "directory", "path", "user_id", "parent_id"))
|
||||||
|
->addRow($fileName, false, $filePath, $userId, $parentId)
|
||||||
|
->returning("uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if ($res === false) {
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
$this->success = false;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$fileIds[] = $sql->getLastInsertId();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $this->createError("Could not create file: " . $fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($token)) {
|
||||||
|
$query = $sql->insert("UserFileTokenFile", array("file_id", "token_id"));
|
||||||
|
foreach ($fileIds as $fileId) {
|
||||||
|
$query->addRow($fileId, $tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $query->execute();
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Download extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"id" => new Parameter("id", Parameter::TYPE_INT),
|
||||||
|
"token" => new StringType("token", 36, true, null)
|
||||||
|
));
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $this->getParam("token");
|
||||||
|
if (!$this->user->isLoggedIn() && is_null($token)) {
|
||||||
|
return $this->createError("Permission denied (expected token)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$fileId = $this->getParam("id");
|
||||||
|
$query = $sql->select("path", "name")
|
||||||
|
->from("UserFile")
|
||||||
|
->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();
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
if (empty($res)) {
|
||||||
|
if (is_null($token)) {
|
||||||
|
return $this->createError("File not found");
|
||||||
|
} else {
|
||||||
|
return $this->createError("Permission denied (token)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($res[0]["directory"]) {
|
||||||
|
return $this->createError("Cannot download directory (yet)");
|
||||||
|
} else {
|
||||||
|
$path = $res[0]["path"];
|
||||||
|
$name = $res[0]["name"];
|
||||||
|
$this->downloadFile($name, $path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Delete extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"id" => new Parameter("id", Parameter::TYPE_INT),
|
||||||
|
"token" => new StringType("token", 36, true, null)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateUploadToken extends FileAPI {
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"maxFiles" => new Parameter("maxFiles", Parameter::TYPE_INT, true, 1),
|
||||||
|
"maxSize" => new Parameter("maxSize", Parameter::TYPE_INT, true, null),
|
||||||
|
"extensions" => new StringType("extensions", 64, true, null),
|
||||||
|
"durability" => new Parameter("durability", Parameter::TYPE_INT, true, 60*24*2)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$maxFiles = $this->getParam("maxFiles");
|
||||||
|
$maxSize = $this->getParam("maxSize");
|
||||||
|
$extensions = $this->getParam("extensions");
|
||||||
|
$durability = $this->getParam("durability");
|
||||||
|
|
||||||
|
if (!is_null($maxFiles) && $maxFiles < 0) {
|
||||||
|
return $this->createError("Invalid number of maximum files.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($maxSize) && $maxSize < 0) {
|
||||||
|
return $this->createError("Invalid maximum size for uploaded files.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($durability) && $durability < 0) {
|
||||||
|
return $this->createError("Invalid durability.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($extensions)) {
|
||||||
|
$extensions = explode(",",$extensions);
|
||||||
|
foreach ($extensions as $i => $ext) {
|
||||||
|
if (strlen($ext) === 0 || (strlen($ext) === 1 && $ext[0] === ".")) {
|
||||||
|
unset ($extensions[$i]);
|
||||||
|
} else if ($ext[0] === ".") {
|
||||||
|
$extensions[$i] = substr($ext, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$extensions = implode(",", $extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$token = generateRandomString(36);
|
||||||
|
$validUntil = (new \DateTime())->modify("+$durability HOURS");
|
||||||
|
|
||||||
|
$res = $sql->insert("UserFileToken",
|
||||||
|
array("token", "token_type", "maxSize", "maxFiles", "extensions", "valid_until", "user_id"))
|
||||||
|
->addRow($token, "upload", $maxSize, $maxFiles, $extensions, $validUntil, $this->user->getId())
|
||||||
|
->returning("uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->result["token"] = $token;
|
||||||
|
$this->result["tokenId"] = $sql->getLastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateDownloadToken extends FileAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"durability" => new Parameter("durability", Parameter::TYPE_INT, true, 60*24*2),
|
||||||
|
"files" => new Parameter("files", Parameter::TYPE_ARRAY, false)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$durability = $this->getParam("durability");
|
||||||
|
$fileIds = $this->getParam("files");
|
||||||
|
|
||||||
|
if (!is_null($durability) && $durability < 0) {
|
||||||
|
return $this->createError("Invalid durability.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($fileIds as $fileId) {
|
||||||
|
if (!is_int($fileId) && is_numeric($fileId)) {
|
||||||
|
$fileId = intval($fileId);
|
||||||
|
}
|
||||||
|
if (!is_int($fileId) || $fileId < 1) {
|
||||||
|
return $this->createError("Invalid file id: $fileId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileIds = array_unique($fileIds);
|
||||||
|
|
||||||
|
// Check for files:
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->select("uid")
|
||||||
|
->from("UserFile")
|
||||||
|
->where(new CondIn("uid", $fileIds))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
if (!$this->success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
$token = generateRandomString(36);
|
||||||
|
$validUntil = (new \DateTime())->modify("+$durability HOURS");
|
||||||
|
$res = $sql->insert("UserFileToken", array("token_type", "valid_until", "user_id", "token"))
|
||||||
|
->addRow("download", $validUntil, $this->user->getId(), $token)
|
||||||
|
->returning("uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
if (!$this->success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenId = $sql->getLastInsertId();
|
||||||
|
$query = $sql->insert("UserFileTokenFile", array("token_id", "file_id"));
|
||||||
|
foreach ($fileIds as $fileId) {
|
||||||
|
$query->addRow($tokenId, $fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $query->execute();
|
||||||
|
$this->success = ($res !== false);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->result["token"] = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
core/Api/PatchSQL.class.php
Normal file
65
core/Api/PatchSQL.class.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Api;
|
||||||
|
|
||||||
|
use Api\Parameter\StringType;
|
||||||
|
use Configuration\DatabaseScript;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
|
class PatchSQL extends Request {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"className" => new StringType("className", 64)
|
||||||
|
));
|
||||||
|
$this->loginRequired = true;
|
||||||
|
$this->csrfTokenRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = $this->getParam("className");
|
||||||
|
$fullClassName = "\\Configuration\\Patch\\" . $className;
|
||||||
|
$path = getClassPath($fullClassName, true);
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
return $this->createError("File not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!class_exists($fullClassName)) {
|
||||||
|
return $this->createError("Class not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$reflection = new \ReflectionClass($fullClassName);
|
||||||
|
if (!$reflection->isInstantiable()) {
|
||||||
|
return $this->createError("Class is not instantiable");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$reflection->isSubclassOf(DatabaseScript::class)) {
|
||||||
|
return $this->createError("Not a database script.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$obj = $reflection->newInstance();
|
||||||
|
$queries = $obj->createQueries($sql);
|
||||||
|
if (!is_array($queries)) {
|
||||||
|
return $this->createError("Database script returned invalid values");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($queries as $query) {
|
||||||
|
if (!$query->execute()) {
|
||||||
|
return $this->createError("Query error: " . $sql->getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success = true;
|
||||||
|
} catch (\ReflectionException $e) {
|
||||||
|
return $this->createError("Error reflecting class: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
@ -187,6 +187,7 @@ class Request {
|
|||||||
public function success() { return $this->success; }
|
public function success() { return $this->success; }
|
||||||
public function loginRequired() { return $this->loginRequired; }
|
public function loginRequired() { return $this->loginRequired; }
|
||||||
public function isExternalCall() { return $this->externalCall; }
|
public function isExternalCall() { return $this->externalCall; }
|
||||||
|
public function clearError() { $this->success = true; $this->lastError = ""; }
|
||||||
|
|
||||||
private function getMethod() {
|
private function getMethod() {
|
||||||
$class = str_replace("\\", "/", get_class($this));
|
$class = str_replace("\\", "/", get_class($this));
|
||||||
|
@ -39,7 +39,7 @@ namespace Api\Settings {
|
|||||||
|
|
||||||
$query = $sql->select("name", "value") ->from("Settings");
|
$query = $sql->select("name", "value") ->from("Settings");
|
||||||
|
|
||||||
if (!is_null($key) && !empty($key)) {
|
if (!is_null($key)) {
|
||||||
$query->where(new CondRegex(new Column("name"), $key));
|
$query->where(new CondRegex(new Column("name"), $key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use Driver\SQL\SQL;
|
|||||||
use \Driver\SQL\Strategy\SetNullStrategy;
|
use \Driver\SQL\Strategy\SetNullStrategy;
|
||||||
use \Driver\SQL\Strategy\CascadeStrategy;
|
use \Driver\SQL\Strategy\CascadeStrategy;
|
||||||
|
|
||||||
class CreateDatabase {
|
class CreateDatabase extends DatabaseScript {
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// explicit serial ids removed due to postgres' serial implementation
|
// explicit serial ids removed due to postgres' serial implementation
|
||||||
@ -192,7 +192,10 @@ class CreateDatabase {
|
|||||||
->addRow("User/edit", array(USER_GROUP_ADMIN), "Allows users to edit details and group memberships of any user")
|
->addRow("User/edit", array(USER_GROUP_ADMIN), "Allows users to edit details and group memberships of any user")
|
||||||
->addRow("User/delete", array(USER_GROUP_ADMIN), "Allows users to delete any other user")
|
->addRow("User/delete", array(USER_GROUP_ADMIN), "Allows users to delete any other user")
|
||||||
->addRow("Permission/fetch", array(USER_GROUP_ADMIN), "Allows users to list all API permissions")
|
->addRow("Permission/fetch", array(USER_GROUP_ADMIN), "Allows users to list all API permissions")
|
||||||
->addRow("Visitors/stats", array(USER_GROUP_ADMIN, USER_GROUP_SUPPORT), "Allows users to see visitor statistics");
|
->addRow("Visitors/stats", array(USER_GROUP_ADMIN, USER_GROUP_SUPPORT), "Allows users to see visitor statistics")
|
||||||
|
->addRow("PatchSQL", array(USER_GROUP_ADMIN), "Allows users to import database patches");
|
||||||
|
|
||||||
|
self::loadPatches($queries, $sql);
|
||||||
|
|
||||||
return $queries;
|
return $queries;
|
||||||
}
|
}
|
||||||
@ -225,4 +228,22 @@ class CreateDatabase {
|
|||||||
"Best Regards<br>" .
|
"Best Regards<br>" .
|
||||||
"{{site_name}} Administration";
|
"{{site_name}} Administration";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function loadPatches(&$queries, $sql) {
|
||||||
|
$patchDirectory = './core/Configuration/Patch/';
|
||||||
|
if (file_exists($patchDirectory) && is_dir($patchDirectory)) {
|
||||||
|
$scan_arr = scandir($patchDirectory);
|
||||||
|
$files_arr = array_diff($scan_arr, array('.','..'));
|
||||||
|
foreach ($files_arr as $file) {
|
||||||
|
$suffix = ".class.php";
|
||||||
|
if (endsWith($file, $suffix)) {
|
||||||
|
$className = substr($file, 0, strlen($file) - strlen($suffix));
|
||||||
|
$className = "\\Configuration\\Patch\\$className";
|
||||||
|
$method = "$className::createQueries";
|
||||||
|
$patchQueries = call_user_func($method, $sql);
|
||||||
|
foreach($patchQueries as $query) $queries[] = $query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
core/Configuration/DatabaseScript.class.php
Normal file
9
core/Configuration/DatabaseScript.class.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Configuration;
|
||||||
|
|
||||||
|
use Driver\SQL\SQL;
|
||||||
|
|
||||||
|
abstract class DatabaseScript {
|
||||||
|
public static abstract function createQueries(SQL $sql);
|
||||||
|
}
|
72
core/Configuration/Patch/file_api.class.php
Normal file
72
core/Configuration/Patch/file_api.class.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Configuration\Patch;
|
||||||
|
|
||||||
|
use Configuration\DatabaseScript;
|
||||||
|
use Driver\SQL\SQL;
|
||||||
|
use Driver\SQL\Column\Column;
|
||||||
|
use Driver\SQL\Strategy\CascadeStrategy;
|
||||||
|
use Driver\SQL\Strategy\UpdateStrategy;
|
||||||
|
|
||||||
|
class file_api extends DatabaseScript {
|
||||||
|
|
||||||
|
public static function createQueries(SQL $sql) {
|
||||||
|
|
||||||
|
$queries = array();
|
||||||
|
|
||||||
|
$queries[] = $sql->insert("ApiPermission", array("method", "groups", "description"))
|
||||||
|
->onDuplicateKeyStrategy(new UpdateStrategy(array("method"), array("method" => new Column("method"))))
|
||||||
|
->addRow("File/Download", array(), "Allows users to download files when logged in, or using a given token")
|
||||||
|
->addRow("File/Upload", array(), "Allows users to upload files when logged in, or using a given token")
|
||||||
|
->addRow("File/ValidateToken", array(), "Allows users to validate a given token")
|
||||||
|
->addRow("File/RevokeToken", array(USER_GROUP_ADMIN), "Allows users to revoke a token")
|
||||||
|
->addRow("File/ListFiles", array(), "Allows users to list all files assigned to an account")
|
||||||
|
->addRow("File/ListTokens", array(USER_GROUP_ADMIN), "Allows users to list all tokens assigned to the virtual filesystem of an account")
|
||||||
|
->addRow("File/CreateDirectory", array(), "Allows users to create a virtual directory")
|
||||||
|
->addRow("File/Rename", array(), "Allows users to rename files in the virtual filesystem")
|
||||||
|
->addRow("File/Move", array(), "Allows users to move files in the virtual filesystem")
|
||||||
|
->addRow("File/Delete", array(), "Allows users to delete files in the virtual filesystem")
|
||||||
|
->addRow("File/CreateUploadToken", array(USER_GROUP_ADMIN), "Allows users to create a token to upload files to the virtual filesystem assigned to the users account")
|
||||||
|
->addRow("File/CreateDownloadToken", array(USER_GROUP_ADMIN), "Allows users to create a token to download files from the virtual filesystem assigned to the users account");
|
||||||
|
|
||||||
|
$queries[] = $sql->, array("request", "action", "target", "extra"))
|
||||||
|
->onDuplicateKeyStrategy(new UpdateStrategy(array("request"), array("request" => new Column("request"))))
|
||||||
|
->addRow("^/files(/.*)?$", "dynamic", "\\Documents\\Files", NULL);
|
||||||
|
|
||||||
|
$queries[] = $sql->createTable("UserFile")
|
||||||
|
->onlyIfNotExists()
|
||||||
|
->addSerial("uid")
|
||||||
|
->addBool("directory")
|
||||||
|
->addString("name", 64, false)
|
||||||
|
->addString("path", 512, true)
|
||||||
|
->addInt("parent_id", true)
|
||||||
|
->addInt("user_id", true)
|
||||||
|
->primaryKey("uid")
|
||||||
|
->unique("parent_id", "name")
|
||||||
|
->foreignKey("parent_id", "UserFile", "uid", new CascadeStrategy())
|
||||||
|
->foreignKey("user_id", "User", "uid", new CascadeStrategy());
|
||||||
|
|
||||||
|
$queries[] = $sql->createTable("UserFileToken")
|
||||||
|
->onlyIfNotExists()
|
||||||
|
->addSerial("uid")
|
||||||
|
->addString("token", 36, false)
|
||||||
|
->addDateTime("valid_until", true)
|
||||||
|
->addEnum("token_type", array("download", "upload"))
|
||||||
|
->addInt("user_id")
|
||||||
|
# upload only:
|
||||||
|
->addInt("maxFiles", true)
|
||||||
|
->addInt("maxSize", true)
|
||||||
|
->addString("extensions", 64, true)
|
||||||
|
->primaryKey("uid")
|
||||||
|
->foreignKey("user_id", "User", "uid");
|
||||||
|
|
||||||
|
$queries[] = $sql->createTable("UserFileTokenFile")
|
||||||
|
->addInt("file_id")
|
||||||
|
->addInt("token_id")
|
||||||
|
->unique("file_id", "token_id")
|
||||||
|
->foreignKey("file_id", "UserFile", "uid")
|
||||||
|
->foreignKey("token_id", "UserFileToken", "uid");
|
||||||
|
|
||||||
|
return $queries;
|
||||||
|
}
|
||||||
|
}
|
61
core/Documents/Files.class.php
Normal file
61
core/Documents/Files.class.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Documents {
|
||||||
|
|
||||||
|
use Documents\Files\FilesBody;
|
||||||
|
use Documents\Files\FilesHead;
|
||||||
|
use Elements\Document;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
|
class Files extends Document {
|
||||||
|
public function __construct(User $user, string $view = NULL) {
|
||||||
|
parent::__construct($user, FilesHead::class, FilesBody::class, $view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Documents\Files {
|
||||||
|
|
||||||
|
use Elements\Head;
|
||||||
|
use Elements\SimpleBody;
|
||||||
|
|
||||||
|
class FilesHead extends Head {
|
||||||
|
|
||||||
|
protected function initSources() {
|
||||||
|
$this->loadBootstrap();
|
||||||
|
$this->loadFontawesome();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initMetas() {
|
||||||
|
return array(
|
||||||
|
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
|
||||||
|
array('name' => 'format-detection', 'content' => 'telephone=yes'),
|
||||||
|
array('charset' => 'utf-8'),
|
||||||
|
array("http-equiv" => 'expires', 'content' => '0'),
|
||||||
|
array("name" => 'robots', 'content' => 'noarchive')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initRawFields() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initTitle() {
|
||||||
|
return "File Control Panel";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilesBody extends SimpleBody {
|
||||||
|
|
||||||
|
public function __construct($document) {
|
||||||
|
parent::__construct($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getContent() {
|
||||||
|
$html = "<noscript>" . $this->createErrorText("Javascript is required for this site to render.") . "</noscript>";
|
||||||
|
$html .= "<div id=\"root\"></div>";
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
core/Elements/EmptyBody.class.php
Normal file
7
core/Elements/EmptyBody.class.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Elements\Body;
|
||||||
|
|
||||||
|
class EmptyBody extends Body {
|
||||||
|
|
||||||
|
}
|
26
core/Elements/EmptyHead.class.php
Normal file
26
core/Elements/EmptyHead.class.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Elements;
|
||||||
|
|
||||||
|
class EmptyHead extends Head {
|
||||||
|
|
||||||
|
public function __construct($document) {
|
||||||
|
parent::__construct($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initSources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initMetas() {
|
||||||
|
return array(
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initRawFields() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initTitle() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
use External\PHPMailer\Exception;
|
|
||||||
|
|
||||||
abstract class View extends StaticView {
|
abstract class View extends StaticView {
|
||||||
|
|
||||||
private Document $document;
|
private Document $document;
|
||||||
@ -81,9 +79,10 @@ abstract class View extends StaticView {
|
|||||||
return $this->createList($items, "ul");
|
return $this->createList($items, "ul");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createLink($link, $title=null) {
|
protected function createLink($link, $title=null, $classes="") {
|
||||||
if(is_null($title)) $title=$link;
|
if(is_null($title)) $title=$link;
|
||||||
return "<a href=\"$link\">$title</a>";
|
if(!empty($classes)) $classes = " class=\"$classes\"";
|
||||||
|
return "<a href=\"$link\"$classes>$title</a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createExternalLink($link, $title=null) {
|
protected function createExternalLink($link, $title=null) {
|
||||||
@ -123,14 +122,91 @@ abstract class View extends StaticView {
|
|||||||
return $this->createStatusText("info", $text, $id, $hidden);
|
return $this->createStatusText("info", $text, $id, $hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createStatusText($type, $text, $id="", $hidden=false) {
|
protected function createStatusText($type, $text, $id="", $hidden=false, $classes="") {
|
||||||
if(strlen($id) > 0) $id = " id=\"$id\"";
|
if(strlen($id) > 0) $id = " id=\"$id\"";
|
||||||
$hidden = ($hidden?" hidden" : "");
|
if($hidden) $classes .= " hidden";
|
||||||
return "<div class=\"alert alert-$type$hidden\" role=\"alert\"$id>$text</div>";
|
if(strlen($classes) > 0) $classes = " $classes";
|
||||||
|
return "<div class=\"alert alert-$type$hidden$classes\" role=\"alert\"$id>$text</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createBadge($type, $text) {
|
protected function createBadge($type, $text) {
|
||||||
$text = htmlspecialchars($text);
|
$text = htmlspecialchars($text);
|
||||||
return "<span class=\"badge badge-$type\">$text</span>";
|
return "<span class=\"badge badge-$type\">$text</span>";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
protected function createJumbotron(string $content, bool $fluid=false, $class="") {
|
||||||
|
$jumbotronClass = "jumbotron" . ($fluid ? "-fluid" : "");
|
||||||
|
if (!empty($class)) $jumbotronClass .= " $class";
|
||||||
|
|
||||||
|
return "
|
||||||
|
<div class=\"row\">
|
||||||
|
<div class=\"col-12\">
|
||||||
|
<div class=\"$jumbotronClass\">
|
||||||
|
$content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createSimpleParagraph(string $content, string $class="") {
|
||||||
|
if($class) $class = " class=\"$class\"";
|
||||||
|
return "<p$class>$content</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createParagraph($title, $id, $content) {
|
||||||
|
$id = replaceCssSelector($id);
|
||||||
|
$iconId = urlencode("$id-icon");
|
||||||
|
return "
|
||||||
|
<div class=\"row mt-4\">
|
||||||
|
<div class=\"col-12\">
|
||||||
|
<h2 id=\"$id\" data-target=\"$iconId\" class=\"inlineLink\">$title</h2>
|
||||||
|
<hr/>
|
||||||
|
$content
|
||||||
|
</div>
|
||||||
|
</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createBootstrapTable($data, string $classes="") {
|
||||||
|
$classes = empty($classes) ? "" : " $classes";
|
||||||
|
$code = "<div class=\"container$classes\">";
|
||||||
|
foreach($data as $row) {
|
||||||
|
$code .= "<div class=\"row mt-2 mb-2\">";
|
||||||
|
$columnCount = count($row);
|
||||||
|
if($columnCount > 0) {
|
||||||
|
$remainingSize = 12;
|
||||||
|
$columnSize = 12 / $columnCount;
|
||||||
|
foreach($row as $col) {
|
||||||
|
$size = ($columnSize <= $remainingSize ? $columnSize : $remainingSize);
|
||||||
|
$content = $col;
|
||||||
|
$class = "";
|
||||||
|
$code .= "<div";
|
||||||
|
|
||||||
|
if(is_array($col)) {
|
||||||
|
$content = "";
|
||||||
|
foreach($col as $key => $val) {
|
||||||
|
if(strcmp($key, "content") === 0) {
|
||||||
|
$content = $val;
|
||||||
|
} else if(strcmp($key, "class") === 0) {
|
||||||
|
$class = " " . $col["class"];
|
||||||
|
} else if(strcmp($key, "cols") === 0 && is_numeric($val)) {
|
||||||
|
$size = intval($val);
|
||||||
|
} else {
|
||||||
|
$code .= " $key=\"$val\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($col["class"])) $class = " " . $col["class"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($size <= 6) $class .= " col-md-" . intval($size * 2);
|
||||||
|
$code .= " class=\"col-lg-$size$class\">$content</div>";
|
||||||
|
$remainingSize -= $size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$code .= "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$code .= "</div>";
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
define("WEBBASE_VERSION", "1.0.4");
|
define("WEBBASE_VERSION", "1.1.0");
|
||||||
|
|
||||||
function getProtocol() {
|
function getProtocol() {
|
||||||
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https" : "http";
|
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https" : "http";
|
||||||
@ -75,6 +75,10 @@ function replaceCssSelector($sel) {
|
|||||||
return preg_replace("~[.#<>]~", "_", preg_replace("~[:\-]~", "", $sel));
|
return preg_replace("~[.#<>]~", "_", preg_replace("~[:\-]~", "", $sel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function urlId($str) {
|
||||||
|
return urlencode(htmlspecialchars(preg_replace("[: ]","-", $str)));
|
||||||
|
}
|
||||||
|
|
||||||
function getClassPath($class, $suffix = true) {
|
function getClassPath($class, $suffix = true) {
|
||||||
$path = str_replace('\\', '/', $class);
|
$path = str_replace('\\', '/', $class);
|
||||||
$path = array_values(array_filter(explode("/", $path)));
|
$path = array_values(array_filter(explode("/", $path)));
|
||||||
@ -134,7 +138,6 @@ function serveStatic(string $webRoot, string $file) {
|
|||||||
$length = $size;
|
$length = $size;
|
||||||
|
|
||||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||||
$partialContent = true;
|
|
||||||
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
|
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
|
||||||
$offset = intval($matches[1]);
|
$offset = intval($matches[1]);
|
||||||
$length = intval($matches[2]) - $offset;
|
$length = intval($matches[2]) - $offset;
|
||||||
@ -166,4 +169,4 @@ function parseClass($class) {
|
|||||||
$parts = explode("\\", $class);
|
$parts = explode("\\", $class);
|
||||||
$parts = array_map('ucfirst', $parts);
|
$parts = array_map('ucfirst', $parts);
|
||||||
return implode("\\", $parts);
|
return implode("\\", $parts);
|
||||||
}
|
}
|
||||||
|
1
files/uploaded/.htaccess
Normal file
1
files/uploaded/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
DENY FROM ALL
|
@ -27,6 +27,8 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
|
$("#username").keypress(function (e) { if(e.which == 13) $("#password").focus(); });
|
||||||
|
$("#password").keypress(function (e) { if(e.which == 13) $("#btnLogin").click(); });
|
||||||
$("#btnLogin").click(function() {
|
$("#btnLogin").click(function() {
|
||||||
const username = $("#username").val();
|
const username = $("#username").val();
|
||||||
const password = $("#password").val();
|
const password = $("#password").val();
|
||||||
@ -45,7 +47,7 @@ $(document).ready(function () {
|
|||||||
btn.prop("disabled", false);
|
btn.prop("disabled", false);
|
||||||
$("#password").val("");
|
$("#password").val("");
|
||||||
createdDiv.hide();
|
createdDiv.hide();
|
||||||
showAlert(res.msg);
|
showAlert("danger", res.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -172,4 +174,4 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user