aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin Borner2019-04-16 14:57:33 +0200
committerMarvin Borner2019-04-16 14:57:33 +0200
commita32ec653bf2acc777d64803189a03a4b07b24096 (patch)
tree25f199a5b15df8d333434e40a49d73f5274b6a10
parent0177a6d90f3310d81eda66193ee4f4832b6e4bbd (diff)
Added file sharing feature
-rw-r--r--src/main/kotlin/App.kt46
-rw-r--r--src/main/kotlin/DatabaseController.kt34
-rw-r--r--src/main/resources/css/files.css21
-rw-r--r--src/main/resources/fonts/ionicons .eotbin0 -> 112662 bytes
-rw-r--r--src/main/resources/js/files.js31
-rw-r--r--src/main/resources/js/fileview.js5
-rw-r--r--src/main/resources/views/files.rocker.html5
-rw-r--r--src/main/resources/views/index.rocker.html12
8 files changed, 147 insertions, 7 deletions
diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt
index dd1cfb4..4572cf1 100644
--- a/src/main/kotlin/App.kt
+++ b/src/main/kotlin/App.kt
@@ -107,6 +107,16 @@ fun main() {
* Deletes file
*/
post("/delete/*", ::delete, roles(Roles.USER))
+
+ /**
+ * Shares file
+ */
+ post("/share/*", ::shareFile, roles(Roles.USER))
+
+ /**
+ * Shows the shared file
+ */
+ get("/shared", ::renderSharedFile, roles(Roles.GUEST))
}
}
@@ -334,6 +344,42 @@ fun delete(ctx: Context) {
}
/**
+ * Shares the requested file via the accessId
+ */
+fun shareFile(ctx: Context) {
+ val userId = getVerifiedUserId(ctx)
+ if (userId > 0) {
+ val accessId = databaseController.getAccessId(ctx.splats()[0], userId)
+ ctx.result("${ctx.host()}/shared?id=$accessId")
+ }
+}
+
+/**
+ * Renders the shared file
+ */
+fun renderSharedFile(ctx: Context) {
+ val accessId = ctx.queryParam("id").toString()
+ val sharedFileData = databaseController.getSharedFile(accessId)
+ if (sharedFileData.first > 0 && sharedFileData.second.isNotEmpty()) {
+ val sharedFileLocation = "$fileHome/${sharedFileData.first}/${sharedFileData.second}"
+ if (isHumanReadable(File(sharedFileLocation))) {
+ ctx.render(
+ "fileview.rocker.html", model(
+ "content", Files.readAllLines(
+ Paths.get(sharedFileLocation),
+ Charsets.UTF_8
+ ).joinToString(separator = "\n"),
+ "filename", File(sharedFileLocation).name,
+ "extension", File(sharedFileLocation).extension
+ )
+ )
+ } else ctx.result(FileInputStream(File(sharedFileLocation)))
+ } else {
+ log.info("Unknown file!")
+ }
+}
+
+/**
* Declares the roles in which a user can be in
*/
enum class Roles : Role {
diff --git a/src/main/kotlin/DatabaseController.kt b/src/main/kotlin/DatabaseController.kt
index 088f342..7b229f4 100644
--- a/src/main/kotlin/DatabaseController.kt
+++ b/src/main/kotlin/DatabaseController.kt
@@ -19,6 +19,7 @@ class DatabaseController(dbFileLocation: String = "main.db") {
val path = text("path").uniqueIndex()
val userId = integer("userId").references(UserData.id)
val accessId = varchar("accessId", 64).uniqueIndex() // TODO: Add file sharing
+ val isShared = bool("isShared").default(false)
}
/**
@@ -237,6 +238,39 @@ class DatabaseController(dbFileLocation: String = "main.db") {
}
/**
+ * Returns the accessId of the given File
+ */
+ fun getAccessId(fileLocation: String, userId: Int): String {
+ return transaction {
+ try {
+ FileLocation.update({ (FileLocation.userId eq userId) and (FileLocation.path eq fileLocation) }) {
+ it[isShared] = true
+ }
+ FileLocation.select { (FileLocation.path eq fileLocation) and (FileLocation.userId eq userId) }.map { it[FileLocation.accessId] }[0]
+ } catch (_: Exception) {
+ ""
+ }
+ }
+ }
+
+ /**
+ * Gets the shared file via [accessId]
+ */
+ fun getSharedFile(accessId: String): Pair<Int, String> {
+ return transaction {
+ try {
+ if (FileLocation.select { FileLocation.accessId eq accessId }.map { it[FileLocation.isShared] }[0])
+ FileLocation.select { FileLocation.accessId eq accessId }.map { it[FileLocation.userId] to it[FileLocation.path] }[0]
+ else
+ Pair(-1, "")
+ } catch (_: org.jetbrains.exposed.exceptions.ExposedSQLException) {
+ log.warning("File does not exist!")
+ Pair(-1, "")
+ }
+ }
+ }
+
+ /**
* Checks whether the site has been set up
*/
fun isSetup(): Boolean {
diff --git a/src/main/resources/css/files.css b/src/main/resources/css/files.css
index 3ceb173..c7302bf 100644
--- a/src/main/resources/css/files.css
+++ b/src/main/resources/css/files.css
@@ -45,11 +45,15 @@ colgroup col:nth-child(2) {
}
colgroup col:nth-child(3) {
- width: 30%;
+ width: 25%;
}
colgroup col:nth-child(4) {
- width: 15%;
+ width: 10%;
+}
+
+colgroup col:nth-child(5) {
+ width: 10%;
}
.drop {
@@ -70,3 +74,16 @@ colgroup col:nth-child(4) {
transform: scale(1.3);
color: red;
}
+
+.share {
+ font-size: 20px;
+ background-color: inherit;
+ border: 0;
+ z-index: 100;
+ cursor: pointer;
+}
+
+.share:hover {
+ transform: scale(1.3);
+ color: dodgerblue;
+}
diff --git a/src/main/resources/fonts/ionicons .eot b/src/main/resources/fonts/ionicons .eot
new file mode 100644
index 0000000..671df91
--- /dev/null
+++ b/src/main/resources/fonts/ionicons .eot
Binary files differ
diff --git a/src/main/resources/js/files.js b/src/main/resources/js/files.js
index 8b8bd87..f776165 100644
--- a/src/main/resources/js/files.js
+++ b/src/main/resources/js/files.js
@@ -33,7 +33,8 @@ drop.addEventListener('drop', e => {
row.insertCell(0).innerHTML = file.name;
row.insertCell(1).innerHTML = bytesToSize(file.size);
row.insertCell(2).innerHTML = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
- row.insertCell(3).innerHTML = "<td><button class='delete'><i class='icon ion-md-trash'></i></button></td>";
+ row.insertCell(3).innerHTML = "<td><button class='share'><i class='icon ion-md-share'></i></button></td>";
+ row.insertCell(4).innerHTML = "<td><button class='delete'><i class='icon ion-md-trash'></i></button></td>";
setListeners();
@@ -128,6 +129,34 @@ function setListeners() {
parent.remove();
})
});
+
+ // share button
+ document.querySelectorAll(".share").forEach(element => {
+ element.addEventListener("click", e => {
+ e.stopPropagation();
+ const request = new XMLHttpRequest();
+ const parent = e.target.closest("tr");
+ const fileName = parent.getAttribute("data-href") || parent.getAttribute("data-path");
+
+ request.open("POST", `/share/${path}/${fileName}`);
+ request.onload = () => {
+ if (request.readyState === 4) {
+ if (request.status === 200) {
+ const input = document.createElement('input');
+ input.setAttribute('value', request.responseText);
+ document.body.appendChild(input);
+ input.select();
+ document.execCommand('copy');
+ document.body.removeChild(input);
+ alert("Copied url to clipboard!")
+ } else {
+ alert("Something went wrong.");
+ }
+ }
+ };
+ request.send();
+ })
+ });
}
setListeners();
diff --git a/src/main/resources/js/fileview.js b/src/main/resources/js/fileview.js
index ace7884..d78c342 100644
--- a/src/main/resources/js/fileview.js
+++ b/src/main/resources/js/fileview.js
@@ -1,6 +1,7 @@
const preview = document.getElementById("preview");
const content = document.getElementById("content");
-const body = document.getElementsByTagName("body")[0];
+const html = document.getElementsByTagName("html")[0];
+const body = document.body;
// buttons
const raw = document.getElementById("raw");
@@ -23,12 +24,14 @@ if (extension === "md" || extension === "html") {
raw.addEventListener("click", () => {
if (preview.style.display === "block") {
+ html.style.overflow = "visible";
body.style.overflow = "visible";
raw.innerText = "Show preview";
preview.style.display = "none";
content.style.display = "block";
settings.style.display = "block";
} else {
+ html.style.overflow = "hidden";
body.style.overflow = "hidden";
raw.innerText = "Show raw";
preview.style.display = "block";
diff --git a/src/main/resources/views/files.rocker.html b/src/main/resources/views/files.rocker.html
index acbe812..0e42891 100644
--- a/src/main/resources/views/files.rocker.html
+++ b/src/main/resources/views/files.rocker.html
@@ -35,6 +35,7 @@
<th data-asc="true">Name</th>
<th data-asc="true">Size</th>
<th data-asc="true">Last modified</th>
+ <th data-asc="true">Share</th>
<th data-asc="true">Delete</th>
</tr>
<tr data-href="../">
@@ -42,6 +43,7 @@
<td></td>
<td></td>
<td></td>
+ <td></td>
</tr>
</thead>
@@ -56,6 +58,9 @@
<td data-size="@fileArray[4]">@fileArray[1]</td>
<td data-date="@fileArray[5]">@fileArray[2]</td>
<td>
+ <button class="share"><i class="icon ion-md-share"></i></button>
+ </td>
+ <td>
<button class="delete"><i class="icon ion-md-trash"></i></button>
</td>
</tr>
diff --git a/src/main/resources/views/index.rocker.html b/src/main/resources/views/index.rocker.html
index 4f5d32b..a0b4035 100644
--- a/src/main/resources/views/index.rocker.html
+++ b/src/main/resources/views/index.rocker.html
@@ -4,10 +4,16 @@
<h1>Welcome to Kloud <i>@username</i>!</h1>
@if(username.length() > 0) {
-<button><a href="/logout/">Logout</a></button>
+<a href="/logout">
+ <button>Logout</button>
+</a>
} else {
-<button><a href="/login/">Login</a></button>
+<a href="/login">
+ <button>Login</button>
+</a>
}
-<button><a href="/files/">Files</a></button>
+<a href="/files/">
+ <button>Files</button>
+</a>
}