Compare commits

2 Commits

Author SHA1 Message Date
Jottyfan
8f9878a8e2 fixed bug on entering themes without teachers 2025-12-15 21:12:29 +01:00
Jottyfan
b048b7c7bc fixed bugs 2025-12-14 21:51:29 +01:00
12 changed files with 93 additions and 39 deletions

View File

@@ -1,14 +1,14 @@
plugins { plugins {
id 'java' id 'java'
id 'org.springframework.boot' version '3.5.0' id 'org.springframework.boot' version '4.0.0'
id "io.spring.dependency-management" version "1.1.6" id "io.spring.dependency-management" version "1.1.7"
id 'war' id 'war'
id 'eclipse' id 'eclipse'
id 'application' id 'application'
} }
group = 'de.jottyfan.bico' group = 'de.jottyfan.bico'
version = '0.2.8' version = '0.2.9'
description = """BibleClassOrganizer""" description = """BibleClassOrganizer"""
@@ -48,6 +48,8 @@ dependencies {
implementation 'org.mnode.ical4j:ical4j:4.2.2' implementation 'org.mnode.ical4j:ical4j:4.2.2'
implementation 'org.jooq:jooq:3.20.10'
implementation 'org.springframework.boot:spring-boot-starter-jooq' implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

View File

@@ -17,7 +17,7 @@ public class SecurityConfig {
SecurityFilterChain filterChain(HttpSecurity http) throws Exception { SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests( http.authorizeHttpRequests(
// @formatter:off // @formatter:off
r -> r.requestMatchers("/", "/error", "/css/**", "/js/**", "/webjars/**", "/template").permitAll() r -> r.requestMatchers("/", "/error", "/css/**", "/js/**", "/webjars/**", "/template", "/updateTheme/*").permitAll()
.requestMatchers("/**").authenticated()) .requestMatchers("/**").authenticated())
.oauth2Login(l -> l.authorizationEndpoint(e -> e.baseUri("/oauth2/authorize-client"))); .oauth2Login(l -> l.authorizationEndpoint(e -> e.baseUri("/oauth2/authorize-client")));
// @formatter:on // @formatter:on

View File

@@ -8,6 +8,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; 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.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import de.jottyfan.bico.modules.CommonController; import de.jottyfan.bico.modules.CommonController;
@@ -23,16 +24,17 @@ public class NextController extends CommonController {
private NextService service; private NextService service;
@GetMapping("/next") @GetMapping("/next")
public String getNext(Model model) { public String getNext(@RequestParam(name = "groupname", required = false) String groupname, Model model) {
model.addAttribute("list", service.getNext(LocalDate.now())); model.addAttribute("list", service.getNext(LocalDate.now(), groupname));
return "/next"; return "/next";
} }
@GetMapping("/next/{date}") @GetMapping("/next/{date}")
public String getNextForDate(@PathVariable("date") String dateStr, Model model) { public String getNextForDate(@PathVariable("date") String dateStr,
@RequestParam(name = "groupname", required = false) String groupname, Model model) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(dateStr, formatter); LocalDate date = LocalDate.parse(dateStr, formatter);
model.addAttribute("list", service.getNext(date)); model.addAttribute("list", service.getNext(date, groupname));
return "/next"; return "/next";
} }
} }

View File

@@ -9,9 +9,11 @@ import java.util.List;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jooq.Condition;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.jooq.Record4; import org.jooq.Record4;
import org.jooq.SelectSeekStep1; import org.jooq.SelectSeekStep1;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -32,15 +34,25 @@ public class NextRepository {
/** /**
* get the next dates from date on * get the next dates from date on
* *
* @param date the date * @param date the date
* @param classname the classname to filter for
* @return the next dates as beans in a list; an empty list at least * @return the next dates as beans in a list; an empty list at least
*/ */
public List<NextBean> getNext(LocalDate date) { public List<NextBean> getNext(LocalDate date, String classname) {
Condition filter = DSL.trueCondition();
if (classname == null) {
filter = DSL.trueCondition();
} else if (classname.equals("bibleclass")) {
filter = V_CALENDAR.BIBLECLASS.isTrue();
} else if (classname.equals("youthgroup")) {
filter = V_CALENDAR.YOUTHGROUP.isTrue();
}
SelectSeekStep1<Record4<String, LocalDate, Boolean, Boolean>, LocalDate> sql = jooq SelectSeekStep1<Record4<String, LocalDate, Boolean, Boolean>, LocalDate> sql = jooq
// @formatter:off // @formatter:off
.selectDistinct(V_CALENDAR.FULLNAME, V_CALENDAR.SLOT_DAY, V_CALENDAR.BIBLECLASS, V_CALENDAR.YOUTHGROUP) .selectDistinct(V_CALENDAR.FULLNAME, V_CALENDAR.SLOT_DAY, V_CALENDAR.BIBLECLASS, V_CALENDAR.YOUTHGROUP)
.from(V_CALENDAR) .from(V_CALENDAR)
.where(V_CALENDAR.SLOT_DAY.ge(date)) .where(V_CALENDAR.SLOT_DAY.ge(date))
.and(filter)
.orderBy(V_CALENDAR.SLOT_DAY.asc()); .orderBy(V_CALENDAR.SLOT_DAY.asc());
// @formatter:on // @formatter:on
LOGGER.trace(sql); LOGGER.trace(sql);
@@ -48,7 +60,8 @@ public class NextRepository {
List<NextBean> list = new ArrayList<>(); List<NextBean> list = new ArrayList<>();
while (i.hasNext()) { while (i.hasNext()) {
Record4<String, LocalDate, Boolean, Boolean> r = i.next(); Record4<String, LocalDate, Boolean, Boolean> r = i.next();
list.add(NextBean.of(r.get(V_CALENDAR.FULLNAME), r.get(V_CALENDAR.SLOT_DAY), r.get(V_CALENDAR.BIBLECLASS), r.get(V_CALENDAR.YOUTHGROUP))); list.add(NextBean.of(r.get(V_CALENDAR.FULLNAME), r.get(V_CALENDAR.SLOT_DAY), r.get(V_CALENDAR.BIBLECLASS),
r.get(V_CALENDAR.YOUTHGROUP)));
} }
return list; return list;
} }

View File

@@ -23,10 +23,11 @@ public class NextService {
* get the next dates * get the next dates
* *
* @param date the date * @param date the date
* @param classname the classname to filter for
* @return the list of next dates * @return the list of next dates
*/ */
public List<NextBean> getNext(LocalDate date) { public List<NextBean> getNext(LocalDate date, String classname) {
return repository.getNext(date); return repository.getNext(date, classname);
} }
} }

View File

@@ -1,7 +1,12 @@
package de.jottyfan.bico.modules.profile; package de.jottyfan.bico.modules.profile;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.CrossOrigin;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -23,12 +28,20 @@ public class ProfileController extends CommonController {
* update the theme of the current user * update the theme of the current user
* *
* @param theme the theme * @param theme the theme
*
*/ */
@CrossOrigin(origins = "*")
@PostMapping("/updateTheme/{theme}") @PostMapping("/updateTheme/{theme}")
public ResponseEntity<?> updateTheme(@PathVariable("theme") String theme) { public ResponseEntity<?> updateTheme(@PathVariable("theme") String theme, Principal principal) {
// TODO: add profile's user name String username = null;
String username = "jotty"; OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) principal;
service.updateTheme(username, theme); if (token != null) {
OAuth2User user = token.getPrincipal();
username = user.getName();
}
if (username != null) {
service.updateTheme(username, theme);
}
return ResponseEntity.ok(null); return ResponseEntity.ok(null);
} }

View File

@@ -15,6 +15,7 @@ import org.jboss.logging.Logger;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep; import org.jooq.DeleteConditionStep;
import org.jooq.InsertOnDuplicateStep; import org.jooq.InsertOnDuplicateStep;
import org.jooq.InsertResultStep;
import org.jooq.Record1; import org.jooq.Record1;
import org.jooq.Record10; import org.jooq.Record10;
import org.jooq.Record4; import org.jooq.Record4;
@@ -127,7 +128,19 @@ public class ThemeRepository {
.where(T_LESSON.FK_SLOT.eq(slotId)); .where(T_LESSON.FK_SLOT.eq(slotId));
// @formatter:on // @formatter:on
LOGGER.trace(sql); LOGGER.trace(sql);
return sql.fetchOne(); TLessonRecord found = sql.fetchOne();
if (found == null) {
InsertResultStep<TLessonRecord> sqlI = jooq
// @formatter:off
.insertInto(T_LESSON,
T_LESSON.FK_SLOT)
.values(slotId)
.returning();
// @formatter:on
LOGGER.trace(sqlI);
found = sqlI.fetchOne();
}
return found;
} }
public void updateLesson(TLessonRecord bean) { public void updateLesson(TLessonRecord bean) {

View File

@@ -91,6 +91,8 @@ body {
.tagju { .tagju {
background: linear-gradient(to bottom, lime, white); background: linear-gradient(to bottom, lime, white);
left: -18px;
top: 22px;
} }
[data-bs-theme=dark] .tagju { [data-bs-theme=dark] .tagju {

View File

@@ -8,7 +8,10 @@ toggleDarkMode = function() {
url: updateUrl, url: updateUrl,
type: "POST", type: "POST",
contentType: "application/json", contentType: "application/json",
dataType: 'json' dataType: 'json',
xhrFields: {
withCredentials: true
}
}); });
} }

View File

@@ -4,12 +4,21 @@
<th:block layout:fragment="content"> <th:block layout:fragment="content">
<div class="borderdist"> <div class="borderdist">
<div class="container" th:if="${hasDateRole || hasBUrole}"> <div class="container" th:if="${hasDateRole || hasBUrole}">
<div class="row"> <div class="row d-flex justify-content-between">
<div class="col-sm-6 col-md-4 col-lg-2 card p-2 m-1" th:each="s : ${list}"> <div class="col-auto">
<a th:href="@{/next(groupname='bibleclass')}" class="btn btn-outline-secondary">nur Bibelunterricht</a>
</div>
<div class="col-auto">
<a th:href="@{/next(groupname='youthgroup')}" class="btn btn-outline-secondary">nur Jungschar</a>
</div>
</div>
<div class="row g-2">
<div class="col-sm-6 col-md-4 col-lg-2 card p-2" th:each="s : ${list}">
<div class="tag tagbu" th:if="${s.isBibleclass}">Bibelunt.</div> <div class="tag tagbu" th:if="${s.isBibleclass}">Bibelunt.</div>
<div class="tag tagju" th:if="${s.isYouthgroup}">Jungsch.</div> <div class="tag tagju" th:if="${s.isYouthgroup}">Jungschar</div>
<div class="tagfollow" th:text="${#temporals.format(s.day, 'dd.MM.yyyy')}"></div> <div class="tagfollow" th:text="${#temporals.format(s.day, 'dd.MM.yyyy')}"></div>
<div th:class="'tagfollow' + ${currentUserName == #strings.toLowerCase(s.fullname) ? ' tagemphasize' : ''}" th:text="${s.fullname}"></div> <div th:class="'tagfollow' + ${currentUserName == #strings.toLowerCase(s.fullname) ? ' tagemphasize' : ''}" th:text="${s.fullname}" th:if="${s.fullname}"></div>
<div class="tagfollow" th:unless="${s.fullname}" style="min-height: 1.5em"></div>
</div> </div>
<div class="alert alert-info" th:if="${list.size() < 1}">Es gibt noch keine neuen Termine oder Zusagen für Termine.</div> <div class="alert alert-info" th:if="${list.size() < 1}">Es gibt noch keine neuen Termine oder Zusagen für Termine.</div>
</div> </div>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" th:data-bs-theme="${theme}">
<head> <head>
<title>Bible Class Organizer</title> <title>Bible Class Organizer</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
@@ -25,10 +25,6 @@
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent" style="margin-right: 20px"> <div class="collapse navbar-collapse" id="navbarSupportedContent" style="margin-right: 20px">
<ul class="navbar-nav mb-2 mb-lg-0" th:if="${hasAnyRole}"> <ul class="navbar-nav mb-2 mb-lg-0" th:if="${hasAnyRole}">
<!--
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/camp/registration}" style="margin-left: 12px">Anmeldung Gemeindefreizeit</a></li>
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/camp/registration/admin}" style="margin-left: 12px" th:if="${isCampAdmin}">Gemeindefreizeitliste</a></li>
-->
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/next}" style="margin-left: 12px" th:if="${hasDateRole || hasBUrole}">Dienstplan</a></li> <li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/next}" style="margin-left: 12px" th:if="${hasDateRole || hasBUrole}">Dienstplan</a></li>
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/sheet}" style="margin-left: 12px" th:if="${hasBUrole}">Einteilung</a></li> <li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/sheet}" style="margin-left: 12px" th:if="${hasBUrole}">Einteilung</a></li>
<li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/subject/list}" style="margin-left: 12px" th:if="${hasBUrole}">Themen</a></li> <li class="nav-item"><a class="btn btn-outline-secondary" th:href="@{/subject/list}" style="margin-left: 12px" th:if="${hasBUrole}">Themen</a></li>

View File

@@ -51,7 +51,7 @@
</div> </div>
</div> </div>
</th:block> </th:block>
<form th:action="@{/theme/update?slotId={id}(id=${slotId})}" method="post" th:object="${lesson}"> <form th:action="@{/theme/update?slotId={id}(id=${slotId})}" method="post" th:object="${lesson}" th:if="${lesson}">
<input type="hidden" th:field="*{pkLesson}" /> <input type="hidden" th:field="*{pkLesson}" />
<div class="row g-2 blockframe"> <div class="row g-2 blockframe">
<div class="col-sm-3">Anmerkungen zur Stunde</div> <div class="col-sm-3">Anmerkungen zur Stunde</div>