UI for related info including related links

pull/10/head
Jens Pelzetter 2021-03-30 20:45:52 +02:00
parent b27b5e5dc5
commit 88b29d2563
10 changed files with 934 additions and 40 deletions

View File

@ -18,7 +18,7 @@ import javax.inject.Inject;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@Dependent @Dependent
class AssetPermissionsModelProvider { public class AssetPermissionsModelProvider {
/** /**
* The {@link AssetPermissionsChecker} instance to use. * The {@link AssetPermissionsChecker} instance to use.

View File

@ -7,10 +7,8 @@ package org.librecms.ui.contentsections;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.api.IdentifierParser;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.PermissionManager; import org.libreccm.security.PermissionManager;
import org.libreccm.security.Role; import org.libreccm.security.Role;
import org.libreccm.security.RoleRepository; import org.libreccm.security.RoleRepository;

View File

@ -42,7 +42,7 @@ public class DocumentFolderRowModel {
return created; return created;
} }
protected void setCreated(final String created) { public void setCreated(final String created) {
this.created = created; this.created = created;
} }
@ -50,7 +50,7 @@ public class DocumentFolderRowModel {
return deletable; return deletable;
} }
protected void setDeletable(final boolean deletable) { public void setDeletable(final boolean deletable) {
this.deletable = deletable; this.deletable = deletable;
} }
@ -62,11 +62,11 @@ public class DocumentFolderRowModel {
return folderPath; return folderPath;
} }
protected void setFolderPath(final String folderPath) { public void setFolderPath(final String folderPath) {
this.folderPath = folderPath; this.folderPath = folderPath;
} }
protected void setFolder(final boolean folder) { public void setFolder(final boolean folder) {
this.folder = folder; this.folder = folder;
} }
@ -78,7 +78,7 @@ public class DocumentFolderRowModel {
return String.join(", ", languages); return String.join(", ", languages);
} }
protected void setLanguages(final SortedSet<String> languages) { public void setLanguages(final SortedSet<String> languages) {
this.languages = languages; this.languages = languages;
} }
@ -86,7 +86,7 @@ public class DocumentFolderRowModel {
return lastEdited; return lastEdited;
} }
protected void setLastEdited(final String lastEdited) { public void setLastEdited(final String lastEdited) {
this.lastEdited = lastEdited; this.lastEdited = lastEdited;
} }
@ -94,7 +94,7 @@ public class DocumentFolderRowModel {
return lastEditPublished; return lastEditPublished;
} }
protected void setLastEditPublished(final boolean lastEditPublished) { public void setLastEditPublished(final boolean lastEditPublished) {
this.lastEditPublished = lastEditPublished; this.lastEditPublished = lastEditPublished;
} }
@ -102,7 +102,7 @@ public class DocumentFolderRowModel {
return name; return name;
} }
protected void setName(final String name) { public void setName(final String name) {
this.name = name; this.name = name;
} }
@ -118,7 +118,7 @@ public class DocumentFolderRowModel {
return title; return title;
} }
protected void setTitle(final String title) { public void setTitle(final String title) {
this.title = title; this.title = title;
} }
@ -126,7 +126,7 @@ public class DocumentFolderRowModel {
return type; return type;
} }
protected void setType(final String type) { public void setType(final String type) {
this.type = type; this.type = type;
} }
@ -134,7 +134,7 @@ public class DocumentFolderRowModel {
return permissions; return permissions;
} }
protected void setPermissions( public void setPermissions(
final DocumentPermissionsModel permissions final DocumentPermissionsModel permissions
) { ) {
this.permissions = permissions; this.permissions = permissions;

View File

@ -18,7 +18,7 @@ import javax.inject.Inject;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@Dependent @Dependent
class DocumentPermissions { public class DocumentPermissions {
@Inject @Inject
private PermissionChecker permissionChecker; private PermissionChecker permissionChecker;

View File

@ -5,14 +5,19 @@
*/ */
package org.librecms.ui.contentsections.documents; package org.librecms.ui.contentsections.documents;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.AttachmentList; import org.librecms.contentsection.AttachmentList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
/** /**
@ -23,6 +28,8 @@ import javax.inject.Named;
@Named("CmsAttachmentListDetailsModel") @Named("CmsAttachmentListDetailsModel")
public class AttachmentListDetailsModel { public class AttachmentListDetailsModel {
@Inject
private GlobalizationHelper globalizationHelper;
private String uuid; private String uuid;
@ -32,10 +39,18 @@ public class AttachmentListDetailsModel {
private Map<String, String> descriptions; private Map<String, String> descriptions;
private List<String> unusedTitleLocales;
private List<String> unusedDescriptionLocales;
public String getUuid() { public String getUuid() {
return uuid; return uuid;
} }
public String getName() {
return name;
}
public Map<String, String> getTitles() { public Map<String, String> getTitles() {
return Collections.unmodifiableMap(titles); return Collections.unmodifiableMap(titles);
} }
@ -44,6 +59,14 @@ public class AttachmentListDetailsModel {
return Collections.unmodifiableMap(descriptions); return Collections.unmodifiableMap(descriptions);
} }
public List<String> getUnusedTitleLocales() {
return Collections.unmodifiableList(unusedTitleLocales);
}
public List<String> getUnusedDescriptionLocales() {
return Collections.unmodifiableList(unusedDescriptionLocales);
}
protected void setAttachmentList(final AttachmentList list) { protected void setAttachmentList(final AttachmentList list) {
Objects.requireNonNull(list); Objects.requireNonNull(list);
uuid = list.getUuid(); uuid = list.getUuid();
@ -70,6 +93,26 @@ public class AttachmentListDetailsModel {
entry -> entry.getValue() entry -> entry.getValue()
) )
); );
final Set<Locale> titleLocales = list
.getTitle()
.getAvailableLocales();
unusedTitleLocales = globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !titleLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
final Set<Locale> descriptionLocales = list
.getDescription()
.getAvailableLocales();
unusedDescriptionLocales = globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !descriptionLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
} }
} }

View File

@ -9,8 +9,11 @@ import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.assets.RelatedLink; import org.librecms.assets.RelatedLink;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
@ -32,8 +35,12 @@ public class InternalLinkDetailsModel {
private String uuid; private String uuid;
private String label;
private Map<String, String> title; private Map<String, String> title;
private List<String> unusedTitleLocales;
private String targetItemUuid; private String targetItemUuid;
private String targetItemName; private String targetItemName;
@ -52,10 +59,18 @@ public class InternalLinkDetailsModel {
return uuid; return uuid;
} }
public String getLabel() {
return label;
}
public Map<String, String> getTitle() { public Map<String, String> getTitle() {
return Collections.unmodifiableMap(title); return Collections.unmodifiableMap(title);
} }
public List<String> getUnusedTitleLocales() {
return Collections.unmodifiableList(unusedTitleLocales);
}
public String getTargetItemUuid() { public String getTargetItemUuid() {
return targetItemUuid; return targetItemUuid;
} }
@ -72,6 +87,9 @@ public class InternalLinkDetailsModel {
Objects.requireNonNull(link); Objects.requireNonNull(link);
uuid = link.getUuid(); uuid = link.getUuid();
label = globalizationHelper.getValueFromLocalizedString(
link.getTitle()
);
title = link title = link
.getTitle() .getTitle()
.getValues() .getValues()
@ -83,6 +101,13 @@ public class InternalLinkDetailsModel {
entry -> entry.getValue() entry -> entry.getValue()
) )
); );
final Set<Locale> titleLocales = link.getTitle().getAvailableLocales();
unusedTitleLocales = globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !titleLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
targetItemUuid = link.getTargetItem().getItemUuid(); targetItemUuid = link.getTargetItem().getItemUuid();
targetItemName = link.getTargetItem().getDisplayName(); targetItemName = link.getTargetItem().getDisplayName();
targetItemTitle = globalizationHelper.getValueFromLocalizedString( targetItemTitle = globalizationHelper.getValueFromLocalizedString(

View File

@ -12,21 +12,47 @@ import org.librecms.assets.AssetTypeInfo;
import org.librecms.assets.AssetTypesManager; import org.librecms.assets.AssetTypesManager;
import org.librecms.assets.RelatedLink; import org.librecms.assets.RelatedLink;
import org.librecms.contentsection.Asset; import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetFolderEntry;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.AssetRepository; import org.librecms.contentsection.AssetRepository;
import org.librecms.contentsection.AttachmentList; import org.librecms.contentsection.AttachmentList;
import org.librecms.contentsection.AttachmentListL10NManager; import org.librecms.contentsection.AttachmentListL10NManager;
import org.librecms.contentsection.AttachmentListManager; import org.librecms.contentsection.AttachmentListManager;
import org.librecms.contentsection.AttachmentListRepository; import org.librecms.contentsection.AttachmentListRepository;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentType;
import org.librecms.contentsection.ContentTypeRepository;
import org.librecms.contentsection.DocumentFolderEntry;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.FolderRepository;
import org.librecms.contentsection.FolderType;
import org.librecms.contentsection.ItemAttachment; import org.librecms.contentsection.ItemAttachment;
import org.librecms.contentsection.ItemAttachmentManager; import org.librecms.contentsection.ItemAttachmentManager;
import org.librecms.ui.contentsections.AssetFolderRowModel;
import org.librecms.ui.contentsections.AssetFolderTree;
import org.librecms.ui.contentsections.AssetFolderTreeNode;
import org.librecms.ui.contentsections.AssetPermissionsModel;
import org.librecms.ui.contentsections.AssetPermissionsModelProvider;
import org.librecms.ui.contentsections.DocumentFolderRowModel;
import org.librecms.ui.contentsections.DocumentFolderTree;
import org.librecms.ui.contentsections.DocumentFolderTreeNode;
import org.librecms.ui.contentsections.DocumentPermissions;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
@ -35,11 +61,15 @@ import javax.inject.Named;
import javax.mvc.Controller; import javax.mvc.Controller;
import javax.mvc.Models; import javax.mvc.Models;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/** /**
* *
@ -54,6 +84,18 @@ public class RelatedInfoStep implements MvcAuthoringStep {
static final String PATH_FRAGMENT = "relatedinfo"; static final String PATH_FRAGMENT = "relatedinfo";
@Inject
private AssetFolderTree assetFolderTree;
/**
* Used to build the {@link AssetPermissionsModel}.
*/
@Inject
private AssetPermissionsModelProvider assetPermissions;
@Inject
private AssetManager assetManager;
@Inject @Inject
private AssetRepository assetRepo; private AssetRepository assetRepo;
@ -72,14 +114,32 @@ public class RelatedInfoStep implements MvcAuthoringStep {
@Inject @Inject
private AttachmentListRepository listRepo; private AttachmentListRepository listRepo;
@Inject
private DocumentFolderTree documentFolderTree;
@Inject
private DocumentPermissions documentPermissions;
@Inject
private FolderManager folderManager;
@Inject
private FolderRepository folderRepo;
@Inject @Inject
private InternalLinkDetailsModel internalLinkDetailsModel; private InternalLinkDetailsModel internalLinkDetailsModel;
@Inject
private ContentItemL10NManager itemL10NManager;
@Inject
private ContentItemManager itemManager;
@Inject @Inject
private ContentItemRepository itemRepo; private ContentItemRepository itemRepo;
@Inject @Inject
private ContentItemManager itemManager; private ContentTypeRepository contentTypeRepo;
@Inject @Inject
private GlobalizationHelper globalizationHelper; private GlobalizationHelper globalizationHelper;
@ -168,7 +228,7 @@ public class RelatedInfoStep implements MvcAuthoringStep {
return "org/librecms/ui/documents/relatedinfo.xhtml"; return "org/librecms/ui/documents/relatedinfo.xhtml";
} }
@Transactional @Transactional(Transactional.TxType.REQUIRED)
public List<AttachmentListDto> getAttachmentLists() { public List<AttachmentListDto> getAttachmentLists() {
return document return document
.getAttachments() .getAttachments()
@ -178,6 +238,125 @@ public class RelatedInfoStep implements MvcAuthoringStep {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@GET
@Path("/asset-folders")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<AssetFolderTreeNode> getAssetFolderTree() {
return assetFolderTree.buildFolderTree(
section, section.getRootAssetsFolder()
);
}
@GET
@Path("/asset-folders/{folderPath}/assets")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<AssetFolderRowModel> getAssetsInFolder(
@PathParam("folderPath") final String folderPath,
@QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults,
@QueryParam("filterTerm") @DefaultValue("") final String filterTerm
) {
final Folder folder;
if (folderPath.isEmpty()) {
folder = section.getRootAssetsFolder();
} else {
final Optional<Folder> folderResult = folderRepo.findByPath(
section, folderPath, FolderType.ASSETS_FOLDER
);
if (folderResult.isPresent()) {
folder = folderResult.get();
} else {
return Collections.emptyList();
}
}
return folderRepo
.getAssetFolderEntries(
folder, firstResult, maxResults, filterTerm
)
.stream()
.map(entry -> buildAssetFolderRowModel(section, entry))
.collect(Collectors.toList());
}
@GET
@Path("/search-assets")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<AssetFolderRowModel> findAssets(
@QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults,
@QueryParam("searchTerm") @DefaultValue("") final String searchTerm
) {
return assetRepo.findByTitleAndContentSection(searchTerm, section)
.stream()
.map(asset -> buildAssetFolderRowModel(section, asset))
.collect(Collectors.toList());
}
@GET
@Path("/document-folders")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<DocumentFolderTreeNode> getDocumentFolderTree() {
return documentFolderTree.buildFolderTree(
section, section.getRootDocumentsFolder()
);
}
@GET
@Path("/document-folders/{folderPath}/documents")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<DocumentFolderRowModel> getDocumentsInFolder(
@PathParam("folderPath") final String folderPath,
@QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults,
@QueryParam("filterTerm") @DefaultValue("") final String filterTerm
) {
final Folder folder;
if (folderPath.isEmpty()) {
folder = section.getRootDocumentsFolder();
} else {
final Optional<Folder> folderResult = folderRepo.findByPath(
section, folderPath, FolderType.ASSETS_FOLDER
);
if (folderResult.isPresent()) {
folder = folderResult.get();
} else {
return Collections.emptyList();
}
}
return folderRepo
.getDocumentFolderEntries(
folder,
firstResult,
maxResults,
filterTerm
)
.stream()
.map(entry -> buildDocumentFolderRowModel(section, entry))
.collect(Collectors.toList());
}
@GET
@Path("/search-documents")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public List<DocumentFolderRowModel> findDocuments(
@QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults,
@QueryParam("searchTerm") @DefaultValue("") final String searchTerm
) {
return itemRepo.findByNameAndContentSection(searchTerm, section)
.stream()
.map(asset -> buildDocumentFolderRowModel(section, asset))
.collect(Collectors.toList());
}
@POST @POST
@Path("/attachmentlists/@add") @Path("/attachmentlists/@add")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -224,7 +403,7 @@ public class RelatedInfoStep implements MvcAuthoringStep {
} }
@POST @POST
@Path("/attachmentlists/{attachmentListIdentifier}/@remove") @Path("/attachmentlists/{attachmentListIdentifier}/@update")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String updateAttachmentList( public String updateAttachmentList(
@PathParam("attachmentListIdentifier") final String listIdentifierParam, @PathParam("attachmentListIdentifier") final String listIdentifierParam,
@ -276,6 +455,176 @@ public class RelatedInfoStep implements MvcAuthoringStep {
); );
} }
@POST
@Path("/attachmentlists/{attachmentListIdentifier}/title/@add")
@Transactional(Transactional.TxType.REQUIRED)
public String addAttachmentListTitle(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getTitle().addValue(new Locale(localeParam), value);
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST
@Path("/attachmentlists/{attachmentListIdentifier}/title/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String updateAttachmentListTitle(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getTitle().addValue(new Locale(localeParam), value);
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST
@Path("/attachmentlists/{attachmentListIdentifier}/title/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String removeAttachmentListTitle(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getTitle().removeValue(new Locale(localeParam));
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST
@Path("/attachmentlists/{attachmentListIdentifier}/description/@add")
@Transactional(Transactional.TxType.REQUIRED)
public String addAttachmentListDescription(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getDescription().addValue(new Locale(localeParam), value);
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST
@Path(
"/attachmentlists/{attachmentListIdentifier}/description/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String updateAttachmentListDescription(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getDescription().addValue(new Locale(localeParam), value);
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST
@Path(
"/attachmentlists/{attachmentListIdentifier}/description/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String removeAttachmentListDescription(
@PathParam("attachmentListIdentifier") final String listIdentifierParam,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
final Optional<AttachmentList> listResult = findAttachmentList(
listIdentifierParam
);
if (!listResult.isPresent()) {
return showAttachmentListNotFound(listIdentifierParam);
}
final AttachmentList list = listResult.get();
list.getDescription().removeValue(new Locale(localeParam));
listRepo.save(list);
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT,
list.getName()
);
}
@POST @POST
@Path("/attachmentlists/{attachmentListIdentifier}/attachments") @Path("/attachmentlists/{attachmentListIdentifier}/attachments")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -407,13 +756,13 @@ public class RelatedInfoStep implements MvcAuthoringStep {
internalLinkDetailsModel.setListIdentifier(list.getName()); internalLinkDetailsModel.setListIdentifier(list.getName());
internalLinkDetailsModel.setInternalLink(link); internalLinkDetailsModel.setInternalLink(link);
return "org/librecms/ui/documents/relatedinfo-create-internallink.xhtml"; return "org/librecms/ui/documents/relatedinfo-internallink-details.xhtml";
} }
@POST @POST
@Path( @Path(
"/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}") "/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}"
)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String updateInternalLinkTarget( public String updateInternalLinkTarget(
@PathParam("attachmentListIdentifier") @PathParam("attachmentListIdentifier")
@ -563,7 +912,8 @@ public class RelatedInfoStep implements MvcAuthoringStep {
@POST @POST
@Path( @Path(
"/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}/title/@edit/{locale}") "/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}/title/@remove/{locale}"
)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String removeInternalLinkTitle( public String removeInternalLinkTitle(
@PathParam("attachmentListIdentifier") @PathParam("attachmentListIdentifier")
@ -856,7 +1206,8 @@ public class RelatedInfoStep implements MvcAuthoringStep {
dto.setAttachmentId(itemAttachment.getAttachmentId()); dto.setAttachmentId(itemAttachment.getAttachmentId());
dto.setInternalLink( dto.setInternalLink(
itemAttachment.getAsset() instanceof RelatedLink itemAttachment.getAsset() instanceof RelatedLink
&& ((RelatedLink) itemAttachment.getAsset()).getTargetItem() != null && ((RelatedLink) itemAttachment.getAsset()).getTargetItem()
!= null
); );
dto.setSortKey(itemAttachment.getSortKey()); dto.setSortKey(itemAttachment.getSortKey());
dto.setTitle( dto.setTitle(
@ -869,4 +1220,296 @@ public class RelatedInfoStep implements MvcAuthoringStep {
return dto; return dto;
} }
private AssetFolderRowModel buildAssetFolderRowModel(
final ContentSection section, final AssetFolderEntry entry
) {
Objects.requireNonNull(section);
Objects.requireNonNull(entry);
final AssetFolderRowModel row = new AssetFolderRowModel();
if (entry.isFolder()) {
final Folder folder = folderRepo
.findById(entry.getEntryId())
.get();
row.setDeletable(false);
row.setFolder(true);
row.setFolderPath(
folderManager
.getFolderPath(folder)
.substring(
folderManager
.getFolderPath(section.getRootAssetsFolder())
.length()
)
);
row.setName(entry.getDisplayName());
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
folder.getTitle()
)
);
row.setType(
globalizationHelper.getLocalizedTextsUtil(
"org.librecms.CmsAdminMessages"
).getText("contentsection.assetfolder.types.folder")
);
row.setPermissions(
assetPermissions.buildAssetPermissionsModel(folder)
);
} else {
final Asset asset = assetRepo
.findById(entry.getEntryId())
.get();
row.setDeletable(!assetManager.isAssetInUse(asset));
row.setFolder(false);
row.setName(entry.getDisplayName());
row.setNoneCmsObject(false);
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
asset.getTitle()
)
);
row.setType(asset.getClass().getName());
row.setPermissions(
assetPermissions.buildAssetPermissionsModel(asset)
);
}
return row;
}
private AssetFolderRowModel buildAssetFolderRowModel(
final ContentSection section, final Asset asset
) {
Objects.requireNonNull(section);
Objects.requireNonNull(asset);
final AssetFolderRowModel row = new AssetFolderRowModel();
row.setDeletable(false);
row.setFolder(false);
row.setName(asset.getDisplayName());
row.setNoneCmsObject(false);
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
asset.getTitle()
)
);
row.setType(asset.getClass().getName());
row.setPermissions(
assetPermissions.buildAssetPermissionsModel(asset)
);
return row;
}
private DocumentFolderRowModel buildDocumentFolderRowModel(
final ContentSection section, final DocumentFolderEntry entry
) {
Objects.requireNonNull(section);
Objects.requireNonNull(entry);
final DocumentFolderRowModel row = new DocumentFolderRowModel();
if (entry.isFolder()) {
final Folder folder = folderRepo
.findById(entry.getEntryId())
.get();
row.setCreated("");
row.setDeletable(
folderManager
.folderIsDeletable(folder)
== FolderManager.FolderIsDeletable.YES
);
row.setFolder(true);
row.setFolderPath(
folderManager
.getFolderPath(folder)
.substring(
folderManager
.getFolderPath(section.getRootDocumentsFolder())
.length()
)
);
row.setLanguages(Collections.emptySortedSet());
row.setLastEditPublished(false);
row.setLastEdited("");
row.setName(entry.getDisplayName());
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
folder.getTitle()
)
);
row.setType(
globalizationHelper.getLocalizedTextsUtil(
"org.librecms.CmsAdminMessages"
).getText("contentsection.documentfolder.types.folder")
);
row.setPermissions(
documentPermissions.buildDocumentPermissionsModel(folder)
);
} else {
final ContentItem contentItem = itemRepo
.findById(entry.getEntryId())
.get();
row.setCreated(
DateTimeFormatter.ISO_DATE.format(
LocalDate.ofInstant(
contentItem.getCreationDate().toInstant(),
ZoneId.systemDefault()
)
)
);
row.setDeletable(!itemManager.isLive(contentItem));
row.setFolder(false);
row.setFolderPath(itemManager.getItemPath(contentItem));
row.setLanguages(
new TreeSet<>(
itemL10NManager
.availableLanguages(contentItem)
.stream()
.map(Locale::toString)
.collect(Collectors.toSet())
)
);
if (itemManager.isLive(contentItem)) {
final LocalDate draftLastModified = LocalDate.ofInstant(
contentItem.getLastModified().toInstant(),
ZoneId.systemDefault()
);
final LocalDate liveLastModified = LocalDate.ofInstant(
itemManager
.getLiveVersion(contentItem, contentItem.getClass())
.map(ContentItem::getLastModified)
.map(Date::toInstant)
.get(),
ZoneId.systemDefault()
);
row.setLastEditPublished(
liveLastModified.isBefore(draftLastModified)
);
} else {
row.setLastEditPublished(false);
}
row.setLastEdited(
DateTimeFormatter.ISO_DATE.format(
LocalDate.ofInstant(
contentItem.getLastModified().toInstant(),
ZoneId.systemDefault()
)
)
);
row.setName(entry.getDisplayName());
row.setNoneCmsObject(false);
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
contentItem.getTitle()
)
);
row.setType(
contentTypeRepo
.findByContentSectionAndClass(
section, contentItem.getClass()
)
.map(ContentType::getLabel)
.map(
label -> globalizationHelper
.getValueFromLocalizedString(
label
)
).orElse("?")
);
row.setPermissions(
documentPermissions.buildDocumentPermissionsModel(
contentItem
)
);
}
return row;
}
private DocumentFolderRowModel buildDocumentFolderRowModel(
final ContentSection section, final ContentItem contentItem
) {
Objects.requireNonNull(section);
Objects.requireNonNull(contentItem);
final DocumentFolderRowModel row = new DocumentFolderRowModel();
row.setCreated(
DateTimeFormatter.ISO_DATE.format(
LocalDate.ofInstant(
contentItem.getCreationDate().toInstant(),
ZoneId.systemDefault()
)
)
);
row.setDeletable(!itemManager.isLive(contentItem));
row.setFolder(false);
row.setFolderPath(itemManager.getItemPath(contentItem));
row.setLanguages(
new TreeSet<>(
itemL10NManager
.availableLanguages(contentItem)
.stream()
.map(Locale::toString)
.collect(Collectors.toSet())
)
);
if (itemManager.isLive(contentItem)) {
final LocalDate draftLastModified = LocalDate.ofInstant(
contentItem.getLastModified().toInstant(),
ZoneId.systemDefault()
);
final LocalDate liveLastModified = LocalDate.ofInstant(
itemManager
.getLiveVersion(contentItem, contentItem.getClass())
.map(ContentItem::getLastModified)
.map(Date::toInstant)
.get(),
ZoneId.systemDefault()
);
row.setLastEditPublished(
liveLastModified.isBefore(draftLastModified)
);
} else {
row.setLastEditPublished(false);
}
row.setLastEdited(
DateTimeFormatter.ISO_DATE.format(
LocalDate.ofInstant(
contentItem.getLastModified().toInstant(),
ZoneId.systemDefault()
)
)
);
row.setName(contentItem.getDisplayName());
row.setNoneCmsObject(false);
row.setTitle(
globalizationHelper.getValueFromLocalizedString(
contentItem.getTitle()
)
);
row.setType(
contentTypeRepo
.findByContentSectionAndClass(
section, contentItem.getClass()
)
.map(ContentType::getLabel)
.map(
label -> globalizationHelper
.getValueFromLocalizedString(
label
)
).orElse("?")
);
row.setPermissions(
documentPermissions.buildDocumentPermissionsModel(
contentItem
)
);
return row;
}
} }

View File

@ -0,0 +1,103 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/documents/document.xhtml">
<ui:param name="activePage" value="document" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.documents.publishstep.title']}" />
<ui:define name="breadcrumb">
<ui:include src="document-breadcrumbs.xhtml" />
<li aria-current="page" class="breadcrumb-item">
#{CmsAdminMessages['contentsection.document.publishstep.title']}
</li>
</ui:define>
<ui:define name="authoringStep">
<h3>
#{CmsDefaultStepsMessageBundle.getMessage('relatedinfo.attachmentlist.details.title', [CmsAttachmentListDetailsModel.name])}
</h3>
<p>
<span>#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name.label']}: </span>
#{CmsAttachmentListDetailsModel.name}
<button class="btn btn-primary"
data-toggle="modal"
data-target="#name-edit-dialog"
type="button">
</button>
</p>
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title"
id="name-edit-dialog"
class="modal fade"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/@update"
class="modal-content"
method="post">
<div class="modal-header">
<h4>
#{CmsDefaultStepsMessageBundle.getMessage('relatedinfo.attachmentlist.details.name_edit_dialog.title', [CmsAttachmentListDetailsModel.name])}
</h4>
<button aria-label="#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name_edit_dialog.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</div>
<div class="modal-body">
<bootstrap:formGroupText
help="#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name_edit_dialog.name.help']}"
inputId="name"
label="#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name_edit_dialog.name.label']}"
name="listName"
required="true" />
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name_edit_dialog.close']}"
</button>
<button class="btn btn-success"
type="submit">
#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.name_edit_dialog.save']}"
</button>
</div>
</form>
</div>
</div>
<libreccm:localizedStringEditor addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/title/@add"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/title/@edit"
editorId="list-title-editor"
hasUnusedLocales="#{!CmsAttachmentListDetailsModel.unusedDescriptionLocales.isEmpty()}"
objectIdentifier="#{CmsAttachmentListDetailsModel.name}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/title/@remove"
title="CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.title_editor.title']}"
unusedLocales="#{CmsAttachmentListDetailsModel.unusedDescriptionLocales}"
values="#{CmsAttachmentListDetailsModel.titles}"
/>
<libreccm:localizedStringEditor addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/description/@add"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/description/@edit"
editorId="list-description-editor"
hasUnusedLocales="#{!CmsAttachmentListDetailsModel.unusedD.isEmpty()}"
objectIdentifier="#{CmsAttachmentListDetailsModel.name}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/description/@remove"
title="CmsDefaultStepsMessageBundle['relatedinfo.attachmentlist.details.description_editor.title']}"
unusedLocales="#{CmsAttachmentListDetailsModel.unusedTitleLocales}"
useTextarea="true"
values="#{CmsAttachmentListDetailsModel.descriptions}"
/>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/documents/document.xhtml">
<ui:param name="activePage" value="document" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.documents.publishstep.title']}" />
<ui:define name="breadcrumb">
<ui:include src="document-breadcrumbs.xhtml" />
<li aria-current="page" class="breadcrumb-item">
#{CmsAdminMessages['contentsection.document.publishstep.title']}
</li>
</ui:define>
<ui:define name="authoringStep">
<h3>
#{CmsDefaultStepsMessageBundle.getMessage('relatedinfo.internallink.details.title', [CmsInternalLinkDetailsModel.label])}
</h3>
<libreccm:localizedStringEditor addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}/internal-links/#{CmsInternalLinkDetailsModel.uuid}/title/@add"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}//internal-links/#{CmsInternalLinkDetailsModel.uuid}/title/@edit"
editorId="list-title-editor"
hasUnusedLocales="#{!CmsAttachmentListDetailsModel.unusedDescriptionLocales.isEmpty()}"
objectIdentifier="#{CmsAttachmentListDetailsModel.name}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringSteps/relatedinfo/attachmentlists/#{list.name}//internal-links/#{CmsInternalLinkDetailsModel.uuid}/title/@remove"
title="CmsDefaultStepsMessageBundle['relatedinfo.internallink.details.title_editor.title']}"
unusedLocales="#{CmsAttachmentListDetailsModel.unusedDescriptionLocales}"
values="#{CmsAttachmentListDetailsModel.titles}"
/>
</ui:define>
</ui:composition>
</html>

