Skip to content

Commit

Permalink
#533 Provide Backend-Switch and improve WK-Overview
Browse files Browse the repository at this point in the history
- provide popup for network-functions in server-status badge
- add select remoteServer function in network-functionset.
- add description for new backend-switch
- add upload-button in wk-overview
- dedicated text-rendering for each connection-scenario
  • Loading branch information
luechtdiode authored and luechtdiode committed Nov 20, 2022
1 parent 523216a commit 99c9bb7
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 89 deletions.
Binary file added Screenshots/backend-switch-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/backend-switch-popup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion docs/HowToSetupTestInstallation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ Einrichten einer Test-Installation

Zunächst muss die App ordentlich heruntergeladen und installiert werden.

Danach kann mit Hilfe folgender Beschreibung eine Test-Instanz konfiguriert werden.
Danach kann im Programm der Server ausgewählt werden. Die Einstellung wird nicht gespeichert.
Nach jedem Programmstart muss sie deshalb wenn nötig wieder eingestellt werden.

Die Einstellung erfolgt über ein PopUp-Menu auf dem blauen Status-Button oben rechts in der
Hauptansicht:

1. ![../Screenshots/backend-switch-popup.png](../Screenshots/backend-switch-popup.png)
2. ![../Screenshots/backend-switch-dialog.png](../Screenshots/backend-switch-dialog.png)

Sollte eine dedizierte Test-Installation eingerichtet werden, bei der die Einstellung nicht immer
wieder eingestellt werden muss, oder bei der andere Server-Adressen hinterlegt sein sollen, weil z.B. mit einem lokalen
Entwicklungs-Server verbunden werden soll, kann mit folgender Anleitung eine Test-Instanz konfiguriert werden.

Die Versionsbezeichnungen können im Laufe geändert haben und müssen ggf. in den Scripts angepasst werden.
Aktuell wird -v2r2 verwendet.
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ app {
remote {
schema = "https"
hostname = "kutuapp.sharevic.net"
hostnames = [
"kutuapp.sharevic.net",
"test-kutuapp.sharevic.net"
]
port = 443
}
}
24 changes: 22 additions & 2 deletions src/main/scala/ch/seidel/kutu/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import java.io.File
import java.net.{Proxy, ProxySelector, URI}
import java.nio.file.{Files, LinkOption, StandardOpenOption}
import java.security.{NoSuchAlgorithmException, SecureRandom}
import java.util.Collections.emptyList
import java.util.UUID
import javax.crypto.KeyGenerator
import scala.collection.mutable
import scala.jdk.CollectionConverters.CollectionHasAsScala
import scala.jdk.javaapi.CollectionConverters

