View layout in progress

This commit is contained in:
Claude Brisson
2023-06-11 13:52:15 +02:00
parent c9d812260a
commit a8d4649404
14 changed files with 129 additions and 48 deletions

View File

@@ -40,7 +40,7 @@ class FileStore(pathStr: String): StoreImplementation {
return path.useDirectoryEntries("*.tour") { entries ->
entries.mapNotNull { entry ->
filenameRegex.matchEntire(entry.fileName.toString())?.groupValues?.get(1)?.toID()
}.toSet()
}.toSortedSet()
}
}

View File

@@ -170,7 +170,7 @@ class ApiServlet : HttpServlet() {
}
// check charset
if (charset != null && EXPECTED_CHARSET != charset) throw ApiException(
if (charset != null && EXPECTED_CHARSET != charset.lowercase(Locale.ROOT).replace("-", "")) throw ApiException(
HttpServletResponse.SC_BAD_REQUEST,
"UTF-8 content expected"
)
@@ -215,6 +215,8 @@ class ApiServlet : HttpServlet() {
HttpServletResponse.SC_BAD_REQUEST,
"Missing 'Accept' header"
)
// CB TODO 1) a reference to a specific API call at this point is a code smell.
// 2) there will e other content types: .tou, .h9, .html
if (!isJson(accept) && (!isXml(accept) || !request.requestURI.matches(Regex("/api/tour/\\d+")))) throw ApiException(
HttpServletResponse.SC_BAD_REQUEST,
"Invalid 'Accept' header"

View File

@@ -196,34 +196,12 @@
<version>70.1</version>
</dependency>
-->
<!-- net clients
<!-- http client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
-->
<!-- server-side events -->
<dependency>
<groupId>com.republicate</groupId>

View File

@@ -9,9 +9,13 @@
<link rel="stylesheet" href="/fonts/fork-awesome-1.2.0/fork-awesome.min.css">
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<div id="header">
<div id="logo">Pairgoth v0.1</div>
<body class="vert flex">
<div id="header" class="horz flex">
<div id="left-header" class="horz flex">
<div id="logo" class="vert flex">
<img src="/img/logo.svg"/>
</div>
</div>
<div id="menu">
<button>New tournament</button>
<button>Players</button>
@@ -20,11 +24,16 @@
<button>Results</button>
<button>Standings</button>
</div>
<div id="right-header">
[flag]
</div>
</div>
<div id="center">
#translate($page)
</div>
<div id="footer">
<div id="footer" class="horz flex">
<div id="version">pairgoth v0.1</div>
<div id="contact"><a href="mailto:pairgoth@jeudego.org">contact</a></div>
</div>
<script type="text/javascript" src="/js/store2-2.14.2.min.js"></script>
<script type="text/javascript" src="/js/tablesort-5.4.0.min.js"></script>

View File

@@ -17,7 +17,8 @@
</toolbox>
<toolbox scope="request">
<tool key="intl" class="org.jeudego.pairgoth.view.IntlTool"/>
<tool key="translate" class="org.jeudego.pairgoth.view.TranslationTool"/>
<tool key="api" class="org.jeudego.pairgoth.view.ApiTool"/>
</toolbox>
</tools>

View File

@@ -0,0 +1,43 @@
package org.jeudego.pairgoth.view
import com.republicate.kson.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.internal.EMPTY_REQUEST
class ApiTool {
companion object {
const val JSON = "application/json"
val apiRoot = System.getProperty("pairgoth.webapp.url").let { base ->
if (base.endsWith('/')) "${base}api/"
else "${base}/api/"
}
}
private val client = OkHttpClient()
private fun prepare(url: String) = Request.Builder().url("$apiRoot$url").header("Accept", JSON)
private fun Json.toRequestBody() = toString().toRequestBody(JSON.toMediaType())
private fun Request.Builder.process(): Json {
client.newCall(build()).execute().use { response ->
if (response.isSuccessful) {
when (response.body?.contentType()?.subtype) {
null -> throw Error("null body or content type")
"json" -> return Json.parse(response.body!!.string()) ?: throw Error("could not parse json")
else -> throw Error("unhandled content type: ${response.body!!.contentType()}")
}
} else throw Error("api call failed: ${response.code} ${response.message}")
}
}
fun get(url: String) = prepare(url).process()
fun post(url: String, payload: Json) = prepare(url)
.post(payload.toRequestBody())
.process()
fun put(url: String, payload: Json) = prepare(url)
.put(payload.toRequestBody())
.process()
fun delete(url: String, payload: Json? = null) = prepare(url)
.delete(payload?.toRequestBody() ?: EMPTY_REQUEST)
.process()
}

View File

@@ -2,7 +2,6 @@ package org.jeudego.pairgoth.view
import org.apache.velocity.Template
import org.apache.velocity.runtime.directive.Parse
import org.jeudego.pairgoth.view.IntlTool
class TranslateDirective : Parse() {
override fun getName(): String {
@@ -11,7 +10,7 @@ class TranslateDirective : Parse() {
override fun getTemplate(path: String, encoding: String): Template? {
val template = super.getTemplate(path, encoding)
val translator = IntlTool.translator.get()
val translator = TranslationTool.translator.get()
?: throw RuntimeException("no current active translator")
return translator.translate(path, template)
}

View File

@@ -4,7 +4,7 @@ import org.apache.velocity.tools.config.ValidScope
import org.jeudego.pairgoth.util.Translator
@ValidScope("request")
class IntlTool {
class TranslationTool {
fun translate(enText: String): String {
return translator.get().translate(enText)

View File

@@ -3,7 +3,7 @@ package org.jeudego.pairgoth.web
import org.jeudego.pairgoth.util.Translator
import org.jeudego.pairgoth.util.Translator.Companion.defaultLanguage
import org.jeudego.pairgoth.util.Translator.Companion.providedLanguages
import org.jeudego.pairgoth.view.IntlTool
import org.jeudego.pairgoth.view.TranslationTool
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.FilterConfig
@@ -25,7 +25,7 @@ class LanguageFilter : Filter {
val reqLang = request.getAttribute("lang") as String?
if (reqLang != null) {
IntlTool.translator.set(Translator.getTranslator(reqLang))
TranslationTool.translator.set(Translator.getTranslator(reqLang))
chain.doFilter(request, response)
} else {
val uri = request.requestURI

View File

@@ -1,19 +1,38 @@
body {
font-size : clamp(2rem, 10vw, 5rem);
font-size : clamp(1rem, 3vw, 3rem);
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
.flex {
display: flex;
flex-flow: column nowrap;
justify-items: stretch;
align-items: stretch;
&.horz {
flex-flow: row nowrap;
align-items: center;
}
&.vert {
flex-flow: column nowrap;
align-items: stretch;
}
}
#header {
height: 1.5em;
display: flex;
flex-flow: row nowrap;
justify-items: center;
align-items: stretch;
height: 2em;
width: 100%;
position: relative;
justify-content: space-between;
#left-header, #right-header {
height: 100%;
}
#logo {
height: 100%;
img {
display: inline-block;
max-height: 100%;
}
}
}
#center {
@@ -24,5 +43,9 @@ body {
}
#footer {
height: 1.5em;
flex: 0;
height: 2em;
margin: 0 2em;
font-size: 0.8em;
justify-content: space-between;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1 +1,4 @@
Known tournaments:
$api.get('tour')

View File

@@ -61,6 +61,11 @@
<artifactId>http2-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>