Model for the folder browser

Former-commit-id: 80b568cc28
pull/10/head
Jens Pelzetter 2021-01-21 21:31:36 +01:00
parent 78a0f86864
commit 6dcf94069e
8 changed files with 509 additions and 44 deletions

View File

@ -117,7 +117,7 @@ import static org.librecms.CmsConstants.*;
+ "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' "
+ "AND i.version = "
+ "org.librecms.contentsection.ContentItemVersion.DRAFT "
+ "AND (LOWER(i.displayName) LIKE LOWER(:term) "
+ "AND (LOWER(i.displayName) LIKE LOWER(CONCAT('%', :term)) "
// + "OR LOWER(i.name.values) LIKE LOWER(:term)"
+ ")) "
+ "ORDER BY o.displayName"
@ -148,7 +148,7 @@ import static org.librecms.CmsConstants.*;
+ "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' "
+ "AND i.version = "
+ "org.librecms.contentsection.ContentItemVersion.DRAFT "
+ "AND (LOWER(i.displayName) LIKE LOWER(:term) "
+ "AND (LOWER(i.displayName) LIKE LOWER(CONCAT('%', :term)) "
// + "OR LOWER(i.name.values) LIKE LOWER(:term)"
+ "))"
// query = "SELECT COUNT(o) FROM CcmObject o "

View File

@ -22,6 +22,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.Category;
import org.libreccm.core.AbstractEntityRepository;
import org.libreccm.core.CcmObject;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.Permission;
import org.libreccm.security.RequiresPrivilege;
@ -31,7 +32,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@ -225,6 +225,70 @@ public class FolderRepository extends AbstractEntityRepository<Long, Folder> {
return query.getResultList();
}
public List<CcmObject> findObjectsInFolder(final Folder folder) {
return findObjectsInFolder(folder, "");
}
public List<CcmObject> findObjectsInFolder(
final Folder folder,
final String term
) {
return getEntityManager()
.createNamedQuery("Folder.findObjects", CcmObject.class)
.setParameter(
"folder",
Objects.requireNonNull(
folder,
"Can't retrieve objects from null."
)
)
.setParameter("term", term)
.getResultList();
}
public List<CcmObject> findObjectsInFolder(
final Folder folder,
final int firstResult,
final int maxResults
) {
return findObjectsInFolder(
folder, firstResult, maxResults, ""
);
}
public List<CcmObject> findObjectsInFolder(
final Folder folder,
final int firstResult,
final int maxResults,
final String term
) {
return getEntityManager()
.createNamedQuery("Folder.findObjects", CcmObject.class)
.setParameter(
"folder",
Objects.requireNonNull(
folder,
"Can't retrieve objects from null."
)
)
.setParameter("term", "")
.setFirstResult(firstResult)
.setMaxResults(maxResults)
.getResultList();
}
public long countObjectsInFolder(final Folder folder) {
return countObjectsInFolder(folder, "");
}
public long countObjectsInFolder(final Folder folder, final String term) {
return getEntityManager()
.createNamedQuery("Folder.countObjects", Long.class)
.setParameter("folder", folder)
.setParameter("term", term)
.getSingleResult();
}
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
@Override

View File

