From 595786d126f050a0eadb0c8167bf0b5953482b42 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Wed, 30 Apr 2025 22:10:05 +0200 Subject: [PATCH] use PostgreSQL for kontor-spring and kontor-api --- kontor-api/src/db/models/database.py | 8 +- kontor-api/src/db/models/metadata.py | 7 +- kontor-scripts/json_to_postgres.py | 59 ++++++++ kontor-spring/Dockerfile | 2 +- .../admin/data/AuthorizationMatrix.java | 35 ----- .../de/thpeetz/kontor/admin/data/Role.java | 30 ---- .../de/thpeetz/kontor/admin/data/User.java | 62 -------- .../AuthorizationMatrixRepository.java | 15 -- .../admin/repository/RoleRepository.java | 19 --- .../admin/repository/UserRepository.java | 17 --- .../kontor/admin/views/AuthorizationForm.java | 112 -------------- .../kontor/admin/views/AuthorizationView.java | 114 -------------- .../thpeetz/kontor/admin/views/RoleForm.java | 102 ------------- .../thpeetz/kontor/admin/views/RoleView.java | 118 --------------- .../thpeetz/kontor/admin/views/UserForm.java | 143 ------------------ .../thpeetz/kontor/admin/views/UserView.java | 135 ----------------- 16 files changed, 67 insertions(+), 911 deletions(-) create mode 100644 kontor-scripts/json_to_postgres.py delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java delete mode 100644 kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java diff --git a/kontor-api/src/db/models/database.py b/kontor-api/src/db/models/database.py index 0d5998b..9a69627 100644 --- a/kontor-api/src/db/models/database.py +++ b/kontor-api/src/db/models/database.py @@ -13,7 +13,7 @@ from sqlalchemy.orm import sessionmaker from src.db.models.tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport from src.db.models.comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType from src.db.models.bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author -from src.db.models.admin import Mail, MailAccount, ModuleData, Role, User, Token, AuthorizationMatrix +from src.db.models.admin import Mail, MailAccount, ModuleData, Token, Assignment, Permission, Profile from src.db.models.metadata import MetaDataTable, MetaDataColumn from src.db.models.media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile @@ -79,10 +79,10 @@ class KontorDB: self.registry[MediaVideo.__tablename__] = MediaVideo self.registry[MetaDataColumn.__tablename__] = MetaDataColumn self.registry[MetaDataTable.__tablename__] = MetaDataTable - self.registry[AuthorizationMatrix.__tablename__] = AuthorizationMatrix + self.registry[Assignment.__tablename__] = Assignment self.registry[Token.__tablename__] = Token - self.registry[User.__tablename__] = User - self.registry[Role.__tablename__] = Role + self.registry[Profile.__tablename__] = Profile + self.registry[Permission.__tablename__] = Permission self.registry[ModuleData.__tablename__] = ModuleData self.registry[MailAccount.__tablename__] = MailAccount self.registry[Mail.__tablename__] = Mail diff --git a/kontor-api/src/db/models/metadata.py b/kontor-api/src/db/models/metadata.py index 081c3af..131f325 100644 --- a/kontor-api/src/db/models/metadata.py +++ b/kontor-api/src/db/models/metadata.py @@ -1,5 +1,4 @@ -from sqlalchemy import Column, String, ForeignKey, Integer -from sqlalchemy.dialects.mysql import BIT +from sqlalchemy import Column, String, ForeignKey, Integer, Boolean from sqlalchemy.orm import relationship from src.db.models.base import Base, BaseMixin @@ -28,8 +27,8 @@ class MetaDataColumn(Base, BaseMixin): table = relationship("MetaDataTable", back_populates="table_columns") column_label = Column(String(255)) filter_label = Column(String(255)) - is_shown = Column(BIT(1)) - show_filter = Column(BIT(1)) + is_shown = Column(Boolean) + show_filter = Column(Boolean) ref_column = Column(String, nullable=True) def __repr__(self): diff --git a/kontor-scripts/json_to_postgres.py b/kontor-scripts/json_to_postgres.py new file mode 100644 index 0000000..e5b1240 --- /dev/null +++ b/kontor-scripts/json_to_postgres.py @@ -0,0 +1,59 @@ +""" +copy data from JSON to Postgres +""" +from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter +from pathlib import Path +from config import get_logger, get_database_cursors +import json +import psycopg2 +from psycopg2.sql import SQL + +parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) +parser.add_argument('--verbose', '-v', action='count', default=0) +parser.add_argument('--config', '-c', default='kontor-docker') +parser.add_argument('--file', '-f', default='~/.sync/media/data.json') +args = parser.parse_args() + +def copy_data(postgres_conn, data_file: Path, log): + postgres_cursor = postgres_conn.cursor() + import_file = Path(data_file) + if not import_file.exists(): + log.info(f"File {data_file} does not exist. Do nothing.") + return + log.info("read json file") + with open(data_file, 'r') as json_file: + json_load = json.load(json_file) + for table in json_load: + log.info(f"{table}: {len(json_load[table])}") + # result[table] = import_table(table, json_load[table]) + truncate_statement = 'TRUNCATE {}'.format(table) + #log.info(f"truncate: {truncate_statement}") + postgres_cursor.execute("SET FOREIGN_KEY_CHECKS = 0") + postgres_cursor.execute(truncate_statement) + items = json_load[table] + for item in items: + #log.info(f"item: {item}") + values = [] + columns = [] + for (key, value) in item.items(): + columns.append(key) + values.append(value) + row = tuple(values) + log.info(f"values: {row}") + insert_statement = 'INSERT INTO {}({}) VALUES({})'.format(table, ', '.join(columns), ', '.join(['%s']*len(columns))) + #log.info(f"statement: {insert_statement}") + postgres_cursor.execute(SQL(insert_statement), row) + try: + postgres_conn.commit() + except psycopg2.Error as error: + log.info('insert failed with %s', error) + + +if __name__ == '__main__': + logger = get_logger(args.verbose, args.config) + logger.info('kontor.json_to_postgres started') + _, _, p_conn = get_database_cursors(logger, args.config) + copy_data(p_conn, args.file, logger) + p_conn.close() + logger.info('kontor.json_to_postgres finished') + diff --git a/kontor-spring/Dockerfile b/kontor-spring/Dockerfile index d4f3463..1cfd701 100644 --- a/kontor-spring/Dockerfile +++ b/kontor-spring/Dockerfile @@ -1,5 +1,5 @@ FROM alpine/java:21-jdk WORKDIR / -ADD build/libs/kontor-spring-0.1.0-SNAPSHOT.jar app.jar +ADD build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar EXPOSE 8000 CMD ["java", "-jar", "-Dspring.profiles.active=prod", "-Dvaadin.productionMode=true", "app.jar"] diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java deleted file mode 100644 index 26b5ebb..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.thpeetz.kontor.admin.data; - -import de.thpeetz.kontor.common.data.AbstractEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@Entity -public class AuthorizationMatrix extends AbstractEntity { - - @ManyToOne - @JoinColumn(name = "user_id") - @NotNull - private User user; - - @ManyToOne - @JoinColumn(name = "role_id") - @NotNull - private Role role; - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("AuthorizationMatrix{"); - sb.append("user=").append(user.getUserName()); - sb.append(", role=").append(role.getName()); - sb.append('}'); - return sb.toString(); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java deleted file mode 100644 index 5ad81c2..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.thpeetz.kontor.admin.data; - -import java.util.List; - -import de.thpeetz.kontor.common.data.AbstractEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToMany; -import jakarta.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@Slf4j -@Getter -@Setter -@ToString -@Entity -public class Role extends AbstractEntity { - - @NotEmpty - private String name; - - @OneToMany(fetch = FetchType.EAGER, mappedBy = "role") - @Nullable - private List matrix; -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java deleted file mode 100644 index ec16233..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java +++ /dev/null @@ -1,62 +0,0 @@ -package de.thpeetz.kontor.admin.data; - -import de.thpeetz.kontor.common.data.AbstractEntity; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Index; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import java.util.LinkedList; -import java.util.List; - -@Slf4j -@Getter -@Setter -@ToString -@Entity -@Table(indexes = @Index(columnList = "userName"), uniqueConstraints = @UniqueConstraint(columnNames = {"userName"})) -public class User extends AbstractEntity { - - private String firstName; - - private String lastName; - - @NotEmpty - private String userName; - - private String email; - - private String password; - - private boolean enabled; - - @OneToMany(fetch = FetchType.EAGER, mappedBy = "user") - @Nullable - private List matrix = new LinkedList<>(); - - @OneToMany(fetch = FetchType.EAGER, mappedBy = "user") - @Nullable - private List tokens = new LinkedList<>(); - - public String getFullName() { - StringBuilder fullNamBuilder = new StringBuilder(); - if (firstName != null) { - fullNamBuilder.append(firstName); - } - if (lastName != null) { - if (fullNamBuilder.length() > 0) { - fullNamBuilder.append(" "); - } - fullNamBuilder.append(lastName); - } - return fullNamBuilder.toString(); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java deleted file mode 100644 index 3e5c30c..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.thpeetz.kontor.admin.repository; - -import java.util.List; - -import de.thpeetz.kontor.admin.data.AuthorizationMatrix; -import de.thpeetz.kontor.admin.data.Role; -import de.thpeetz.kontor.admin.data.User; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface AuthorizationMatrixRepository extends JpaRepository { - - List findByUser(User user); - - List findByRole(Role role); -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java deleted file mode 100644 index 0d1b466..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.thpeetz.kontor.admin.repository; - -import java.util.List; - -import de.thpeetz.kontor.admin.data.Role; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface RoleRepository extends JpaRepository { - - @Query("select r from Role r " + - "where lower(r.name) like lower(concat('%', :searchTerm, '%')) ") - List search(@Param("searchTerm") String searchTerm); - - @Query("select r from Role r " + - "where lower(r.name) like lower(:name) ") - Role findByName(@Param("name") String name); -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java deleted file mode 100644 index c8c20f3..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.thpeetz.kontor.admin.repository; - -import java.util.List; - -import de.thpeetz.kontor.admin.data.User; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface UserRepository extends JpaRepository { - - @Query("select u from User u " + - "where lower(u.lastName) like lower(concat('%', :searchTerm, '%')) ") - List search(@Param("searchTerm") String searchTerm); - - User findByUserName(String userName); -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java deleted file mode 100644 index da593f8..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import java.util.List; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.Key; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.button.ButtonVariant; -import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.formlayout.FormLayout; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.data.binder.BeanValidationBinder; -import com.vaadin.flow.data.binder.Binder; - -import de.thpeetz.kontor.admin.data.AuthorizationMatrix; -import de.thpeetz.kontor.admin.data.Role; -import de.thpeetz.kontor.admin.data.User; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class AuthorizationForm extends FormLayout { - - ComboBox user = new ComboBox<>("User"); - ComboBox role = new ComboBox<>("Role"); - - Button save = new Button("Save"); - Button delete = new Button("Delete"); - Button close = new Button("Cancel"); - - Binder binder = new BeanValidationBinder<>(AuthorizationMatrix.class); - - public AuthorizationForm(List users, List roles) { - addClassName("authorizationmatrix-form"); - binder.bindInstanceFields(this); - - user.setItems(users); - user.setItemLabelGenerator(User::getUserName); - role.setItems(roles); - role.setItemLabelGenerator(Role::getName); - add(user, role, createButtonsLayout()); - } - - private HorizontalLayout createButtonsLayout() { - save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); - delete.addThemeVariants(ButtonVariant.LUMO_ERROR); - close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); - - save.addClickShortcut(Key.ENTER); - close.addClickShortcut(Key.ESCAPE); - - save.addClickListener(event -> validateAndSave()); - delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean()))); - close.addClickListener(event -> fireEvent(new CloseEvent(this))); - - binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); - return new HorizontalLayout(save, delete, close); - } - - private void validateAndSave() { - if (binder.isValid()) { - fireEvent(new SaveEvent(this, binder.getBean())); - } - } - - public void setAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) { - binder.setBean(authorizationMatrix); - } - - public abstract static class AuthorizationFormEvent extends ComponentEvent { - private AuthorizationMatrix authorizationMatrix; - - protected AuthorizationFormEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) { - super(source, false); - this.authorizationMatrix = authorizationMatrix; - } - - public AuthorizationMatrix getAuthorizationMatrix() { - return authorizationMatrix; - } - } - - public static class SaveEvent extends AuthorizationFormEvent { - SaveEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) { - super(source, authorizationMatrix); - } - } - - public static class DeleteEvent extends AuthorizationFormEvent { - DeleteEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) { - super(source, authorizationMatrix); - } - } - - public static class CloseEvent extends AuthorizationFormEvent { - CloseEvent(AuthorizationForm source) { - super(source, null); - } - } - - public void addDeleteListener(ComponentEventListener listener) { - addListener(DeleteEvent.class, listener); - } - - public void addSaveListener(ComponentEventListener listener) { - addListener(SaveEvent.class, listener); - } - - public void addCloseListener(ComponentEventListener listener) { - addListener(CloseEvent.class, listener); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java deleted file mode 100644 index 435ae8f..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java +++ /dev/null @@ -1,114 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import org.springframework.context.annotation.Scope; - -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; - -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.spring.annotation.SpringComponent; - -import de.thpeetz.kontor.admin.AdminConstants; -import de.thpeetz.kontor.admin.data.AuthorizationMatrix; -import de.thpeetz.kontor.admin.services.AdminService; -import de.thpeetz.kontor.common.views.MainLayout; -import jakarta.annotation.security.RolesAllowed; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@SpringComponent -@Scope("prototype") -@RolesAllowed("ROLE_ADMIN") -@Route(value = AdminConstants.AUTHORIZATION_ROUTE, layout = MainLayout.class) -@PageTitle("Authorization | Admin | Kontor") -public class AuthorizationView extends VerticalLayout { - - Grid grid = new Grid<>(AuthorizationMatrix.class); - AuthorizationForm form; - AdminService service; - - public AuthorizationView(AdminService service) { - this.service = service; - addClassName("authoriaztionmatrix-view"); - setSizeFull(); - configureGrid(); - configureForm(); - - add(getToolbar(), getContent()); - updateList(); - } - - private void configureGrid() { - grid.addClassName("authorizationmatrix-grid"); - grid.setSizeFull(); - grid.setColumns("user.userName", "role.name"); - grid.getColumns().forEach(col -> col.setAutoWidth(true)); - grid.asSingleSelect().addValueChangeListener(event -> editAuthorizationMatrix(event.getValue())); - } - - private void configureForm() { - form = new AuthorizationForm(service.findAllUsers(), service.findAllRoles()); - form.setWidth("25em"); - form.addSaveListener(this::saveAuthorizationMatrix); - form.addDeleteListener(this::deleteAuthorizationMatrix); - form.addCloseListener(e -> closeEditor()); - } - - private void saveAuthorizationMatrix(AuthorizationForm.SaveEvent event) { - AuthorizationMatrix authorizationMatrix = event.getAuthorizationMatrix(); - service.saveAuthorizationMatrix(authorizationMatrix); - updateList(); - closeEditor(); - } - - private void deleteAuthorizationMatrix(AuthorizationForm.DeleteEvent event) { - service.deleteAuthorizationMatrix(event.getAuthorizationMatrix()); - updateList(); - closeEditor(); - } - - private Component getContent() { - HorizontalLayout content = new HorizontalLayout(grid, form); - content.setFlexGrow(2, grid); - content.setFlexGrow(1, form); - content.addClassName("content"); - content.setSizeFull(); - return content; - } - - private HorizontalLayout getToolbar() { - Button addAuthorizationMaxtrixButton = new Button("Add permssion", click -> addAuthorizationMatrix()); - HorizontalLayout toolbar = new HorizontalLayout(addAuthorizationMaxtrixButton); - toolbar.addClassName("toolbar"); - return toolbar; - } - - public void editAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) { - if (authorizationMatrix == null) { - closeEditor(); - } else { - form.setAuthorizationMatrix(authorizationMatrix); - form.setVisible(true); - addClassName("editing"); - } - } - - public void closeEditor() { - form.setAuthorizationMatrix(null); - form.setVisible(false); - removeClassName("editing"); - } - - private void addAuthorizationMatrix() { - grid.asSingleSelect().clear(); - editAuthorizationMatrix(new AuthorizationMatrix()); - } - - private void updateList() { - grid.setItems(service.findAllAuthorizationMatrices()); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java deleted file mode 100644 index 671d0f5..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java +++ /dev/null @@ -1,102 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.Key; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.button.ButtonVariant; -import com.vaadin.flow.component.formlayout.FormLayout; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.binder.BeanValidationBinder; -import com.vaadin.flow.data.binder.Binder; - -import de.thpeetz.kontor.admin.data.Role; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RoleForm extends FormLayout { - - TextField name = new TextField("Role name"); - - Button save = new Button("Save"); - Button delete = new Button("Delete"); - Button close = new Button("Cancel"); - - Binder binder = new BeanValidationBinder<>(Role.class); - - public RoleForm() { - addClassName("role-form"); - binder.bindInstanceFields(this); - add(name, createButtonsLayout()); - } - - private HorizontalLayout createButtonsLayout() { - save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); - delete.addThemeVariants(ButtonVariant.LUMO_ERROR); - close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); - - save.addClickShortcut(Key.ENTER); - close.addClickShortcut(Key.ESCAPE); - - save.addClickListener(event -> validateAndSave()); - delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean()))); - close.addClickListener(event -> fireEvent(new CloseEvent(this))); - - binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); - return new HorizontalLayout(save, delete, close); - } - - private void validateAndSave() { - if (binder.isValid()) { - fireEvent(new SaveEvent(this, binder.getBean())); - } - } - - public void setRole(Role role) { - binder.setBean(role); - } - - public abstract static class RoleFormEvent extends ComponentEvent { - private Role role; - - protected RoleFormEvent(RoleForm source, Role role) { - super(source, false); - this.role = role; - } - - public Role getRole() { - return role; - } - } - - public static class SaveEvent extends RoleFormEvent { - SaveEvent(RoleForm source, Role role) { - super(source, role); - } - } - - public static class DeleteEvent extends RoleFormEvent { - DeleteEvent(RoleForm source, Role role) { - super(source, role); - } - } - - public static class CloseEvent extends RoleFormEvent { - CloseEvent(RoleForm source) { - super(source, null); - } - } - - public void addDeleteListener(ComponentEventListener listener) { - addListener(DeleteEvent.class, listener); - } - - public void addSaveListener(ComponentEventListener listener) { - addListener(SaveEvent.class, listener); - } - - public void addCloseListener(ComponentEventListener listener) { - addListener(CloseEvent.class, listener); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java deleted file mode 100644 index 1f13c9a..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java +++ /dev/null @@ -1,118 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import org.springframework.context.annotation.Scope; - -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.value.ValueChangeMode; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.spring.annotation.SpringComponent; - -import de.thpeetz.kontor.admin.AdminConstants; -import de.thpeetz.kontor.admin.data.Role; -import de.thpeetz.kontor.admin.services.AdminService; -import de.thpeetz.kontor.common.views.MainLayout; -import jakarta.annotation.security.RolesAllowed; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@SpringComponent -@Scope("prototype") -@RolesAllowed("ROLE_ADMIN") -@Route(value = AdminConstants.ROLE_ROUTE, layout = MainLayout.class) -@PageTitle("Rollen | Admin | Kontor") -public class RoleView extends VerticalLayout { - Grid grid = new Grid<>(Role.class); - TextField filterText = new TextField(); - RoleForm form; - AdminService service; - - public RoleView(AdminService service) { - this.service = service; - addClassName("user-view"); - setSizeFull(); - configureGrid(); - configureForm(); - - add(getToolbar(), getContent()); - updateList(); - } - - private void configureGrid() { - grid.addClassName("user-grid"); - grid.setSizeFull(); - grid.setColumns("name"); - grid.getColumns().forEach(col -> col.setAutoWidth(true)); - grid.asSingleSelect().addValueChangeListener(event -> editRole(event.getValue())); - } - - private void configureForm() { - form = new RoleForm(); - form.setWidth("25em"); - form.addSaveListener(this::saveRole); - form.addDeleteListener(this::deleteRole); - form.addCloseListener(e -> closeEditor()); - } - - private void saveRole(RoleForm.SaveEvent event) { - service.saveRole(event.getRole()); - updateList(); - closeEditor(); - } - - private void deleteRole(RoleForm.DeleteEvent event) { - service.deleteRole(event.getRole()); - updateList(); - closeEditor(); - } - - private Component getContent() { - HorizontalLayout content = new HorizontalLayout(grid, form); - content.setFlexGrow(2, grid); - content.setFlexGrow(1, form); - content.addClassName("content"); - content.setSizeFull(); - return content; - } - - private HorizontalLayout getToolbar() { - filterText.setPlaceholder("Filter by user name..."); - filterText.setClearButtonVisible(true); - filterText.setValueChangeMode(ValueChangeMode.LAZY); - filterText.addValueChangeListener(e -> updateList()); - Button addUserButton = new Button("Add user", click -> addUser()); - HorizontalLayout toolbar = new HorizontalLayout(filterText, addUserButton); - toolbar.addClassName("toolbar"); - return toolbar; - } - - public void editRole(Role role) { - if (role == null) { - closeEditor(); - } else { - form.setRole(role); - form.setVisible(true); - addClassName("editing"); - } - } - - public void closeEditor() { - form.setRole(null); - form.setVisible(false); - removeClassName("editing"); - } - - private void addUser() { - grid.asSingleSelect().clear(); - editRole(new Role()); - } - - private void updateList() { - grid.setItems(service.findAllRoles(filterText.getValue())); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java deleted file mode 100644 index 21298f3..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java +++ /dev/null @@ -1,143 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.Key; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.button.ButtonVariant; -import com.vaadin.flow.component.checkbox.Checkbox; -import com.vaadin.flow.component.checkbox.CheckboxGroup; -import com.vaadin.flow.component.checkbox.CheckboxGroupVariant; -import com.vaadin.flow.component.formlayout.FormLayout; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.textfield.EmailField; -import com.vaadin.flow.component.textfield.PasswordField; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.binder.BeanValidationBinder; -import com.vaadin.flow.data.binder.Binder; -import de.thpeetz.kontor.admin.data.Role; -import de.thpeetz.kontor.admin.data.User; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; - -@Slf4j -public class UserForm extends FormLayout { - - TextField userName = new TextField("User name"); - PasswordField password = new PasswordField("Password"); - EmailField email = new EmailField("Email"); - TextField firstName = new TextField("First name"); - TextField lastName = new TextField("Last name"); - Checkbox enabled = new Checkbox("Enabled"); - String originalPassword; - - CheckboxGroup permissions = new CheckboxGroup<>("Permissions"); - - Button save = new Button("Save"); - Button delete = new Button("Delete"); - Button close = new Button("Cancel"); - - Binder binder = new BeanValidationBinder<>(User.class); - - public UserForm() { - addClassName("user-form"); - binder.bindInstanceFields(this); - add(userName, password, email, firstName, lastName, enabled, configurePermissionsGroup(), createButtonsLayout()); - } - - private CheckboxGroup configurePermissionsGroup() { - permissions.addThemeVariants(CheckboxGroupVariant.LUMO_VERTICAL); - permissions.setItemLabelGenerator(Role::getName); - permissions.addValueChangeListener(event -> { - log.debug("permissions changed: {}", event); - }); - return permissions; - } - - private HorizontalLayout createButtonsLayout() { - save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); - delete.addThemeVariants(ButtonVariant.LUMO_ERROR); - close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); - - save.addClickShortcut(Key.ENTER); - close.addClickShortcut(Key.ESCAPE); - - save.addClickListener(event -> validateAndSave()); - delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean()))); - close.addClickListener(event -> fireEvent(new CloseEvent(this))); - - binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); - return new HorizontalLayout(save, delete, close); - } - - private void validateAndSave() { - if (binder.isValid()) { - fireEvent(new SaveEvent(this, binder.getBean())); - } - } - - public void setUser(User user) { - binder.setBean(user); - //log.debug("UserForm.setUser: {}", user); - if (user != null) { - this.originalPassword = user.getPassword(); - } else { - this.originalPassword = null; - } - } - - public void setRoles(List roles, User user) { - permissions.setItems(roles); - user.getMatrix().stream().forEach(authorizationMatrix -> { - permissions.select(authorizationMatrix.getRole()); - }); - } - - public boolean hasPasswordChanged(User user) { - return !originalPassword.equals(user.getPassword()); - } - - public abstract static class UserFormEvent extends ComponentEvent { - private User user; - - protected UserFormEvent(UserForm source, User user) { - super(source, false); - this.user = user; - } - - public User getUser() { - return user; - } - } - - public static class SaveEvent extends UserFormEvent { - SaveEvent(UserForm source, User user) { - super(source, user); - } - } - - public static class DeleteEvent extends UserFormEvent { - DeleteEvent(UserForm source, User user) { - super(source, user); - } - } - - public static class CloseEvent extends UserFormEvent { - CloseEvent(UserForm source) { - super(source, null); - } - } - - public void addDeleteListener(ComponentEventListener listener) { - addListener(DeleteEvent.class, listener); - } - - public void addSaveListener(ComponentEventListener listener) { - addListener(SaveEvent.class, listener); - } - - public void addCloseListener(ComponentEventListener listener) { - addListener(CloseEvent.class, listener); - } -} diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java deleted file mode 100644 index 9f981ec..0000000 --- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java +++ /dev/null @@ -1,135 +0,0 @@ -package de.thpeetz.kontor.admin.views; - -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.value.ValueChangeMode; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.spring.annotation.SpringComponent; -import de.thpeetz.kontor.admin.data.Role; -import de.thpeetz.kontor.admin.data.User; -import de.thpeetz.kontor.admin.services.KontorUserDetailsService; -import de.thpeetz.kontor.common.views.MainLayout; -import jakarta.annotation.security.RolesAllowed; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Scope; -import org.springframework.security.crypto.password.PasswordEncoder; - -import java.util.List; -import java.util.stream.Collectors; - -@Slf4j -@SpringComponent -@Scope("prototype") -@RolesAllowed("ROLE_ADMIN") -@Route(value = "admin/user", layout = MainLayout.class) -@PageTitle("User | Admin | Kontor") -public class UserView extends VerticalLayout { - - Grid grid = new Grid<>(User.class); - TextField filterText = new TextField(); - UserForm form; - KontorUserDetailsService service; - - @Autowired - PasswordEncoder passwordEncoder; - - public UserView(KontorUserDetailsService service) { - this.service = service; - addClassName("user-view"); - setSizeFull(); - configureGrid(); - configureForm(); - - add(getToolbar(), getContent()); - updateList(); - } - - private void configureGrid() { - grid.addClassName("user-grid"); - grid.setSizeFull(); - grid.setColumns("userName", "email", "firstName", "lastName", "enabled"); - grid.getColumns().forEach(col -> col.setAutoWidth(true)); - grid.asSingleSelect().addValueChangeListener(event -> editUser(event.getValue())); - } - - private void configureForm() { - form = new UserForm(); - form.setWidth("25em"); - form.setVisible(false); - form.addSaveListener(this::saveUser); - form.addDeleteListener(this::deleteUser); - form.addCloseListener(e -> closeEditor()); - } - - private void saveUser(UserForm.SaveEvent event) { - User user = event.getUser(); - log.debug("UserView.saveUser: {}", user); - List permissions = form.permissions.getSelectedItems().stream().collect(Collectors.toList()); - log.info("selected permissions: {}", permissions); - if (form.hasPasswordChanged(user)) { - user.setPassword(passwordEncoder.encode(user.getPassword())); - log.debug("password changed for user {}", user); - } - service.saveUser(user, permissions); - updateList(); - closeEditor(); - } - - private void deleteUser(UserForm.DeleteEvent event) { - service.deleteUser(event.getUser()); - updateList(); - closeEditor(); - } - - private Component getContent() { - HorizontalLayout content = new HorizontalLayout(grid, form); - content.setFlexGrow(2, grid); - content.setFlexGrow(1, form); - content.addClassName("content"); - content.setSizeFull(); - return content; - } - - private HorizontalLayout getToolbar() { - filterText.setPlaceholder("Filter by user name..."); - filterText.setClearButtonVisible(true); - filterText.setValueChangeMode(ValueChangeMode.LAZY); - filterText.addValueChangeListener(e -> updateList()); - Button addUserButton = new Button("Add user", click -> addUser()); - HorizontalLayout toolbar = new HorizontalLayout(filterText, addUserButton); - toolbar.addClassName("toolbar"); - return toolbar; - } - - public void editUser(User user) { - if (user == null) { - closeEditor(); - } else { - form.setUser(user); - form.setRoles(service.findAllRoles(), user); - form.setVisible(true); - addClassName("editing"); - } - } - - public void closeEditor() { - form.setUser(null); - form.setVisible(false); - removeClassName("editing"); - } - - private void addUser() { - grid.asSingleSelect().clear(); - editUser(new User()); - } - - private void updateList() { - grid.setItems(service.findAllUsers(filterText.getValue())); - } -}