DocumentFolderBrower view for content sections

Former-commit-id: e518759964
pull/10/head
Jens Pelzetter 2021-01-27 21:20:49 +01:00
parent 81a6e93ddb
commit 5b7ba6c146
15 changed files with 530 additions and 71 deletions

View File

@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.api.Identifier; import org.libreccm.api.Identifier;
import org.libreccm.api.IdentifierParser; import org.libreccm.api.IdentifierParser;
import org.libreccm.core.CcmObject;
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.PermissionChecker;
@ -26,12 +25,15 @@ import org.librecms.contentsection.DocumentFolderEntry;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager; import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.FolderRepository; import org.librecms.contentsection.FolderRepository;
import org.librecms.contentsection.FolderType;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.contenttypes.Article; import org.librecms.contenttypes.Article;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -111,11 +113,13 @@ public class ContentSectionController {
private PermissionChecker permissionChecker; private PermissionChecker permissionChecker;
@GET @GET
@Path("/document-folders") @Path("/document-folders{folderPath:(/.+)?}")
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String listItems( public String listItems(
@PathParam("sectionIdentifier") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("folderPath") final String folderPath,
@QueryParam("filterTerm") @DefaultValue("") final String filterTerm,
@QueryParam("firstResult") @DefaultValue("0") final int firstResult, @QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults @QueryParam("maxResults") @DefaultValue("20") final int maxResults
) { ) {
@ -155,22 +159,60 @@ public class ContentSectionController {
System.currentTimeMillis() - permissionCheckStart System.currentTimeMillis() - permissionCheckStart
); );
final Folder folder;
if (folderPath.isBlank()) {
folder = section.getRootDocumentsFolder();
documentFolderModel.setBreadcrumbs(Collections.emptyList());
} else {
final Optional<Folder> folderResult = folderRepo
.findByPath(section,
folderPath,
FolderType.DOCUMENTS_FOLDER
);
if (folderResult.isPresent()) {
folder = folderResult.get();
final List<DocumentFolderBreadcrumbModel> breadcrumbs
= new ArrayList<>();
final List<String> tokens = Arrays
.stream(folderPath.split("/"))
.filter(token -> !token.isEmpty())
.collect(Collectors.toList());
for (final String token : tokens) {
final String path = breadcrumbs
.stream()
.map(DocumentFolderBreadcrumbModel::getPathToken)
.collect(Collectors.joining("/"));
final DocumentFolderBreadcrumbModel breadcrumb
= new DocumentFolderBreadcrumbModel();
breadcrumb.setPath(path);
breadcrumb.setPathToken(token);
breadcrumbs.add(breadcrumb);
}
breadcrumbs
.get(breadcrumbs.size() - 1)
.setCurrentFolder(true);
documentFolderModel.setBreadcrumbs(breadcrumbs);
} else {
models.put("contentSection", section.getLabel());
models.put("folderPath", folderPath);
return "org/librecms/ui/content-section/document-folder-not-found.xhtml";
}
}
final long objectsStart = System.currentTimeMillis(); final long objectsStart = System.currentTimeMillis();
final List<DocumentFolderEntry> folderEntries = folderRepo final List<DocumentFolderEntry> folderEntries = folderRepo
.getDocumentFolderEntries( .getDocumentFolderEntries(
section.getRootDocumentsFolder(), folder,
firstResult, firstResult,
maxResults, maxResults,
"" filterTerm
); );
LOGGER.info( LOGGER.info(
"Retrieved objects in {} ms", "Retrieved objects in {} ms",
System.currentTimeMillis() - objectsStart System.currentTimeMillis() - objectsStart
); );
documentFolderModel.setCount( documentFolderModel.setCount(
folderRepo.countDocumentFolderEntries( folderRepo.countDocumentFolderEntries(folder, filterTerm)
section.getRootDocumentsFolder(), ""
)
); );
documentFolderModel.setFirstResult(firstResult); documentFolderModel.setFirstResult(firstResult);
documentFolderModel.setMaxResults(maxResults); documentFolderModel.setMaxResults(maxResults);
@ -179,6 +221,8 @@ public class ContentSectionController {
System.currentTimeMillis() - objectsStart System.currentTimeMillis() - objectsStart
); );
contentSectionModel.setFolders(buildFolderTree(section, folder));
final long rowsStart = System.currentTimeMillis(); final long rowsStart = System.currentTimeMillis();
documentFolderModel.setRows( documentFolderModel.setRows(
folderEntries folderEntries
@ -186,8 +230,10 @@ public class ContentSectionController {
.map(entry -> buildRowModel(section, entry)) .map(entry -> buildRowModel(section, entry))
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
LOGGER.info("Build rows in {} ms.", System.currentTimeMillis() LOGGER.info(
- rowsStart); "Build rows in {} ms.",
System.currentTimeMillis() - rowsStart
);
return "org/librecms/ui/content-section/document-folder.xhtml"; return "org/librecms/ui/content-section/document-folder.xhtml";
} else { } else {
@ -294,6 +340,60 @@ public class ContentSectionController {
} }
} }
private List<FolderTreeNode> buildFolderTree(
final ContentSection section, final Folder currentFolder
) {
final Folder root = section.getRootDocumentsFolder();
final String currentFolderPath = folderManager
.getFolderPath(currentFolder)
.substring(
folderManager
.getFolderPath(section.getRootDocumentsFolder())
.length() - 1
);
return root
.getSubFolders()
.stream()
.sorted()
.map(folder -> buildFolderTreeNode(section, currentFolderPath,
folder))
.collect(Collectors.toList());
}
private FolderTreeNode buildFolderTreeNode(
final ContentSection section,
final String currentFolderPath,
final Folder folder
) {
final String folderPath = folderManager
.getFolderPath(folder)
.substring(
folderManager
.getFolderPath(section.getRootDocumentsFolder())
.length() - 1
);
final FolderTreeNode node = new FolderTreeNode();
node.setFolderId(folder.getObjectId());
node.setUuid(folder.getUuid());
node.setName(folder.getName());
node.setOpen(currentFolderPath.startsWith(folderPath));
node.setSelected(currentFolderPath.equals(folderPath));
node.setSubFolders(
folder
.getSubFolders()
.stream()
.sorted()
.map(subFolder -> buildFolderTreeNode(section,
currentFolderPath,
subFolder))
.collect(Collectors.toList())
);
return node;
}
private DocumentFolderRowModel buildRowModel( private DocumentFolderRowModel buildRowModel(
final ContentSection section, final DocumentFolderEntry entry final ContentSection section, final DocumentFolderEntry entry
) { ) {
@ -312,6 +412,15 @@ public class ContentSectionController {
== FolderManager.FolderIsDeletable.YES == FolderManager.FolderIsDeletable.YES
); );
row.setFolder(true); row.setFolder(true);
row.setFolderPath(
folderManager
.getFolderPath(folder)
.substring(
folderManager
.getFolderPath(section.getRootDocumentsFolder())
.length()
)
);
row.setLanguages(Collections.emptySortedSet()); row.setLanguages(Collections.emptySortedSet());
row.setLastEditPublished(false); row.setLastEditPublished(false);
row.setLastEdited(""); row.setLastEdited("");

View File

@ -7,6 +7,9 @@ package org.librecms.ui;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -23,6 +26,8 @@ public class ContentSectionModel {
private ContentSection section; private ContentSection section;
private List<FolderTreeNode> folders;
protected void setSection(final ContentSection section) { protected void setSection(final ContentSection section) {
this.section = Objects.requireNonNull( this.section = Objects.requireNonNull(
section, "Parameter section can't be null" section, "Parameter section can't be null"
@ -36,4 +41,12 @@ public class ContentSectionModel {
.orElse(""); .orElse("");
} }
public List<FolderTreeNode> getFolders() {
return Collections.unmodifiableList(folders);
}
protected void setFolders(final List<FolderTreeNode> folders) {
this.folders = new ArrayList<>(folders);
}
} }

View File

@ -0,0 +1,46 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class DocumentFolderBreadcrumbModel {
private String pathToken;
private String path;
private boolean currentFolder;
public String getPathToken() {
return pathToken;
}
public void setPathToken(final String pathToken) {
this.pathToken = pathToken;
}
public String getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
}
public boolean isCurrentFolder() {
return currentFolder;
}
public void setCurrentFolder(final boolean currentFolder) {
this.currentFolder = currentFolder;
}
}

View File

@ -29,6 +29,8 @@ public class DocumentFolderModel {
private List<DocumentFolderRowModel> rows; private List<DocumentFolderRowModel> rows;
private List<DocumentFolderBreadcrumbModel> breadcrumbs;
public long getCount() { public long getCount() {
return count; return count;
} }
@ -41,6 +43,7 @@ public class DocumentFolderModel {
return firstResult; return firstResult;
} }
protected void setFirstResult(final int firstResult) { protected void setFirstResult(final int firstResult) {
this.firstResult = firstResult; this.firstResult = firstResult;
} }
@ -58,7 +61,7 @@ public class DocumentFolderModel {
} }
public long getCurrentPage() { public long getCurrentPage() {
return (long) Math.ceil((double) firstResult / maxResults ) + 1; return (long) Math.ceil((double) firstResult / maxResults) + 1;
} }
public List<DocumentFolderRowModel> getRows() { public List<DocumentFolderRowModel> getRows() {
@ -69,4 +72,15 @@ public class DocumentFolderModel {
this.rows = new ArrayList<>(rows); this.rows = new ArrayList<>(rows);
} }
public List<DocumentFolderBreadcrumbModel> getBreadcrumbs() {
return Collections.unmodifiableList(breadcrumbs);
}
public void setBreadcrumbs(
final List<DocumentFolderBreadcrumbModel> breadcrumbs
) {
this.breadcrumbs = new ArrayList<>(breadcrumbs);
}
} }

View File

@ -20,6 +20,8 @@ public class DocumentFolderRowModel {
private boolean folder; private boolean folder;
private String folderPath;
private SortedSet<String> languages; private SortedSet<String> languages;
private String lastEdited; private String lastEdited;
@ -54,6 +56,14 @@ public class DocumentFolderRowModel {
return folder; return folder;
} }
public String getFolderPath() {
return folderPath;
}
protected void setFolderPath(final String folderPath) {
this.folderPath = folderPath;
}
protected void setFolder(final boolean folder) { protected void setFolder(final boolean folder) {
this.folder = folder; this.folder = folder;
} }

View File

@ -0,0 +1,88 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui;
import java.util.List;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FolderTreeNode {
private long folderId;
private String uuid;
private String name;
private String path;
private List<FolderTreeNode> subFolders;
private boolean open;
private boolean selected;
public long getFolderId() {
return folderId;
}
public void setFolderId(final long folderId) {
this.folderId = folderId;
}
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 getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
}
public List<FolderTreeNode> getSubFolders() {
return subFolders;
}
public void setSubFolders(final List<FolderTreeNode> subFolders) {
this.subFolders = subFolders;
}
public boolean isOpen() {
return open;
}
public void setOpen(final boolean open) {
this.open = open;
}
public boolean isSelected() {
return selected;
}
public void setSelected(final boolean selected) {
this.selected = selected;
}
}

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
xmlns:cms="http://xmlns.jcp.org/jsf/composite/components/cms">
<cc:interface shortDescription="Component for nodes in a folder tree">
<cc:attribute name="basePath"
required="true"
shortDescription="Base path (mvc.basePath and contentsection" />
<cc:attribute name="collapsed"
required="true"
shortDescription="Is the folder collapsed?"
type="boolean" />
<cc:attribute name="name"
required="true"
shortDescription="The name of the folder." />
<cc:attribute name="path"
required="true"
shortDescription="The path of the folder." />
<cc:attribute name="selected"
required="true"
shortDescription="Is the folder selected?"
type="boolean" />
<cc:attribute name="subFolders"
required="false"
type="java.util.Collection" />
</cc:interface>
<cc:implementation>
<c:choose>
<c:when test="#{not empty subFolders}">
<li class="folder-tree-node list-group-item">
<div class="d-flex">
<button class="btn btn-light p-0 subfolders-toggler"
data-toggle="collapse"
data-target="##{cc.attrs.name}-subfolders"
aria-expanded="false"
aria-controls="##{cc.attrs.name}-subfolders"
type="button">
<span class="sr-only">#{CmsAdminMessages['contentsection.documentfolder.foldersnav.subfolders.expand']}
</span>
</button>
<a class="pl-0"
href="#{cc.attrs.basePath}/#{cc.attrs.path}">#{cc.attrs.name}</a>
</div>
<ul class="border-0 #{collapsed ? 'collapse' : 'collapse.show'} list-group"
id="##{cc.attrs.name}-subfolders">
<c:forEach items="#{cc.attrs.subFolders}"
var="subFolder">
<cms:treeNode basePath="#{cc.attrs.basePath}"
collapsed="#{!subFolder.open}"
name="#{subFolder.name}"
path="#{subFolder.path}"
selected="#{subFolder.selected}"
subFolders="#{subFolder.subFolders}" />
</c:forEach>
</ul>
</li>
</c:when>
<c:otherwise>
<li class="folder-tree-node list-group-item">
<a href="#{cc.attrs.href}">#{cc.attrs.name}</a>
</li>
</c:otherwise>
</c:choose>
</cc:implementation>
</html>