@ -7,17 +7,42 @@ package org.librecms.ui;
import org.libreccm.api.Identifier;
import org.libreccm.api.IdentifierParser;
import org.libreccm.core.CcmObject;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionRepository;
import org.librecms.contentsection.ContentType;
import org.librecms.contentsection.ContentTypeManager;
import org.librecms.contentsection.ContentTypeRepository;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.FolderRepository;
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.Locale;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
/**
@ -31,16 +56,37 @@ public class ContentSectionController {
@Inject
private CmsAdminMessages cmsAdminMessages;
@Inject
private ContentItemManager itemManager;
@Inject
private ContentItemL10NManager itemL10NManager;
@Inject
private ContentSectionModel contentSectionModel;
@Inject
private ContentTypeManager contentTypeManager;
@Inject
private ContentTypeRepository contentTypeRepo;
@Inject
private FolderBrowserModel folderBrowserModel;
@Inject
private FolderManager folderManager;
@Inject
private FolderRepository folderRepo;
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private Models models;
@Inject
private ContentSectionRepository sectionRepo;
@ -50,8 +96,11 @@ public class ContentSectionController {
@GET
@Path("/folderbrowser")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String listItems(
@PathParam("sectionIdentifier") final String sectionIdentifier
@PathParam("sectionIdentifier") final String sectionIdentifier,
@QueryParam("firstResult") @DefaultValue("0") final int firstResult,
@QueryParam("maxResults") @DefaultValue("20") final int maxResults
) {
final Identifier identifier = identifierParser.parseIdentifier(
sectionIdentifier
@ -95,12 +144,160 @@ public class ContentSectionController {
);
break;
}
contentSectionModel.setSection(section);
folderBrowserModel.setSection(section);
final List<CcmObject> objects = folderRepo
.findObjectsInFolder(
section.getRootDocumentsFolder(), firstResult, maxResults
);
folderBrowserModel.setCount(
folderRepo.countObjectsInFolder(section.getRootDocumentsFolder())
);
folderBrowserModel.setFirstResult(firstResult);
folderBrowserModel.setMaxResults(maxResults);
folderBrowserModel.setRows(
objects
.stream()
.map(object -> buildRowModel(section, object))
.collect(Collectors.toList())
);
return "org/librecms/ui/content-section/folderbrowser.xhtml";
}
private FolderBrowserRowModel buildRowModel(
final ContentSection section, final CcmObject object
) {
Objects.requireNonNull(section);
Objects.requireNonNull(object);
if (object instanceof ContentItem) {
return buildRowModel(section, (ContentItem) object);
} else if (object instanceof Folder) {
return buildRowModel(section, (Folder) object);
} else {
final FolderBrowserRowModel row = new FolderBrowserRowModel();
row.setCreated("");
row.setDeletable(false);
row.setIsFolder(false);
row.setLanguages(Collections.emptySortedSet());
row.setLastEditPublished(false);
row.setLastEdited("");
row.setName(object.getDisplayName());
row.setTitle("");
row.setType(object.getClass().getSimpleName());
return row;
}
}
private FolderBrowserRowModel buildRowModel(
final ContentSection section, final ContentItem contentItem
) {
Objects.requireNonNull(section);
Objects.requireNonNull(contentItem);
final FolderBrowserRowModel row = new FolderBrowserRowModel();
row.setCreated(
DateTimeFormatter.ISO_DATE.format(
LocalDate.ofInstant(
contentItem.getCreationDate().toInstant(),
ZoneId.systemDefault()
)
)
);
row.setDeletable(!itemManager.isLive(contentItem));
row.setIsFolder(false);
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("?")
);
return row;
}
private FolderBrowserRowModel buildRowModel(
final ContentSection section, final Folder folder
) {
Objects.requireNonNull(section);
Objects.requireNonNull(folder);
final FolderBrowserRowModel row = new FolderBrowserRowModel();
row.setCreated("");
row.setDeletable(
folderManager.folderIsDeletable(folder)
== FolderManager.FolderIsDeletable.YES
);
row.setIsFolder(true);
row.setLanguages(Collections.emptySortedSet());
row.setLastEditPublished(false);
row.setLastEdited("");
row.setName(folder.getDisplayName());
row.setNoneCmsObject(false);
row.setTitle(
globalizationHelper.getValueFromLocalizedString(folder.getTitle())
);
row.setType(
globalizationHelper.getLocalizedTextsUtil(
"org.libreccms.CmsAdminMessages"
).getText("contentsection.folderbrowser.types.folder")
);
return row;
}
}

View File

@ -5,9 +5,10 @@
*/
package org.librecms.ui;
import org.librecms.contentsection.ContentSection;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Collections;
import java.util.List;
import javax.enterprise.context.RequestScoped;
@ -17,15 +18,45 @@ import javax.enterprise.context.RequestScoped;
*/
@RequestScoped
public class FolderBrowserModel {
private ContentSection section;
protected void setSection(final ContentSection section) {
this.section = Objects.requireNonNull(
section, "Parameter section can't be null"
);
private long count;
private int firstResult;
private int maxResults;
private List<FolderBrowserRowModel> rows;
public long getCount() {
return count;
}
public void setCount(final long count) {
this.count = count;
}
public int getFirstResult() {
return firstResult;
}
protected void setFirstResult(final int firstResult) {
this.firstResult = firstResult;
}
public int getMaxResults() {
return maxResults;
}
protected void setMaxResults(final int maxResults) {
this.maxResults = maxResults;
}
public List<FolderBrowserRowModel> getRows() {
return Collections.unmodifiableList(rows);
}
protected void setRows(final List<FolderBrowserRowModel> rows) {
this.rows = new ArrayList<>(rows);
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.Collections;
import java.util.SortedSet;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FolderBrowserRowModel {
private String created;
private boolean deletable;
private boolean isFolder;
private SortedSet<String> languages;
private String lastEdited;
private boolean lastEditPublished;
private String name;
private boolean noneCmsObject;
private String title;
private String type;
public String getCreated() {
return created;
}
protected void setCreated(final String created) {
this.created = created;
}
public boolean isDeletable() {
return deletable;
}
protected void setDeletable(final boolean deletable) {
this.deletable = deletable;
}
public boolean isIsFolder() {
return isFolder;
}
protected void setIsFolder(final boolean isFolder) {
this.isFolder = isFolder;
}
public SortedSet<String> getLanguages() {
return Collections.unmodifiableSortedSet(languages);
}
protected void setLanguages(final SortedSet<String> languages) {
this.languages = languages;
}
public String getLastEdited() {
return lastEdited;
}
protected void setLastEdited(final String lastEdited) {
this.lastEdited = lastEdited;
}
public boolean isLastEditPublished() {
return lastEditPublished;
}
protected void setLastEditPublished(final boolean lastEditPublished) {
this.lastEditPublished = lastEditPublished;
}
public String getName() {
return name;
}
protected void setName(final String name) {
this.name = name;
}
public boolean isNoneCmsObject() {
return noneCmsObject;
}
public void setNoneCmsObject(boolean noneCmsObject) {
this.noneCmsObject = noneCmsObject;
}
public String getTitle() {
return title;
}
protected void setTitle(final String title) {
this.title = title;
}
public String getType() {
return type;
}
protected void setType(final String type) {
this.type = type;
}
}

View File

@ -20,13 +20,55 @@
<div class="row">
<nav class="col-sm-3 folderbrowser">
<ul class="nav flex-column border">
<li class="nav-item">
<a class="nav-link" href="#">Folder 1</a>
<!-- <ul class="nav flex-column border">
<li class="nav-item">
<a class="nav-link" href="#">Folder 1</a>
</li>
<li class="nav-item">
<div class="d-flex">
<button class="btn btn-light pr-0 subfolders-toggler"
data-toggle="collapse"
data-target="#folder-2-subfolders"
aria-expanded="false"
aria-controls="folder-2-subfolders"
type="button">
<bootstrap:svgIcon icon="caret-right-fill" />
<span class="sr-only">
#{CmsAdminMessages['contentsection.folderbrowser.foldersnav.subfolders.expand']}
</span>
</button>
<a class="nav-link pl-0" href="#">Folder 2</a>
</div>
<ul class="nav flex-column collapse"
id="folder-2-subfolders">
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-2</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-3</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 3</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 4</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 5</a>
</li>
</ul>-->
<ul class="list-group">
<li class="list-group-item">
<a class="" href="#">Folder 1</a>
</li>
<li class="nav-item">
<li class="list-group-item">
<div class="d-flex">
<button class="btn btn-light pr-0 subfolders-toggler"
<button class="btn btn-light p-0 subfolders-toggler"
data-toggle="collapse"
data-target="#folder-2-subfolders"
aria-expanded="false"
@ -37,29 +79,29 @@
#{CmsAdminMessages['contentsection.folderbrowser.foldersnav.subfolders.expand']}
</span>
</button>
<a class="nav-link pl-0" href="#">Folder 2</a>
<a class="pl-0" href="#">Folder 2</a>
</div>
<ul class="nav flex-column collapse"
<ul class="border-0 collapse list-group "
id="folder-2-subfolders">
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-1</a>
<li class="border-0 list-group-item">
<a class="" href="#">Folder 2-1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-2</a>
<li class="border-0 list-group-item">
<a class="" href="#">Folder 2-2</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 2-3</a>
<li class="border-0 list-group-item">
<a class="" href="#">Folder 2-3</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 3</a>
<li class="list-group-item">
<a class="" href="#">Folder 3</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 4</a>
<li class="list-group-item">
<a class="" href="#">Folder 4</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Folder 5</a>
<li class="list-group-item">
<a class="" href="#">Folder 5</a>
</li>
</ul>
</nav>
@ -105,7 +147,13 @@
<td>An item</td>
<td>Article</td>
<td>2021-01-20 09:03</td>
<td>2021-01-20 10:11 (#{CmsAdminMessages['contentsection.folderbrowser.cols.lastedit.published']})</td>
<td>
<span>2021-01-20 10:11</span>
<span aria-describedby="an-item-publication-state-desc">
<bootstrap:svgIcon icon="eye" />
</span>
<span class="sr-only" id="an-item-publication-state-desc">#{CmsAdminMessages['contentsection.folderbrowser.cols.lastedit.published']}</span>
</td>
<td></td>
</tr>
<tr>
@ -114,7 +162,13 @@
<td>Unppublished item item</td>
<td>Article</td>
<td>2021-01-20 10:31</td>
<td>2021-01-20 10:34 (#{CmsAdminMessages['contentsection.folderbrowser.cols.lastedit.unpublished']})</td>
<td>
<span>2021-01-20 10:34</span>
<span aria-describedby="unpublished-item-publication-state-desc">
<bootstrap:svgIcon icon="eye-slash" />
</span>
<span class="sr-only" id="unpublished-item-publication-state-desc">#{CmsAdminMessages['contentsection.folderbrowser.cols.lastedit.unpublished']}</span>
</td>
<td>
<button class="btn btn-danger"
type="button">

View File

@ -37,3 +37,4 @@ contentsection.folderbrowser.cols.lastedit.published=Published
contentsection.folderbrowser.cols.lastedit.unpublished=Not published yet
contentsection.folderbrowser.actions.delete.button.label=Delete
contentsection.folderbrowser.foldersnav.subfolders.expand=Show subfolders of
org.libreccms.CmsAdminMessages=Folder

View File

@ -37,3 +37,4 @@ contentsection.folderbrowser.cols.lastedit.published=Publiziert
contentsection.folderbrowser.cols.lastedit.unpublished=Nicht nicht publiziert
contentsection.folderbrowser.actions.delete.button.label=L\u00f6schen
contentsection.folderbrowser.foldersnav.subfolders.expand=Unterordner anzeigen
org.libreccms.CmsAdminMessages=Ordner