Added basic forms for parties
This commit is contained in:
+5
-5
@@ -22,20 +22,20 @@ public class WebSecurityConfiguration {
|
|||||||
public SecurityFilterChain securityFilterChain(@NonNull HttpSecurity http) throws Exception { // (2)
|
public SecurityFilterChain securityFilterChain(@NonNull HttpSecurity http) throws Exception { // (2)
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests()
|
.authorizeHttpRequests()
|
||||||
.requestMatchers("/wahlrecht/v1/**")
|
.requestMatchers("/wahlrecht/v1/**", "/wahlrecht/version")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.and()
|
.and()
|
||||||
.authorizeHttpRequests()
|
.authorizeHttpRequests()
|
||||||
.requestMatchers("/resources/**")
|
.requestMatchers("/resources/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.and()
|
.and()
|
||||||
.csrf()
|
|
||||||
.disable()
|
|
||||||
.authorizeHttpRequests()
|
.authorizeHttpRequests()
|
||||||
.requestMatchers("/wahlrecht/v1/election", "/wahlrecht/v1/party")
|
.requestMatchers("/wahlrecht/v1/election",
|
||||||
|
"/wahlrecht/v1/party/**",
|
||||||
|
"/wahlrecht/v1/nomination/**")
|
||||||
.authenticated() // (3)
|
.authenticated() // (3)
|
||||||
.and()
|
.and()
|
||||||
.formLogin(); // (7)
|
.httpBasic(); // (7)
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-12
@@ -15,7 +15,6 @@ import java.util.zip.ZipEntry;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
@@ -30,38 +29,35 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
@RequestMapping(value = "/wahlrecht")
|
@RequestMapping(value = "/wahlrecht")
|
||||||
public class VersionHtmlController {
|
public class VersionHtmlController {
|
||||||
|
|
||||||
@GetMapping(path = "/html/version.html")
|
@GetMapping(path = "/version")
|
||||||
public String version() {
|
public String version() {
|
||||||
return "version";
|
return "version";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAdvice
|
|
||||||
public static class VersionControllerAdvice {
|
|
||||||
|
|
||||||
@ModelAttribute("version")
|
@ModelAttribute("version")
|
||||||
public String getApplicationVersion() {
|
private String getApplicationVersion() {
|
||||||
return getTitle() + " " + getVersion();
|
return getTitle() + " " + getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("footerString")
|
@ModelAttribute("footerString")
|
||||||
public String getApplicationVersion(@RequestHeader("host") String hostName) {
|
private String getApplicationVersion(@RequestHeader("host") String hostName) {
|
||||||
return getTitle() + " " + getVersion() + " - " + hostName;
|
return getTitle() + " " + getVersion() + " - " + hostName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTitle() {
|
private String getTitle() {
|
||||||
return Optional.ofNullable(VersionControllerAdvice.class.getPackage().getImplementationTitle())
|
return Optional.ofNullable(VersionHtmlController.class.getPackage().getImplementationTitle())
|
||||||
.filter(s -> !s.isBlank())
|
.filter(s -> !s.isBlank())
|
||||||
.orElse("application");
|
.orElse("application");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return Optional.ofNullable(VersionControllerAdvice.class.getPackage().getImplementationVersion())
|
return Optional.ofNullable(VersionHtmlController.class.getPackage().getImplementationVersion())
|
||||||
.filter(s -> !s.isBlank())
|
.filter(s -> !s.isBlank())
|
||||||
.orElse("DEVELOPER");
|
.orElse("DEVELOPER");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("hostname")
|
@ModelAttribute("hostname")
|
||||||
public String getHostname() {
|
private String getHostname() {
|
||||||
try {
|
try {
|
||||||
return InetAddress.getLocalHost().getHostName();
|
return InetAddress.getLocalHost().getHostName();
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
@@ -89,7 +85,5 @@ public class VersionHtmlController {
|
|||||||
return List.of(getTitle() + " " + getVersion());
|
return List.of(getTitle() + " " + getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-3
@@ -1,8 +1,12 @@
|
|||||||
package de.twomartens.wahlrecht.controller.v1;
|
package de.twomartens.wahlrecht.controller.v1;
|
||||||
|
|
||||||
|
import de.twomartens.wahlrecht.mapper.v1.ElectionMapper;
|
||||||
import de.twomartens.wahlrecht.mapper.v1.PartyInElectionMapper;
|
import de.twomartens.wahlrecht.mapper.v1.PartyInElectionMapper;
|
||||||
import de.twomartens.wahlrecht.model.dto.ErrorMessage;
|
import de.twomartens.wahlrecht.model.dto.ErrorMessage;
|
||||||
|
import de.twomartens.wahlrecht.model.dto.v1.Election;
|
||||||
import de.twomartens.wahlrecht.model.dto.v1.PartyInElection;
|
import de.twomartens.wahlrecht.model.dto.v1.PartyInElection;
|
||||||
|
import de.twomartens.wahlrecht.service.ConstituencyService;
|
||||||
|
import de.twomartens.wahlrecht.service.ElectionService;
|
||||||
import de.twomartens.wahlrecht.service.PartyService;
|
import de.twomartens.wahlrecht.service.PartyService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -20,22 +24,28 @@ import org.mapstruct.factory.Mappers;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@Controller
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RequestMapping(value = "/wahlrecht/v1")
|
@RequestMapping(value = "/wahlrecht/v1")
|
||||||
@Tag(name = "Parties", description = "all requests relating to parties")
|
@Tag(name = "Parties", description = "all requests relating to parties")
|
||||||
public class PartyController {
|
public class PartyController {
|
||||||
|
|
||||||
private final PartyInElectionMapper mapper = Mappers.getMapper(PartyInElectionMapper.class);
|
private final PartyInElectionMapper mapper = Mappers.getMapper(PartyInElectionMapper.class);
|
||||||
|
private final ElectionMapper electionMapper = Mappers.getMapper(ElectionMapper.class);
|
||||||
private final PartyService service;
|
private final PartyService service;
|
||||||
|
private final ElectionService electionService;
|
||||||
|
private final ConstituencyService constituencyService;
|
||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Returns all stored parties for given election name",
|
summary = "Returns all stored parties for given election name",
|
||||||
@@ -85,10 +95,55 @@ public class PartyController {
|
|||||||
)
|
)
|
||||||
@PutMapping("/party")
|
@PutMapping("/party")
|
||||||
@SecurityRequirement(name = "basicAuth")
|
@SecurityRequirement(name = "basicAuth")
|
||||||
public ResponseEntity<?> putElection(@RequestBody PartyInElection party) {
|
public ResponseEntity<?> putParty(@RequestBody PartyInElection party) {
|
||||||
boolean createdNew = service.storeParty(mapper.mapToDB(party));
|
boolean createdNew = service.storeParty(mapper.mapToDB(party));
|
||||||
return createdNew
|
return createdNew
|
||||||
? new ResponseEntity<>(HttpStatus.CREATED)
|
? new ResponseEntity<>(HttpStatus.CREATED)
|
||||||
: new ResponseEntity<>(HttpStatus.OK);
|
: new ResponseEntity<>(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/party")
|
||||||
|
public String submitCreatePartyForm(@ModelAttribute PartyInElection party, Model model) {
|
||||||
|
service.storeParty(mapper.mapToDB(party));
|
||||||
|
model.addAttribute("party", party);
|
||||||
|
model.addAttribute("isCreate", false);
|
||||||
|
return "partyUpdate";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/party")
|
||||||
|
public String createPartyForm(Model model) {
|
||||||
|
model.addAttribute("party", new PartyInElection());
|
||||||
|
model.addAttribute("isCreate", true);
|
||||||
|
return "partyCreate";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/party/{electionName}/{abbreviation}")
|
||||||
|
public String submitUpdatePartyForm(
|
||||||
|
@PathVariable String electionName,
|
||||||
|
@PathVariable String abbreviation,
|
||||||
|
@ModelAttribute PartyInElection party,
|
||||||
|
Model model) {
|
||||||
|
return submitCreatePartyForm(party, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/party/{electionName}/{abbreviation}")
|
||||||
|
public String updatePartyForm(
|
||||||
|
@PathVariable String electionName,
|
||||||
|
@PathVariable String abbreviation,
|
||||||
|
Model model) {
|
||||||
|
PartyInElection party = mapper.mapToExternal(
|
||||||
|
service.getPartyByElectionNameAndAbbreviation(electionName, abbreviation));
|
||||||
|
model.addAttribute("party", party);
|
||||||
|
model.addAttribute("isCreate", false);
|
||||||
|
|
||||||
|
return "partyUpdate";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("elections")
|
||||||
|
Collection<Election> elections() {
|
||||||
|
return electionService.getElections().stream()
|
||||||
|
.map(electionMapper::mapToExternal)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
package de.twomartens.wahlrecht.model.dto.v1;
|
package de.twomartens.wahlrecht.model.dto.v1;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
public record Nomination(String name, String partyAbbreviation, String electionName,
|
@Getter
|
||||||
boolean supportVotesOnNomination,
|
@Setter
|
||||||
List<Candidate> candidates) {
|
@EqualsAndHashCode
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class Nomination {
|
||||||
|
|
||||||
|
String electionName;
|
||||||
|
String partyAbbreviation;
|
||||||
|
String name;
|
||||||
|
boolean supportVotesOnNomination;
|
||||||
|
List<Candidate> candidates;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-4
@@ -1,9 +1,27 @@
|
|||||||
package de.twomartens.wahlrecht.model.dto.v1;
|
package de.twomartens.wahlrecht.model.dto.v1;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.ToString.Include;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
public record PartyInElection(String electionName, String abbreviation, String name,
|
@Getter
|
||||||
Nomination overallNomination,
|
@Setter
|
||||||
Map<Integer, Nomination> constituencyNominations) {
|
@EqualsAndHashCode
|
||||||
|
@ToString(onlyExplicitlyIncluded = true)
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class PartyInElection {
|
||||||
|
String electionName;
|
||||||
|
@Include
|
||||||
|
String abbreviation;
|
||||||
|
String name;
|
||||||
|
Nomination overallNomination;
|
||||||
|
@NonNull
|
||||||
|
Map<Integer, Nomination> constituencyNominations = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ public class NominationService {
|
|||||||
private final CandidateService candidateService;
|
private final CandidateService candidateService;
|
||||||
|
|
||||||
public Nomination storeNomination(Nomination nomination) {
|
public Nomination storeNomination(Nomination nomination) {
|
||||||
|
if (nomination == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Optional<Nomination> existingOptional = repository
|
Optional<Nomination> existingOptional = repository
|
||||||
.findByNameAndElectionNameAndPartyAbbreviation(nomination.getName(),
|
.findByNameAndElectionNameAndPartyAbbreviation(nomination.getName(),
|
||||||
nomination.getElectionName(), nomination.getPartyAbbreviation());
|
nomination.getElectionName(), nomination.getPartyAbbreviation());
|
||||||
|
|||||||
@@ -28,10 +28,21 @@ public class PartyService {
|
|||||||
return parties;
|
return parties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PartyInElection getPartyByElectionNameAndAbbreviation(String electionName,
|
||||||
|
String abbreviation) {
|
||||||
|
Optional<PartyInElection> optionalParty = partyRepository.findByAbbreviationAndElectionName(
|
||||||
|
abbreviation, electionName);
|
||||||
|
if (optionalParty.isEmpty()) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||||
|
"no party found for %s and %s".formatted(electionName, abbreviation));
|
||||||
|
}
|
||||||
|
return optionalParty.get();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean storeParty(PartyInElection partyInElection) {
|
public boolean storeParty(PartyInElection partyInElection) {
|
||||||
boolean createdNew = true;
|
boolean createdNew = true;
|
||||||
Optional<PartyInElection> existingOptional = partyRepository
|
Optional<PartyInElection> existingOptional = partyRepository
|
||||||
.findByAbbreviationAndElectionName(partyInElection.getName(),
|
.findByAbbreviationAndElectionName(partyInElection.getAbbreviation(),
|
||||||
partyInElection.getElectionName());
|
partyInElection.getElectionName());
|
||||||
Map<Integer, Nomination> constituencyNominations = new HashMap<>();
|
Map<Integer, Nomination> constituencyNominations = new HashMap<>();
|
||||||
partyInElection.getConstituencyNominations()
|
partyInElection.getConstituencyNominations()
|
||||||
@@ -40,6 +51,9 @@ public class PartyService {
|
|||||||
if (existingOptional.isPresent()) {
|
if (existingOptional.isPresent()) {
|
||||||
PartyInElection existing = existingOptional.get();
|
PartyInElection existing = existingOptional.get();
|
||||||
existing.setName(partyInElection.getName());
|
existing.setName(partyInElection.getName());
|
||||||
|
if (constituencyNominations.isEmpty()) {
|
||||||
|
constituencyNominations.putAll(existing.getConstituencyNominations());
|
||||||
|
}
|
||||||
partyInElection = existing;
|
partyInElection = existing;
|
||||||
createdNew = false;
|
createdNew = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="de">
|
||||||
|
<head th:fragment="head">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title th:text="${party}"></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form action="#" th:action="@{/wahlrecht/v1/party}"
|
||||||
|
th:object="${party}" method="post">
|
||||||
|
<div>
|
||||||
|
<label for="electionName" class="form-label">Wahlname</label>
|
||||||
|
<select name="electionName" id="electionName" class="form-select"
|
||||||
|
th:field="*{electionName}">
|
||||||
|
<option th:each="election : ${elections}" th:text="${election.name()}"
|
||||||
|
th:value="${election.name()}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="partyAbbreviation" class="form-label">Parteiabkürzung</label>
|
||||||
|
<input type="text" name="abbreviation" id="partyAbbreviation" class="form-control"
|
||||||
|
th:field="*{abbreviation}">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="partyName" class="form-label">Parteiname</label>
|
||||||
|
<input type="text" name="name" id="partyName" class="form-control"
|
||||||
|
th:field="*{name}">
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary"
|
||||||
|
th:text="${party.name} != null ? 'Aktualisieren' : 'Erstellen'">
|
||||||
|
Erstellen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="de">
|
||||||
|
<head th:fragment="head">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title th:text="${party}"></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form action="#" th:action="@{/wahlrecht/v1/party/{electionName}/{abbreviation}(electionName=${party.electionName}, abbreviation=${party.abbreviation})}"
|
||||||
|
th:object="${party}" method="post">
|
||||||
|
<div>
|
||||||
|
<label for="electionName" class="form-label">Wahlname</label>
|
||||||
|
<select name="electionName" id="electionName" class="form-select"
|
||||||
|
th:field="*{electionName}">
|
||||||
|
<option th:each="election : ${elections}" th:text="${election.name()}"
|
||||||
|
th:value="${election.name()}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="partyAbbreviation" class="form-label">Parteiabkürzung</label>
|
||||||
|
<input type="text" name="abbreviation" id="partyAbbreviation" class="form-control"
|
||||||
|
th:field="*{abbreviation}">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="partyName" class="form-label">Parteiname</label>
|
||||||
|
<input type="text" name="name" id="partyName" class="form-control"
|
||||||
|
th:field="*{name}">
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary"
|
||||||
|
th:text="${party.name} != null ? 'Aktualisieren' : 'Erstellen'">
|
||||||
|
Erstellen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||||
<head th:fragment="head">
|
<head th:fragment="head">
|
||||||
<title><th:block th:text="${version}"/></title>
|
<title th:text="${version}"></title>
|
||||||
<style>
|
<style>
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html{background:#000000;color:#ffffff;font:15px/22px arial,sans-serif}
|
html{background:#000000;color:#ffffff;font:15px/22px arial,sans-serif}
|
||||||
|
|||||||
Reference in New Issue
Block a user