View File

@ -7,7 +7,7 @@
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml"> <ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml">
<ui:param name="activePage" value="folderBrowser" /> <ui:param name="activePage" value="folderBrowser" />
<ui:param name="title" value="#{CmsAdminMessages['folderbrowser.title']}" /> <ui:param name="title" value="#{CmsAdminMessages['contentsection.not_found.title']}" />
<ui:define name="breadcrumb"> <ui:define name="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
#{CmsAdminMessages['contentsections.list.label']} #{CmsAdminMessages['contentsections.list.label']}

View File

@ -26,10 +26,10 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link #{activePage == 'folderBrowser' ? 'active' : ''}" <a class="nav-link #{activePage == 'documentFolders' ? 'active' : ''}"
href='#{mvc.basePath}/#{ContentSectionModel.sectionName}/folderbrowser'> href='#{mvc.basePath}/#{ContentSectionModel.sectionName}/document-folders'>
<bootstrap:svgIcon icon="folder2-open" /> <bootstrap:svgIcon icon="folder2-open" />
<span>#{CmsAdminMessages['folderbrowser.title']}</span> <span>#{CmsAdminMessages['contentsection.documentfolder.title']}</span>
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -0,0 +1,27 @@
<!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/contentsection.xhtml">
<ui:param name="activePage" value="documentFolders" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.documentfolder.title']}" />
<ui:define name="breadcrumb">
<li class="breadcrumb-item">
#{CmsAdminMessages['contentsection.documentfolder.title']}
</li>
</ui:define>
<ui:define name="main">
<div class="container">
<div class="alert alert-warning">
#{CmsAdminMessages.getMessage("contentsections.documentfolder.not_found", [contentSection, folderPath])}
</div>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -2,27 +2,63 @@
<html xmlns="http://www.w3.org/1999/xhtml" <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap" xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm" xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml"> <ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml">
<ui:param name="activePage" value="folderBrowser" /> <ui:param name="activePage" value="documentFolders" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.documentfolder.title']}" /> <ui:param name="title" value="#{CmsAdminMessages['contentsection.documentfolder.title']}" />
<ui:define name="breadcrumb"> <ui:define name="breadcrumb">
<li class="breadcrumb-item"> <c:choose>
#{CmsAdminMessages['contentsections.list.label']} <c:when test="#{DocumentFolderModel.breadcrumbs.isEmpty()}">
<li aria-current="page" class="breadcrumb-item">
#{CmsAdminMessages['contentsection.documentfolder.title']}
</li> </li>
</c:when>
<c:otherwise>
<li class="breadcrumb-item">
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/document-folders">
#{CmsAdminMessages['contentsection.documentfolder.title']}
</a>
</li>
</c:otherwise>
</c:choose>
<c:forEach items="#{DocumentFolderModel.breadcrumbs}"
var="breadcrumb">
<c:choose>
<c:when test="#{breadcrumb.currentFolder}">
<li class="breadcrumb-item #{breadcrumb.currentFolder ? 'active' : ''}">
#{breadcrumb.pathToken}
</li>
</c:when>
<c:otherwise>
<li class="breadcrumb-item">
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/document-folders/#{breadcrumb.path}">#{breadcrumb.pathToken}</a>
</li>
</c:otherwise>
</c:choose>
</c:forEach>
</ui:define> </ui:define>
<ui:define name="main"> <ui:define name="main">
<div class="container-fluid"> <div class="container-fluid">
<h1>#{CmsAdminMessages.getMessage("contentsection.documentfolder.heading", [ContentSectionModel.sectionName])}</h1> <h1>#{CmsAdminMessages.getMessage("contentsection.documentfolder.heading", [ContentSectionModel.sectionName])}</h1>
<div class="row"> <div class="row">
<nav class="col-sm-3 documentfolder"> <nav class="col-sm-3 documentfolder foldertree">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <c:forEach items="#{ContentSectionModel.folders}"
var="folder">
<cms:treeNode basePath="#{mvc.basePath}/#{ContentSectionModel.sectionName}/document-folders"
collapsed="#{!folder.open}"
name="#{folder.name}"
path="#{folder.path}"
selected="#{folder.selected}"
subFolders="#{folder.subFolders}" />
</c:forEach>
<!-- <li class="list-group-item">
<a class="" href="#">Folder 1</a> <a class="" href="#">Folder 1</a>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
@ -33,7 +69,7 @@
aria-expanded="false" aria-expanded="false"
aria-controls="folder-2-subfolders" aria-controls="folder-2-subfolders"
type="button"> type="button">
<!--<bootstrap:svgIcon icon="caret-right-fill" />--> <bootstrap:svgIcon icon="caret-right-fill" />
<span class="sr-only"> <span class="sr-only">
#{CmsAdminMessages['contentsection.documentfolder.foldersnav.subfolders.expand']} #{CmsAdminMessages['contentsection.documentfolder.foldersnav.subfolders.expand']}
</span> </span>
@ -61,14 +97,43 @@
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<a class="" href="#">Folder 5</a> <a class="" href="#">Folder 5</a>
</li> </li>-->
</ul> </ul>
</nav> </nav>
<div class="col-sm-9"> <div class="col-sm-9">
<div class="d-flex"> <div class="d-flex justify-content-between mb-2">
<form action="#" class="form-inline mr-2" method="GET">
<div class="input-group">
<div class="input-group-prepend">
<label class="mr-2" for="document-folder-filter">
<span>#{CmsAdminMessages['contentsection.documentfolder.filter.label']}</span>
</label>
</div>
<input id="documentfolder-filter"
name="filterTerm"
type="text" />
<div class="input-group-append">
<button class="btn btn-secondary"
type="submit">
<bootstrap:svgIcon icon="search" />
<span class="sr-only">#{CmsAdminMessages['contentsection.documentfolder.filter.submit']}</span>
</button>
</div>
</div>
</form>
<p> <p>
${CmsAdminMessages.getMessage("contentsection.documentfolder.pageof", [DocumentFolderModel.currentPage, DocumentFolderModel.numberOfPages, DocumentFolderModel.maxResults, DocumentFolderModel.count])} ${CmsAdminMessages.getMessage("contentsection.documentfolder.pageof", [DocumentFolderModel.currentPage, DocumentFolderModel.numberOfPages])}
</p> </p>
<div>
<button class="btn btn-primary" title="#{CmsAdminMessages['contentsection.documentfolder.add_subfolder']}">
<bootstrap:svgIcon icon="folder-plus" />
<span class="sr-only">#{CmsAdminMessages['contentsection.documentfolder.add_subfolder']}</span>
</button>
<button class="btn btn-primary" title="#{CmsAdminMessages['contentsection.documentfolder.add_document']}">
<bootstrap:svgIcon icon="file-earmark-plus" />
<span class="sr-only">#{CmsAdminMessages['contentsection.documentfolder.add_document']}</span>
</button>
</div>
</div> </div>
<table class="table table-hover documentfolder"> <table class="table table-hover documentfolder">
<thead class="thead-light"> <thead class="thead-light">
@ -100,7 +165,16 @@
<c:forEach items="#{DocumentFolderModel.rows}" <c:forEach items="#{DocumentFolderModel.rows}"
var="row"> var="row">
<tr> <tr>
<td><a href="#">#{row.name}</a></td> <td>
<c:choose>
<c:when test="#{row.folder}">
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/document-folders/#{row.folderPath}">#{row.name}</a>
</c:when>
<c:otherwise>
<a href="#">#{row.name}</a>
</c:otherwise>
</c:choose>
</td>
<td> <td>
#{row.languagesAsString} #{row.languagesAsString}
</td> </td>

