Room per User and KalenderTag

This commit is contained in:
mbremer
2021-08-01 11:49:32 +02:00
parent 6f02239bdc
commit 0d4dcd4df4
12 changed files with 228 additions and 34 deletions

View File

@@ -8,6 +8,7 @@ import lombok.Setter;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -17,7 +18,7 @@ import javax.validation.constraints.NotNull;
@AllArgsConstructor @AllArgsConstructor
@Entity @Entity
public class Image extends PanacheEntity { public class Image extends PanacheEntity {
@NotNull @NotBlank
@Column(nullable = false) @Column(nullable = false)
private String name; private String name;
@NotNull @NotNull

View File

@@ -1,10 +1,11 @@
package de.mbremer.kalender; package de.mbremer.kalender;
import de.mbremer.room.Room;
import de.mbremer.secutity.User; import de.mbremer.secutity.User;
import de.mbremer.secutity.UserService;
import io.quarkus.qute.Location; import io.quarkus.qute.Location;
import io.quarkus.qute.Template; import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance; import io.quarkus.qute.TemplateInstance;
import io.quarkus.security.identity.SecurityIdentity;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
@@ -18,7 +19,6 @@ import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
@Path("/kalender") @Path("/kalender")
@RolesAllowed({"USER", "ADMIN"}) @RolesAllowed({"USER", "ADMIN"})
@@ -27,7 +27,9 @@ public class KalenderResource {
@Inject @Inject
Logger log; Logger log;
@Inject @Inject
SecurityIdentity identity; UserService userService;
@Inject
KalenderService kalenderService;
@Inject @Inject
Template kalenderpage; Template kalenderpage;
@Inject @Inject
@@ -40,32 +42,14 @@ public class KalenderResource {
@GET @GET
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
public TemplateInstance kalender(@QueryParam("offset") @DefaultValue("0") int offsetInWeeks) { public TemplateInstance kalender(@QueryParam("offset") @DefaultValue("0") int offsetInWeeks) {
List<KalenderTag> week = getWeek(offsetInWeeks);
return kalenderpage return kalenderpage
.data("today", LocalDate.now()) .data("today", LocalDate.now())
.data("offset", offsetInWeeks) .data("offset", offsetInWeeks)
.data("week", week); .data("week", kalenderService.getWeek(offsetInWeeks));
}
private List<KalenderTag> getWeek(int offsetInWeeks) {
User currentUser = getCurrentUser();
LocalDate today = LocalDate.now();
LocalDate montag = today.minusDays(today.getDayOfWeek().getValue() - 1).plusDays(7 * offsetInWeeks);
return Stream.iterate(0, i -> i < 5, i -> ++i)
.map(d -> {
LocalDate day = montag.plusDays(d);
KalenderTag tag = (KalenderTag) KalenderTag.find("day", day).singleResultOptional().orElse(new KalenderTag(day));
tag.setCurrentUserInOffice(currentUser.equals(tag.getInOffice()));
tag.setToday(LocalDate.now().equals(day));
return tag;
})
.collect(Collectors.toList());
} }
private List<Event> getEvents(int offsetInWeeks) { private List<Event> getEvents(int offsetInWeeks) {
List<KalenderTag> week = getWeek(offsetInWeeks); List<KalenderTag> week = kalenderService.getWeek(offsetInWeeks);
return week.stream() return week.stream()
.filter(d -> d.isCurrentUserInOffice()) .filter(d -> d.isCurrentUserInOffice())
.map(d-> new Event(LocalDateTime.now(), d.getDay())) .map(d-> new Event(LocalDateTime.now(), d.getDay()))
@@ -80,32 +64,30 @@ public class KalenderResource {
@QueryParam("offset") int offsetInWeeks, @MultipartForm KalenderTagForm kalenderForm) { @QueryParam("offset") int offsetInWeeks, @MultipartForm KalenderTagForm kalenderForm) {
LocalDate dayParsed = LocalDate.parse(day); LocalDate dayParsed = LocalDate.parse(day);
User currentUser = getCurrentUser(); User currentUser = userService.getCurrentUser();
Room room = currentUser.getRoom();
try { try {
KalenderTag tag = KalenderTag.find("day", dayParsed).singleResult(); KalenderTag tag = KalenderTag.find("day = ?1 and room = ?2", dayParsed, room).singleResult();
if (kalenderForm.isInOffice() && tag.getInOffice() == null) { if (kalenderForm.isInOffice() && tag.getInOffice() == null) {
tag.setInOffice(currentUser); tag.setInOffice(currentUser);
} else if (!kalenderForm.isInOffice() && currentUser.equals(tag.getInOffice())) { } else if (!kalenderForm.isInOffice() && currentUser.equals(tag.getInOffice())) {
tag.setInOffice(null); tag.setInOffice(null);
} }
} catch (NoResultException e) { } catch (NoResultException e) {
KalenderTag tag = new KalenderTag(dayParsed); KalenderTag tag = new KalenderTag(dayParsed, room);
tag.setInOffice(currentUser); tag.setInOffice(currentUser);
tag.setRoom(currentUser.getRoom());
tag.persist(); tag.persist();
} }
return kalender(offsetInWeeks); return kalender(offsetInWeeks);
} }
private User getCurrentUser() {
return User.find("username", identity.getPrincipal().getName()).singleResult();
}
@GET @GET
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
@Path("export/txt") @Path("export/txt")
public TemplateInstance exportTxt(@QueryParam("offset") int offset) { public TemplateInstance exportTxt(@QueryParam("offset") int offset) {
return textExport.data("week", getWeek(offset)); return textExport.data("week", kalenderService.getWeek(offset));
} }
@GET @GET

View File

@@ -0,0 +1,36 @@
package de.mbremer.kalender;
import de.mbremer.room.Room;
import de.mbremer.secutity.User;
import de.mbremer.secutity.UserService;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ApplicationScoped
public class KalenderService {
@Inject
UserService userService;
public List<KalenderTag> getWeek(int offsetInWeeks) {
User currentUser = userService.getCurrentUser();
Room room = currentUser.getRoom();
LocalDate today = LocalDate.now();
LocalDate montag = today.minusDays(today.getDayOfWeek().getValue() - 1).plusDays(7 * offsetInWeeks);
return Stream.iterate(0, i -> i < 5, i -> ++i)
.map(d -> {
LocalDate day = montag.plusDays(d);
KalenderTag tag =
(KalenderTag) KalenderTag.find("day = ?1 and room = ?2", day, room).singleResultOptional()
.orElse(new KalenderTag(day, room));
tag.setCurrentUserInOffice(currentUser.equals(tag.getInOffice()));
tag.setToday(LocalDate.now().equals(day));
return tag;
})
.collect(Collectors.toList());
}
}

View File

@@ -1,5 +1,6 @@
package de.mbremer.kalender; package de.mbremer.kalender;
import de.mbremer.room.Room;
import de.mbremer.secutity.User; import de.mbremer.secutity.User;
import io.quarkus.hibernate.orm.panache.PanacheEntity; import io.quarkus.hibernate.orm.panache.PanacheEntity;
import lombok.Getter; import lombok.Getter;
@@ -20,12 +21,15 @@ public class KalenderTag extends PanacheEntity {
private LocalDate day; private LocalDate day;
@OneToOne @OneToOne
private User inOffice; private User inOffice;
@OneToOne
private Room room;
@Transient @Transient
private boolean currentUserInOffice; private boolean currentUserInOffice;
@Transient @Transient
private boolean today; private boolean today;
public KalenderTag(LocalDate day) { public KalenderTag(LocalDate day, Room room) {
this.day = day; this.day = day;
this.room = room;
} }
} }

View File

@@ -0,0 +1,22 @@
package de.mbremer.room;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.validation.constraints.NotBlank;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Room extends PanacheEntity {
@NotBlank
@Column(nullable = false)
private String name;
}

View File

@@ -0,0 +1,11 @@
package de.mbremer.room;
import javax.ws.rs.FormParam;
public class RoomForm {
public @FormParam("name") String name;
public Room getRoom() {
return new Room(name);
}
}

View File

@@ -1,37 +1,52 @@
package de.mbremer.room; package de.mbremer.room;
import de.mbremer.image.ImageService; import de.mbremer.image.ImageService;
import de.mbremer.kalender.KalenderTag;
import de.mbremer.secutity.User;
import de.mbremer.secutity.UserService;
import io.quarkus.qute.Template; import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance; import io.quarkus.qute.TemplateInstance;
import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.SecurityIdentity;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.annotation.security.RolesAllowed; import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.transaction.Transactional;
import javax.validation.ValidationException; import javax.validation.ValidationException;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.IOException; import java.io.IOException;
import static io.quarkus.panache.common.Sort.ascending;
@Path("/room") @Path("/room")
@ApplicationScoped @ApplicationScoped
@RolesAllowed({"USER", "ADMIN"}) @RolesAllowed({"USER", "ADMIN"})
public class RoomResource { public class RoomResource {
@Inject
Logger log;
@Inject @Inject
SecurityIdentity identity; SecurityIdentity identity;
@Inject @Inject
ImageService imageService; ImageService imageService;
@Inject @Inject
UserService userService;
@Inject
Template room; Template room;
@GET @GET
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
public TemplateInstance getRoom() { public TemplateInstance getRoom() {
Room currentRoom = userService.getCurrentUser().getRoom();
return room return room
.data("is_admin", identity.hasRole("ADMIN")); .data("is_admin", identity.hasRole("ADMIN"))
.data("rooms", Room.listAll(ascending("name")))
.data("current_room", currentRoom == null ? "" : currentRoom.getName());
} }
@POST @POST
@@ -58,4 +73,46 @@ public class RoomResource {
byte[] roomplan = imageService.getRoomplan(); byte[] roomplan = imageService.getRoomplan();
return roomplan.length == 0 ? Response.noContent().build() : Response.ok(roomplan).build(); return roomplan.length == 0 ? Response.noContent().build() : Response.ok(roomplan).build();
} }
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_HTML)
@Transactional
@Path("/new")
public TemplateInstance add(@MultipartForm RoomForm roomForm) {
Room room = roomForm.getRoom();
if (Room.count("name", room.getName()) > 0) {
return getRoom().data("error", "Der Raum " + room.getName() + " ist bereits vorhanden");
}
room.persist();
if (Room.count() == 1) {
log.info("Migriere alle KalenderTage ohne Rooom zu " + room.getName());
KalenderTag.find("room is null").stream()
.map(k -> (KalenderTag) k).forEach(k -> k.setRoom(room));
}
return getRoom().data("info", "Raum " + room.getName() + " angelegt.");
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
@Transactional
@Path("/user")
public TemplateInstance setRoom(@FormParam("room") String roomName) {
Room room;
try {
room = Room.find("name", roomName).singleResult();
} catch (NoResultException e) {
return getRoom().data("error", "Raum " + roomName + " existiert nicht.");
}
User user = userService.getCurrentUser();
user.setRoom(room);
log.info("Setze Raum " + roomName + " für User " + user.getUsername());
return getRoom();
}
} }

View File

@@ -1,5 +1,6 @@
package de.mbremer.secutity; package de.mbremer.secutity;
import de.mbremer.room.Room;
import io.quarkus.elytron.security.common.BcryptUtil; import io.quarkus.elytron.security.common.BcryptUtil;
import io.quarkus.hibernate.orm.panache.PanacheEntity; import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.security.jpa.Password; import io.quarkus.security.jpa.Password;
@@ -31,6 +32,11 @@ public class User extends PanacheEntity {
@Column(nullable = false) @Column(nullable = false)
private String password; private String password;
@OneToOne
@Getter
@Setter
private Room room;
/** /**
* ADMIN or USER. * ADMIN or USER.
*/ */

View File

@@ -0,0 +1,17 @@
package de.mbremer.secutity;
import io.quarkus.security.identity.SecurityIdentity;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@ApplicationScoped
public class UserService {
@Inject
SecurityIdentity identity;
public User getCurrentUser() {
return User.find("username", identity.getPrincipal().getName()).singleResult();
}
}

View File

@@ -0,0 +1,10 @@
create table room (
id bigint not null primary key,
name varchar not null unique
);
alter table users add column room_id bigint references room;
alter table kalendertag add column room_id bigint references room;
alter table kalendertag drop constraint kalendertag_day_key;

View File

@@ -0,0 +1,25 @@
<div class="modal fade" id="roomModal" tabindex="-1" aria-labelledby="roomModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="roomModalLabel">Raum anlegen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="/room/new" method="POST" name="roomForm" enctype="multipart/form-data">
<div class="modal-body row mb-3">
<div class="align-items-center col-md-10 mx-auto col-lg-11">
<div class="form-floating mb-3">
<input type="text" name="name" class="form-control" id="name" placeholder="Raumname" required>
<label for="name">Name</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-primary">Speichern</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -2,6 +2,24 @@
{#room_active}active{/} {#room_active}active{/}
{#contents} {#contents}
<div class="mt-2">
{#if rooms.size == 0}
Kein Raum vorhanden
{#else}
<h2>Raum wählen</h2>
{/if}
<div class="btn-group" role="group">
{#for room in rooms}
<form action="/room/user" method="POST" name="chooseRoomForm" enctype="application/x-www-form-urlencoded">
<input type="radio" class="btn-check" name="room" value="{room.name}" id="radio{room.name}" autocomplete="off"
onchange="this.form.submit()" {#if room.name == current_room}checked{/if} >
<label class="btn btn-outline-primary" for="radio{room.name}">{room.name}</label>
</form>
{/for}
</div>
</div>
<div class="mt-2"> <div class="mt-2">
<img src="/room/plan" alt="kein Raumplan vorhanden"> <img src="/room/plan" alt="kein Raumplan vorhanden">
</div> </div>
@@ -17,6 +35,11 @@
<input class="btn btn-primary" type="submit" value="Upload" /> <input class="btn btn-primary" type="submit" value="Upload" />
</form> </form>
</div> </div>
<div class="mt-2">
<a class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#roomModal" role="button">neuer Raum</a>
</div>
{#include room-modal.html}{/include}
{/if} {/if}
{/contents} {/contents}