add MediaActor

This commit is contained in:
Thomas Peetz
2025-02-10 00:27:23 +01:00
parent f5fb743503
commit 6d54c7f315
10 changed files with 369 additions and 15 deletions
@@ -25,10 +25,10 @@ import jakarta.annotation.security.PermitAll;
@PageTitle("Artist | Comics | Kontor")
public class ArtistView extends VerticalLayout {
Grid<Artist> grid = new Grid<>(Artist.class);
TextField filterText = new TextField();
ArtistForm form;
ComicService service;
Grid<Artist> grid = new Grid<>(Artist.class);
TextField filterText = new TextField();
ArtistForm form;
ComicService service;
public ArtistView(ComicService service) {
this.service = service;
@@ -2,6 +2,7 @@ package de.thpeetz.kontor.media;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.sidenav.SideNavItem;
import de.thpeetz.kontor.media.views.MediaActorView;
import de.thpeetz.kontor.media.views.MediaArticleView;
import de.thpeetz.kontor.media.views.MediaFileView;
import de.thpeetz.kontor.media.views.MediaVideoView;
@@ -15,9 +16,11 @@ public class MediaConstants {
public static final String MEDIAVIDEO_ROUTE = "media/mediavideo";
public static final String MEDIAARTICLE_ROUTE = "media/mediaarticle";
public static final String MEDIA_ROLE = "ROLE_MEDIA";
public static final String MEDIAACTOR_ROUTE = "media/mediaactor";
private static final String MEDIAFILE = "Media Files";
private static final String MEDIAVIDEO = "Media Videos";
private static final String MEDIAARTICLE = "Media Article";
private static final String MEDIAACTOR = "Media Actor";
public static SideNavItem getMediaNavigation(ArrayList<String> roles) {
SideNavItem media = new SideNavItem(MEDIA, MEDIAFILE_ROUTE, VaadinIcon.VIMEO.create());
@@ -25,6 +28,7 @@ public class MediaConstants {
media.addItem(new SideNavItem(MEDIAARTICLE, MediaArticleView.class));
if (roles.contains(MEDIA_ROLE)) {
media.addItem(new SideNavItem(MEDIAFILE, MediaFileView.class));
media.addItem(new SideNavItem(MEDIAACTOR, MediaActorView.class));
}
return media;
}
@@ -0,0 +1,28 @@
package de.thpeetz.kontor.media.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.annotation.Nullable;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
@Slf4j
@Entity
public class MediaActor extends AbstractEntity {
@NotEmpty
@Column(unique = true)
private String name;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "media_actor", cascade = CascadeType.REFRESH, orphanRemoval = true)
@Nullable
List<MediaActorFile> mediaActorFiles;
}
@@ -0,0 +1,30 @@
package de.thpeetz.kontor.media.data;
import de.thpeetz.kontor.comics.data.Artist;
import de.thpeetz.kontor.comics.data.Comic;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(indexes = {@Index(columnList = "media_file_id, media_actor_id") },
uniqueConstraints = @UniqueConstraint(columnNames = {"media_file_id", "media_actor_id" })
)
public class MediaActorFile extends AbstractEntity {
@ManyToOne
@JoinColumn(name = "media_file_id")
@NotNull
private MediaFile media_file;
@ManyToOne
@JoinColumn(name = "media_actor_id")
@NotNull
private MediaActor media_actor;
}
@@ -0,0 +1,6 @@
package de.thpeetz.kontor.media.data;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MediaActorFileRepository extends JpaRepository<MediaActorFile, String> {
}
@@ -0,0 +1,17 @@
package de.thpeetz.kontor.media.data;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface MediaActorRepository extends JpaRepository<MediaActor, String> {
@Query("select m from MediaActor m " +
"where lower(m.name) like lower(concat('%', :searchTerm, '%')) ")
List<MediaActor> search(@Param("searchTerm") String searchTerm);
List<MediaActor> findByNameIgnoreCase(String name);
MediaActor findByName(String name);
}
@@ -2,14 +2,14 @@ package de.thpeetz.kontor.media.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.annotation.Nullable;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.persistence.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Slf4j
@Getter
@Setter
@@ -37,4 +37,7 @@ public class MediaFile extends AbstractEntity {
@Nullable
private String path;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "media_file", cascade = CascadeType.REFRESH, orphanRemoval = true)
@Nullable
List<MediaActorFile> mediaActorFiles;
}
@@ -1,7 +1,6 @@
package de.thpeetz.kontor.media.services;
import de.thpeetz.kontor.media.data.MediaFile;
import de.thpeetz.kontor.media.data.MediaFileRepository;
import de.thpeetz.kontor.media.data.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -13,19 +12,22 @@ public class MediaFileService {
private final MediaFileRepository mediaFileRepository;
public MediaFileService(MediaFileRepository mediaFileRepository) {
private final MediaActorRepository mediaActorRepository;
public MediaFileService(MediaFileRepository mediaFileRepository, MediaActorRepository mediaActorRepository) {
this.mediaFileRepository = mediaFileRepository;
this.mediaActorRepository = mediaActorRepository;
}
public List<MediaFile> findAllMediaFiles(String stringFilter) {
List<MediaFile> results;
if (stringFilter == null || stringFilter.isEmpty()) {
log.debug("Found " + mediaFileRepository.count()+ " entries");
return mediaFileRepository.findAll();
results = mediaFileRepository.findAll();
} else {
List<MediaFile> results = mediaFileRepository.search(stringFilter);
log.debug("Found " + results.size() + " entries");
return results;
results = mediaFileRepository.search(stringFilter);
}
log.debug("Found " + results.size() + " entries");
return results;
}
public void saveMediaFile(MediaFile mediaFile) {
@@ -39,4 +41,27 @@ public class MediaFileService {
public void deleteMediaFile(MediaFile mediaFile) {
mediaFileRepository.delete(mediaFile);
}
public List<MediaActor> findAllMediaActors(String stringFilter) {
List<MediaActor> results;
if (stringFilter == null || stringFilter.isEmpty()) {
results = mediaActorRepository.findAll();
} else {
results = mediaActorRepository.search(stringFilter);
}
log.debug("Found " + results.size() + " entries");
return results;
}
public void saveMediaActor(MediaActor mediaActor) {
if (mediaActor == null) {
log.warn("MediaActor is null. Are you sure you have connected your form to the application?");
return;
}
mediaActorRepository.save(mediaActor);
}
public void deleteMediaActor(MediaActor mediaActor) {
mediaActorRepository.delete(mediaActor);
}
}
@@ -0,0 +1,116 @@
package de.thpeetz.kontor.media.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.grid.Grid;
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.media.data.MediaActor;
import de.thpeetz.kontor.media.data.MediaActorFile;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Slf4j
public class MediaActorForm extends FormLayout {
TextField name = new TextField("Name");
// Grid<MediaActorFile> mediaActorFiles = new Grid<>(MediaActorFile.class);
Button save = new Button("Save");
Button delete = new Button("Delete");
Button close = new Button("Cancel");
Binder<MediaActor> binder = new BeanValidationBinder<>(MediaActor.class);
public MediaActorForm() {
addClassName("media-actor-form");
binder.bindInstanceFields(this);
// mediaActorFiles.setColumns("media_file.title");
// mediaActorFiles.getColumnByKey("mediaFile.title").setHeader("MediaFile");
// mediaActorFiles.getColumns().forEach(col -> col.setAutoWidth(true));
// add(name, mediaActorFiles, createButtonsLayout());
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 MediaActorForm.DeleteEvent(this, binder.getBean())));
close.addClickListener(event -> fireEvent(new MediaActorForm.CloseEvent(this)));
binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
return new HorizontalLayout(save, delete, close);
}
private void validateAndSave() {
if (binder.isValid()) {
fireEvent(new MediaActorForm.SaveEvent(this, binder.getBean()));
}
}
public void setMediaActor(MediaActor mediaActor) {
binder.setBean(mediaActor);
}
public void setMediaActorFiles(List<MediaActorFile> mediaActorFiles) {
log.info("Setting comic works: {}", mediaActorFiles);
// this.mediaActorFiles.setItems(mediaActorFiles);
}
public abstract static class MediaActorFormEvent extends ComponentEvent<MediaActorForm> {
private MediaActor mediaActor;
protected MediaActorFormEvent(MediaActorForm source, MediaActor mediaActor) {
super(source, false);
this.mediaActor = mediaActor;
}
public MediaActor getMediaActor() {
return mediaActor;
}
}
public static class SaveEvent extends MediaActorForm.MediaActorFormEvent {
SaveEvent(MediaActorForm source, MediaActor mediaActor) {
super(source, mediaActor);
}
}
public static class DeleteEvent extends MediaActorForm.MediaActorFormEvent {
DeleteEvent(MediaActorForm source, MediaActor mediaActor) {
super(source, mediaActor);
}
}
public static class CloseEvent extends MediaActorForm.MediaActorFormEvent {
CloseEvent(MediaActorForm source) {
super(source, null);
}
}
public void addDeleteListener(ComponentEventListener<MediaActorForm.DeleteEvent> listener) {
addListener(MediaActorForm.DeleteEvent.class, listener);
}
public void addSaveListener(ComponentEventListener<MediaActorForm.SaveEvent> listener) {
addListener(MediaActorForm.SaveEvent.class, listener);
}
public void addCloseListener(ComponentEventListener<MediaActorForm.CloseEvent> listener) {
addListener(MediaActorForm.CloseEvent.class, listener);
}
}
@@ -0,0 +1,125 @@
package de.thpeetz.kontor.media.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.comics.data.Artist;
import de.thpeetz.kontor.comics.views.ArtistForm;
import de.thpeetz.kontor.common.views.MainLayout;
import de.thpeetz.kontor.media.MediaConstants;
import de.thpeetz.kontor.media.data.MediaActor;
import de.thpeetz.kontor.media.services.MediaFileService;
import jakarta.annotation.security.PermitAll;
import lombok.Getter;
import org.springframework.context.annotation.Scope;
@SpringComponent
@Scope("prototype")
@PermitAll
@Route(value = MediaConstants.MEDIAACTOR_ROUTE, layout = MainLayout.class)
@PageTitle("Actor | Media | Kontor")
public class MediaActorView extends VerticalLayout {
@Getter
Grid<MediaActor> grid = new Grid<>(MediaActor.class);
TextField filterText = new TextField();
@Getter
MediaActorForm form;
MediaFileService service;
public MediaActorView(MediaFileService service) {
this.service = service;
addClassName("media-actor-view");
setSizeFull();
configureGrid();
configureForm();
add(getToolbar(), getContent());
updateList();
}
private void configureGrid() {
grid.addClassName("artist-grid");
grid.setSizeFull();
grid.setColumns("name");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
grid.asSingleSelect().addValueChangeListener(event -> editMediaActor(event.getValue()));
}
private void configureForm() {
form = new MediaActorForm();
form.setWidth("25em");
form.setVisible(false);
form.addSaveListener(this::saveMediaActor);
form.addDeleteListener(this::deleteMediaActor);
form.addCloseListener(e -> closeEditor());
}
private void saveMediaActor(MediaActorForm.SaveEvent event) {
service.saveMediaActor(event.getMediaActor());
updateList();
closeEditor();
}
private void deleteMediaActor(MediaActorForm.DeleteEvent event) {
service.deleteMediaActor(event.getMediaActor());
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 name...");
filterText.setClearButtonVisible(true);
filterText.setValueChangeMode(ValueChangeMode.LAZY);
filterText.addValueChangeListener(e -> updateList());
Button addMediaActorButton = new Button("Add actor");
addMediaActorButton.addClickListener(click -> addMediaActor());
HorizontalLayout toolbar = new HorizontalLayout(filterText, addMediaActorButton);
toolbar.addClassName("toolbar");
return toolbar;
}
public void editMediaActor(MediaActor mediaActor) {
if (mediaActor == null) {
closeEditor();
} else {
form.setMediaActor(mediaActor);
form.setMediaActorFiles(mediaActor.getMediaActorFiles());
form.setVisible(true);
addClassName("editing");
}
}
private void closeEditor() {
form.setMediaActor(null);
form.setVisible(false);
removeClassName("editing");
}
private void addMediaActor() {
grid.asSingleSelect().clear();
editMediaActor(new MediaActor());
}
public void updateList() {
grid.setItems(service.findAllMediaActors(filterText.getValue()));
}
}