View File

@ -7,7 +7,7 @@
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml"> <ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml">
<ui:param name="activePage" value="folderBrowser" /> <ui:param name="activePage" value="folderBrowser" />
<ui:param name="title" value="#{CmsAdminMessages['folderbrowser.title']}" /> <ui:param name="title" value="Test data generator" />
<ui:define name="breadcrumb"> <ui:define name="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
#{CmsAdminMessages['contentsections.list.label']} #{CmsAdminMessages['contentsections.list.label']}

View File

@ -24,7 +24,7 @@ contentsections.edit_dialog.close=Cancel
contentsections.edit_dialog.name.label=Name contentsections.edit_dialog.name.label=Name
contentsections.edit_dialog.save=Rename content section contentsections.edit_dialog.save=Rename content section
contentsections.edit_dialog.name.help=The name of the content section. Can only contain the letters a to z, the numbers 0-9, the hyphen and the underscore. contentsections.edit_dialog.name.help=The name of the content section. Can only contain the letters a to z, the numbers 0-9, the hyphen and the underscore.
contentsection.documentfolder.title=Document Folders contentsection.documentfolder.title=Documents
contentsection.documentfolder.heading=Content Section {0} Documents Folder contentsection.documentfolder.heading=Content Section {0} Documents Folder
contentsection.documentfolder.headers.name.label=Name contentsection.documentfolder.headers.name.label=Name
contentsection.documentfolder.headers.languages.label=Languages contentsection.documentfolder.headers.languages.label=Languages
@ -44,4 +44,10 @@ contentsection.documentfolder.pagination.next_page=Next page
contentsection.not_found=No content section identifed by {0} available. contentsection.not_found=No content section identifed by {0} available.
contentsection.accessdenied=Your are not permitted to access content section {0}. contentsection.accessdenied=Your are not permitted to access content section {0}.
contentsection.documentfolder.types.folder=Folder contentsection.documentfolder.types.folder=Folder
contentsection.documentfolder.pageof=Page {0} of {1}. Showing max. {2} of {3} documents per page. contentsection.documentfolder.pageof=Page {0} of {1}.
contentsection.documentfolder.add_document=Create document
contentsection.documentfolder.add_subfolder=Add subfolder
contentsection.documentfolder.filter.label=Filter documents
contentsection.documentfolder.filter.submit=Apply filter
contentsections.documentfolder.not_found=Not folder with path {1} found in content section {0}.
contentsection.not_found.title=Content Section not found