View File

@ -89,6 +89,13 @@
<bootstrap:svgIcon icon="plus-circle" /> <bootstrap:svgIcon icon="plus-circle" />
<span>#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlists.attachment.add.label']}</span> <span>#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlists.attachment.add.label']}</span>
</button> </button>
<button class="btn btn-primary"
data-target="#attachmentlist-#{list.name}-add-internallink-dialog"
data-toggle="modal"
type="button">
<bootstrap:svgIcon icon="link-45deg" />
<span>#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlists.internal_link.add.label']}</span>
</button>
</div> </div>
<div aria-hidden="true" <div aria-hidden="true"
aria-labelledby="attachmentlist-#{list.name}-add-attachment-dialog-title" aria-labelledby="attachmentlist-#{list.name}-add-attachment-dialog-title"
@ -119,6 +126,42 @@
</div> </div>
</div> </div>
<div aria-hidden="true"
aria-labelledby="attachmentlist-#{list.name}-add-internallink-dialog-title"
class="modal fade"
id="attachmentlist-#{list.name}-add-internallink-dialog"
tabindex="-1">
<div class="modal-dialog">
<form class="modal-content">
<div class="modal-header">
<h3 class="modal-title">
#{CmsDefaultStepsMessageBundle['relatedinfo.attachmentlists.internallink.add.dialog.title']}
</h3>
<button aria-label="#{CmsDefaultStepsMessageBundle['relatedinfo.internallink.add.dialog.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</div>
<div class="modal-body">
<div class="alert alert-info" role="alert">
Not implemented yet, requires JavaScript for good user experience.
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsDefaultStepsMessageBundle['relatedinfo.internallink.add.dialog.close']}
</button>
<button class="btn btn-success">
#{CmsDefaultStepsMessageBundle['relatedinfo.internallink.add.dialog.save']}
</button>
</div>
</form>
</div>
</div>
<!-- ToDo: Button and Dialog for adding internal links --> <!-- ToDo: Button and Dialog for adding internal links -->