diff --git a/build.gradle b/build.gradle index d6a397d..bbe3222 100644 --- a/build.gradle +++ b/build.gradle @@ -90,6 +90,7 @@ kotlin { implementation "io.ktor:ktor-client-apache:$ktor_version" implementation 'org.xerial:sqlite-jdbc:3.25.2' + api 'mysql:mysql-connector-java:8.0.16' implementation 'org.jetbrains.exposed:exposed:0.12.2' implementation 'org.mindrot:jbcrypt:0.4' diff --git a/portal.toml b/portal.toml index 74ecea1..9236ca5 100644 --- a/portal.toml +++ b/portal.toml @@ -18,3 +18,9 @@ wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action [twitter] timeline = "https://twitter.com/kiforbiter?ref_src=twsrc%5Etfw" + +[database] +type = "sqlite" +url = "" +username = "" +password = "" diff --git a/src/jsMain/kotlin/de/kif/frontend/main.kt b/src/jsMain/kotlin/de/kif/frontend/main.kt index aa72d9c..2e59890 100644 --- a/src/jsMain/kotlin/de/kif/frontend/main.kt +++ b/src/jsMain/kotlin/de/kif/frontend/main.kt @@ -1,5 +1,8 @@ package de.kif.frontend +import de.kif.frontend.repository.RoomRepository +import de.kif.frontend.repository.ScheduleRepository +import de.kif.frontend.repository.WorkGroupRepository import de.kif.frontend.views.board.initBoard import de.kif.frontend.views.calendar.initCalendar import de.kif.frontend.views.initAnnouncement @@ -61,4 +64,35 @@ fun main() = init { } } } + + val url = window.location.pathname + if ("brett" in url || "wand" in url) { + ScheduleRepository.onCreate { + window.location.reload() + } + ScheduleRepository.onUpdate { + window.location.reload() + } + ScheduleRepository.onDelete { + window.location.reload() + } + RoomRepository.onCreate { + window.location.reload() + } + RoomRepository.onUpdate { + window.location.reload() + } + RoomRepository.onDelete { + window.location.reload() + } + WorkGroupRepository.onCreate { + window.location.reload() + } + WorkGroupRepository.onUpdate { + window.location.reload() + } + WorkGroupRepository.onDelete { + window.location.reload() + } + } } diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt index c049313..ec63b6b 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt @@ -26,11 +26,12 @@ class Calendar(calendar: HTMLElement) : View(calendar) { private val htmlBody = document.body ?: createHtmlView() - val day = (calendarTable.dataset["day"]?.toIntOrNull() ?: -1).also { println(it) } - val reloadOnFinish = (calendarTable.dataset["reload"]?.toBoolean() ?: false).also { println(it) } - val referenceDate = (calendarTable.dataset["reference"]?.toLongOrNull() ?: -1L).also { println(it) } - val nowDate = (calendarTable.dataset["now"]?.toLongOrNull() ?: -1L).also { println(it) } - val timeDifference = (Date.now().toLong() - nowDate).also { println(it) } + val day = (calendarTable.dataset["day"]?.toIntOrNull() ?: -1) + val reloadOnFinish = (calendarTable.dataset["reload"]?.toBoolean() ?: false) + val hideEmpty = (calendarTable.dataset["hide-empty"]?.toBoolean() ?: false) + val referenceDate = (calendarTable.dataset["reference"]?.toLongOrNull() ?: -1L) + val nowDate = (calendarTable.dataset["now"]?.toLongOrNull() ?: -1L) + val timeDifference = (Date.now().toLong() - nowDate) fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { scrollAllVerticalBy(pixel, scrollBehavior) diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt index 5c34148..a66b0d3 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt @@ -74,18 +74,12 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection first().time) { remove(first()) } diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarRow.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarRow.kt index 6d84a17..dde5910 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarRow.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarRow.kt @@ -8,7 +8,7 @@ import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLSpanElement import org.w3c.dom.set -class CalendarRow(calendar: CalendarBody, view: HTMLElement) : ViewCollection(view) { +class CalendarRow(val calendar: CalendarBody, view: HTMLElement) : ViewCollection(view) { val day = calendar.day val time = dataset["time"]?.toIntOrNull() ?: 0 @@ -49,8 +49,6 @@ class CalendarRow(calendar: CalendarBody, view: HTMLElement) : ViewCollection() + val url by required() + val username by required() + val password by required() + } + + object Database { + val type by c(DatabaseSpec.type) + val url by c(DatabaseSpec.url) + val username by c(DatabaseSpec.username) + val password by c(DatabaseSpec.password) + } + init { var config = Config { addSpec(ServerSpec) @@ -142,6 +156,7 @@ object Configuration { addSpec(GeneralSpec) addSpec(TwitterSpec) addSpec(ResoSpec) + addSpec(DatabaseSpec) }.from.toml.resource("portal.toml") for (file in Files.list(Paths.get("."))) { diff --git a/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt b/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt index f4272be..d56496e 100644 --- a/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt +++ b/src/jvmMain/kotlin/de/kif/backend/database/Connection.kt @@ -26,14 +26,39 @@ object Connection { ) fun init() { - val dbPath = Configuration.Path.databasePath.toString() - Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC") + val type = Configuration.Database.type + val url = Configuration.Database.url + val username = Configuration.Database.username + val password = Configuration.Database.password + when (type) { + "mysql" -> { + Database.connect( + "jdbc:mysql://$url:3306/akplan?user=$username&password=$password&serverTimezone=UTC", + "com.mysql.cj.jdbc.Driver", + username, + password + ) + } + "mariadb" -> { + Database.connect( + "jdbc:mariadb://$url:3306/akplan?user=$username&password=$password&serverTimezone=UTC", + "org.mariadb.jdbc.Driver", + username, + password + ) + } + else -> { + val dbPath = Configuration.Path.databasePath.toString() + Database.connect("jdbc:sqlite:$dbPath", "org.sqlite.JDBC") + } + } + TransactionManager.manager.defaultIsolationLevel = TRANSACTION_SERIALIZABLE try { create() } catch (e: ExposedSQLException) { - logger.error { "Cannot initialize the database!" } + logger.error(e) { "Cannot initialize the database!" } print("Do you want to recreate the database (you will lose all data)? [y/N]: ") val result = readLine() diff --git a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt index 0b997b1..2b8604e 100644 --- a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt +++ b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt @@ -29,7 +29,7 @@ object DbWorkGroup : Table() { val accessible = bool("accessible") val language = enumeration("language", Language::class) - val leader = text("leader").default("[]") + val leader = text("leader") val length = integer("length") val constraints = text("constraints") @@ -49,9 +49,9 @@ object DbRoom : Table() { val whiteboard = bool("whiteboard") val blackboard = bool("blackboard") val accessible = bool("accessible") - val pool = bool("pool").default(false) + val pool = bool("pool") - val blocked = text("blocked").default("[]") + val blocked = text("blocked") val createdAt = long("createdAt") val updatedAt = long("updatedAt") @@ -63,8 +63,8 @@ object DbSchedule : Table() { val roomId = long("room_id").index() val day = integer("day").index() val time = integer("time_slot") - val lockRoom = bool("lock_room").default(false) - val lockTime = bool("lock_time").default(false) + val lockRoom = bool("lock_room") + val lockTime = bool("lock_time") val createdAt = long("createdAt") val updatedAt = long("updatedAt") diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Account.kt b/src/jvmMain/kotlin/de/kif/backend/route/Account.kt index 95ae47c..cbe92e3 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Account.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Account.kt @@ -6,10 +6,12 @@ import de.kif.backend.prefix import de.kif.backend.repository.* import de.kif.backend.route.api.error import de.kif.backend.util.Backup +import de.kif.backend.util.PushService import de.kif.backend.util.WikiImporter import de.kif.backend.view.respondMain import de.kif.common.RepositoryType import de.kif.common.model.Permission +import de.kif.common.model.Post import io.ktor.application.call import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode @@ -51,6 +53,10 @@ fun Route.account() { +"Sicherung" } + a(href = "$prefix/account/reload", classes = "form-btn") { + +"Alle Geräte neu laden" + } + if (user.checkPermission(Permission.SCHEDULE)) { a(href = "$prefix/account/import", classes = "form-btn") { +"Aus Wiki importieren" @@ -516,4 +522,8 @@ fun Route.account() { call.error(HttpStatusCode.Unauthorized) } } + get("/account/reload") { + PushService.signature = Post.generateUrl() + call.respondRedirect("$prefix/account") + } } diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Board.kt b/src/jvmMain/kotlin/de/kif/backend/route/Board.kt index 6a92f63..050565d 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Board.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Board.kt @@ -175,7 +175,8 @@ fun Route.board() { max, rooms, schedules, - reloadAfterFinish = true + reloadAfterFinish = true, + hideEmptyRooms = true ) } div("board-twitter") { diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt b/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt index e7b351e..e4f02de 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt @@ -72,7 +72,8 @@ fun DIV.renderCalendar( to: Int, rooms: List, schedules: Map>>, - reloadAfterFinish: Boolean = false + reloadAfterFinish: Boolean = false, + hideEmptyRooms: Boolean = false ) { val gridLabelWidth = 60 val minutesOfDay = to - from @@ -91,6 +92,7 @@ fun DIV.renderCalendar( attributes["data-reference"] = Configuration.Schedule.referenceDate.time.toString() attributes["data-now"] = now.timeInMillis.toString() attributes["data-reload"] = reloadAfterFinish.toString() + attributes["data-hide-empty"] = hideEmptyRooms.toString() div("calendar-table-box ${orientation.name.toLowerCase().replace("_", "-")}") { div("calendar-header") { @@ -103,6 +105,7 @@ fun DIV.renderCalendar( for (room in rooms) { div("calendar-cell") { attributes["data-room"] = room.id.toString() + attributes["data-empty"] = (hideEmptyRooms && room !in schedules).toString() span { +room.name @@ -147,6 +150,7 @@ fun DIV.renderCalendar( div("calendar-cell") { attributes["data-room"] = room.id.toString() attributes["data-blocked"] = blocked.toString() + attributes["data-empty"] = (hideEmptyRooms && room !in schedules).toString() title = room.name + " - " + timeString diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Wall.kt b/src/jvmMain/kotlin/de/kif/backend/route/Wall.kt index 25c48e0..5dc6b73 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Wall.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Wall.kt @@ -1,11 +1,11 @@ package de.kif.backend.route -import com.soywiz.klock.* -import com.soywiz.klock.locale.german import de.kif.backend.Configuration import de.kif.backend.repository.RoomRepository import de.kif.backend.repository.ScheduleRepository import de.kif.backend.view.respondMain +import de.kif.common.formatDate +import de.kif.common.formatDateWithoutYear import de.kif.common.model.Room import de.kif.common.model.Schedule import io.ktor.routing.Route @@ -63,6 +63,8 @@ fun Route.wall() { wallStart + 2 ).map { genWallData(it) } + val rooms = RoomRepository.all() + var min = days.mapNotNull { it.min }.min() ?: 12 * 60 val max = days.mapNotNull { it.max }.max() ?: 12 * 60 @@ -70,18 +72,15 @@ fun Route.wall() { min = max } - val refDate = DateTime(Configuration.Schedule.referenceDate.time) + val refDate = Configuration.Schedule.referenceDate.time respondMain(true, true) { content { div("wall") { for (day in days) { - val date = refDate + day.number.days - val dateString = DateFormat("EEEE, d. MMMM") - .withLocale(KlockLocale.german) - .format(date) - + val date = refDate + (day.number * 1000 * 60 * 60 * 24) + val dateString = formatDateWithoutYear(date, Configuration.Schedule.offset) div("wall-box") { div("wall-name") { @@ -95,8 +94,9 @@ fun Route.wall() { day.number, min, max, - day.schedules.keys.toList().sortedBy { it.id }, - day.schedules + rooms, + day.schedules, + hideEmptyRooms = true ) } } diff --git a/src/jvmMain/kotlin/de/kif/backend/util/PushService.kt b/src/jvmMain/kotlin/de/kif/backend/util/PushService.kt index df6ea58..fb632b9 100644 --- a/src/jvmMain/kotlin/de/kif/backend/util/PushService.kt +++ b/src/jvmMain/kotlin/de/kif/backend/util/PushService.kt @@ -18,7 +18,7 @@ import kotlin.concurrent.thread object PushService { internal var leastValidTimestamp = System.currentTimeMillis() - val signature = Post.generateUrl() + var signature = Post.generateUrl() private val messages: MutableList> = mutableListOf() diff --git a/src/jvmMain/resources/portal.toml b/src/jvmMain/resources/portal.toml index 809ffa1..9222a60 100644 --- a/src/jvmMain/resources/portal.toml +++ b/src/jvmMain/resources/portal.toml @@ -32,3 +32,9 @@ backup_interval = 3600000 [twitter] timeline = "" + +[database] +type = "sqlite" +url = "" +username = "" +password = ""