View File

@ -24,7 +24,7 @@ contentsections.edit_dialog.close=Abbrechen
contentsections.edit_dialog.name.label=Name contentsections.edit_dialog.name.label=Name
contentsections.edit_dialog.save=Content Section umbenennen contentsections.edit_dialog.save=Content Section umbenennen
contentsections.edit_dialog.name.help=Der Name der Content Section. Darf nur die Zeichen a bis z, 0-9, the Bindestrich und den Unterstrich enthalten. contentsections.edit_dialog.name.help=Der Name der Content Section. Darf nur die Zeichen a bis z, 0-9, the Bindestrich und den Unterstrich enthalten.
contentsection.documentfolder.title=Dokumenten-Ordner contentsection.documentfolder.title=Dokumente
contentsection.documentfolder.heading=Content Section {0} Dokumenten-Ordner contentsection.documentfolder.heading=Content Section {0} Dokumenten-Ordner
contentsection.documentfolder.headers.name.label=Name contentsection.documentfolder.headers.name.label=Name
contentsection.documentfolder.headers.languages.label=Sprachen contentsection.documentfolder.headers.languages.label=Sprachen
@ -44,4 +44,10 @@ contentsection.documentfolder.pagination.next_page=Eine Seite weiter
contentsection.not_found=Keine Content Section mit dem Identifier {0} gefunden. contentsection.not_found=Keine Content Section mit dem Identifier {0} gefunden.
contentsection.accessdenied=Sind sind nicht berechtigt auf die Content Section {0} zuzugreifen. contentsection.accessdenied=Sind sind nicht berechtigt auf die Content Section {0} zuzugreifen.
contentsection.documentfolder.types.folder=Ordner contentsection.documentfolder.types.folder=Ordner
contentsection.documentfolder.pageof=Seite {0} von {1}. Zeige max. {2} von {3} Dokumenten pro Seite. contentsection.documentfolder.pageof=Seite {0} von {1}.
contentsection.documentfolder.add_document=Neues Dokument erstellen
contentsection.documentfolder.add_subfolder=Unterordner erstellen
contentsection.documentfolder.filter.label=Dokumente filtern
contentsection.documentfolder.filter.submit=Filter anwenden
contentsections.documentfolder.not_found=Es wurde kein Ordner mit dem Pfad {1} in der Content Section {0} gefunden.
contentsection.not_found.title=Content Section nicht gefunden

View File

@ -38,7 +38,7 @@ $pre-scrollable-max-height: 21.25rem;
// Navbar default colors have insufficient contrast // Navbar default colors have insufficient contrast
$navbar-dark-color: #fff; $navbar-dark-color: #fff;
nav.folderbrowser { nav.foldertree {
& > ul.nav { & > ul.nav {
margin-left: 1em; margin-left: 1em;
margin-right: 1em; margin-right: 1em;