F diff --git a/css/style.css b/css/style.css --- a/css/style.css +++ b/css/style.cssdisplay: flex;flex: 1 0 0;align-items: stretch;++ user-select: none;+ -webkit-user-select: none;+ -ms-user-select: none;}#page > * {justify-content: center;}- .filesystem > h2,form > h2 {color: #4d4d4d;margin: 0;border-top-right-radius: 0.5rem;}++ .window > h2 button {+ border: none;+ padding: 0.3rem 1rem;+ background: inherit;+ border-radius: 0;+ }++ .window > h2 button:not(.pathentry):hover {+ background: white;+ }++ .window > h2 > .separator {+ flex: 0 0 1px;+ align-self: stretch;+ background: #bbb;+ }++ .window > h2 > *:first-child {+ border-top-left-radius: 0.5rem;+ }form p {margin: 1rem 0px 0.3rem 0px;}-.hero_form_error {animation: fadein 0.2s;background-color: #ff4d4d;display: none;}- .filesystem {+ .window {background: #fafafa;- margin: 1rem;+ margin: 0rem;padding: 0;box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);border-radius: 0.5rem;border: 1px solid #b9b9b9;display: block;+ position: absolute;+ top: 0;+ left: 0;+ width: 800px;+ height: 600px;+ }++ .window > h2 {+ color: #4d4d4d;+ background: #f0f0f0;+ margin: 0;+ border-top-left-radius: 0.5rem;+ border-top-right-radius: 0.5rem;++ cursor: grab;++ display: flex;+ align-items: stretch;+ font-weight: normal;+ padding: 0rem;+ border-bottom: 1px solid #bbb;+ font-size: 1.3rem;}+.files {padding: 0.3rem;display: grid;filter: brightness(120%);}- .filesystem > h2 {- display: flex;- align-items: stretch;- font-weight: normal;- padding: 0rem;- border-bottom: 1px solid #bbb;- font-size: 1.3rem;- }-- .filesystem > h2 button {- border: none;- padding: 0.3rem 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;- }.path {display: flex;F diff --git a/loggedin.js b/loggedin.js --- a/loggedin.js +++ b/loggedin.jsconst 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");+ const current_directory = document.getElementById("current_directory");const upload_parent_directory = document.getElementById("upload_parent_directory");the_file.onchange = on_file_added;- var pwd = [];+ var windows = [];+ var focus = null;var context_menu = null;var dragging = null;body: new FormData(upload_form)}).then((resp) => {if (resp.status == 200) {- load_dir();+ openfile(true);} else {alert("Upload failed");}}}- function load_dir() {- while (the_path.children.length > 1)+ function update_path_visuals() {+ var the_path = focus.visuals.getElementsByClassName('path')[0];++ while (the_path.children.length > 0)the_path.removeChild(the_path.lastChild);- for (let i = 0; i < pwd.length; i++) {- var d = pwd[i];+ for (let i = -1; i < focus.pwd.length; i++) {+ var d;+ if (i >= 0) {+ d = focus.pwd[i];+ var separator_div = document.createElement('div');+ separator_div.classList.add('separator');+ the_path.appendChild(separator_div);+ separator_div.innerText = "»";+ }+ else+ d = "Root";- 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');add_link_functionality(entry, i + 1);}+ }++ function openfile_nondir() {+ var data = new FormData();+ data.append('folder', get_path(focus.pwd.length - 1));+ data.append('filename', focus.pwd[focus.pwd.length - 1]);++ var xhr = new XMLHttpRequest();++ focus.pwd.push();++ update_path_visuals();++ xhr.open('POST', '/php/readfile.php', true);++ focus.filecontents.innerText = "Loading...";+ focus.filecontents.style.display = 'block';+ focus.filegrid.style.display = 'none';++ xhr.onload = function () {+ focus.filecontents.innerText = xhr.responseText;+ };+ xhr.send(data);+ }++ function opendir() {++ update_path_visuals();var data = new FormData();data.append('path', get_path());};xhr.send(data);++ focus.filecontents.style.display = 'none';+ focus.filegrid.style.display = 'grid';+ }++ function openfile(is_directory) {+ if (is_directory) {+ opendir();+ } else {+ openfile_nondir();+ }}function delete_file(filename) {var xhr = new XMLHttpRequest();xhr.open('POST', '/php/delete.php', true);xhr.onload = function () {- load_dir();+ openfile(true);};xhr.send(data);}var xhr = new XMLHttpRequest();xhr.open('POST', '/php/rename.php', true);xhr.onload = function () {- load_dir();+ openfile(true);};xhr.send(data);}var xhr = new XMLHttpRequest();xhr.open('POST', '/php/move.php', true);xhr.onload = function () {- load_dir();+ openfile(true);};xhr.send(data);}var xhr = new XMLHttpRequest();xhr.open('POST', '/php/mkdir.php', true);xhr.onload = function () {- load_dir();+ openfile(true);};xhr.send(data);}- function begin_drag(e, fileview) {+ function begin_drag_fileview(e, fileview) {if (dragging)end_drag();dragging_placeholder = document.createElement('div');- fileview.visuals.parentNode.insertBefore(dragging_placeholder, fileview.visuals);+ dragging_fileview = fileview;dragging = fileview.visuals;- dragging_fileview = fileview;++ // document.body.appendChild(dragging);++ begin_drag(e, fileview.visuals,);+ }++ function begin_drag(e, obj) {++ dragging = obj;dragging.classList.add("dragged");var elemRect = dragging.getBoundingClientRect();+ dragging_offset_x = e.clientX - elemRect.left;+ dragging_offset_y = -e.clientY + elemRect.top;+- dragging_offset_x = elemRect.width - (elemRect.left - e.clientX);- dragging_offset_y = elemRect.top - e.clientY;+ if (dragging_placeholder)+ obj.parentNode.insertBefore(dragging_placeholder, obj);++ dragging.style.left = (e.clientX - dragging_offset_x) + "px";+ dragging.style.top = (e.clientY + dragging_offset_y) + "px";- dragging.style.position = "absolute";dragging.style.width = elemRect.width + "px";dragging.style.height = elemRect.height + "px";- document.body.appendChild(dragging);- dragging.style.left = (e.clientX - dragging_offset_x) + "px";- dragging.style.top = (e.clientY + dragging_offset_y) + "px";+ dragging.style.position = "absolute";+ document.body.appendChild(dragging);}- function end_drag(e) {- dragging_placeholder.parentNode.insertBefore(dragging, dragging_placeholder);- dragging_placeholder.remove();- dragging.style.removeProperty("position");- dragging.style.removeProperty("width");- dragging.style.removeProperty("height");- dragging.style.removeProperty("left");- dragging.style.removeProperty("top");+ function end_drag(_e) {+ if (dragging_placeholder) {+ dragging_placeholder.parentNode.insertBefore(dragging, dragging_placeholder);+ dragging_placeholder.remove();+ dragging_placeholder = null;+ }++ if (dragging_fileview) {+ dragging.style.removeProperty("position");+ dragging.style.removeProperty("width");+ dragging.style.removeProperty("height");+ dragging.style.removeProperty("left");+ dragging.style.removeProperty("top");+ dragging_fileview = null;+ }+dragging.classList.remove("dragged");dragging = null;}function add_link_functionality(link, length) {link.onclick = () => {- pwd.length = length,- load_dir();+ focus.pwd.length = length,+ openfile(true);}link.onmouseup = (e) => {- if (dragging) {+ if (dragging && dragging_fileview) {var new_folder = get_path(length);move_file(new_folder, dragging_fileview.filename);end_drag();}}- add_link_functionality(document.getElementById("home_path_entry"), 0);+ class Window {+ constructor(pwd) {+ this.pwd = pwd;+ }+ }- function open_file(fileview) {- var data = new FormData();- data.append('folder', get_path());- data.append('filename', fileview.filename);- var xhr = new XMLHttpRequest();- xhr.open('POST', '/php/readfile.php', true);- xhr.onload = function () {- console.log(xhr.responseText);- };- xhr.send(data);+ function make_window(pwd) {+ var wnd = new Window(pwd);+ windows.push(wnd);++ var wnd_html = document.createElement('div');+ wnd_html.classList.add('window');++ var h2 = document.createElement('h2');+ wnd_html.appendChild(h2);++ h2.onmousedown = (e) => {+ begin_drag(e, wnd_html);+ e.preventDefault();+ };++ var upload_btn = document.createElement('button');+ upload_btn.innerText = "Upload";+ upload_btn.onclick = () => { begin_upload(); }+ h2.appendChild(upload_btn);++ var separator = document.createElement('div');+ separator.classList.add('separator');+ h2.appendChild(separator);++ var new_folder_btn = document.createElement('button');+ new_folder_btn.innerText = "New Folder";+ new_folder_btn.onclick = () => { new_folder(); }+ h2.appendChild(new_folder_btn);++ separator = document.createElement('div');+ separator.classList.add('separator');+ h2.appendChild(separator);++ path = document.createElement('div');+ path.classList.add('path');+ h2.appendChild(path);++ wnd_html.style.width = "800px";+ wnd_html.style.height = "600px";+ wnd_html.style.position = "absolute";+ wnd_html.style.left = "300px";+ wnd_html.style.top = "300px";++ wnd.visuals = wnd_html;++ var filegrid = document.createElement('div');+ filegrid.classList.add('files');+ wnd_html.appendChild(filegrid);+ wnd.filegrid = filegrid;++ var filecontents = document.createElement('div');+ filegrid.classList.add('filecontents');+ wnd_html.appendChild(filecontents);+ wnd.filecontents = filecontents;++ document.body.appendChild(wnd_html);+ return wnd;}+function add_file_visuals(fileview) {var visuals = document.createElement('div');fileview.visuals = visuals;if (fileview.is_directory) {img.src="/mimeicons/directory.png";- visuals.onclick = () => {- pwd.push(fileview.filename);- load_dir();- }} else {img.src=`/mimeicons/${fileview.mimetype.replace("/", "-")}.png`;- visuals.onclick = () => {- open_file(fileview);- }+ }++ visuals.onclick = () => {+ focus.pwd.push(fileview.filename);+ openfile(fileview.is_directory);}visuals.oncontextmenu = (e) => {if (!dragging) {context(e, [['Open', () => {- if (fileview.is_directory) {- pwd.push(fileview.filename);- load_dir();- } else {- open_file(fileview);- }+ focus.pwd.push(fileview.filename);+ openfile(fileview.is_directory);}],+ ['Open in New Window', () => {alert('not implemented')}],['Rename', () => { rename_file(fileview.filename); }],['Share', () => {alert('not implemented')}],['Delete', () => { delete_file(fileview.filename); }],}visuals.ondragstart = (e) => {- begin_drag(e, fileview);+ begin_drag_fileview(e, fileview);e.preventDefault();};visuals.appendChild(img);visuals.appendChild(filename);- current_directory.appendChild(visuals);+ focus.visuals.getElementsByClassName('files')[0].appendChild(visuals);}function begin_upload() {function get_path(max_length) {if (max_length == undefined) {- max_length = pwd.length;+ max_length = focus.pwd.length;}var path = "/";for (let i = 0; i < max_length; i++) {- path += pwd[i];+ path += focus.pwd[i];if (i != max_length - 1)path += "/";}}}- document.body.onmouseup = (e) => {+ document.body.onmouseup = (_e) => {if (dragging)end_drag();}context_menu.remove();}- load_dir();++ var root_window = make_window([]);+ focus = root_window;+ openfile(true);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="new_folder()">New Folder</button>- <div class="separator"></div>- <div class="path" id="the_path">- <button class="pathentry" id="home_path_entry">- <?php- $user=$_SESSION['user_object'];- echo $user->username;- ?>- 's files</button>- </div>- </h2>-- <div class="files" id="current_directory">- </div>- </div>-- </div>-<form id="upload_form" style="display:none;" action="php/upload.php" method="post" enctype="multipart/form-data"><input id="filename" name="filename"><input type="file" name="the_file" id="the_file">F diff --git a/php/configuration.php b/php/configuration.php --- a/php/configuration.php +++ b/php/configuration.php$storage_root = "/srv/apache/testing/project/files/";}- /*if we save deleted files just in case of an error*/- $has_trash=true;+$password_hash_algo=PASSWORD_BCRYPT;$has_email_verification=false;F diff --git a/php/database.php b/php/database.php --- a/php/database.php +++ b/php/database.php}}- /*returns NULL if directory or error*/- function get_code_of_node(int $node_id)- {- global $storage_root;-- $prep=$this->pdo->prepare("select code- from nodes- where node_id=:id- ");- $prep->bindParam(':id',$node_id);- if($prep->execute()==false)- {- error_log("could not execute sql statement in get_file_location_of_node");- return NULL;- }- $hold=$prep->fetch(PDO::FETCH_ASSOC);- if(count($hold)!=1)- {- return NULL;- }else- {- /*BEWARE*/- return $hold["code"];- }- }- /*- we remove the node and- 1. move the file represented by the node to the trash folder- 2. remove the file- depends on the conf file- */- function delete_node_by_id(int $node_id)- {- global $has_trash;- global $storage_root;-- $location=get_file_location_of_node($node_id);-- /*actually delete the file*/- if($has_trash)- {- /*BEWARE*/- if(!copy($storage_root."/".$location,$storage_root."/trash/".$location))- {- error_log("could not copy file aborting node deletion in delete_node_by_id");- return;- }- }- unlink($storage_root,"/".$location);-- if($location==NULL)- {- error_log("trying to delete a node that does not exist in delete_node_by_id!");- return;- }- $prep=$this->pdo->prepare("delete- from nodes- where node_id=:id- ");- $prep->bindParam(':id',$node_id);- if($prep->execute()==false)- {- error_log("sql statement in delete_node_by_id could not execute");- return NULL;- }- }/*this is used to create seperate roots for the users*/function create_dangling_directory(): intreturn $id["id"];}-/*links source to target*/function link_nodes(int $target_id,int $source_id,string $name,string $note){error_log("there was an error with the statement ni link_nodes");}}-- function create_home_directory():int- {- $ret=$this->create_dangling_directory();- $trash_folder_id=$this->create_dangling_directory();- $this->link_nodes($ret,$trash_folder_id,"trash","trash folder");- return $ret;- }-function check_if_name_is_taken(string $filename,int $dir_id):bool{if($this->get_node_id($filename,$dir_id)!=NULL)}else{$hashed_pass=password_hash($password,$password_hash_algo);- $home_dir=$this->create_home_directory();+ $home_dir=$this->create_dangling_directory();$prep=$this->pdo->prepare("insert into users(username,password,email,home_directory) values(:username,:password,:email,:dir)");$prep->bindParam(':username',$user);$prep->bindParam(':password',$hashed_pass);F diff --git a/php/delete.php b/php/delete.php deleted file mode 100644 --- a/php/delete.php +++ /dev/null- <?php----- ?>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";+/*path is in terms of the simulated filesystem*/function get_directory(string $abstract_path,User $user){-global $database;- if($abstract_path[0] != "/")- {+ if($abstract_path[0] != "/") {return NULL;}- $component = strtok($abstract_path,"/");- $current_dir = $user->home_directory;+ $component = strtok($abstract_path,"/");+ $current_dir = $user->home_directory;- while($component)- {+ while ($component) {$current_dir = $database->get_node_id($component, $current_dir);- $component = strtok("/");- }+ $component = strtok("/");+ };- return $current_dir;+ return $current_dir;}/*returns an assoc arrat of Node-s*/global $database;$parent_dir_id=get_directory($abstract_path,$user);-if($database->check_if_name_is_taken($directory_name,$parent_dir_id)){return NULL;F diff --git a/php/readfile.php b/php/readfile.php --- a/php/readfile.php +++ b/php/readfile.php$dir = get_directory($folder, $user);if (!$dir) {- error_log("/php/readfile.php - invalid directory");+ error_log("/php/readfile.php - invalid directory '$folder'");http_response_code(409);exit(0);}}}if (!$file_node) {- error_log("/php/readfile.php - invalid filename");+ error_log("/php/readfile.php - invalid filename '$filename'");http_response_code(409);exit(0);}F diff --git a/sql/fileshare.sql b/sql/fileshare.sql --- a/sql/fileshare.sql +++ b/sql/fileshare.sqlemail varchar(50),home_directory int not null,primary key (user_id),- foreign key (home_directory) references nodes(node_id) on delete cascade+ foreign key (home_directory) references nodes(node_id));create table node_access (can_view boolean not null default true,can_edit boolean not null default false,- foreign key (node_id) references nodes(node_id) on delete cascade,- foreign key (user_id) references users(user_id) on delete cascade+ foreign key (node_id) references nodes(node_id),+ foreign key (user_id) references users(user_id));/*we can name a node in many different ways */create table node_links (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) on delete cascade,- foreign key (node_id) references nodes(node_id) on delete cascade+ foreign key (directory_id) references nodes(node_id),+ foreign key (node_id) references nodes(node_id));