added camp document editor, see #24
This commit is contained in:
@@ -11,7 +11,6 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -35,16 +34,11 @@ public class SecurityConfiguration {
|
||||
// @formatter:off
|
||||
.oauth2Login(o -> o.defaultSuccessUrl("/"))
|
||||
.logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr)))
|
||||
.authorizeHttpRequests(o -> o.requestMatchers(
|
||||
AntPathRequestMatcher.antMatcher("/dashboard/**"),
|
||||
AntPathRequestMatcher.antMatcher("/business/**"),
|
||||
AntPathRequestMatcher.antMatcher("/confirmation/**"),
|
||||
AntPathRequestMatcher.antMatcher("/userlogin/**")
|
||||
).authenticated()
|
||||
.authorizeHttpRequests(o -> o.requestMatchers("/dashboard/**", "/business/**", "/confirmation/**","/userlogin/**").authenticated()
|
||||
.anyRequest().permitAll())
|
||||
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
|
||||
.sessionManagement(o -> o.init(sec));
|
||||
// @formatter:on
|
||||
return sec.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
|
||||
import de.jottyfan.camporganizer.module.camplist.CommonController;
|
||||
@@ -192,4 +193,41 @@ public class AdminController extends CommonController {
|
||||
redirect.addAttribute("error", error);
|
||||
return error != null ? "redirect:/admin/camp/edit/" + id : "redirect:/admin/camp";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/campdocument")
|
||||
public String getCampDocumentList(Model model, HttpServletRequest request) {
|
||||
model.addAttribute("campmap", service.getCampMap());
|
||||
model.addAttribute("documentmap", service.getDocumentsMap());
|
||||
model.addAttribute("camps", service.getAllCamps());
|
||||
model.addAttribute("documents", service.getDocumentsForCamps());
|
||||
model.addAttribute("campdocuments", service.getAllCampDocuments());
|
||||
model.addAttribute("campdocument", new CampDocumentBean());
|
||||
return "/admin/campdocument";
|
||||
}
|
||||
|
||||
@PostMapping("/admin/campdocument/add")
|
||||
public String addCampDocument(@Valid @ModelAttribute("bean") CampDocumentBean bean, final BindingResult bindingResult,
|
||||
Model model, HttpServletRequest request, RedirectAttributes redirect) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
for (ObjectError error : bindingResult.getAllErrors()) {
|
||||
LOGGER.error("error {}: {}", error.getCode(), error.getDefaultMessage());
|
||||
}
|
||||
model.addAttribute("campmap", service.getCampMap());
|
||||
model.addAttribute("documentmap", service.getDocumentsMap());
|
||||
model.addAttribute("camps", service.getAllCamps());
|
||||
model.addAttribute("documents", service.getDocumentsForCamps());
|
||||
model.addAttribute("campdocuments", service.getAllCampDocuments());
|
||||
return "/admin/campdocument";
|
||||
}
|
||||
service.upsertCampDocument(bean);
|
||||
return "redirect:/admin/campdocument";
|
||||
}
|
||||
|
||||
@GetMapping("/admin/campdocument/delete/{id}")
|
||||
public String deleteCampDocument(@PathVariable("id") Integer id, Model model, HttpServletRequest request,
|
||||
RedirectAttributes redirect) {
|
||||
service.deleteCampDocument(id);
|
||||
return "redirect:/admin/campdocument";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.jottyfan.camporganizer.module.admin;
|
||||
|
||||
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
|
||||
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPDOCUMENT;
|
||||
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
|
||||
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
|
||||
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE;
|
||||
@@ -38,6 +39,7 @@ import org.jooq.UpdateSetMoreStep;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -46,6 +48,7 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
|
||||
import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype;
|
||||
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampdocumentRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampprofileRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentroleRecord;
|
||||
@@ -53,6 +56,7 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
|
||||
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.IntKeyValueBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
|
||||
@@ -70,11 +74,17 @@ import jakarta.validation.Valid;
|
||||
@Transactional(transactionManager = "transactionManager")
|
||||
public class AdminRepository {
|
||||
|
||||
private final GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(AdminRepository.class);
|
||||
|
||||
@Autowired
|
||||
private DSLContext jooq;
|
||||
|
||||
AdminRepository(GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak) {
|
||||
this.userAuthoritiesMapperForKeycloak = userAuthoritiesMapperForKeycloak;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the document with that ID
|
||||
*
|
||||
@@ -669,4 +679,64 @@ public class AdminRepository {
|
||||
LOGGER.trace(sql);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* get all camp document beans
|
||||
*
|
||||
* @return a list of camp document beans; an empty list at least
|
||||
*/
|
||||
public List<CampDocumentBean> getAllCampDocuments() {
|
||||
SelectWhereStep<TCampdocumentRecord> sql = jooq.selectFrom(T_CAMPDOCUMENT);
|
||||
LOGGER.trace(sql);
|
||||
return sql.fetchInto(CampDocumentBean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a camp document combination; if the combination already exists, ignore this
|
||||
*
|
||||
* @param fkCamp the camp id
|
||||
* @param fkDocument the document id
|
||||
*/
|
||||
public void insertCampDocument(Integer fkCamp, Integer fkDocument) {
|
||||
InsertReturningStep<TCampdocumentRecord> sql = jooq
|
||||
// @formatter:off
|
||||
.insertInto(T_CAMPDOCUMENT,
|
||||
T_CAMPDOCUMENT.FK_CAMP,
|
||||
T_CAMPDOCUMENT.FK_DOCUMENT)
|
||||
.values(fkCamp, fkDocument)
|
||||
.onConflict(T_CAMPDOCUMENT.FK_CAMP, T_CAMPDOCUMENT.FK_DOCUMENT)
|
||||
.doNothing();
|
||||
// @formatter:off
|
||||
LOGGER.trace(sql);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* update the camp document combination
|
||||
*
|
||||
* @param pk the ID of the combination
|
||||
* @param fkCamp the ID of the camp
|
||||
* @param fkDocument the ID of the document
|
||||
*/
|
||||
public void updateCampDocument(Integer pk, Integer fkCamp, Integer fkDocument) {
|
||||
UpdateConditionStep<TCampdocumentRecord> sql = jooq
|
||||
// @formatter:off
|
||||
.update(T_CAMPDOCUMENT)
|
||||
.set(T_CAMPDOCUMENT.FK_CAMP, fkCamp)
|
||||
.set(T_CAMPDOCUMENT.FK_DOCUMENT, fkDocument)
|
||||
.where(T_CAMPDOCUMENT.PK.eq(pk));
|
||||
// @formatter:on
|
||||
LOGGER.trace(sql);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* delete from camp document
|
||||
* @param id the ID of the camp document
|
||||
*/
|
||||
public void deleteCampDocument(Integer id) {
|
||||
DeleteConditionStep<TCampdocumentRecord> sql = jooq.deleteFrom(T_CAMPDOCUMENT).where(T_CAMPDOCUMENT.PK.eq(id));
|
||||
LOGGER.trace(sql);
|
||||
sql.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -14,11 +15,13 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.CampDocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
|
||||
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
|
||||
import de.jottyfan.camporganizer.module.mail.MailBean;
|
||||
import de.jottyfan.camporganizer.module.mail.MailRepository;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -186,4 +189,62 @@ public class AdminService {
|
||||
public String deleteCamp(Integer id) {
|
||||
return adminRepository.deleteCamp(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all camp documents
|
||||
*
|
||||
* @return a list of camp documents; an empty list at least
|
||||
*/
|
||||
public List<CampDocumentBean> getAllCampDocuments() {
|
||||
return adminRepository.getAllCampDocuments();
|
||||
}
|
||||
|
||||
/**
|
||||
* get all documents for camps
|
||||
*
|
||||
* @return the list of documents for camps
|
||||
*/
|
||||
public List<DocumentBean> getDocumentsForCamps() {
|
||||
return adminRepository.getAllDocumentsWith(T_DOCUMENT.DOCTYPE.eq(EnumDocument.camp));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a map of camp beans
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<Integer, CampBean> getCampMap() {
|
||||
return getAllCamps().stream().collect(Collectors.toMap(CampBean::getPk, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a map of document beans
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public Map<Integer, DocumentBean> getDocumentsMap() {
|
||||
return getAllDocuments().stream().collect(Collectors.toMap(DocumentBean::getPk, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* upsert a camp document bean
|
||||
*
|
||||
* @param bean the bean
|
||||
*/
|
||||
public void upsertCampDocument(@Valid CampDocumentBean bean) {
|
||||
if (bean.getPk() == null) {
|
||||
adminRepository.insertCampDocument(bean.getFkCamp(), bean.getFkDocument());
|
||||
} else {
|
||||
adminRepository.updateCampDocument(bean.getPk(), bean.getFkCamp(), bean.getFkDocument());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete the camp document combination
|
||||
*
|
||||
* @param id the ID of the combination
|
||||
*/
|
||||
public void deleteCampDocument(Integer id) {
|
||||
adminRepository.deleteCampDocument(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,11 @@ public class CampBean implements Serializable {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFullname();
|
||||
}
|
||||
|
||||
public String getFullname() {
|
||||
return new StringBuilder().append(name).append(" ").append(arrive == null ? "?" : arrive.format(DateTimeFormatter.ofPattern("yyyy"))).toString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package de.jottyfan.camporganizer.module.admin.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class CampDocumentBean implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer pk;
|
||||
private Integer fkCamp;
|
||||
private Integer fkDocument;
|
||||
|
||||
/**
|
||||
* @return the pk
|
||||
*/
|
||||
public Integer getPk() {
|
||||
return pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pk the pk to set
|
||||
*/
|
||||
public void setPk(Integer pk) {
|
||||
this.pk = pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fkCamp
|
||||
*/
|
||||
public Integer getFkCamp() {
|
||||
return fkCamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fkCamp the fkCamp to set
|
||||
*/
|
||||
public void setFkCamp(Integer fkCamp) {
|
||||
this.fkCamp = fkCamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fkDocument
|
||||
*/
|
||||
public Integer getFkDocument() {
|
||||
return fkDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fkDocument the fkDocument to set
|
||||
*/
|
||||
public void setFkDocument(Integer fkDocument) {
|
||||
this.fkDocument = fkDocument;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,13 @@ public class DocumentBean implements Serializable{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(name).append(".").append(filetype);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Integer getPk() {
|
||||
return pk;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||
<body>
|
||||
<th:block layout:fragment="content">
|
||||
<div class="tablebox" sec:authorize="hasRole('admin')">
|
||||
<div class="alert alert-info">
|
||||
Hier werden zusätzliche Dokumente für Freizeiten bereitgestellt. Die Wegbeschreibung, die Bestätigung und die Jahrespläne sind davon nicht betroffen.
|
||||
</div>
|
||||
<form th:action="@{/admin/campdocument/add}" method="post" th:object="${campdocument}">
|
||||
<table id="cammpdocs" class="table table-striped" style="width: 100% !important">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Freizeit</td>
|
||||
<td>Dokument</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="cd : ${campdocuments}">
|
||||
<td>
|
||||
<div class="dropdown" style="display: inline">
|
||||
<button class="btn btn-outline-danger dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" th:href="@{/admin/campdocument/delete/{id}(id=${cd.pk})}">Kombination endgültig löschen</a>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td th:text="${campmap.get(cd.fkCamp)}"></td>
|
||||
<td th:text="${documentmap.get(cd.fkDocument)}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><button type="submit" class="btn btn-outline-primary"><i class="fa fa-plus"></i></button></td>
|
||||
<td>
|
||||
<select id="campnew" th:field="*{fkCamp}" class="form-select select2-single">
|
||||
<option value="" label="--- bitte wählen ---" />
|
||||
<option th:each="c : ${camps}" th:value="${c.pk}" th:label="${c}" />
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select id="documentnew" th:field="*{fkDocument}" class="form-select select2-single">
|
||||
<option value="" label="--- bitte wählen ---" />
|
||||
<option th:each="d : ${documents}" th:value="${d.pk}" th:label="${d}" />
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</form>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#campdocs").DataTable({
|
||||
language : locale_de
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</th:block>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,8 +4,8 @@
|
||||
<title>Camp Organizer 2</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.3.3/css/bootstrap.min.css} " />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/6.5.2/css/all.min.css} " />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.3.8/css/bootstrap.min.css} " />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/7.0.1/css/all.min.css} " />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.13.5/css/jquery.dataTables.min.css}" />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/select2/4.0.13/css/select2.min.css}" />
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/select2-bootstrap-5-theme.min.css}" />
|
||||
@@ -13,7 +13,7 @@
|
||||
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/fancyapps__fancybox/3.5.7/dist/jquery.fancybox.min.css}">
|
||||
<script th:src="@{/webjars/jquery/3.7.1/jquery.min.js}"></script>
|
||||
<script th:src="@{/webjars/fancyapps__fancybox/3.5.7/dist/jquery.fancybox.min.js}"></script>
|
||||
<script th:src="@{/webjars/bootstrap/5.3.3/js/bootstrap.bundle.min.js}"></script>
|
||||
<script th:src="@{/webjars/bootstrap/5.3.8/js/bootstrap.bundle.min.js}"></script>
|
||||
<script th:src="@{/webjars/datatables/1.13.5/js/jquery.dataTables.min.js}"></script>
|
||||
<script th:src="@{/webjars/select2/4.0.13/js/select2.full.min.js}"></script>
|
||||
<script th:src="@{/js/dataTables.de.js}"></script>
|
||||
@@ -107,7 +107,8 @@
|
||||
<li><a th:href="@{/admin/mail}" class="dropdown-item menufont">Testmail</a></li>
|
||||
<li><a th:href="@{/admin/document}" class="dropdown-item menufont">Dokumente</a></li>
|
||||
<li><a th:href="@{/admin/location}" class="dropdown-item menufont">Freizeitheime</a></li>
|
||||
<li><a th:href="@{/admin/camp}" class="dropdown-item menufont">Freizeiten</a>
|
||||
<li><a th:href="@{/admin/camp}" class="dropdown-item menufont">Freizeiten</a></li>
|
||||
<li><a th:href="@{/admin/campdocument}" class="dropdown-item menufont">Freizeit-Dokumente</a></li>
|
||||
<li class="dropdown dropend">
|
||||
<a class="dropdown-item dropdown-toggle menufont" href="#" role="button" data-bs-auto-close='outside' data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Rechteverwaltung</a>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -135,9 +136,7 @@
|
||||
<span class="icon_profile"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" th:if="${#strings.isEmpty(currentUser)}">
|
||||
<li><a class="dropdown-item" th:href="@{/dashboard}">einloggen</a></li>
|
||||
<li><hr /></li>
|
||||
<li><a class="dropdown-item" th:href="@{/migration/login}">Login umziehen</a></li>
|
||||
<li><a class="dropdown-item" th:href="@{/dashboard}">einloggen / registrieren</a></li>
|
||||
</ul>
|
||||
<ul class="dropdown-menu dropdown-menu-end" th:unless="${#strings.isEmpty(currentUser)}">
|
||||
<li><a class="dropdown-item" th:href="@{${keycloakProfileUrl}}" target="_blank">Benutzername ändern</a></li>
|
||||
|
||||
Reference in New Issue
Block a user