F diff --git a/css/style.css b/css/style.css --- a/css/style.css +++ b/css/style.cssbody {background: #f0f0f0;color: black;- font-family: Roboto, sans-serif;+ font-family: sans-serif;overflow: hidden;}}form {- background: #fbfbfb;+ background: white;margin: 4.5rem;padding-top: 0;box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);border-radius: 0.5rem;+ border-radius: 0.5rem;border: 1px solid #b9b9b9;}min-width: 300px;}- button,- input:not([type=file])- {+ input:not([type=file]) {border: 1px solid #bbb;- padding: 0.5rem 0.8rem;+ padding: 0.8rem 2rem 0.5rem 2rem;font-size: inherit;font-family: inherit;border-radius: 0.3rem;- background: #fcfcfc;+ background: #f6f6f6;}input[type=button], button, input[type=submit] {cursor: pointer;}+ input[type=button]:hover, button:hover, input[type=submit]:hover {+ background: white;+ }input[type=submit] {margin-top: 2rem;outline: none;}-- input:hover,- button:hover- {- background: white;+ input:focus,+ input:hover {+ background: white;}input:focus {padding: 0;box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);border-radius: 0.5rem;+ border-radius: 0.5rem;border: 1px solid #b9b9b9;display: block;padding: 1rem;display: grid;- grid-gap: 0rem;+ grid-gap: 20px;grid-auto-rows: 10rem;grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));}border-color: #ddd;}- .file > img {- flex: 1 0 0;- }-.file:hover > img {- filter: brightness(120%);- }-- .filesystem > h2 {- display: flex;- align-items: stretch;- font-weight: normal;- padding: 0rem;- border-bottom: 1px solid #bbb;-- }-- .filesystem > h2 button {- border: none;- padding: 0.3rem 1.1rem;- background: inherit;- border-radius: 0;- }-- .filesystem > h2 button:not(.pathentry):hover {- background: white;- }-- .filesystem > h2 > .separator {- flex: 0 0 1px;- align-self: stretch;- background: #bbb;- }-- .filesystem > h2 > *:first-child {- border-top-left-radius: 0.5rem;+ filter: brightness(150%);}.path {- display: flex;- align-items: stretch;- font-size: 0.8em;+ font-size: 1.5rem;}- .path > .separator {- user-select: none;- -webkit-user-select: none;- -ms-user-select: none;- padding: 0 0.2rem;- align-self: center;+ .path > a {+ color: #333;+ text-decoration: none;}- .pathentry:hover {+ .path > a:hover {text-decoration: underline;}- .context {- background: white;- position: absolute;- z-index: 1000;- list-style-type: none;- margin: 0;- padding: 0;- top: 0;- left: 0;-- border: 1px solid #ccc;- box-shadow: 0 0.3rem 0.5rem rgba(0,0,0,0.2);- }-- .context > li {- padding: 0.4rem 1.5rem;- margin: 0;-- user-select: none;- -webkit-user-select: none;- -ms-user-select: none;- }-- .context > li:hover {- background: #2e91db;- color: white;+ .filesystem > h2 {+ display: flex;+ align-items: baseline;+ font-weight: normal;}F diff --git a/index.php b/index.php --- a/index.php +++ b/index.php<html><head><meta charset="utf-8">- <link rel="preconnect" href="https://fonts.gstatic.com">- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet"><link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon"><title>shady file upload</title> <link rel="stylesheet" type="text/css" href="css/style.css"></head>F diff --git a/loggedin.js b/loggedin.js --- a/loggedin.js +++ b/loggedin.js- var FORM_ASYNC = true;+ var FORM_ASYNC = false;const upload_form = document.getElementById("upload_form");const the_file = document.getElementById("the_file");const filename_input = document.getElementById("filename");const upload_btn = document.getElementById("upload_btn");- const the_path = document.getElementById("the_path");const current_directory = document.getElementById("current_directory");the_file.onchange = on_file_added;- var pwd = [];- const pending_uploads = [];-- var context_menu = null;-- class FileView {- constructor(filename, visuals, mimetype, is_directory) {- this.filename = filename;- this.visuals = visuals;- this.mimetype = mimetype;- this.is_directory = is_directory;- }- }+ var files = [];- class PendingUpload {- constructor(fileview) {- this.fileview = fileview;- }- }+ const pwd = "/";- var files = [];+ const pending_uploads = [];function on_file_added(_e) {if (the_file.files.length >= 1) {return;}- var fileview = add_file_visuals(filename_input.value, false, "pending");-// Send the form asynchronously through the fetch apifetch(upload_form.action, {method: upload_form.method,body: new FormData(upload_form)}).then((resp) => {if (resp.status == 200) {- done_upload(fileview);- } else {+ add_file_visuals(filename_input.value, true);+ }+ else {alert("Upload failed");}}, () => {alert("Upload failed")});-- pending_uploads.push(fileview);++}else {alert("No files selected");}- }-- function done_upload(fileview) {- var index = pending_uploads.indexOf(fileview);- if (index >= 0)- pending_uploads.splice(index, 1);- load_dir();}- function load_dir() {-- while (the_path.children.length > 1)- the_path.removeChild(the_path.lastChild);-- for (let i = 0; i < pwd.length; i++) {- var d = pwd[i];-- var separator_div = document.createElement('div');- separator_div.classList.add('separator');- the_path.appendChild(separator_div);- separator_div.innerText = "ยป";-- var entry = document.createElement('button');- entry.classList.add('pathentry');- entry.innerText = d;- the_path.appendChild(entry);-- entry.onclick = () => {- pwd.length = i + 1;- load_dir();- }- }-+ function load_dir(pwd) {var data = new FormData();- data.append('path', get_path());+ data.append('path', '/');var xhr = new XMLHttpRequest();xhr.open('POST', '/php/readdir.php', true);xhr.onload = function () {for (const f of files)- f.visuals.remove();+ f[1].remove();files = [];var json = JSON.parse(this.responseText);for (const f of json) {- add_file_visuals(f.name, f.is_directory, f.mimetype);+ add_file_visuals(f.name, f.mimetype);}};xhr.send(data);}- function delete_file(filename) {- var file_full_path = get_path() + filename;-- var data = new FormData();- data.append('path', file_full_path);-- var xhr = new XMLHttpRequest();- xhr.open('POST', '/php/delete.php', true);- xhr.onload = function () {- load_dir();- };- xhr.send(data);- }-- function rename_file(filename) {- var file_full_path = get_path() + filename;-- var new_name = prompt(`Rename ${filename} to`, filename);- if (!new_name)- return;-- var data = new FormData();- data.append('path', file_full_path);- data.append('new_name', new_name);-- var xhr = new XMLHttpRequest();- xhr.open('POST', '/php/rename.php', true);- xhr.onload = function () {- load_dir();- };- xhr.send(data);- }-- function add_file_visuals(name, is_directory, mimetype) {+ function add_file_visuals(name, mimetype) {var fileDiv = document.createElement('div');var img = document.createElement('img');var filename = document.createElement('div');- if (is_directory) {- img.src="/mimeicons/directory.png";- fileDiv.onclick = () => {- pwd.push(name);- load_dir();- }- } else {- img.src=`/mimeicons/${mimetype.replace("/", "-")}.png`;- }-- fileDiv.oncontextmenu = (e) => {- context(e, [- ['Open', () => {- if (is_directory) {- pwd.push(name);- load_dir();- } else {- alert('not implemented');- }- }],- ['Rename', () => { rename_file(name); }],- ['Share', () => {alert('not implemented')}],- ['Delete', () => { delete_file(name); }],- ]);- e.preventDefault();- }-+ img.src="/mimeicons/application-pdf.png";fileDiv.classList.add('file');filename.classList.add('filename');filename.innerText = name;- if (mimetype == "pending")- fileDiv.classList.add('pending');-fileDiv.appendChild(img);fileDiv.appendChild(filename);current_directory.appendChild(fileDiv);- var file = new FileView(name, fileDiv, mimetype, is_directory);- files.push(file);- return file;+ files.push([name, fileDiv]);++ return fileDiv;}function begin_upload() {the_file.click();}- function context(e, entries) {- if (context_menu)- context_menu.remove();-- context_menu = document.createElement('ul');- context_menu.classList.add('context');-- context_menu.style.left = e.clientX + "px";- context_menu.style.top = e.clientY + "px";--- for (const e of entries) {- const li = document.createElement('li');- li.innerText = e[0];- li.onclick = e[1];- context_menu.appendChild(li);- }-- document.body.appendChild(context_menu);- }-- function get_path() {- var path = "/";- for (const d of pwd)- path += d + "/";- return path;- }-- document.body.onclick = () => {- if (context_menu)- context_menu.remove();- }-- load_dir();+ load_dir("/");F diff --git a/loggedin.php b/loggedin.php --- a/loggedin.php +++ b/loggedin.php<div><div class="filesystem">- <h2 style="display: flex; gap: 0rem;">- <button id="upload_btn" onclick="begin_upload()">Upload</button>- <div class="separator"></div>- <button id="upload_btn" onclick="begin_upload()">New Folder</button>- <div class="separator"></div>- <div class="path" id="the_path">- <!-- <a class="pathentry" href="#"> /</a><a class="pathentry" href="#">foo/</a><a class="pathentry" href="#">bar</a> -->- <button class="pathentry" onclick="pwd.length = 0; load_dir();"><?php echo $_SESSION['username'] ?>'s files</button>- </div>+ <h2 style="display: flex; gap: 1rem;">+ <div class="path">+ <a class="pathentry" href="#"> <?php echo $_SESSION['username'] ?>'s files/</a><a class="pathentry" href="#">foo/</a><a class="pathentry" href="#">bar</a></div>+ <input id="upload_btn" type="button" value="Upload" onclick="begin_upload()"></h2><div class="files" id="current_directory"><input type="file" name="the_file" id="the_file"></form>-<script src="loggedin.js"></script>F diff --git a/mimeicons/directory.png b/mimeicons/directory.png deleted file mode 100644B Binary files a/mimeicons/directory.png and /dev/null differF diff --git a/mimeicons/pending.png b/mimeicons/pending.png deleted file mode 100644B Binary files a/mimeicons/pending.png and /dev/null differF diff --git a/mimeicons/text-plain.png b/mimeicons/text-plain.png deleted file mode 100644B Binary files a/mimeicons/text-plain.png and /dev/null differF diff --git a/php/database.php b/php/database.php --- a/php/database.php +++ b/php/database.php{$ret=new User;- $prep=$this->pdo->prepare("select user_id,username,email,password from users where username=:username");+ $prep=$this->pdo->prepare("select user_id,username,email,password,home_directory from users where username=:username");$prep->bindParam(':username',$user);$prep->execute();$ret->user_id=$hold["user_id"];$ret->username=$hold["username"];$ret->email_address=$hold["email"];+ $ret->home_directory=$hold["home_directory"];return $ret;}else{$ret=$statement->execute(PDO::FETCH_ASSOC);return $ret["home_directory"];}- function get_node_id($name,$directory_id)+++ /*returns assoc array*/+ function get_nodes_with_name($name){- $hold=NULL;- $statement=NULL;- $ret=[];- if($name != NULL)+ $statement=$this->pdo->prepare(+ "select node_id+ from node_links+ where name=:name"+ );+ $statement->bindParam(':name',$name);+ if($statement->execute()==NULL){- if($directory_id!=NULL)- {- $statement=$this->pdo->prepare(- "select nl.node_id as id from node_links nl- inner join nodes n on n.node_id=nl.node_id- where name=:name and directory_id=:directory_id)");- $statement->bindParam(':name',$name);- $statement->bindParam(':directory_id',$directory_id);- }else- {- /*get all node_ids with the name name*/- $statement=$this->pdo->prepare("select node_id as id from nodes where name=:name");- $statement->bindParam(':name',$name);- }- if($statement==NULL)- {- error_log("statement is null");- exit(1);- }- }else {- $statement=$this->pdo->prepare("select node_id as id from node_links where directory_id=:dir_id");- $statement->bindParam(':dir_id',$directory_id);+ error_log("there was a problem with the sql statement at get_nodes_with_name");+ return [];+ }+ return $statement->fetchAll(PDO::FETCH_ASSOC);+ }++ /*returns assoc array*/+ function get_node_with_code($code)+ {+ $statement=$this->pdo->prepare(+ "select node_id as id+ from nodes+ where code=:code"+ );+ $statement->bindParam(':code',$code);+ if($statement->execute()==NULL)+ {+ error_log("there was a problem with the sql statement at get_nodes_with_code");+ return [];}+ return $statement->fetchAll(PDO::FETCH_ASSOC);+ }+ /* I think this only makes sense if node is a dir*/+ /* returns assoc array of nodes*/+ function get_links_of(int $node_id)+ {+ $statement=$this->pdo->prepare("+ select node_links.node_id as id,+ node_links.name as name,+ node_links.note as note,+ nodes.is_directory as is_directory,+ nodes.code as code+ from node_links+ inner join nodes on+ nodes.node_id=node_links.node_id+ where nodes.node_id=:id+ ");+ $statement->bindParam(':id',$node_id);+ if($statement->execute()==false)+ {+ error_log("there is an error in the sql statement in get_links_of");+ return [];+ }+ return $statement->fetchAll(PDO::FETCH_ASSOC);+ }++ /*if both are not null returns the id of the node with the name name in the directory_id node*/+ function get_node_id($name,$directory_id)+ {+ $statement=$this->pdo->prepare(+ "select nl.node_id as id from node_links nl+ inner join nodes n on n.node_id=nl.node_id+ where name=:name and directory_id=:directory_id)");+ $statement->bindParam(':name',$name);+ $statement->bindParam(':directory_id',$directory_id);if($statement->execute()==false){error_log("there is an error in the sql statement in get_node_id");- exit(1);+ return NULL;}-- while($hold=$statement->fetch(PDO::FETCH_ASSOC))+ $hold=$statement->fetch(PDO::FETCH_ASSOC);+ if($hold==false)+ {+ return NULL;+ }else{- print_r($hold);- array_push($ret,$hold["id"]);+ return $hold["id"];}- return $ret;}++++++ /*is used to get a random codename for the node as well*/function get_random_node_name(string $prefix){do{$proposal=uniqid($prefix,true);- }while($this->get_node_id($proposal,NULL)!=NULL);+ }while(count($this->get_nodes_with_name($proposal))!=0);return $proposal;}- /*returns NULL if node doesn't exist*/- /*if name is NULL return all node ids in the directory*/- /*if directory is NULL return all node ids with the name name*/- /*if both are null return NULL*/- /*returns node id*/++++++ /*this is used to create seperate roots for the users*/function create_dangling_directory(): int{- $dir_name=$this->get_random_node_name("");+ $code_name=$this->get_random_node_name("");global $storage_root;- $prep=$this->pdo->prepare("insert into nodes(is_directory,relative_path,name) values(true,:root,:name)");- $prep->bindParam(':name',$dir_name);+ $prep=$this->pdo->prepare("insert into nodes(is_directory,relative_path,code) values(true,:root,:code)");+ $prep->bindParam(':code',$code_name);$prep->bindParam(':root',$storage_root);if($prep->execute()==false){exit(1);}- $id=$this->get_node_id($dir_name,NULL);+ $id=$this->get_node_with_code($code_name);if(count($id)!=1){error_log("created a dangling directory but couldn't find it afterward. Fatal error!");}//print count($id);- return $id[0];+ return $id[0]["id"];+ }++ /*links source to target*/+ function link_nodes(int $target_id,int $source_id,string $name,string $note)+ {+ $statement=$this->pdo->prepare("+ insert into++ ");}- /*returns the file name as it must be in the filesystem*/- function create_file_node(string $filename): string+ /*returns the file name as it must be in the filesystem relative to the storage root*/+ function create_file_node(string $filename,int $dir_id): string{global $storage_root;+ /*checkout the directory*/+ $dir_prep=$this->pdo->prepare("+ select+ nodes.is_directory as is_directory,+ node_access.can_edit as can_edit+ from nodes+ inner join node_access on+ nodes.node_id=node_access.node_id+ where nodes.node_id=:dir_id+ ");+ if($dir_prep->execute()==false)+ {+ error_log("could not exedude dir sql statement in create_file_node");+ return "error";+ }+ $dir=$dir_prep->fetch(PDO::FETCH_ASSOC);+ if($dir["is_directory"]==false)+ {+ return "error";+ }+ if($dir["can_edit"]==false)+ {+ /*TODO*/+ return "error";+ }++ /*generate the node*/$code=$this->get_random_node_name("");if($filename==NULL)return "error";- $prep=$this->pdo->prepare("insert into nodes(is_directory,relative_path,name,code)- values(false,:root,:name,:code)+ $prep=$this->pdo->prepare("insert into nodes(is_directory,relative_path,code)+ values(false,:root,:code)");- $prep->bindParam(':name',$filename);$prep->bindParam(':root',$storage_root);$prep->bindParam(':code',$code);/*not so quiet error*/return "error";}+ /*link the node to the directory*/return $code;}+ /*checks if there is a link between two node_id-s*/function are_linked(int $directory_id,int $node_id): bool{$prepare=$this->pdo->prepare("select node_idF diff --git a/php/login.php b/php/login.php --- a/php/login.php +++ b/php/login.php}$_SESSION['username'] = $user->username;+ $_SESSION['user_object'] = $user;+header('Location: /');?>F diff --git a/php/node.php b/php/node.php --- a/php/node.php +++ b/php/node.php<?phprequire_once "database.php";+ require_once "user.php";- class Node++ /*returns an assoc arrat of Node-s*/+ /*path is in terms of the simulated filesystem*/+ function get_directory(string $abstract_path,User $user){- public $node_id;- public $is_directory;- public $relative_path;- public $type;- public $name;- public $note;- function __construct($node_id)+ if($abstract_path[0]!="/"){- $this->node_id=$node_id;+ return NULL;}- }-- class Current_Directory extends Node- {- /*an array of the dir_ids taken to reach here*/- public $path;-- function __construct($user_id)+ if($component=strtok($abstract_path,"/")==false){- $this->dir_id=get_home_id($user_id);- $this->path=[$dir_id];+ return NULL;}- function change_directory($directory_id):bool+ $current_dir=$database->get_node($component,$user->home_directory);+ if($current_dir==NULL)+ return NULL;+ /*traverse path*/+ while($component=strtok("/")){- global $database;- if(!$database->is_directory($directory_id))- {- return false;- }-+ $current_dir=get_node($component,$current_dir);+ if($current_dir==NULL)+ return NULL;}+ return get_links_of(NULL,$current_dir);}?>F diff --git a/php/upload.php b/php/upload.php --- a/php/upload.php +++ b/php/upload.phprequire_once "database.php";require_once "configuration.php";+ echo 1;if (!isset( $_POST["filename"]) || !isset($_FILES["the_file"])){- http_response_code(400);error_log("someone tried to upload something impropperly");+ http_response_code(400);exit(1);}+ echo 2;- $file = $_FILES["the_file"];- $filename= $_POST["filename"];+ $file=$_FILES["the_file"];+ $filename=$_POST["filename"];+ echo 3;$codename=$database->create_file_node($filename);+ echo $codename;if($codename=="error"){+ error_log("could not create file_node in upload.php");http_response_code(400);exit(0);}F diff --git a/php/user.php b/php/user.php --- a/php/user.php +++ b/php/user.phppublic $user_id;public $username;public $email_address;- public $current_directory;+ public $home_directory;}?>F diff --git a/sql/fileshare.sql b/sql/fileshare.sql --- a/sql/fileshare.sql +++ b/sql/fileshare.sqlis_directory boolean default false,relative_path varchar(500) not null,type varchar(20) not null default 'data',- note varchar(200) not null default "",code varchar(100) not null default "error",primary key (node_id));directory_id int not null,node_id int not null,name varchar(100) not null default 'no name',+ note varchar(200) not null default "",check (directory_id != node_id),foreign key (directory_id) references nodes(node_id),foreign key (node_id) references nodes(node_id)