object Config extends KuTuSSLContext {
Expand Down Expand Up @@ -217,7 +219,22 @@ object Config extends KuTuSSLContext {
lazy val jwtTokenExpiryPeriodInDays: Int = jwtConfig.getInt("tokenExpiryPeriodInDays")
lazy val jwtHeader: JwtHeader = JwtHeader(jwtConfig.getString("algorithm"), jwtConfig.getString("contenttype"))

lazy val remoteHost: String = if (appRemoteConfig.hasPath("hostname")) appRemoteConfig.getString("hostname") else "kutuapp"
val defaultRemoteHost: String = if (appRemoteConfig.hasPath("hostname"))
appRemoteConfig.getString("hostname")
else "kutuapp"
private var _remoteHost: String = defaultRemoteHost
def getRemoteHosts: List[String] = ((
if (appRemoteConfig.hasPath("hostnames"))
appRemoteConfig.getStringList("hostnames")
else
emptyList[String]()
)
.asScala.toList :+ defaultRemoteHost).toSet.toList.sorted
def setRemoteHost(host: String): Unit = {
_remoteHost = host
}
def remoteHost = _remoteHost

def remoteSchema: String = if(_isLocalHostServer) {
if(hasHttpsConfig) "https" else "http"
} else if (appRemoteConfig.hasPath("schema")) {
Expand All @@ -236,7 +253,10 @@ object Config extends KuTuSSLContext {
}
def isLocalHostServer: Boolean = _isLocalHostServer
lazy val remoteHostPort: String = if (appRemoteConfig.hasPath("port")) appRemoteConfig.getString("port") else "443"
def remoteBaseUrl: String = if(_isLocalHostServer) if(hasHttpsConfig)s"https://${_localHostRemoteIP.getOrElse(httpHostname)}:$httpPort" else s"http://${_localHostRemoteIP.getOrElse(httpHostname)}:$httpPort" else s"$remoteSchema://$remoteHost:$remoteHostPort"
def remoteBaseUrl: String = if(_isLocalHostServer)
if(hasHttpsConfig)s"https://${_localHostRemoteIP.getOrElse(httpHostname)}:$httpPort"
else s"http://${_localHostRemoteIP.getOrElse(httpHostname)}:$httpPort"
else s"$remoteSchema://$remoteHost:$remoteHostPort"

def remoteOperatingBaseUrl: String = remoteBaseUrl //s"http://$remoteHost:$remotePort/operating"
def remoteAdminBaseUrl: String = remoteBaseUrl//s"$remoteBaseUrl/wkadmin"
Expand Down
9 changes: 9 additions & 0 deletions src/main/scala/ch/seidel/kutu/ConnectionStates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,13 @@ object ConnectionStates {
_connectedWithProperty.setValue("")
_connectedProperty.setValue(false)
}

private val _remoteServerProperty = new StringProperty()
_remoteServerProperty.setValue(Config.remoteBaseUrl)
val remoteServerProperty = new ReadOnlyStringProperty(_remoteServerProperty)

def switchRemoteHost(host: String): Unit = {
Config.setRemoteHost(host)
_remoteServerProperty.setValue(Config.remoteHost)
}
}
119 changes: 93 additions & 26 deletions src/main/scala/ch/seidel/kutu/KuTuApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,11 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport
new Button("OK") {
disable <== when(Bindings.createBooleanBinding(() => {
cmbProgramm.selectionModel.value.getSelectedIndex == -1 ||
txtDatum.value.isNull.value || txtTitel.text.isEmpty.value
txtDatum.value.isNull.value || txtTitel.text.value.isEmpty
},
cmbProgramm.selectionModel.value.selectedIndexProperty, txtDatum.value, txtTitel.text
cmbProgramm.selectionModel.value.selectedIndexProperty,
txtDatum.value,
txtTitel.text
)) choose true otherwise false
onAction = handleAction { implicit e: ActionEvent =>
try {
Expand Down Expand Up @@ -844,6 +846,69 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport
item
}

def makeSelectBackendMenu = {
val item = makeMenuAction("Server auswählen") { (caption, action) =>
implicit val e = action
val filteredModel = ObservableBuffer.from(Config.getRemoteHosts)
val header = new VBox {
children.addAll(
new Label{
text = s"Server Origin"
style = "-fx-font-size: 1.2em;-fx-font-weight: bold;-fx-padding: 8px 0 2px 0;-fx-text-fill: #0072aa;"
styleClass += "toolbar-header"},
new Label(s" Aktuell: ${Config.remoteHost}"),
new Label(s" Default: ${Config.defaultRemoteHost}")
)
}
val serverList = new ListView[String](filteredModel)
serverList.selectionModel.value.setSelectionMode(SelectionMode.Single)
serverList.selectionModel.value.select(Config.remoteHost)
PageDisplayer.showInDialog(caption, new DisplayablePage() {
def getPage: Node = {
new BorderPane {
hgrow = Priority.Always
vgrow = Priority.Always
minWidth = 600
center = new BorderPane {
hgrow = Priority.Always
vgrow = Priority.Always
top = header
center = serverList
minWidth = 550
}

}
}
}, new Button("Default") {
onAction = (_: ActionEvent) => {
ConnectionStates.switchRemoteHost(Config.defaultRemoteHost)
}
}, new Button("OK") {
disable <== when(serverList.selectionModel.value.selectedItemProperty.isNull()) choose true otherwise false
onAction = (_: ActionEvent) => {
if (!serverList.selectionModel().isEmpty) {
serverList.items.value.zipWithIndex.filter {
x => serverList.selectionModel.value.isSelected(x._2)
}.collectFirst{ selected =>
val (server, _) = selected
ConnectionStates.switchRemoteHost(server)
}
}
}
})
}
item.disable <== when(Bindings.createBooleanBinding(() =>
ConnectionStates.connectedWithProperty.value.nonEmpty
|| modelWettkampfModus.value
|| LocalServerStates.localServerProperty.value,
ConnectionStates.connectedWithProperty,
ConnectionStates.remoteServerProperty,
modelWettkampfModus,
LocalServerStates.localServerProperty
)) choose true otherwise false
item
}

def connectAndShare(p: WettkampfView, caption: String, action: ActionEvent) = {
implicit val e = action
val process = KuTuApp.invokeAsyncWithBusyIndicator {
Expand Down Expand Up @@ -976,7 +1041,10 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport
txtTitel.text.isEmpty.value ||
txtNotificationEMail.text.isEmpty.value
},
cmbProgramm.selectionModel.value.selectedIndexProperty, txtDatum.value, txtTitel.text
cmbProgramm.selectionModel.value.selectedIndexProperty,
txtDatum.value,
txtNotificationEMail.text,
txtTitel.text
)) choose true otherwise false
onAction = handleAction { implicit e: ActionEvent =>
val w = createWettkampf(
Expand Down Expand Up @@ -1521,6 +1589,19 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport

override def start(): Unit = {
markAthletesInactiveOlderThan(3)

rootTreeItem = new TreeItem[String]("Dashboard") {
expanded = true
children = tree.getTree
}
controlsView = new TreeView[String]() {
minWidth = 5
maxWidth = 400
prefWidth = 200
editable = true
root = rootTreeItem
id = "page-tree"
}
val btnWettkampfModus = new ToggleButton("Wettkampf-Modus") {
id = "wettkampfmodusButton"
selected <==> modelWettkampfModus
Expand Down Expand Up @@ -1562,31 +1643,16 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport
s"Server: ${Config.remoteBaseUrl} online\nVersion: ${Config.appFullVersion}, Built: ${Config.builddate}"
}
//visible <== ConnectionStates.connectedProperty
}, ConnectionStates.connectedWithProperty, LocalServerStates.localServerProperty)
}
val screen = Screen.primary
rootTreeItem = new TreeItem[String]("Dashboard") {
expanded = true
children = tree.getTree
}
controlsView = new TreeView[String]() {
minWidth = 5
maxWidth = 400
prefWidth = 200
editable = true
root = rootTreeItem
id = "page-tree"
}, ConnectionStates.connectedWithProperty, LocalServerStates.localServerProperty, ConnectionStates.remoteServerProperty)
contextMenu = new ContextMenu {
items += makeSelectBackendMenu
items += makeStartServerMenu
items += makeStopServerMenu
items += makeProxyLoginMenu
items += makeWettkampfHerunterladenMenu
}
}
val centerPane = PageDisplayer.choosePage(modelWettkampfModus, None, "dashBoard", tree)
val sscrollPane = new ScrollPane {
minWidth = 5
maxWidth = 400
prefWidth = 200
fitToWidth = true
fitToHeight = true
id = "page-tree"
content = controlsView
}
val splitPane = new SplitPane {
dividerPositions = 0.2
id = "page-splitpane"
Expand Down Expand Up @@ -1655,6 +1721,7 @@ object KuTuApp extends JFXApp3 with KutuService with JsonSupport with JwtSupport
items += makeNeuerWettkampfImportierenMenu
items += new Menu("Netzwerk") {
//items += makeLoginMenu
items += makeSelectBackendMenu
items += makeStartServerMenu
items += makeStopServerMenu
items += makeProxyLoginMenu
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/ch/seidel/kutu/domain/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ package object domain {

def hasRemote(homedir: String, origin: String): Boolean = {
val path = fromOriginFilePath(homedir, origin)
return path.toFile.exists
path.toFile.exists
}

def removeRemote(homedir: String, origin: String): Unit = {
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/ch/seidel/kutu/http/Core.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ trait KuTuAppHTTPServer extends ApiService with JsonSupport {
}

try {
dgs.connect(InetAddress.getByAddress(Array[Byte](1, 1, 1, 1)), 0)
dgs.connect(InetAddress.getByAddress(Array[Byte](1, 1, 1, 1)), 53)
val networkInterface = NetworkInterface
.getByInetAddress(dgs.getLocalAddress)
val internetAccessAdresses = if (networkInterface == null) List.empty else networkInterface
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ch.seidel.kutu.renderer

import ch.seidel.kutu.Config

import java.io.File
import ch.seidel.kutu.Config.remoteBaseUrl
import ch.seidel.kutu.Config.{homedir, remoteBaseUrl, remoteHostOrigin}
import ch.seidel.kutu.KuTuApp.enc
import ch.seidel.kutu.domain._
import ch.seidel.kutu.renderer.PrintUtil._
Expand Down Expand Up @@ -105,16 +107,8 @@ trait WettkampfOverviewToHtmlRenderer {
tr .blockstart:not(:first-child) {
border-left: 1px solid lightgray;
}
ul {
margin: 0px;
padding: 0px;
border: 0px;
list-style: none;
overflow: auto;
}
li {
float: left;
width: 100%;
font-size: 12px;
}
.headline {
display: block;
Expand Down Expand Up @@ -175,6 +169,8 @@ trait WettkampfOverviewToHtmlRenderer {
val totSum = tiSum + tuSum

val logoHtml = (if (logo.exists) s"""<img class=logo src="${logo.imageSrcForWebEngine}" title="Logo"/>""" else s"")
val hasRemote = wettkampf.toWettkampf.hasSecred(homedir, remoteHostOrigin)
val isLocalServer = Config.isLocalHostServer
val registrationURL = s"$remoteBaseUrl/registration/${wettkampf.uuid.get}"
val regQRUrl = toQRCodeImage(registrationURL)

Expand Down Expand Up @@ -240,14 +236,37 @@ trait WettkampfOverviewToHtmlRenderer {
</div>
<h2>Anmeldungen</h2>
<div class=headline>
<img class=qrcode src="$regQRUrl"/>
<h3>Wettkampf-Registrierung / Online-Anmeldungen</h3>
<p class=wordwrapper>Zum Versenden an die Vereinsverantwortlichen oder für in die Wettkampf-Ausschreibung.<br>
<a href="$registrationURL" target="_blank">$registrationURL</a>
</p>
<h3>EMail des Wettkampf-Administrators</h3>
<p>An diese EMail Adresse werden Notifikations-Meldungen versendet, sobald sich an den Anmeldungen Mutationen ergeben.<br>
${if (wettkampf.notificationEMail.nonEmpty) s"""<a href="mailto://${wettkampf.notificationEMail}" target="_blank">${wettkampf.notificationEMail}</a>""" else "<strong>Keine EMail hinterlegt!</strong>"}
${
if (!isLocalServer) {
if (hasRemote)
s"""<img class=qrcode src="$regQRUrl"/>
<h3>Wettkampf-Registrierung / Online-Anmeldungen</h3>
<p class=wordwrapper>Zum Versenden an die Vereinsverantwortlichen oder für in die Wettkampf-Ausschreibung.<br>
<a href="$registrationURL" target="_blank">$registrationURL</a>
</p>"""
else
s"""<h3>Wettkampf-Registrierung / Online-Anmeldungen</h3>
<p class=wordwrapper>
Der Wettkampf ist nur lokal gespeichert, resp. noch nicht auf den Server ${Config.remoteHost} hochgeladen.</p>
<p class=wordwrapper>
Folgende Online-Funktionen sind nur verfügbar, wenn der Wettkampf hochgeladen wird. <em>(siehe Funktion "Upload")</em>
<ul>
<li>Anmeldungen über die Vereinsverantwortlichen</li>
<li>Online Resultat-Erfassung über die Wertungsrichter</li>
<li>Online Rangliste bereitstellen</li>
</ul>
</p>
"""
} else ""
}
${
if (!isLocalServer) {
s"""<h3>EMail des Wettkampf-Administrators</h3>
<p>An diese EMail Adresse werden Notifikations-Meldungen versendet, sobald sich an den Anmeldungen Mutationen ergeben.<br>
${if (wettkampf.notificationEMail.nonEmpty) s"""<a href="mailto://${wettkampf.notificationEMail}" target="_blank">${wettkampf.notificationEMail}</a>""" else "<strong>Keine EMail hinterlegt!</strong>"}
"""
} else ""
}
</p>
<h3>Zusammenstellung der Anmeldungen</h3>
</div>
Expand Down Expand Up @@ -280,20 +299,23 @@ trait WettkampfOverviewToHtmlRenderer {
</table><br>
</div>
<em>(Ohne Reserven)</em>
<h2 id="usefullinks">Weitere nützliche Links</h2>
<div class=headline>
<img class=qrcode src="$startlistQRUrl"/>
<h3>Online Liste der Teilnehmer/-Innen</h3><p class=wordwrapper>
Liste aller angemeldeten Teilnehmer/-Innen mit ihrer Starteinteilung.<br>
<a href="$startlistURL" target="_blank">$startlistURL</a>
</p>
</div>
<div class=headline>
<img class=qrcode src="$lastQRUrl"/>
<h3>Online Wettkampfresultate</h3><p class=wordwrapper>Direkter Link in die Online App, wo die letzten Resultate publiziert werden.<br>
<a href="$lastResultsURL" target="_blank">$lastResultsURL</a>
${ if (hasRemote)
s"""
<h2 id="usefullinks">Weitere nützliche Links</h2>
<div class=headline>
<img class=qrcode src="$startlistQRUrl"/>
<h3>Online Liste der Teilnehmer/-Innen</h3><p class=wordwrapper>
Liste aller angemeldeten Teilnehmer/-Innen mit ihrer Starteinteilung.<br>
<a href="$startlistURL" target="_blank">$startlistURL</a>
</p>
</div>
<div class=headline>
<img class=qrcode src="$lastQRUrl"/>
<h3>Online Wettkampfresultate</h3><p class=wordwrapper>Direkter Link in die Online App, wo die letzten Resultate publiziert werden.<br>
<a href="$lastResultsURL" target="_blank">$lastResultsURL</a>
</div>"""
}
</div>
</div>
"""
}

Expand Down
Loading

0 comments on commit 99c9bb7

Please sign in to comment.