Compare commits
No commits in common. "2b290930867138ead7468294f8c43e2a27c8a962" and "dce2567160555ee7a37f5011216d80683930b239" have entirely different histories.
2b29093086
...
dce2567160
15 changed files with 251 additions and 255 deletions
|
@ -99,6 +99,10 @@ kotlin {
|
||||||
implementation "com.soywiz:klock-jvm:$klockVersion"
|
implementation "com.soywiz:klock-jvm:$klockVersion"
|
||||||
implementation "com.soywiz:klock-locale-jvm:$klockVersion"
|
implementation "com.soywiz:klock-locale-jvm:$klockVersion"
|
||||||
|
|
||||||
|
//implementation 'com.twitter:hbc-core:2.2.0'
|
||||||
|
//implementation 'com.twitter:hbc-twitter4j:2.2.0'
|
||||||
|
api 'org.twitter4j:twitter4j-stream:4.0.1'
|
||||||
|
|
||||||
implementation 'com.github.uchuhimo:konf:master-SNAPSHOT'
|
implementation 'com.github.uchuhimo:konf:master-SNAPSHOT'
|
||||||
implementation 'com.vladsch.flexmark:flexmark-all:0.42.10'
|
implementation 'com.vladsch.flexmark:flexmark-all:0.42.10'
|
||||||
api 'io.github.microutils:kotlin-logging:1.6.23'
|
api 'io.github.microutils:kotlin-logging:1.6.23'
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
$background-primary-color: #fff;
|
$background-primary-color: #fff;
|
||||||
$background-secondary-color: #f5f5f5;
|
$background-secondary-color: #fcfcfc;
|
||||||
$background-card-color: #fff;
|
|
||||||
|
|
||||||
$text-primary-color: #333;
|
$text-primary-color: #333;
|
||||||
$text-secondary-color: rgba($text-primary-color, 0.5);
|
$text-secondary-color: rgba($text-primary-color, 0.5);
|
||||||
|
@ -16,7 +15,7 @@ $input-border-color: #888;
|
||||||
$table-border-color: rgba($text-primary-color, 0.1);
|
$table-border-color: rgba($text-primary-color, 0.1);
|
||||||
$table-header-color: rgba($text-primary-color, 0.06);
|
$table-header-color: rgba($text-primary-color, 0.06);
|
||||||
|
|
||||||
$shadow-color: rgba(#000, 0.3);
|
$shadow-color: rgba($text-primary-color, 0.8);
|
||||||
|
|
||||||
$icon-color-focused: rgba($text-primary-color, 0.87);
|
$icon-color-focused: rgba($text-primary-color, 0.87);
|
||||||
$icon-color: rgba($text-primary-color, 0.54);
|
$icon-color: rgba($text-primary-color, 0.54);
|
||||||
|
@ -31,7 +30,6 @@ $lever-enabled-color: $primary-color;
|
||||||
:root {
|
:root {
|
||||||
--background-primary-color: $background-primary-color;
|
--background-primary-color: $background-primary-color;
|
||||||
--background-secondary-color: $background-secondary-color;
|
--background-secondary-color: $background-secondary-color;
|
||||||
--background-card-color: $background-card-color;
|
|
||||||
|
|
||||||
--text-primary-color: $text-primary-color;
|
--text-primary-color: $text-primary-color;
|
||||||
--text-secondary-color: $text-secondary-color;
|
--text-secondary-color: $text-secondary-color;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,124 +1,73 @@
|
||||||
@import "../config";
|
@import "../config";
|
||||||
|
|
||||||
.board-header {
|
.board-header {
|
||||||
line-height: 3rem;
|
line-height: 6rem;
|
||||||
flex-grow: 1;
|
display: flex;
|
||||||
font-family: "Bungee", sans-serif;
|
padding: 0 2rem;
|
||||||
font-weight: normal;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
padding-left: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-card {
|
& > div {
|
||||||
background-color: var(--background-card-color);
|
flex-grow: 1;
|
||||||
box-shadow: 0 0.1rem 0.2rem var(--shadow-color);
|
font-family: "Bungee", sans-serif;
|
||||||
border-radius: $border-radius;
|
font-weight: normal;
|
||||||
overflow: hidden;
|
font-size: 1.1rem;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
padding: 1rem 0.4rem 2rem;
|
padding: 1rem 1rem 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: calc(100vh - 0.5rem);
|
height: calc(100vh - 6rem);
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
padding: 0 0.4rem;
|
|
||||||
|
|
||||||
&:nth-child(1) {
|
|
||||||
flex-grow: 4;
|
|
||||||
|
|
||||||
padding-left: 0.15rem;
|
|
||||||
padding-right: 0.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
flex-grow: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3) {
|
|
||||||
flex-grow: 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-twitter {
|
|
||||||
.board-card {
|
|
||||||
height: calc(100% - 1.3rem);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
margin-top: -1px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-post {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
.post-name {
|
|
||||||
top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
padding-top: 2.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-schedule-box {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-schedule {
|
.board-schedule {
|
||||||
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
|
||||||
flex-basis: 0;
|
|
||||||
min-width: 15rem;
|
|
||||||
|
|
||||||
padding: 0.6rem;
|
padding: 1rem 1rem;
|
||||||
margin: 0 0.25rem 0.5rem;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-bottom: solid 1px var(--table-border-color)
|
border-bottom: solid 1px var(--table-border-color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-schedule-bottom {
|
.board-schedule-room {
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
padding-right: 0.5rem;
|
line-height: 2rem;
|
||||||
line-height: 1rem;
|
}
|
||||||
clear: both;
|
|
||||||
|
|
||||||
& > span {
|
.board-schedule-time {
|
||||||
|
position: absolute;
|
||||||
&:first-child {
|
top: 1rem;
|
||||||
float: left;
|
right: 1rem;
|
||||||
}
|
line-height: 2rem;
|
||||||
&:last-child {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-schedule-color {
|
.board-schedule-color {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.25rem;
|
top: 3.25rem;
|
||||||
left: 0.6rem;
|
left: 1rem;
|
||||||
width: 0.8rem;
|
width: 0.5rem;
|
||||||
height: 0.8rem;
|
height: 1.5rem;
|
||||||
border-radius: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-schedule-name {
|
.board-schedule-name {
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
line-height: 1.3rem;
|
line-height: 2rem;
|
||||||
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
padding-top: 0.35rem;
|
|
||||||
padding-bottom: 0.35rem;
|
|
||||||
}
|
}
|
|
@ -12,10 +12,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview-side {
|
.overview-side {
|
||||||
min-width: 30%;
|
min-width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview-twitter {
|
.overview-twitter {
|
||||||
|
height: 20rem;
|
||||||
|
background-color: #b3e6f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
|
@ -32,8 +34,6 @@
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
font-weight: bold;
|
|
||||||
font-family: 'Montserrat', sans-serif;
|
|
||||||
|
|
||||||
&:empty::before {
|
&:empty::before {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
$background-primary-color: #2d2d2d;
|
$background-primary-color: #2d2d2d;
|
||||||
$background-secondary-color: #373737;
|
$background-secondary-color: #373737;
|
||||||
$background-card-color: rgba($text-primary-color, 0.06);
|
|
||||||
|
|
||||||
$text-primary-color: #fff;
|
$text-primary-color: #fff;
|
||||||
$text-secondary-color: rgba($text-primary-color, 0.5);
|
$text-secondary-color: rgba($text-primary-color, 0.5);
|
||||||
|
|
||||||
$primary-color: #ef5350;
|
$primary-color: #dd213d;
|
||||||
$primary-text-color: #fff;
|
$primary-text-color: #fff;
|
||||||
|
|
||||||
$error-color: #F00;
|
$error-color: #F00;
|
||||||
|
@ -18,7 +17,7 @@ $input-border-color: #888;
|
||||||
$table-border-color: rgba($text-primary-color, 0.1);
|
$table-border-color: rgba($text-primary-color, 0.1);
|
||||||
$table-header-color: rgba($text-primary-color, 0.06);
|
$table-header-color: rgba($text-primary-color, 0.06);
|
||||||
|
|
||||||
$shadow-color: rgba(#000, 0.3);
|
$shadow-color: rgba($text-primary-color, 0.8);
|
||||||
|
|
||||||
$icon-color-focused: rgba($text-primary-color, 1.0);
|
$icon-color-focused: rgba($text-primary-color, 1.0);
|
||||||
$icon-color: rgba($text-primary-color, 0.7);
|
$icon-color: rgba($text-primary-color, 0.7);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
$background-primary-color: #ffc3e1;
|
$background-primary-color: #ffc3e1;
|
||||||
$background-secondary-color: #ffa5d2;
|
$background-secondary-color: #ffa5d2;
|
||||||
$background-card-color: rgba($text-primary-color, 0.06);
|
|
||||||
|
|
||||||
$text-primary-color: #333;
|
$text-primary-color: #333;
|
||||||
$text-secondary-color: rgba($text-primary-color, 0.5);
|
$text-secondary-color: rgba($text-primary-color, 0.5);
|
||||||
|
@ -18,7 +17,7 @@ $input-border-color: #888;
|
||||||
$table-border-color: rgba($text-primary-color, 0.1);
|
$table-border-color: rgba($text-primary-color, 0.1);
|
||||||
$table-header-color: rgba($text-primary-color, 0.06);
|
$table-header-color: rgba($text-primary-color, 0.06);
|
||||||
|
|
||||||
$shadow-color: rgba(#000, 0.3);
|
$shadow-color: rgba($text-primary-color, 0.8);
|
||||||
|
|
||||||
$icon-color-focused: rgba($text-primary-color, 0.87);
|
$icon-color-focused: rgba($text-primary-color, 0.87);
|
||||||
$icon-color: rgba($text-primary-color, 0.54);
|
$icon-color: rgba($text-primary-color, 0.54);
|
||||||
|
|
|
@ -14,8 +14,8 @@ body, html {
|
||||||
color: var(--text-primary-color);
|
color: var(--text-primary-color);
|
||||||
background: var(--background-secondary-color);
|
background: var(--background-secondary-color);
|
||||||
|
|
||||||
font-family: 'Raleway', 'Montserrat', Roboto, Arial, sans-serif;
|
font-family: 'Montserrat', Roboto, Arial, sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -91,11 +91,13 @@ object Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private object TwitterSpec : ConfigSpec("twitter") {
|
private object TwitterSpec : ConfigSpec("twitter") {
|
||||||
val timeline by required<String>("timeline")
|
val username by required<String>("username")
|
||||||
|
val password by required<String>("password")
|
||||||
}
|
}
|
||||||
|
|
||||||
object Twitter {
|
object Twitter {
|
||||||
val timeline by c(TwitterSpec.timeline)
|
val username by c(TwitterSpec.username)
|
||||||
|
val password by c(TwitterSpec.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
142
src/jvmMain/kotlin/de/kif/backend/Twitter.kt
Normal file
142
src/jvmMain/kotlin/de/kif/backend/Twitter.kt
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package de.kif.backend
|
||||||
|
|
||||||
|
import twitter4j.*
|
||||||
|
import twitter4j.conf.ConfigurationBuilder
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
fun twitter() {
|
||||||
|
val msgQueue = LinkedBlockingQueue<String>(100000)
|
||||||
|
val eventQueue = LinkedBlockingQueue<Event>(1000)
|
||||||
|
|
||||||
|
val hosts = HttpHosts(Constants.STREAM_HOST)
|
||||||
|
|
||||||
|
val filterEndpoint = StatusesFilterEndpoint()
|
||||||
|
filterEndpoint.trackTerms(listOf("kif"))
|
||||||
|
|
||||||
|
println(Configuration.Twitter.username)
|
||||||
|
println(Configuration.Twitter.password)
|
||||||
|
|
||||||
|
val authentication = BasicAuth(
|
||||||
|
Configuration.Twitter.username,
|
||||||
|
Configuration.Twitter.password
|
||||||
|
)
|
||||||
|
|
||||||
|
val builder = ClientBuilder()
|
||||||
|
.name("kif-portal")
|
||||||
|
.hosts(hosts)
|
||||||
|
.authentication(authentication)
|
||||||
|
.endpoint(filterEndpoint)
|
||||||
|
.processor(StringDelimitedProcessor(msgQueue))
|
||||||
|
.eventMessageQueue(eventQueue)
|
||||||
|
|
||||||
|
val client = builder.build()
|
||||||
|
|
||||||
|
val listener = object: StatusStreamHandler {
|
||||||
|
override fun onUnknownMessageType(msg: String?) {
|
||||||
|
println("onUnknownMessageType")
|
||||||
|
println(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisconnectMessage(message: DisconnectMessage?) {
|
||||||
|
println("onDisconnectMessage")
|
||||||
|
println(message?.disconnectReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStallWarningMessage(warning: StallWarningMessage?) {
|
||||||
|
println("onStallWarningMessage")
|
||||||
|
println(warning?.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
|
||||||
|
println("onTrackLimitationNotice")
|
||||||
|
println(numberOfLimitedStatuses)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStallWarning(warning: StallWarning?) {
|
||||||
|
println("onStallWarning")
|
||||||
|
println(warning?.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onException(ex: Exception?) {
|
||||||
|
println("onException")
|
||||||
|
ex?.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDeletionNotice(statusDeletionNotice: StatusDeletionNotice?) {
|
||||||
|
println("onDeletionNotice")
|
||||||
|
println(statusDeletionNotice?.statusId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStatus(status: Status?) {
|
||||||
|
println("onStatus")
|
||||||
|
println(status?.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScrubGeo(userId: Long, upToStatusId: Long) {
|
||||||
|
println("onScrubGeo")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val t4jClient = Twitter4jStatusClient(
|
||||||
|
client,
|
||||||
|
msgQueue,
|
||||||
|
listOf(listener),
|
||||||
|
Executors.newFixedThreadPool(4)
|
||||||
|
)
|
||||||
|
|
||||||
|
t4jClient.connect()
|
||||||
|
|
||||||
|
t4jClient.process()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun twitter() {
|
||||||
|
val cb = ConfigurationBuilder()
|
||||||
|
cb.setDebugEnabled(true)
|
||||||
|
.setUser(Configuration.Twitter.username)
|
||||||
|
.setPassword(Configuration.Twitter.password)
|
||||||
|
|
||||||
|
val listener = object : StatusListener {
|
||||||
|
|
||||||
|
override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
|
||||||
|
println("onTrackLimitationNotice")
|
||||||
|
println(numberOfLimitedStatuses)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStallWarning(warning: StallWarning?) {
|
||||||
|
println("onStallWarning")
|
||||||
|
println(warning?.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onException(ex: Exception?) {
|
||||||
|
println("onException")
|
||||||
|
ex?.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDeletionNotice(statusDeletionNotice: StatusDeletionNotice?) {
|
||||||
|
println("onDeletionNotice")
|
||||||
|
println(statusDeletionNotice?.statusId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStatus(status: Status?) {
|
||||||
|
println("onStatus")
|
||||||
|
println(status?.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScrubGeo(userId: Long, upToStatusId: Long) {
|
||||||
|
println("onScrubGeo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val twitterStream = TwitterStreamFactory(cb.build()).instance
|
||||||
|
|
||||||
|
addTwitterStreamListener(twitterStream, listener)
|
||||||
|
|
||||||
|
twitterStream.sample()
|
||||||
|
}
|
|
@ -2,15 +2,18 @@ package de.kif.backend.route
|
||||||
|
|
||||||
import de.kif.backend.authenticate
|
import de.kif.backend.authenticate
|
||||||
import de.kif.backend.authenticateOrRedirect
|
import de.kif.backend.authenticateOrRedirect
|
||||||
|
import de.kif.backend.util.Backup
|
||||||
import de.kif.backend.repository.TrackRepository
|
import de.kif.backend.repository.TrackRepository
|
||||||
import de.kif.backend.repository.WorkGroupRepository
|
import de.kif.backend.repository.WorkGroupRepository
|
||||||
import de.kif.backend.route.api.error
|
import de.kif.backend.route.api.error
|
||||||
import de.kif.backend.util.Backup
|
|
||||||
import de.kif.backend.util.WikiImporter
|
import de.kif.backend.util.WikiImporter
|
||||||
|
import de.kif.backend.view.MainTemplate
|
||||||
|
import de.kif.backend.view.MenuTemplate
|
||||||
import de.kif.backend.view.respondMain
|
import de.kif.backend.view.respondMain
|
||||||
import de.kif.common.RepositoryType
|
import de.kif.common.RepositoryType
|
||||||
import de.kif.common.model.Permission
|
import de.kif.common.model.Permission
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
|
import io.ktor.html.respondHtmlTemplate
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.PartData
|
||||||
|
@ -32,6 +35,10 @@ private val logger = KotlinLogging.logger {}
|
||||||
fun Route.account() {
|
fun Route.account() {
|
||||||
get("/account") {
|
get("/account") {
|
||||||
authenticateOrRedirect { user ->
|
authenticateOrRedirect { user ->
|
||||||
|
|
||||||
|
val tracks = TrackRepository.all()
|
||||||
|
val wikiSections = WikiImporter.loadSections()
|
||||||
|
|
||||||
respondMain {
|
respondMain {
|
||||||
content {
|
content {
|
||||||
h1 { +"Account" }
|
h1 { +"Account" }
|
||||||
|
@ -47,26 +54,6 @@ fun Route.account() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a(href = "/account/backup", classes = "form-btn") {
|
|
||||||
+"Backup"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.checkPermission(Permission.ADMIN)) {
|
|
||||||
a(href = "/account/import", classes = "form-btn") {
|
|
||||||
+"Import wiki"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/account/backup") {
|
|
||||||
authenticateOrRedirect { user ->
|
|
||||||
respondMain {
|
|
||||||
content {
|
|
||||||
h1 { +"Backup" }
|
|
||||||
|
|
||||||
div("account-backup") {
|
div("account-backup") {
|
||||||
if (user.checkPermission(Permission.ROOM)) {
|
if (user.checkPermission(Permission.ROOM)) {
|
||||||
a("/account/backup/rooms.json", classes = "form-btn") {
|
a("/account/backup/rooms.json", classes = "form-btn") {
|
||||||
|
@ -112,11 +99,9 @@ fun Route.account() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.checkPermission(Permission.ADMIN)) {
|
if (user.checkPermission(Permission.ADMIN)) {
|
||||||
h1 { +"Restore" }
|
|
||||||
|
|
||||||
div("account-import") {
|
div("account-import") {
|
||||||
form(
|
form(
|
||||||
action = "/account/restore",
|
action = "/account/import",
|
||||||
method = FormMethod.post,
|
method = FormMethod.post,
|
||||||
encType = FormEncType.multipartFormData
|
encType = FormEncType.multipartFormData
|
||||||
) {
|
) {
|
||||||
|
@ -161,25 +146,12 @@ fun Route.account() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/account/import") {
|
|
||||||
authenticateOrRedirect(Permission.ADMIN) { user ->
|
|
||||||
val tracks = TrackRepository.all()
|
|
||||||
val wikiSections = WikiImporter.loadSections()
|
|
||||||
|
|
||||||
respondMain {
|
|
||||||
content {
|
|
||||||
h1 { +"Import wiki" }
|
|
||||||
|
|
||||||
if (user.checkPermission(Permission.ADMIN)) {
|
if (user.checkPermission(Permission.ADMIN)) {
|
||||||
div("account-import-wiki") {
|
div("account-import-wiki") {
|
||||||
span { +"Import work group data from the kif wiki" }
|
span { +"Import work group data from the kif wiki" }
|
||||||
|
|
||||||
form(action = "/account/import", method = FormMethod.post) {
|
form(action = "/account/import-wiki", method = FormMethod.post) {
|
||||||
for ((index, section) in wikiSections.withIndex()) {
|
for ((index, section) in wikiSections.withIndex()) {
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
label {
|
label {
|
||||||
|
@ -305,7 +277,7 @@ fun Route.account() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post("/account/restore") {
|
post("/account/import") {
|
||||||
authenticate(Permission.ADMIN) {
|
authenticate(Permission.ADMIN) {
|
||||||
var reset = false
|
var reset = false
|
||||||
var import = ""
|
var import = ""
|
||||||
|
@ -336,7 +308,7 @@ fun Route.account() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post("/account/import") {
|
post("/account/import-wiki") {
|
||||||
authenticate(Permission.ADMIN) {
|
authenticate(Permission.ADMIN) {
|
||||||
val params = call.receiveParameters().toMap().mapValues { (_, list) ->
|
val params = call.receiveParameters().toMap().mapValues { (_, list) ->
|
||||||
list.firstOrNull()
|
list.firstOrNull()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import de.kif.backend.Configuration
|
||||||
import de.kif.backend.repository.PostRepository
|
import de.kif.backend.repository.PostRepository
|
||||||
import de.kif.backend.repository.ScheduleRepository
|
import de.kif.backend.repository.ScheduleRepository
|
||||||
import de.kif.backend.view.respondMain
|
import de.kif.backend.view.respondMain
|
||||||
|
import de.kif.common.formatDateTime
|
||||||
import de.kif.common.model.Schedule
|
import de.kif.common.model.Schedule
|
||||||
import io.ktor.routing.Route
|
import io.ktor.routing.Route
|
||||||
import io.ktor.routing.get
|
import io.ktor.routing.get
|
||||||
|
@ -11,11 +12,10 @@ import kotlinx.css.CSSBuilder
|
||||||
import kotlinx.css.Color
|
import kotlinx.css.Color
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.span
|
import kotlinx.html.span
|
||||||
import kotlinx.html.unsafe
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun Route.board() {
|
fun Route.board() {
|
||||||
get("/brett") {
|
get("/board") {
|
||||||
val postList = PostRepository.all().asReversed()
|
val postList = PostRepository.all().asReversed()
|
||||||
val scheduleList = ScheduleRepository.all().map {
|
val scheduleList = ScheduleRepository.all().map {
|
||||||
it to it.getAbsoluteStartTime() * 60
|
it to it.getAbsoluteStartTime() * 60
|
||||||
|
@ -24,9 +24,8 @@ fun Route.board() {
|
||||||
val referenceTime = Configuration.Schedule.referenceDate.time / 1000
|
val referenceTime = Configuration.Schedule.referenceDate.time / 1000
|
||||||
val now = referenceTime - (Date().time / 1000)
|
val now = referenceTime - (Date().time / 1000)
|
||||||
|
|
||||||
respondMain(true, true) { theme ->
|
respondMain(true, true) {
|
||||||
content {
|
content {
|
||||||
/*
|
|
||||||
div("board-header") {
|
div("board-header") {
|
||||||
div {
|
div {
|
||||||
+"KIF 47.0"
|
+"KIF 47.0"
|
||||||
|
@ -35,96 +34,46 @@ fun Route.board() {
|
||||||
+formatDateTime(Date().time)
|
+formatDateTime(Date().time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
div("board") {
|
div("board") {
|
||||||
div("board-schedules") {
|
div("board-schedules") {
|
||||||
attributes["data-reference"] = referenceTime.toString()
|
attributes["data-reference"] = referenceTime.toString()
|
||||||
|
|
||||||
div("board-header") {
|
for ((schedule, time) in scheduleList) {
|
||||||
+"AKs"
|
div("board-schedule") {
|
||||||
}
|
attributes["data-id"] = schedule.id.toString()
|
||||||
|
|
||||||
div("board-schedule-box") {
|
span("board-schedule-room") {
|
||||||
for ((schedule, time) in scheduleList) {
|
+schedule.room.name
|
||||||
div("board-card board-schedule") {
|
}
|
||||||
attributes["data-id"] = schedule.id.toString()
|
span("board-schedule-time") {
|
||||||
|
attributes["data-time"] = time.toString()
|
||||||
|
attributes["data-duration"] = schedule.workGroup.length.toString()
|
||||||
|
|
||||||
span("board-schedule-color") {
|
+Schedule.timeDifferenceToString(time + now)
|
||||||
attributes["style"] = CSSBuilder().apply {
|
}
|
||||||
val c = schedule.workGroup.track?.color
|
span("board-schedule-color") {
|
||||||
if (c != null) {
|
attributes["style"] = CSSBuilder().apply {
|
||||||
backgroundColor = Color(c.toString())
|
val c = schedule.workGroup.track?.color
|
||||||
}
|
if (c != null) {
|
||||||
}.toString()
|
backgroundColor = Color(c.toString())
|
||||||
}
|
|
||||||
span("board-schedule-name") {
|
|
||||||
+schedule.workGroup.name
|
|
||||||
}
|
|
||||||
|
|
||||||
div("board-schedule-bottom") {
|
|
||||||
span("board-schedule-time") {
|
|
||||||
attributes["data-time"] = time.toString()
|
|
||||||
attributes["data-duration"] = schedule.workGroup.length.toString()
|
|
||||||
|
|
||||||
|
|
||||||
val startTime = (time % MINUTES_OF_DAY).let {
|
|
||||||
if (it < 0) it + MINUTES_OF_DAY else it
|
|
||||||
}
|
|
||||||
val sm = (startTime % 60).toString().padStart(2, '0')
|
|
||||||
val sh = (startTime / 60).toString().padStart(2, '0')
|
|
||||||
val startTimeString = "$sh:$sm"
|
|
||||||
|
|
||||||
val endTime = ((time + schedule.workGroup.length) % MINUTES_OF_DAY).let {
|
|
||||||
if (it < 0) it + MINUTES_OF_DAY else it
|
|
||||||
}
|
|
||||||
val em = (endTime % 60).toString().padStart(2, '0')
|
|
||||||
val eh = (endTime / 60).toString().padStart(2, '0')
|
|
||||||
val endTimeString = "$eh:$em"
|
|
||||||
|
|
||||||
+"$startTimeString - $endTimeString"
|
|
||||||
}
|
}
|
||||||
|
}.toString()
|
||||||
span("board-schedule-room") {
|
}
|
||||||
+schedule.room.name
|
span("board-schedule-name") {
|
||||||
}
|
+schedule.workGroup.name
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div("board-posts") {
|
div("board-posts") {
|
||||||
div("board-header") {
|
|
||||||
+"News"
|
|
||||||
}
|
|
||||||
for (post in postList) {
|
for (post in postList) {
|
||||||
createPost(post, false, "board-card board-post")
|
createPost(post, false, "board-post overview-post")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div("board-twitter") {
|
div("board-twitter") {
|
||||||
div("board-header") {
|
|
||||||
+"Tweets"
|
|
||||||
}
|
|
||||||
div("board-card") {
|
|
||||||
unsafe {
|
|
||||||
raw(
|
|
||||||
"""
|
|
||||||
<a
|
|
||||||
class="twitter-timeline"
|
|
||||||
href="${Configuration.Twitter.timeline}"
|
|
||||||
data-chrome="transparent noheader nofooter"
|
|
||||||
data-theme="${if (theme.dark) "dark" else "light"}"
|
|
||||||
data-link-color="${theme.primaryColor}"
|
|
||||||
data-cards="hidden"
|
|
||||||
data-lang="de"
|
|
||||||
data-dnt="true"
|
|
||||||
>Tweets by kiforbiter</a>
|
|
||||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ fun Route.overview() {
|
||||||
|
|
||||||
val postList = PostRepository.all().asReversed()
|
val postList = PostRepository.all().asReversed()
|
||||||
|
|
||||||
respondMain { theme ->
|
respondMain {
|
||||||
content {
|
content {
|
||||||
div("overview") {
|
div("overview") {
|
||||||
div("overview-main") {
|
div("overview-main") {
|
||||||
|
@ -94,21 +94,7 @@ fun Route.overview() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div("overview-twitter") {
|
div("overview-twitter") {
|
||||||
unsafe {
|
+"The Twitter Wall"
|
||||||
raw("""
|
|
||||||
<a
|
|
||||||
class="twitter-timeline"
|
|
||||||
href="${Configuration.Twitter.timeline}"
|
|
||||||
data-chrome="transparent"
|
|
||||||
data-theme="${if (theme.dark) "dark" else "light"}"
|
|
||||||
data-link-color="${theme.primaryColor}"
|
|
||||||
data-cards="hidden"
|
|
||||||
data-lang="de"
|
|
||||||
data-dnt="true"
|
|
||||||
>Tweets by kiforbiter</a>
|
|
||||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package de.kif.backend.view
|
||||||
|
|
||||||
import de.kif.backend.PortalSession
|
import de.kif.backend.PortalSession
|
||||||
import de.kif.backend.Resources
|
import de.kif.backend.Resources
|
||||||
|
import de.kif.backend.authenticate
|
||||||
import de.kif.common.model.User
|
import de.kif.common.model.User
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
|
@ -33,7 +34,7 @@ class MainTemplate(
|
||||||
link(href = "/static/external/material-icons.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
|
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 = "/static/external/font/Montserrat.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
|
||||||
link(
|
link(
|
||||||
href = "https://fonts.googleapis.com/css?family=Bungee|Oswald|Raleway",
|
href = "https://fonts.googleapis.com/css?family=Bungee|Oswald",
|
||||||
type = LinkType.textCss,
|
type = LinkType.textCss,
|
||||||
rel = LinkRel.stylesheet
|
rel = LinkRel.stylesheet
|
||||||
)
|
)
|
||||||
|
@ -49,9 +50,6 @@ class MainTemplate(
|
||||||
Theme.PRINCESS -> {
|
Theme.PRINCESS -> {
|
||||||
link(href = "/static/style/princess.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
|
link(href = "/static/style/princess.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
|
||||||
}
|
}
|
||||||
Theme.BRETT -> {
|
|
||||||
link(href = "/static/style/board.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
script(src = "/static/require.min.js") {}
|
script(src = "/static/require.min.js") {}
|
||||||
|
@ -81,7 +79,8 @@ class MainTemplate(
|
||||||
div("footer-credit") {
|
div("footer-credit") {
|
||||||
}
|
}
|
||||||
div("footer-theme") {
|
div("footer-theme") {
|
||||||
for ((it, name) in Theme.displayThemes) {
|
for (it in Theme.values()) {
|
||||||
|
val name = it.name.toLowerCase()
|
||||||
a("?theme=${it.name}", classes = if (theme == it) "selected" else "") {
|
a("?theme=${it.name}", classes = if (theme == it) "selected" else "") {
|
||||||
id = "theme-$name"
|
id = "theme-$name"
|
||||||
+name.capitalize()
|
+name.capitalize()
|
||||||
|
@ -95,19 +94,14 @@ class MainTemplate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Theme(val display: Boolean, val dark: Boolean, val primaryColor: String) {
|
enum class Theme {
|
||||||
LIGHT(true, false, "#B11D33"),
|
LIGHT, DARK, PRINCESS;
|
||||||
DARK(true, true, "#ef5350"),
|
|
||||||
PRINCESS(true, false, "#B11D33"),
|
|
||||||
BRETT(false, false, "#B11D33");
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val lookup = values().toList().associateBy { it.name }
|
private val loopup = values().toList().associateBy { it.name }
|
||||||
|
|
||||||
val displayThemes = values().filter { it.display }.map { it to it.name.toLowerCase() }
|
|
||||||
|
|
||||||
fun lookup(name: String?): Theme {
|
fun lookup(name: String?): Theme {
|
||||||
return lookup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
|
return loopup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +109,7 @@ enum class Theme(val display: Boolean, val dark: Boolean, val primaryColor: Stri
|
||||||
suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
||||||
noMenu: Boolean = false,
|
noMenu: Boolean = false,
|
||||||
stretch: Boolean = false,
|
stretch: Boolean = false,
|
||||||
body: MainTemplate.(Theme) -> Unit
|
body: MainTemplate.() -> Unit
|
||||||
) {
|
) {
|
||||||
val param = call.request.queryParameters["theme"]
|
val param = call.request.queryParameters["theme"]
|
||||||
val url = call.request.uri.substring(1)
|
val url = call.request.uri.substring(1)
|
||||||
|
@ -130,17 +124,15 @@ suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
|
||||||
)
|
)
|
||||||
call.respondRedirect(call.request.path())
|
call.respondRedirect(call.request.path())
|
||||||
} else {
|
} else {
|
||||||
val theme = Theme.lookup(call.request.cookies["theme"])
|
|
||||||
call.respondHtmlTemplate(
|
call.respondHtmlTemplate(
|
||||||
MainTemplate(
|
MainTemplate(
|
||||||
theme,
|
Theme.lookup(call.request.cookies["theme"]),
|
||||||
url,
|
url,
|
||||||
user,
|
user,
|
||||||
noMenu,
|
noMenu,
|
||||||
stretch
|
stretch
|
||||||
)
|
),
|
||||||
) {
|
body = body
|
||||||
body(theme)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
src/jvmMain/kotlin/twitter4j/AddListener.kt
Normal file
5
src/jvmMain/kotlin/twitter4j/AddListener.kt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package twitter4j
|
||||||
|
|
||||||
|
fun addTwitterStreamListener(stream: TwitterStream, listener: StatusListener) {
|
||||||
|
stream.addListener(listener)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue