From c6620d3395ccab6983643b5581f1b7e0bcaf2e53 Mon Sep 17 00:00:00 2001 From: Lars Westermann Date: Thu, 30 May 2019 21:30:46 +0200 Subject: [PATCH] Add backup --- .../kotlin/de/kif/common/model/Post.kt | 1 + .../frontend/views/WorkGroupConstraints.kt | 51 +++-- .../de/kif/frontend/views/table/TableLine.kt | 2 +- src/jsMain/resources/style/style.scss | 65 ++++++- .../kotlin/de/kif/backend/backup/Backup.kt | 82 ++++++++ .../de/kif/backend/database/Connection.kt | 58 +++++- .../kotlin/de/kif/backend/database/Schema.kt | 1 + .../kif/backend/repository/PostRepository.kt | 5 +- .../kotlin/de/kif/backend/route/Account.kt | 181 ++++++++++++++++++ .../kotlin/de/kif/backend/route/Overview.kt | 32 +++- .../kotlin/de/kif/backend/route/WorkGroup.kt | 2 - .../de/kif/backend/util/LogbackFilter.kt | 18 ++ .../de/kif/backend/view/MainTemplate.kt | 10 +- src/jvmMain/resources/logback.xml | 2 +- 14 files changed, 471 insertions(+), 39 deletions(-) create mode 100644 src/jvmMain/kotlin/de/kif/backend/backup/Backup.kt create mode 100644 src/jvmMain/kotlin/de/kif/backend/util/LogbackFilter.kt diff --git a/src/commonMain/kotlin/de/kif/common/model/Post.kt b/src/commonMain/kotlin/de/kif/common/model/Post.kt index 532b087..6b8f1ad 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Post.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Post.kt @@ -12,6 +12,7 @@ data class Post( val url: String, val image: String?, val pinned: Boolean, + val hideOnProjector: Boolean, override val createdAt: Long = 0, override val updateAt: Long = 0 ) : Model { diff --git a/src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt b/src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt index 80e4fc9..9286364 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt @@ -1,5 +1,6 @@ package de.kif.frontend.views +import de.kif.frontend.iterator import de.kif.frontend.launch import de.kif.frontend.repository.WorkGroupRepository import de.westermann.kobserve.event.EventListener @@ -14,16 +15,12 @@ fun initWorkGroupConstraints() { var index = 10000 val constraints = - ListView.wrap(document.getElementsByClassName("work-group-constraints")[0] as HTMLElement) + document.getElementsByClassName("work-group-constraints")[0] as HTMLElement val addButton = View.wrap(document.getElementsByClassName("work-group-constraints-add")[0] as HTMLElement) val addList = ListView.wrap(document.getElementsByClassName("work-group-constraints-add-list")[0] as HTMLElement) - console.log(constraints.html) - console.log(addButton.html) - console.log(addList.html) - addButton.onClick { addList.classList += "active" @@ -39,9 +36,12 @@ fun initWorkGroupConstraints() { addList.textView("Add only on day") { onClick { - constraints.html.appendChild(View.wrap(createHtmlView()) { + constraints.appendChild(View.wrap(createHtmlView()) { classList += "input-group" - html.appendChild(TextView("On day").apply { classList += "form-btn" }.html) + html.appendChild(TextView("On day").apply { + classList += "form-btn" + onClick { this@wrap.html.remove() } + }.html) html.appendChild(InputView(InputType.NUMBER).apply { classList += "form-control" html.name = "constraint-only-on-day-${index++}" @@ -53,9 +53,12 @@ fun initWorkGroupConstraints() { } addList.textView("Add only after time") { onClick { - constraints.html.appendChild(View.wrap(createHtmlView()) { + constraints.appendChild(View.wrap(createHtmlView()) { classList += "input-group" - html.appendChild(TextView("After time").apply { classList += "form-btn" }.html) + html.appendChild(TextView("After time").apply { + classList += "form-btn" + onClick { this@wrap.html.remove() } + }.html) html.appendChild(InputView(InputType.NUMBER).apply { classList += "form-control" html.name = "constraint-only-after-time-${index++}" @@ -67,9 +70,12 @@ fun initWorkGroupConstraints() { } addList.textView("Add not at same time") { onClick { - constraints.html.appendChild(View.wrap(createHtmlView()) { + constraints.appendChild(View.wrap(createHtmlView()) { classList += "input-group" - html.appendChild(TextView("Not with").apply { classList += "form-btn" }.html) + html.appendChild(TextView("Not with").apply { + classList += "form-btn" + onClick { this@wrap.html.remove() } + }.html) val select = createHtmlView() select.classList.add("form-control") @@ -92,9 +98,12 @@ fun initWorkGroupConstraints() { } addList.textView("Add only after work group") { onClick { - constraints.html.appendChild(View.wrap(createHtmlView()) { + constraints.appendChild(View.wrap(createHtmlView()) { classList += "input-group" - html.appendChild(TextView("After AK").apply { classList += "form-btn" }.html) + html.appendChild(TextView("After AK").apply { + classList += "form-btn" + onClick { this@wrap.html.remove() } + }.html) val select = createHtmlView() select.classList.add("form-control") @@ -115,4 +124,18 @@ fun initWorkGroupConstraints() { }.html) } } -} \ No newline at end of file + + console.log(constraints) + for (child in constraints.children.iterator()) { + console.log(child) + if (child.classList.contains("input-group")) { + val span = child.firstElementChild as HTMLElement + console.log(span) + + span.addEventListener("click", org.w3c.dom.events.EventListener { + println("click") + child.remove() + }) + } + } +} diff --git a/src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt b/src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt index ecd55d2..8c37218 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/table/TableLine.kt @@ -43,7 +43,7 @@ open class TableLine(line: HTMLElement) : View(line) { protected fun setupBoolean(view: TextView, onSave: () -> Unit) { view.classList += "no-select" view.tabIndex = 0 - view.onDblClick { + view.onClick { onSave() } view.onKeyDown { diff --git a/src/jsMain/resources/style/style.scss b/src/jsMain/resources/style/style.scss index f39779c..d0994bb 100644 --- a/src/jsMain/resources/style/style.scss +++ b/src/jsMain/resources/style/style.scss @@ -23,6 +23,7 @@ $input-border-color: #888; $table-border-color: rgba($text-primary-color, 0.1); $table-header-color: rgba($text-primary-color, 0.06); +$border-radius: 0.2rem; $transitionTime: 150ms; $bg-disabled-color: rgba($text-primary-color, .26); @@ -45,7 +46,7 @@ body, html { padding: 0; & > *:last-child { - margin-bottom: 1rem; + //margin-bottom: 1rem; } } @@ -323,7 +324,7 @@ a { height: 2.5rem; width: 100%; background-color: $background-primary-color; - border-radius: 0.2rem; + border-radius: $border-radius; margin: 1px; transition: border-color $transitionTime; @@ -433,7 +434,7 @@ select:-moz-focusring { display: inline-block; margin-right: 0.6rem; - border-radius: 0.2rem; + border-radius: $border-radius; font-weight: 600; text-transform: uppercase; font-size: 0.9rem; @@ -484,8 +485,14 @@ form { margin-top: 1px; } + span { + white-space: nowrap; + } + & > * { margin-right: 0; + flex-grow: 1; + flex-shrink: 1; &:not(:first-child) { border-top-left-radius: 0; @@ -547,7 +554,7 @@ form { .calendar-work-group { position: relative; display: block; - border-radius: 0.2rem; + border-radius: $border-radius; line-height: 2rem; font-size: 0.8rem; white-space: nowrap; @@ -615,7 +622,7 @@ form { right: 0; background-color: #fff; padding: 0.2rem 0.5rem; - border-radius: 0.2rem; + border-radius: $border-radius; display: none; z-index: 10; @@ -643,7 +650,7 @@ form { .calendar-entry { position: absolute; display: block; - border-radius: 0.2rem; + border-radius: $border-radius; z-index: 1; line-height: 2rem; font-size: 0.8rem; @@ -934,26 +941,62 @@ form { .work-group-constraints { position: relative; + + & > label { + margin-bottom: 0.8rem; + } + + .input-group { + margin-bottom: 0.5rem; + + span { + width: 4rem; + position: relative; + text-align: center; + overflow: hidden; + + &:hover::after { + content: 'DELETE'; + position: absolute; + top: 0; + left: 0; + width: 100%; + text-align: center; + + font-weight: bold; + color: $primary-text-color; + background: $primary-color; + } + } + + .form-control { + width: 12rem; + } + } } .work-group-constraints-add { position: absolute; - top: 0; + top: -0.5rem; right: 0; } .work-group-constraints-add-list { position: absolute; - top: 0; + top: -2rem; right: 0; z-index: 1; display: none; + padding: 0.5rem 0; background: $background-primary-color; - border: solid 1px $table-border-color; + border: solid 1px $input-border-color; + border-radius: $border-radius; span { padding: 0 0.5rem; + line-height: 2rem; + display: block; &:hover { background-color: $table-header-color; @@ -1022,6 +1065,10 @@ form { flex-direction: column; } +.post-column-left, .post-column-right { + flex-grow: 1; +} + .post-image { width: 100%; margin: 0.4rem 0 0; diff --git a/src/jvmMain/kotlin/de/kif/backend/backup/Backup.kt b/src/jvmMain/kotlin/de/kif/backend/backup/Backup.kt new file mode 100644 index 0000000..bb2c28c --- /dev/null +++ b/src/jvmMain/kotlin/de/kif/backend/backup/Backup.kt @@ -0,0 +1,82 @@ +package de.kif.backend.backup + +import de.kif.backend.database.Connection +import de.kif.backend.repository.* +import de.kif.common.Message +import de.kif.common.RepositoryType +import de.kif.common.model.* +import kotlinx.serialization.Serializable + +@Serializable +data class Backup( + val posts: List = emptyList(), + val rooms: List = emptyList(), + val schedules: List = emptyList(), + val tracks: List = emptyList(), + val users: List = emptyList(), + val workGroups: List = emptyList() +) { + companion object { + suspend fun backup(vararg repositories: RepositoryType): String { + var backup = Backup() + + val repositorySet = repositories.toMutableSet() + + if (RepositoryType.SCHEDULE in repositorySet) { + repositorySet += RepositoryType.ROOM + repositorySet += RepositoryType.WORK_GROUP + } + + if (RepositoryType.WORK_GROUP in repositorySet) { + repositorySet += RepositoryType.TRACK + } + + for (repository in repositorySet) { + backup = when (repository) { + RepositoryType.ROOM -> backup.copy(rooms = RoomRepository.all()) + RepositoryType.SCHEDULE -> backup.copy(schedules = ScheduleRepository.all()) + RepositoryType.TRACK -> backup.copy(tracks = TrackRepository.all()) + RepositoryType.USER -> backup.copy(users = UserRepository.all()) + RepositoryType.WORK_GROUP -> backup.copy(workGroups = WorkGroupRepository.all()) + RepositoryType.POST -> backup.copy(posts = PostRepository.all()) + } + } + + return Message.json.stringify(serializer(), backup) + } + + @Suppress("UNUSED_VARIABLE") + suspend fun import(data: String) { + val backup = Message.json.parse(serializer(), data) + + val userMap = backup.users.associateWith { UserRepository.create(it) } + val postMap = backup.posts.associateWith { PostRepository.create(it) } + + val roomMap = backup.rooms.associateWith { RoomRepository.create(it) } + val trackMap = backup.tracks.associateWith { TrackRepository.create(it) } + val workGroupMap = backup.workGroups.associateWith { + var workGroup = it + val track = workGroup.track + if (track != null) { + workGroup = workGroup.copy(track = track.copy(id = trackMap[track] ?: return@associateWith -1L)) + } + + WorkGroupRepository.create(workGroup) + } + val scheduleMap = backup.schedules.associateWith { + ScheduleRepository.create( + it.copy( + room = it.room.copy(id = roomMap[it.room] ?: return@associateWith -1L), + workGroup = it.workGroup.copy(id = workGroupMap[it.workGroup] ?: return@associateWith -1L) + ) + ) + } + } + + suspend fun restore(data: String) { + Connection.reset() + + import(data) + } + } +} diff --git a/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt b/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt index c040b38..f4272be 100644 --- a/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt +++ b/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt @@ -3,28 +3,72 @@ package de.kif.backend.database import de.kif.backend.Configuration import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import mu.KotlinLogging +import org.jetbrains.exposed.exceptions.ExposedSQLException import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.transaction +import java.nio.file.Files import java.sql.Connection.TRANSACTION_SERIALIZABLE +import kotlin.system.exitProcess object Connection { + + private val logger = KotlinLogging.logger {} + + private val schemaList = arrayOf( + DbTrack, DbWorkGroup, + DbRoom, DbSchedule, + DbUser, DbUserPermission, + DbPost + ) + fun init() { val dbPath = Configuration.Path.databasePath.toString() Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC") TransactionManager.manager.defaultIsolationLevel = TRANSACTION_SERIALIZABLE - transaction { - SchemaUtils.create( - DbTrack, DbWorkGroup, - DbRoom, DbSchedule, - DbUser, DbUserPermission, - DbPost - ) + try { + create() + } catch (e: ExposedSQLException) { + logger.error { "Cannot initialize the database!" } + + print("Do you want to recreate the database (you will lose all data)? [y/N]: ") + val result = readLine() + + if (result == null || result.toLowerCase() !in "yes") { + exitProcess(1) + } else { + reset() + } } } + + private fun create() { + transaction { + SchemaUtils.createMissingTablesAndColumns(*schemaList) + } + } + + private fun delete() { + transaction { + SchemaUtils.drop(*schemaList) + } + } + + fun reset() { + delete() + + Configuration.Path.uploadsPath.toFile().deleteRecursively() + Configuration.Path.sessionsPath.toFile().deleteRecursively() + + Files.createDirectory(Configuration.Path.uploadsPath) + Files.createDirectory(Configuration.Path.sessionsPath) + + create() + } } suspend fun dbQuery(block: () -> T): T = withContext(Dispatchers.IO) { diff --git a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt index 2f76926..dcc1296 100644 --- a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt +++ b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt @@ -85,6 +85,7 @@ object DbPost : Table() { val url = varchar("url", 64).uniqueIndex() val image = varchar("image", 64).nullable() val pinned = bool("pinned") + val hideOnProjector = bool("hideOnProjector") val createdAt = long("createdAt") val updatedAt = long("updatedAt") diff --git a/src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt b/src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt index 304a0cd..d09bae7 100644 --- a/src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt +++ b/src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt @@ -25,11 +25,12 @@ object PostRepository : Repository { val url = row[DbPost.url] val image = row[DbPost.image] val pinned = row[DbPost.pinned] + val hideOnProjector = row[DbPost.hideOnProjector] val createdAt = row[DbPost.createdAt] val updatedAt = row[DbPost.updatedAt] - return Post(id, name, content, url, image, pinned, createdAt, updatedAt) + return Post(id, name, content, url, image, pinned, hideOnProjector, createdAt, updatedAt) } override suspend fun get(id: Long): Post? { @@ -60,6 +61,7 @@ object PostRepository : Repository { it[url] = model.url it[image] = model.image it[pinned] = model.pinned + it[hideOnProjector] = model.hideOnProjector it[createdAt] = now it[updatedAt] = now @@ -91,6 +93,7 @@ object PostRepository : Repository { it[url] = model.url it[image] = model.image it[pinned] = model.pinned + it[hideOnProjector] = model.hideOnProjector it[updatedAt] = now } diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Account.kt b/src/jvmMain/kotlin/de/kif/backend/route/Account.kt index 3fa2f5a..1e47ec2 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Account.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Account.kt @@ -1,12 +1,26 @@ package de.kif.backend.route +import de.kif.backend.authenticate import de.kif.backend.authenticateOrRedirect +import de.kif.backend.backup.Backup +import de.kif.backend.route.api.error import de.kif.backend.view.MainTemplate import de.kif.backend.view.MenuTemplate +import de.kif.common.RepositoryType +import de.kif.common.model.Permission import io.ktor.application.call import io.ktor.html.respondHtmlTemplate +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.PartData +import io.ktor.http.content.forEachPart +import io.ktor.http.content.streamProvider +import io.ktor.request.receiveMultipart +import io.ktor.response.respondRedirect +import io.ktor.response.respondText import io.ktor.routing.Route import io.ktor.routing.get +import io.ktor.routing.post import kotlinx.html.* fun Route.account() { @@ -30,8 +44,175 @@ fun Route.account() { } } } + + div { + if (user.checkPermission(Permission.ROOM)) { + a("/account/backup/rooms.json", classes = "form-btn") { + attributes["download"] = "rooms-backup" + +"Create room backup" + } + } + if (user.checkPermission(Permission.USER)) { + a("/account/backup/users.json", classes = "form-btn") { + attributes["download"] = "users-backup" + +"Create user backup" + } + } + if (user.checkPermission(Permission.POST)) { + a("/account/backup/posts.json", classes = "form-btn") { + attributes["download"] = "posts-backup" + +"Create post backup" + } + } + if (user.checkPermission(Permission.WORK_GROUP)) { + a("/account/backup/work-groups.json", classes = "form-btn") { + attributes["download"] = "work-groups-backup" + +"Create work group backup" + } + } + if ( + user.checkPermission(Permission.WORK_GROUP) && + user.checkPermission(Permission.ROOM) && + user.checkPermission(Permission.SCHEDULE) + ) { + a("/account/backup/schedules.json", classes = "form-btn") { + attributes["download"] = "schedules-backup" + +"Create schedule backup" + } + } + + if (user.checkPermission(Permission.ADMIN)) { + a("/account/backup.json", classes = "form-btn") { + attributes["download"] = "backup.json" + +"Create backup" + } + } + } + div { + form( + action = "/account/import", + method = FormMethod.post, + encType = FormEncType.multipartFormData + ) { + div("form-group") { + label { + htmlFor = "backup" + +"Backup image" + } + input( + name = "backup", + classes = "form-btn", + type = InputType.file + ) { + id = "backup" + value = "Select backup image" + accept = ".json" + } + } + + div("form-switch-group") { + div("form-group form-switch") { + input( + name = "reset", + classes = "form-control", + type = InputType.checkBox + ) { + id = "reset" + checked = false + } + label { + htmlFor = "reset" + +"Reset" + } + } + } + + div("form-group") { + button(type = ButtonType.submit, classes = "form-btn btn-primary") { + +"Import" + } + } + } + } } } } } + + get("/account/backup/rooms.json") { + authenticate(Permission.ROOM) { + call.respondText(Backup.backup(RepositoryType.ROOM), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + get("/account/backup/users.json") { + authenticate(Permission.USER) { + call.respondText(Backup.backup(RepositoryType.USER), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + get("/account/backup/posts.json") { + authenticate(Permission.POST) { + call.respondText(Backup.backup(RepositoryType.POST), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + get("/account/backup/work-groups.json") { + authenticate(Permission.WORK_GROUP) { + call.respondText(Backup.backup(RepositoryType.WORK_GROUP), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + get("/account/backup/schedules.json") { + authenticate(Permission.ROOM, Permission.WORK_GROUP, Permission.SCHEDULE) { + call.respondText(Backup.backup(RepositoryType.SCHEDULE), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + get("/account/backup.json") { + authenticate(Permission.ADMIN) { + call.respondText(Backup.backup(*RepositoryType.values()), ContentType.Application.Json, HttpStatusCode.OK) + } onFailure { + call.error(HttpStatusCode.Unauthorized) + } + } + + post("/account/import") { + authenticateOrRedirect(Permission.ADMIN) { + var reset = false + var import = "" + + call.receiveMultipart().forEachPart { part -> + val name = part.name ?: return@forEachPart + when (part) { + is PartData.FormItem -> { + if (name == "reset" && part.value == "on") { + reset = true + } + } + is PartData.FileItem -> { + import = part.streamProvider().bufferedReader().readText() + } + } + } + + if (reset) { + Backup.restore(import) + } else { + Backup.import(import) + } + + call.respondRedirect("/account") + } + } } diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Overview.kt b/src/jvmMain/kotlin/de/kif/backend/route/Overview.kt index 31bc748..5411979 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Overview.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Overview.kt @@ -236,6 +236,20 @@ fun Route.overview() { +"Pinned" } } + div("form-group form-switch") { + input( + name = "hide-on-projector", + classes = "form-control", + type = InputType.checkBox + ) { + id = "hide-on-projector" + checked = editPost.hideOnProjector + } + label { + htmlFor = "hide-on-projector" + +"Hide on projector" + } + } } div("form-group") { @@ -317,6 +331,7 @@ fun Route.overview() { params["url"]?.let { post = post.copy(url = it) } params["content"]?.let { post = post.copy(content = it) } params["pinned"]?.let { post = post.copy(pinned = it == "on") } + params["hide-on-projector"]?.let { post = post.copy(hideOnProjector = it == "on") } if (params["image-delete"] == "on") { val currentImage = post.image @@ -430,6 +445,20 @@ fun Route.overview() { +"Pinned" } } + div("form-group form-switch") { + input( + name = "hide-on-projector", + classes = "form-control", + type = InputType.checkBox + ) { + id = "hide-on-projector" + checked = false + } + label { + htmlFor = "hide-on-projector" + +"Hide on projector" + } + } } div("form-group") { @@ -502,8 +531,9 @@ fun Route.overview() { val content = params["content"] ?: return@post val url = params["url"] ?: return@post val pinned = params["pinned"] == "on" + val hideOnProjector = params["hide-on-projector"] == "on" - val post = Post(null, name, content, url, imageUploadName, pinned) + val post = Post(null, name, content, url, imageUploadName, pinned, hideOnProjector) PostRepository.create(post) diff --git a/src/jvmMain/kotlin/de/kif/backend/route/WorkGroup.kt b/src/jvmMain/kotlin/de/kif/backend/route/WorkGroup.kt index 8fccf48..92ff07e 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/WorkGroup.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/WorkGroup.kt @@ -354,8 +354,6 @@ fun Route.workGroup() { +"Accessible" } } - - div("work-group-constraints") } div("form-group work-group-constraints") { diff --git a/src/jvmMain/kotlin/de/kif/backend/util/LogbackFilter.kt b/src/jvmMain/kotlin/de/kif/backend/util/LogbackFilter.kt new file mode 100644 index 0000000..7496e2a --- /dev/null +++ b/src/jvmMain/kotlin/de/kif/backend/util/LogbackFilter.kt @@ -0,0 +1,18 @@ +package de.kif.backend.util + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.filter.Filter +import ch.qos.logback.core.spi.FilterReply + +class LogbackFilter : Filter() { + override fun decide(event: ILoggingEvent?): FilterReply = if (event == null) { + FilterReply.NEUTRAL + } else { + if (event.loggerName.contains("Exposed".toRegex())) { + if (event.level.toInt() > Level.ERROR_INT) FilterReply.ACCEPT else FilterReply.DENY + } else { + FilterReply.ACCEPT + } + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/de/kif/backend/view/MainTemplate.kt b/src/jvmMain/kotlin/de/kif/backend/view/MainTemplate.kt index a1fb02c..7ce0b3c 100644 --- a/src/jvmMain/kotlin/de/kif/backend/view/MainTemplate.kt +++ b/src/jvmMain/kotlin/de/kif/backend/view/MainTemplate.kt @@ -9,18 +9,22 @@ import kotlinx.html.* class MainTemplate : Template { val content = Placeholder() - val menuTemplate =TemplatePlaceholder() + val menuTemplate = TemplatePlaceholder() override fun HTML.apply() { head { meta(charset = "utf-8") - meta(name="viewport",content = "width=device-width, initial-scale=1.0") + meta(name = "viewport", content = "width=device-width, initial-scale=1.0") title("KIF Portal") link(href = "/static/external/material-icons.css", type = LinkType.textCss, rel = LinkRel.stylesheet) link(href = "/static/external/font/Montserrat.css", type = LinkType.textCss, rel = LinkRel.stylesheet) - link(href = "https://fonts.googleapis.com/css?family=Bungee|Oswald", type = LinkType.textCss, rel = LinkRel.stylesheet) + link( + href = "https://fonts.googleapis.com/css?family=Bungee|Oswald", + type = LinkType.textCss, + rel = LinkRel.stylesheet + ) link(href = "/static/style/style.css", type = LinkType.textCss, rel = LinkRel.stylesheet) script(src = "/static/require.min.js") {} diff --git a/src/jvmMain/resources/logback.xml b/src/jvmMain/resources/logback.xml index 57a363c..3ce917e 100644 --- a/src/jvmMain/resources/logback.xml +++ b/src/jvmMain/resources/logback.xml @@ -1,7 +1,7 @@ - + true