From f4b73670af8bb15f407fa9bae02a7ee23484046b Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Wed, 4 Aug 2021 20:32:52 +0200 Subject: [PATCH] Java part for the media authoring step. --- .../documents/CmsMvcAuthoringSteps.java | 30 +- .../documents/media/MediaAttachmentDto.java | 122 ++ .../documents/media/MediaDetailsModel.java | 100 ++ .../media/MediaListDetailsModel.java | 156 ++ .../documents/media/MediaListDto.java | 130 ++ .../documents/media/MediaStep.java | 1411 +++++++++++++++++ .../media/MediaStepAttachmentOrder.java | 86 + .../documents/media/MediaStepModel.java | 110 ++ .../documents/media/MediaStepService.java | 196 +++ .../documents/media/MovedAttachment.java | 57 + .../ui/MvcAssetStepsBundle.properties | 2 + .../ui/MvcAssetStepsBundle_de.properties | 2 + 12 files changed, 2387 insertions(+), 15 deletions(-) create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaAttachmentDto.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaDetailsModel.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDetailsModel.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDto.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStep.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepAttachmentOrder.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepModel.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepService.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MovedAttachment.java diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java index bc32f285b..5d16b6f31 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java @@ -18,13 +18,14 @@ */ package org.librecms.ui.contentsections.documents; +import org.librecms.ui.contentsections.documents.media.MediaStep; +import org.librecms.ui.contentsections.documents.media.MediaStepService; import org.librecms.ui.contentsections.documents.relatedinfo.RelatedInfoStep; import org.librecms.ui.contentsections.documents.relatedinfo.RelatedInfoStepService; import org.librecms.ui.contenttypes.MvcArticlePropertiesStep; import org.librecms.ui.contenttypes.MvcArticleTextBodyStep; import org.librecms.ui.contenttypes.MvcArticleTextBodyStepResources; -import java.util.HashSet; import java.util.Set; import javax.enterprise.context.ApplicationScoped; @@ -38,24 +39,23 @@ public class CmsMvcAuthoringSteps implements MvcAuthoringSteps { @Override public Set> getClasses() { - final Set> classes = new HashSet<>(); - classes.add(ExampleAuthoringStep.class); - - classes.add(CategorizationStep.class); - classes.add(RelatedInfoStep.class); - classes.add(MvcArticlePropertiesStep.class); - classes.add(MvcArticleTextBodyStep.class); - - return classes; + return Set.of( + ExampleAuthoringStep.class, + CategorizationStep.class, + MediaStep.class, + RelatedInfoStep.class, + MvcArticlePropertiesStep.class, + MvcArticleTextBodyStep.class + ); } @Override public Set> getResourceClasses() { - final Set> classes = new HashSet<>(); - classes.add(MvcArticleTextBodyStepResources.class); - classes.add(RelatedInfoStepService.class); - - return classes; + return Set.of( + MediaStepService.class, + MvcArticleTextBodyStepResources.class, + RelatedInfoStepService.class + ); } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaAttachmentDto.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaAttachmentDto.java new file mode 100644 index 000000000..14cfcf27a --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaAttachmentDto.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +/** + * A DTO for providing data about an {@link ItemAttachment} containing a media + * asset in a form suitable for a MVC view. + * + * @author Jens Pelzetter + */ +public class MediaAttachmentDto { + + /** + * The ID of the attachment. + */ + private long attachmentId; + + /** + * The UUID of the attachment. + */ + private String uuid; + + /** + * The sort key of the attachment. + */ + private long sortKey; + + /** + * The name of the asset type. + */ + private String assetType; + + /** + * Label for the type of the asset of the attachment. + */ + private String assetTypeLabel; + + /** + * The UUID of the attachment asset. + */ + private String assetUuid; + + /** + * The title of the media asset assigned to an content item. This value is + * determined from the title of the asset using {@link GlobalizationHelper#getValueFromLocalizedString(org.libreccm.l10n.LocalizedString) + * }. + */ + private String title; + + public long getAttachmentId() { + return attachmentId; + } + + public void setAttachmentId(final long attachmentId) { + this.attachmentId = attachmentId; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(final String uuid) { + this.uuid = uuid; + } + + public long getSortKey() { + return sortKey; + } + + public void setSortKey(long sortKey) { + this.sortKey = sortKey; + } + + public String getAssetTypeLabel() { + return assetTypeLabel; + } + + public void setAssetTypeLabel(final String assetTypeLabel) { + this.assetTypeLabel = assetTypeLabel; + } + + public String getTitle() { + return title; + } + + public void setTitle(final String title) { + this.title = title; + } + + public String getAssetUuid() { + return assetUuid; + } + + public void setAssetUuid(final String assetUuid) { + this.assetUuid = assetUuid; + } + + public String getAssetType() { + return assetType; + } + + public void setAssetType(final String assetType) { + this.assetType = assetType; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaDetailsModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaDetailsModel.java new file mode 100644 index 000000000..2ab6acbd7 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaDetailsModel.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsMediaDetailsModel") +public class MediaDetailsModel { + + private String baseUrl; + + /** + * The identifier of the {@link AttachmentList} of the link. + */ + private String listIdentifier; + + /** + * The UUID of the link. + */ + private String uuid; + + private String name; + + private String sectionName; + + private String mediaType; + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(final String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getListIdentifier() { + return listIdentifier; + } + + public void setListIdentifier(final String listIdentifier) { + this.listIdentifier = listIdentifier; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getSectionName() { + return sectionName; + } + + public void setSectionName(final String sectionName) { + this.sectionName = sectionName; + } + + public String getMediaType() { + return mediaType; + } + + public void setMediaType(final String mediaType) { + this.mediaType = mediaType; + } + + + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDetailsModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDetailsModel.java new file mode 100644 index 000000000..109cf4860 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDetailsModel.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import org.libreccm.l10n.GlobalizationHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Model used by the media list details view. + * + * @see MediaStep + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsMediaListDetailsModel") +public class MediaListDetailsModel { + + /** + * The {@link GlobalizationHelper} is used here for retrieving some + * localized values. The purpose of this model is to provide the properties + * of an {@link AttachmentList} is a form that is directly usable in the + * template of the details view. The data of the model is provided by the + * {@link RelatedInfoStep}. + */ + @Inject + private GlobalizationHelper globalizationHelper; + + private boolean canEdit; + + /** + * UUID of the {@link AttachmentList} shown. + */ + private String uuid; + + /** + * Name of the {@link AttachmentList} shown. + */ + private String name; + + /** + * Preprocessed localized title values of the {@link AttachmentList}. This + * map is generated by transforming the locales the + * {@link AttachmentList#title} to a string using {@link Locale#toString()}. + */ + private Map titles; + + /** + * Preprocessed localized description values of the {@link AttachmentList}. + * This map is generated by transforming the locales the + * {@link AttachmentList#description} to a string using + * {@link Locale#toString()}. + */ + private Map descriptions; + + /** + * Not used locales for the title. These are the locales returned by + * {@link GlobalizationHelper#getAvailableLocales()} minus the locales + * returned by {@link LocalizedString#getAvailableLocales()} for + * {@link AttachmentList#title}. + */ + private List unusedTitleLocales; + + /** + * Not used locales for the description. These are the locales returned by + * {@link GlobalizationHelper#getAvailableLocales()} minus the locales + * returned by {@link LocalizedString#getAvailableLocales()} for + * {@link AttachmentList#description}. + */ + private List unusedDescriptionLocales; + + public String getUuid() { + return uuid; + } + + public void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public Map getTitles() { + return Collections.unmodifiableMap(titles); + } + + public void setTitles(final Map titles) { + this.titles = new HashMap<>(titles); + } + + public Map getDescriptions() { + return Collections.unmodifiableMap(descriptions); + } + + public void setDescriptions(final Map descriptions) { + this.descriptions = new HashMap<>(descriptions); + } + + public List getUnusedTitleLocales() { + return Collections.unmodifiableList(unusedTitleLocales); + } + + public void setUnusedTitleLocales(final List unusedTitleLocales) { + this.unusedTitleLocales = new ArrayList<>(unusedTitleLocales); + } + + public List getUnusedDescriptionLocales() { + return Collections.unmodifiableList(unusedDescriptionLocales); + } + + public void setUnusedDescriptionLocales( + final List unusedDescriptionLocales + ) { + this.unusedDescriptionLocales + = new ArrayList<>(unusedDescriptionLocales); + } + + public boolean getCanEdit() { + return canEdit; + } + + public void setCanEdit(final boolean canEdit) { + this.canEdit = canEdit; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDto.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDto.java new file mode 100644 index 000000000..ed132b9cd --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaListDto.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A data transfer object used by the template for the listing of the + * {@link AttachmentList}s containing media attachments of a + * {@link ContentItem}. + * + * @see MediaStep + * + * @author Jens Pelzetter + */ +public class MediaListDto { + + /** + * The ID of the list. + */ + private long listId; + + /** + * The UUID of the list. + */ + private String uuid; + + /** + * The name of the list. + */ + private String name; + + /** + * The order value of the list. + */ + private long order; + + /** + * The title of the list. This value is determined using + * {@link GlobalizationHelper#getValueFromLocalizedString(org.libreccm.l10n.LocalizedString)}. + */ + private String title; + + /** + * The description of the list. This value is determined using + * {@link GlobalizationHelper#getValueFromLocalizedString(org.libreccm.l10n.LocalizedString)}. + */ + private String description; + + /** + * The @link{ItemAttachment}s associated with the {@link AttachmentList}. + */ + private List attachments; + + public long getListId() { + return listId; + } + + public void setListId(final long listId) { + this.listId = listId; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public long getOrder() { + return order; + } + + public void setOrder(long order) { + this.order = order; + } + + public String getTitle() { + return title; + } + + public void setTitle(final String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public List getAttachments() { + return Collections.unmodifiableList(attachments); + } + + public void setAttachments(final List attachments) { + this.attachments = new ArrayList<>(attachments); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStep.java new file mode 100644 index 000000000..01b488e9b --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStep.java @@ -0,0 +1,1411 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import org.libreccm.api.Identifier; +import org.libreccm.api.IdentifierParser; +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.security.PermissionChecker; +import org.libreccm.ui.BaseUrl; +import org.librecms.assets.AssetTypesManager; +import org.librecms.contentsection.Asset; +import org.librecms.contentsection.AssetManager; +import org.librecms.contentsection.AssetRepository; +import org.librecms.contentsection.AttachmentList; +import org.librecms.contentsection.AttachmentListManager; +import org.librecms.contentsection.AttachmentListRepository; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ItemAttachment; +import org.librecms.contentsection.ItemAttachmentManager; +import org.librecms.contentsection.privileges.ItemPrivileges; +import org.librecms.ui.contentsections.ContentSectionNotFoundException; +import org.librecms.ui.contentsections.ItemPermissionChecker; +import org.librecms.ui.contentsections.documents.AbstractMvcAuthoringStep; +import org.librecms.ui.contentsections.documents.DefaultAuthoringStepConstants; +import org.librecms.ui.contentsections.documents.DocumentNotFoundException; +import org.librecms.ui.contentsections.documents.DocumentUi; +import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; +import org.librecms.ui.contentsections.documents.relatedinfo.ItemAttachmentDto; + +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.mvc.Models; +import javax.servlet.http.HttpServletRequest; +import javax.transaction.Transactional; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "media") +@Controller +@MvcAuthoringStepDef( + bundle = DefaultAuthoringStepConstants.BUNDLE, + descriptionKey = "authoringsteps.media.description", + labelKey = "authoringsteps.media.label", + supportedDocumentType = ContentItem.class +) +public class MediaStep extends AbstractMvcAuthoringStep { + + /** + * The path fragment of the step. + */ + static final String PATH_FRAGMENT = "media"; + + private static final String MEDIA_LIST_PREFIX = ".media-"; + + /** + * {@link AssetManager} instance of managing {@link Asset}s. + */ + @Inject + private AssetManager assetManager; + + /** + * Used to retrieve and save {@link Asset}s. + */ + @Inject + private AssetRepository assetRepo; + + /** + * Provides access to the available asset types. + */ + @Inject + private AssetTypesManager assetTypesManager; + + @Inject + private BaseUrl baseUrl; + + /** + * Model for the details view of an {@link AttachmentList} containing media. + */ + @Inject + private MediaListDetailsModel listDetailsModel; + + /** + * Manager for {@link AttachmentList}s. + */ + @Inject + private AttachmentListManager listManager; + + /** + * Used to retrieve and save {@link AttachmentList}s. + */ + @Inject + private AttachmentListRepository listRepo; + + @Inject + private DocumentUi documentUi; + + @Context + private HttpServletRequest request; + + @Inject + private MediaDetailsModel mediaDetailsModel; + + /** + * Used to retrieve the current content item. + */ + @Inject + private ContentItemRepository itemRepo; + + /** + * Used for globalization stuff. + */ + @Inject + private GlobalizationHelper globalizationHelper; + + /** + * Used to parse identifiers. + */ + @Inject + private IdentifierParser identifierParser; + + /** + * Manages {@link ItemAttachment}. + */ + @Inject + private ItemAttachmentManager attachmentManager; + + @Inject + private ItemPermissionChecker itemPermissionChecker; + + /** + * Used to provide data for the views without a named bean. + */ + @Inject + private Models models; + + @Inject + private PermissionChecker permissionChecker; + + @Inject + private MediaStepModel mediaStepModel; + + @Override + public Class getStepClass() { + return MediaStep.class; + } + + @Override + @Transactional(Transactional.TxType.REQUIRED) + protected void init() throws ContentSectionNotFoundException, + DocumentNotFoundException { + super.init(); + mediaStepModel.setMediaLists( + getDocument() + .getAttachments() + .stream() + .filter(list -> !list.getName().startsWith(MEDIA_LIST_PREFIX)) + .map(this::buildMediaListDto) + .collect(Collectors.toList()) + ); + mediaStepModel.setMediaAssetPickerBaseUrl(baseUrl.getBaseUrl(request)); + + mediaStepModel.setSectionName(getContentSection().getLabel()); + } + + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + return "org/librecms/ui/contentsection/documents/media.xhtml"; + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Adds a new media list. + * + * @param sectionIdentifier + * @param documentPath + * @param name The name of the list. + * @param title The title of the list for the language returned + * by {@link GlobalizationHelper#getNegotiatedLocale() + * } . + * @param description The description of the list of the default + * locale {@link GlobalizationHelper#getNegotiatedLocale(). + * + * @return A redirect to the list of attachment lists. + */ + @POST + @Path("/medialists/@add") + @Transactional(Transactional.TxType.REQUIRED) + public String addMedia( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("listName") + final String name, + @FormParam("listTitle") + final String title, + @FormParam("listDescription") + final String description + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final ContentItem document = getDocument(); + final AttachmentList list = listManager.createAttachmentList( + document, String.join("", MEDIA_LIST_PREFIX, name) + ); + list.getTitle().addValue( + globalizationHelper.getNegotiatedLocale(), title + ); + list.getDescription().addValue( + globalizationHelper.getNegotiatedLocale(), description + ); + listRepo.save(list); + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Shows the details of an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * + * @return The template for the details view. + */ + @GET + @Path("/medialists/{mediaListIdentifier}/@details") + @Transactional(Transactional.TxType.REQUIRED) + public String showMediaListDetails( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + + listDetailsModel.setUuid(list.getUuid()); + listDetailsModel.setName(list.getName()); + listDetailsModel.setTitles( + list + .getTitle() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + entry -> entry.getValue() + ) + ) + ); + listDetailsModel.setDescriptions( + list + .getDescription() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + entry -> entry.getValue() + ) + ) + ); + + final Set titleLocales = list + .getTitle() + .getAvailableLocales(); + listDetailsModel.setUnusedTitleLocales(globalizationHelper + .getAvailableLocales() + .stream() + .filter(locale -> !titleLocales.contains(locale)) + .map(Locale::toString) + .collect(Collectors.toList()) + ); + + final Set descriptionLocales = list + .getDescription() + .getAvailableLocales(); + listDetailsModel.setUnusedDescriptionLocales( + globalizationHelper + .getAvailableLocales() + .stream() + .filter(locale -> !descriptionLocales.contains(locale)) + .map(Locale::toString) + .collect(Collectors.toList()) + ); + + listDetailsModel.setCanEdit( + itemPermissionChecker.canEditItem(getDocument()) + ); + + return "org/librecms/ui/contentsection/documents/media-medialist-details.xhtml"; + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Updates an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list to update. + * @param name The new name of the list. + * + * @return A redirect to the list of media lists. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/@update") + @Transactional(Transactional.TxType.REQUIRED) + public String updateAttachmentList( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @FormParam("listName") + final String name + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.setName(name); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes an media list and all media attachments of the list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list to remove. + * @param confirmed The value of the confirm parameter. Must + * contain {@code true} (as string not as + * boolean), otherwise this method does nothing. + * + * @return A redirect to the list of attachment lists. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/@remove") + @Transactional(Transactional.TxType.REQUIRED) + public String removeAttachmentList( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @FormParam("confirmed") + final String confirmed + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + if ("true".equalsIgnoreCase(confirmed)) { + listManager.removeAttachmentList(listResult.get()); + } + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Adds a localized title to an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the new title value. + * @param value The value of the new title value. + * + * @return A redirect to the details view of the attachment list. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/title/@add") + @Transactional(Transactional.TxType.REQUIRED) + public String addAttachmentListTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @FormParam("locale") + final String localeParam, + @FormParam("value") + final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getTitle().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Updates a localized title value of an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the title value to update. + * @param value The new title value. + * + * @return A redirect to the details view of the attachment list. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/title/@edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String updateAttachmentListTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam, + @FormParam("value") + final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getTitle().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes a localized title value of an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the title value to remove. + * + * @return A redirect to the details view of the attachment list. + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/title/@remove/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String removeAttachmentListTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getTitle().removeValue(new Locale(localeParam)); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Adds a localized description to an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the new description value. + * @param value The value of the new description value. + * + * @return A redirect to the details view of the media list. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/description/@add") + @Transactional(Transactional.TxType.REQUIRED) + public String addAttachmentListDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @FormParam("locale") + final String localeParam, + @FormParam("value") + final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getDescription().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Updates a localized description value of an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the description value to update. + * @param value The new description value. + * + * @return A redirect to the details view of the media list. + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/description/@edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String updateAttachmentListDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam, + @FormParam("value") + final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getDescription().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes a localized description value of an media list. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list. + * @param localeParam The locale of the description value to remove. + * + * @return A redirect to the details view of the media list. + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/description/@remove/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String removeAttachmentListDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + + final AttachmentList list = listResult.get(); + list.getDescription().removeValue(new Locale(localeParam)); + listRepo.save(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Create new attachment. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the list to which the + * attachment is added. + * @param mediaIdentifierParam The identifier of the media asset to use for + * the media attachment. + * + * @return A redirect to the list of attachment lists and attachments. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/attachments/@create") + @Transactional(Transactional.TxType.REQUIRED) + public String createAttachment( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @FormParam("mediaIdentifier") + final String mediaIdentifierParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional assetResult; + final Identifier assetIdentifier = identifierParser.parseIdentifier( + mediaIdentifierParam + ); + switch (assetIdentifier.getType()) { + case ID: + assetResult = assetRepo.findById( + Long.parseLong( + assetIdentifier.getIdentifier() + ) + ); + break; + case UUID: + assetResult = assetRepo.findByUuid( + assetIdentifier.getIdentifier() + ); + break; + default: + assetResult = assetRepo.findByPath( + getContentSection(), + assetIdentifier.getIdentifier() + ); + break; + } + + if (!assetResult.isPresent()) { + models + .put("section", getContentSection().getLabel()); + models.put("assetUuid", mediaIdentifierParam); + return "org/librecms/ui/contentsection/documents/asset-not-found.xhtml"; + } + + final Asset asset = assetResult.get(); + + attachmentManager.attachAsset(asset, list); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes a media attachment from an {@link AttachmentList}.The + * {@link Asset} of the attachment will not be deleted. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifier of the {@link AttachmentList} + * to which the attachment belongs. + * @param attachmentUuid The UUID of the attachment to remove. + * @param confirmed The value of the {@code confirm} parameter. If + * the value anything other than the string + * {@code true} the method does nothing. + * + * @return + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/attachments/{attachmentUuid}/@remove") + @Transactional(Transactional.TxType.REQUIRED) + public String removeAttachment( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("attachmentUuid") + final String attachmentUuid, + @FormParam("confirmed") + final String confirmed + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent() && "true".equalsIgnoreCase(confirmed)) { + final Asset asset = result.get().getAsset(); + attachmentManager.unattachAsset(asset, list); +// if (asset instanceof RelatedLink) { +// assetRepo.delete(asset); +// } + } + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Move an media list one position up. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifer of the list to move. + * + * @return A redirect to list of attachment lists. + */ + @POST + @Path( + "/medialists/{mediaListListIdentifier}/@moveUp") + @Transactional(Transactional.TxType.REQUIRED) + public String moveListUp( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + listManager.moveUp(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Move an attachment list one position down. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifer of the list to move. + * + * @return A redirect to list of attachment lists. + */ + @POST + @Path("/medialists/{mediaListIdentifier}/@moveDown") + @Transactional(Transactional.TxType.REQUIRED) + public String moveListDown( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + listManager.moveDown(list); + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Move an attachment one position up. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifer to which the attachment belongs. + * @param attachmentUuid The UUID of the attachment ot move. + * + * @return A redirect to list of attachment lists and attachments. + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/attachments/{attachmentUuid}/@moveUp") + @Transactional(Transactional.TxType.REQUIRED) + public String moveAttachmentUp( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("attachmentUuid") + final String attachmentUuid + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent()) { + final ItemAttachment attachment = result.get(); + final Asset asset = attachment.getAsset(); + attachmentManager.moveUp(asset, list); + } + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Move an attachment one position down. + * + * @param sectionIdentifier + * @param documentPath + * @param listIdentifierParam The identifer to which the attachment belongs. + * @param attachmentUuid The UUID of the attachment ot move. + * + * @return A redirect to list of attachment lists and attachements. + */ + @POST + @Path( + "/medialists/{mediaListIdentifier}/attachments/{attachmentUuid}/@moveDown") + @Transactional(Transactional.TxType.REQUIRED) + public String moveAttachmentDown( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("mediaListIdentifier") + final String listIdentifierParam, + @PathParam("attachmentUuid") + final String attachmentUuid + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, getDocument() + )) { + final Optional listResult = findMediaList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showMediaListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent()) { + final ItemAttachment attachment = result.get(); + final Asset asset = attachment.getAsset(); + attachmentManager.moveDown(asset, list); + } + + return buildRedirectPathForStep( + String.format("/medialists/%s/@details", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * A helper function to find a media list. + * + * @param mediaListIdentifier The idenfifier of the attachment list. + * + * @return An {@link Optional} with the attachment list or an empty optional + * if the current content item has no list with the provided + * identifier. + */ + private Optional findMediaList( + final String mediaListIdentifier + ) { + final ContentItem document = getDocument(); + final Identifier identifier = identifierParser.parseIdentifier( + mediaListIdentifier + ); + final Optional listResult; + switch (identifier.getType()) { + case ID: + listResult = listRepo + .findForItemAndId( + document, Long.parseLong(identifier.getIdentifier()) + ); + break; + case UUID: + listResult = listRepo + .findForItemAndUuid( + document, identifier.getIdentifier() + ); + break; + default: + listResult = listRepo + .findForItemAndName( + document, identifier.getIdentifier() + ); + break; + } + + return listResult; + } + + /** + * Show the "media list not found" error page. + * + * @param listIdentifier The identifier of the list that was not found. + * + * @return The template for the "attachment list not found" page. + */ + private String showMediaListNotFound(final String listIdentifier) { + models.put("contentItem", getDocumentPath()); + models.put("listIdentifier", listIdentifier); + return "org/librecms/ui/contentsection/documents/medialist-not-found.xhtml"; + } + + private MediaListDto buildMediaListDto( + final AttachmentList attachmentList + ) { + final MediaListDto dto = new MediaListDto(); + dto.setAttachments( + attachmentList + .getAttachments() + .stream() + .map(this::buildMediaAttachmentDto) + .collect(Collectors.toList()) + ); + dto.setDescription( + globalizationHelper + .getValueFromLocalizedString( + attachmentList.getDescription() + ) + ); + dto.setListId(attachmentList.getListId()); + dto.setName(attachmentList.getName()); + dto.setOrder(attachmentList.getListOrder()); + dto.setTitle( + globalizationHelper + .getValueFromLocalizedString( + attachmentList.getTitle() + ) + ); + dto.setUuid(attachmentList.getUuid()); + return dto; + } + + /** + * Helper function for building a {@link ItemAttachmentDto} for an + * {@link ItemAttachment}. + * + * @param itemAttachment The {@link ItemAttachment} from which the + * {@link ItemAttachmentDto} is build. + * + * @return The {@link ItemAttachmentDto}. + */ + private MediaAttachmentDto buildMediaAttachmentDto( + final ItemAttachment itemAttachment + ) { + final MediaAttachmentDto dto = new MediaAttachmentDto(); + dto.setAssetType( + Optional + .ofNullable(itemAttachment.getAsset()) + .map(Asset::getClass) + .map(clazz -> assetTypesManager.getAssetTypeInfo(clazz)) + .map(info -> info.getAssetClass().getName()) + .orElse("") + ); + dto.setAssetTypeLabel( + Optional + .ofNullable(itemAttachment.getAsset()) + .map(Asset::getClass) + .map(clazz -> assetTypesManager.getAssetTypeInfo(clazz)) + .map( + info -> globalizationHelper.getLocalizedTextsUtil( + info.getLabelBundle()).getText(info.getLabelKey()) + ).orElse("") + ); + dto.setAssetUuid( + Optional + .ofNullable(itemAttachment.getAsset()) + .map(Asset::getUuid) + .orElse(null) + ); + dto.setAttachmentId(itemAttachment.getAttachmentId()); + dto.setSortKey(itemAttachment.getSortKey()); + dto.setTitle( + Optional + .ofNullable(itemAttachment.getAsset()) + .map( + asset -> globalizationHelper.getValueFromLocalizedString( + asset.getTitle() + ) + ) + .orElse("") + ); + dto.setUuid(itemAttachment.getUuid()); + return dto; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepAttachmentOrder.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepAttachmentOrder.java new file mode 100644 index 000000000..a6672d93f --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepAttachmentOrder.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * + * @author Jens Pelzetter + */ +public class MediaStepAttachmentOrder { + + private List attachmentListsOrder; + + private Map> attachmentsOrder; + + private List movedAttachments; + + public List getAttachmentListsOrder() { + return Collections.unmodifiableList(attachmentListsOrder); + } + + public void setAttachmentListsOrder(final List attachmentListsOrder) { + this.attachmentListsOrder = new ArrayList<>(attachmentListsOrder); + } + + public Map> getAttachmentsOrder() { + return Collections.unmodifiableMap(attachmentsOrder); + } + + public void setAttachmentsOrder( + final Map> attachmentsOrder + ) { + this.attachmentsOrder = attachmentsOrder + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey(), + entry -> new ArrayList<>(entry.getValue()) + ) + ); + } + + public List getMovedAttachments() { + return Collections.unmodifiableList(movedAttachments); + } + + public void setMovedAttachments(final List movedAttachments) { + this.movedAttachments = new ArrayList<>(movedAttachments); + } + + @Override + public String toString() { + return String.format( + "attachmentListsOrder = %s, " + + "attachmentsOrder = %s, " + + "movedAttachments = %s", + Objects.toString(attachmentListsOrder), + Objects.toString(attachmentsOrder), + Objects.toString(movedAttachments) + ); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepModel.java new file mode 100644 index 000000000..a601b4ac4 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepModel.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import org.librecms.assets.AudioAsset; +import org.librecms.assets.ExternalAudioAsset; +import org.librecms.assets.ExternalVideoAsset; +import org.librecms.assets.Image; +import org.librecms.assets.VideoAsset; +import org.librecms.contentsection.AttachmentList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsMediaStep") +public class MediaStepModel { + + private List mediaLists; + + private String mediaAssetPickerBaseUrl; + + private String sectionName; + + /** + * Gets the {@link AttachmentList}s containing media assets of the current + * content item and converts them to {@link MediaListDto}s to make data + * about the lists available in the views. + * + * @return A list of the {@link AttachmentList} of the current content item. + */ + public List getMediaLists() { + return Collections.unmodifiableList(mediaLists); + } + + public List getAttachmentsLists() { + return Collections.unmodifiableList(mediaLists); + } + + public void setAttachmentsLists(List mediaLists) { + this.mediaLists = mediaLists; + } + + protected void setMediaLists( + final List attachmentLists + ) { + this.mediaLists = new ArrayList<>(attachmentLists); + } + + public String getMediaAssetPickerBaseUrl() { + return mediaAssetPickerBaseUrl; + } + + public void setMediaAssetPickerBaseUrl(final String mediaAssetPickerBaseUrl) { + this.mediaAssetPickerBaseUrl = mediaAssetPickerBaseUrl; + } + + public String getSectionName() { + return sectionName; + } + + public void setSectionName(final String sectionName) { + this.sectionName = sectionName; + } + + public String getAudioAssetType() { + return AudioAsset.class.getName(); + } + + public String getExternalAudioAssetType() { + return ExternalAudioAsset.class.getName(); + } + + public String getExternalVideoAssetType() { + return ExternalVideoAsset.class.getName(); + } + + public String getImageType() { + return Image.class.getName(); + } + + public String getVideoAssetType() { + return VideoAsset.class.getName(); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepService.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepService.java new file mode 100644 index 000000000..4496511a3 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MediaStepService.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +import org.librecms.contentsection.AttachmentList; +import org.librecms.contentsection.AttachmentListRepository; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ItemAttachment; +import org.librecms.contentsection.ItemAttachmentManager; +import org.librecms.ui.contentsections.ContentSectionsUi; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; + +import java.util.List; +import java.util.Map; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "media-service") +public class MediaStepService { + + @Inject + private AttachmentListRepository attachmentListRepo; + + @Inject + private ItemAttachmentManager attachmentManager; + + @Inject + private ContentItemRepository itemRepo; + + @Inject + private ContentSectionsUi sectionsUi; + + @POST + @Path("/save-order") + @Consumes(MediaType.APPLICATION_JSON) + @Transactional(Transactional.TxType.REQUIRED) + public Response saveOrder( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + final MediaStepAttachmentOrder order + ) { + final ContentSection contentSection = sectionsUi + .findContentSection(sectionIdentifier) + .orElseThrow( + () -> new NotFoundException( + String.format( + "No content identifed by %s found.", + sectionIdentifier + ) + ) + ); + + final ContentItem document = itemRepo + .findByPath(contentSection, documentPath) + .orElseThrow( + () -> new NotFoundException( + String.format( + "No document for path %s in section %s.", + documentPath, + contentSection.getLabel() + ) + ) + ); + + final List attachmentLists = document.getAttachments(); + final List attachmentListsOrder = order + .getAttachmentListsOrder(); + + if (attachmentListsOrder.size() != attachmentLists.size()) { + throw new BadRequestException( + String.format( + "Size of lists of attachment lists does not match list of " + + "attachment order list. attachmentLists.size = %d, " + + "attachmentListsOrder.size = %d.", + attachmentLists.size(), + attachmentListsOrder.size() + ) + ); + } + + for (int i = 0; i < attachmentListsOrder.size(); i++) { + final String listUuid = attachmentListsOrder.get(i); + final AttachmentList attachmentList = attachmentLists + .stream() + .filter(list -> listUuid.equals(list.getUuid())) + .findAny() + .orElseThrow( + () -> new BadRequestException( + String.format( + "attachmentListsOrder has an entry for attachment " + + "list %s, but there no attachment list with " + + "that UUID.", + listUuid + ) + ) + ); + + attachmentList.setListOrder(i); + attachmentListRepo.save(attachmentList); + } + + for (final Map.Entry> attachmentsOrder : order + .getAttachmentsOrder().entrySet()) { + final AttachmentList attachmentList = document + .getAttachments() + .stream() + .filter(list -> attachmentsOrder.getKey().equals(list.getUuid())) + .findAny() + .orElseThrow( + () -> new BadRequestException( + String.format( + "attachmentsOrder contains an entry for " + + "attachment list %s, but there no attachment " + + "list with that UUID.", + attachmentsOrder.getKey() + ) + ) + ); + + final List> attachments = attachmentList + .getAttachments(); + if (attachments.size() != attachmentsOrder.getValue().size()) { + throw new BadRequestException( + String.format( + "Size of attachmentsOrder list does not match the size" + + "of the attachments list. " + + "attachmentsOrder.size = %d, " + + "attachmentsList.size = %d", + attachmentsOrder.getValue().size(), + attachments.size() + ) + ); + } + + for (int i = 0; i < attachmentsOrder.getValue().size(); i++) { + final String attachmentUuid = attachmentsOrder.getValue().get(i); + final ItemAttachment attachment = attachments + .stream() + .filter(current -> attachmentUuid.equals(current.getUuid())) + .findAny() + .orElseThrow( + () -> new BadRequestException( + String.format( + "attachmentOrder order for attachment list %s " + + "has an entry for attachment %s but " + + "there is attachment with that UUID in " + + "the list.", + attachmentList.getUuid(), + attachmentUuid + ) + ) + ); + attachment.setSortKey(i); + attachmentManager.save(attachment); + } + } + + return Response.ok().build(); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MovedAttachment.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MovedAttachment.java new file mode 100644 index 000000000..159a4e454 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/media/MovedAttachment.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.ui.contentsections.documents.media; + +/** + * + * @author Jens Pelzetter + */ +public class MovedAttachment { + + private String attachmentUuid; + + private String fromListUuid; + + private String toListUuid; + + public String getAttachmentUuid() { + return attachmentUuid; + } + + public void setAttachmentUuid(final String attachmentUuid) { + this.attachmentUuid = attachmentUuid; + } + + public String getFromListUuid() { + return fromListUuid; + } + + public void setFromListUuid(final String fromListUuid) { + this.fromListUuid = fromListUuid; + } + + public String getToListUuid() { + return toListUuid; + } + + public void setToListUuid(final String toListUuid) { + this.toListUuid = toListUuid; + } + +} diff --git a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties index bd0e39926..d855abc7e 100644 --- a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties +++ b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties @@ -533,3 +533,5 @@ videoasset.editstep.legelmetadata.remove.submit=Remove legal metadata videoasset.editstep.legelmetadata.rightsholder=Rights Holder videoasset.editstep.legelmetadata.creator=Creator categorization.tree.expand=Expand/Hide subcategories +authoringsteps.media.description=Add different media, for example images, videos or audio asset to a content item. +authoringsteps.media.label=Media diff --git a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties index f736aeda9..9784e44a1 100644 --- a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties +++ b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties @@ -533,3 +533,5 @@ videoasset.editstep.legelmetadata.remove.submit=Rechtliche Informationen entfern videoasset.editstep.legelmetadata.rightsholder=Rechteinhaber videoasset.editstep.legelmetadata.creator=K\u00fcnstler categorization.tree.expand=Unterkategorien anzeigen/verbergen +authoringsteps.media.description=F\u00fcgen Sie Medien, z.B. Bilder, Videos oder Audio-Dateien einem Dokument hinzu. +authoringsteps.media.label=Medien