From 2665ee2c61fda493195bbc46441bb9240200e281 Mon Sep 17 00:00:00 2001 From: jensp Date: Mon, 23 Oct 2017 07:18:38 +0000 Subject: [PATCH] CCM NG: ItemListComponent for the PageModel git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5072 8810af33-2d31-482b-a856-94f89814c4df --- .../librecms/pagemodel/ItemListComponent.java | 44 +-- .../pagemodel/ItemListComponentRenderer.java | 257 ++++++++++++++++++ 2 files changed, 280 insertions(+), 21 deletions(-) create mode 100644 ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentRenderer.java diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponent.java b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponent.java index e6bc4f5bf..6796800fc 100644 --- a/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponent.java +++ b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponent.java @@ -56,15 +56,17 @@ public class ItemListComponent extends ComponentModel { private boolean descending; /** - * Include only the types listed here into the list + * Include only items of the specified type into the list. This must be a + * subtype of {@link ContentItem}. */ - @ElementCollection - @CollectionTable(name = "ITEM_LIST_LIMIT_TO_TYPES", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "ITEM_LIST_ID") - }) - private Set limitToTypes; + @Column(name = "LIMIT_TO_TYPE") + private String limitToType; + + /** + * Maximum number of items shown on one page. + */ + @Column(name = "PAGE_SIZE") + private int pageSize; /** * Order the list by this properties. Warning: All items must have the @@ -87,22 +89,22 @@ public class ItemListComponent extends ComponentModel { this.descending = descending; } - public Set getLimitToTypes() { - return Collections.unmodifiableSet(limitToTypes); + public String getLimitToType() { + return limitToType; } - public void addLimitToTypes(final String type) { - limitToTypes.add(type); + public void setLimitToTypes(final String limitToType) { + this.limitToType = limitToType; } - public void removeLimitToType(final String type) { - limitToTypes.remove(type); + public int getPageSize() { + return pageSize; } - - public void setLimitToTypes(final Set limitToTypes) { - this.limitToTypes = new HashSet<>(limitToTypes); + + public void setPageSize(final int pageSize) { + this.pageSize = pageSize; } - + public List getListOrder() { return Collections.unmodifiableList(listOrder); } @@ -123,7 +125,7 @@ public class ItemListComponent extends ComponentModel { public int hashCode() { int hash = super.hashCode(); hash = 41 * hash + (descending ? 1 : 0); - hash = 41 * hash + Objects.hashCode(limitToTypes); + hash = 41 * hash + Objects.hashCode(limitToType); hash = 41 * hash + Objects.hashCode(listOrder); return hash; } @@ -152,7 +154,7 @@ public class ItemListComponent extends ComponentModel { if (descending != other.isDescending()) { return false; } - if (!Objects.equals(limitToTypes, other.getLimitToTypes())) { + if (!Objects.equals(limitToType, other.getLimitToType())) { return false; } return Objects.equals(listOrder, other.getListOrder()); @@ -169,7 +171,7 @@ public class ItemListComponent extends ComponentModel { + "limitToTypes = %s, " + "listOrder = %s%s", descending, - Objects.toString(limitToTypes), + limitToType, Objects.toString(listOrder), data)); } diff --git a/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentRenderer.java b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentRenderer.java new file mode 100644 index 000000000..4e6652a20 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pagemodel/ItemListComponentRenderer.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2017 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.pagemodel; + +import com.arsdigita.kernel.KernelConfig; + +import org.libreccm.categorization.Categorization; +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.core.UnexpectedErrorException; +import org.libreccm.pagemodel.ComponentModelType; + +import java.util.Map; + +import javax.enterprise.context.RequestScoped; + +import org.libreccm.pagemodel.ComponentRenderer; +import org.librecms.contentsection.ContentItem; +import org.librecms.pagemodel.contentitems.AbstractContentItemRenderer; +import org.librecms.pagemodel.contentitems.ContentItemRenderers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Root; +import javax.servlet.http.HttpServletRequest; + +import static org.librecms.pages.PagesConstants.*; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@ComponentModelType(componentModel = ItemListComponent.class) +public class ItemListComponentRenderer + implements ComponentRenderer { + + @Inject + private ConfigurationManager confManager; + + @Inject + private ContentItemRenderers itemRenderers; + + @Inject + private EntityManager entityManager; + + @Inject + private HttpServletRequest request; + + @Override + public Map renderComponent( + final ItemListComponent componentModel, + final Map parameters) { + + Objects.requireNonNull(componentModel); + Objects.requireNonNull(parameters); + + if (!parameters.containsKey(PARAMETER_CATEGORY)) { + throw new IllegalArgumentException( + "The parameters map passed to this GreetingItem component does " + + "not include the parameter \"category\""); + } + + if (!(parameters.get(PARAMETER_CATEGORY) instanceof Category)) { + throw new IllegalArgumentException(String + .format("The parameters map passed to this GreetingItem " + + "component contains the parameter \"category\", but the " + + "parameter is not of type \"%s\" but of type \"%s\".", + Category.class.getName(), + parameters.get(PARAMETER_CATEGORY).getClass().getName())); + } + + final Category category = (Category) parameters.get(PARAMETER_CATEGORY); + final Locale language; + if (parameters.containsKey(PARAMETER_LANGUAGE)) { + language = new Locale((String) parameters.get(PARAMETER_LANGUAGE)); + } else { + final KernelConfig kernelConfig = confManager + .findConfiguration(KernelConfig.class); + language = kernelConfig.getDefaultLocale(); + } + + final List categories; + if (componentModel.isDescending()) { + categories = collectCategories(category); + } else { + categories = new ArrayList<>(); + } + categories.add(category); + + final Class limitToType = getLimitToType( + componentModel); + + final List items = findItems( + limitToType, + categories, + componentModel.getListOrder(), + componentModel.getPageSize()); + + final Map result = new HashMap<>(); + result.put("items", + items + .stream() + .map(item -> renderItem(item, language)) + .collect(Collectors.toList())); + return result; + } + + private List collectCategories(final Category category) { + + if (category.getSubCategories().isEmpty()) { + return Collections.emptyList(); + } else { + final List categories = new ArrayList<>(); + for (final Category subCategory : category.getSubCategories()) { + categories.add(subCategory); + categories.addAll(collectCategories(subCategory)); + } + return categories; + } + } + + private List findItems( + final Class limitToType, + final List categories, + final List listOrder, + final int pageSize) { + + final CriteriaBuilder criteriaBuilder = entityManager + .getCriteriaBuilder(); + final CriteriaQuery criteriaQuery + = criteriaBuilder + .createQuery(limitToType); + final Root from = criteriaQuery + .from(limitToType); + final Join catJoin = from + .join("categories"); + + criteriaQuery.where(catJoin.get("category").in(categories)); + + criteriaQuery + .orderBy(listOrder + .stream() + .map(order -> createOrder(order, from, criteriaBuilder)) + .collect(Collectors.toList())); + + return entityManager + .createQuery(criteriaQuery) + .setFirstResult(getOffset(pageSize)) + .setMaxResults(pageSize) + .getResultList(); + } + + private Class getLimitToType( + final ItemListComponent componentModel) { + + final String className = componentModel.getLimitToType(); + + if (className == null + || className.matches("\\s*")) { + return ContentItem.class; + } else { + final Class clazz; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException ex) { + throw new UnexpectedErrorException(ex); + } + + if (ContentItem.class.isAssignableFrom(clazz)) { + @SuppressWarnings("unchecked") + final Class type + = (Class) clazz; + return type; + } else { + throw new UnexpectedErrorException(String + .format( + "The type \"%s\" set in ItemList is not a subtype of " + + "\"%s\".", + clazz.getName(), + ContentItem.class.getName())); + } + } + } + + private Order createOrder(final String order, + final Root from, + final CriteriaBuilder criteriaBuilder) { + + if (order.endsWith(" ASC")) { + final String colName = order + .substring(0, order.length() - " ASC".length()); + return (criteriaBuilder.asc(from.get(colName))); + } else if (order.endsWith(" DESC")) { + final String colName = order + .substring(0, order.length() - " DESC".length()); + return criteriaBuilder.desc(from.get(colName)); + } else { + return criteriaBuilder.asc(from.get(order)); + } + } + + private int getOffset(final int pageSize) { + + if (request.getParameterMap().containsKey("page")) { + final String value = request.getParameter("page"); + if (value.matches("\\d*")) { + final int page = Integer.valueOf(value); + + return page * pageSize; + } else { + return 0; + } + } else { + return 0; + } + } + + private Map renderItem(final ContentItem item, + final Locale language) { + + final AbstractContentItemRenderer renderer = itemRenderers + .findRenderer(item.getClass()); + return renderer.render(item, language); + } + +}