Opitimizations of models for MVC based pages application.

pull/10/head
Jens Pelzetter 2021-12-04 17:52:31 +01:00
parent 23f1fb1992
commit 4f48eb9354
8 changed files with 706 additions and 89 deletions

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<title>LibreCCM Devel Theme</title>
</head>
<body>
<h1>LibreCCM Devel</h1>
<h2>Site Info</h2>
<dl>
<dt>host</dt>
<dd>${CmsPagesSiteInfoModel.host}</dd>
<dt>domain</dt>
<dd>${CmsPagesSiteInfoModel.domain}</dd>
<dt>name</dt>
<dd>${CmsPagesSiteInfoModel.name}</dd>
<dt>Current category</dt>
<dd>${CmsPagesCategoryModel.category.name}</dd>
</dl>
<h2>Category Tree</h2>
<ul>
<!--<#list CmsPagesCategoryModel.categoryTree.subCategories as category>
<@categoryTreeNode category />
</#list>-->
<@categoryTreeNode CmsPagesCategoryModel.categoryTree />
</ul>
<h2>(Index) Item</h2>
<#if (CmsPagesCategoryModel.category.hasIndexItem)>
<dl>
<dt>Name</dt>
<dd>${CmsPagesCategorizedItemModel.name}</dd>
<dt>Title</dt>
<dd>${CmsPagesCategorizedItemModel.title}</dd>
<dt>Description</dt>
<dd>${CmsPagesCategorizedItemModel.description}</dd>
</dl>
<#else>
<p>No (Index) Item</p>
</#if>
<h2>Item List</h2>
<ul>
<#list CmsPagesItemListModel.items as item>
<li>
<b>${item.title} (${item.name})</b>
${item.description}
</li>
</#list>
</ul>
</body>
</html>
<#macro categoryTreeNode category>
<li>
${category.name}
<#if (category.subCategories?size > 0)>
<ul>
<#list category.subCategories as subCategory>
<@categoryTreeNode subCategory />
</#list>
</ul>
</#if>
</li>
</#macro>

View File

@ -195,7 +195,7 @@ public class PagesController {
@Inject
private PagePropertiesModel pagePropertiesModel;
@Inject
private PagesRepository pagesRepo;
@ -330,20 +330,45 @@ public class PagesController {
@DefaultValue("")
final String preview
) {
final Versions versions = generateFromPreviewParam(preview);
globalizationHelper.setSelectedLocale(new Locale(language));
contentItemModel.setItemName(itemName);
contentItemModel.setItemVersion(versions.getContentItemVersion());
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/");
final Page page = pageManager.findPageForCategory(category);
pagePropertiesModel.setProperties(page.getProperties());
return themesMvc.getMvcTemplate(
uriInfo, "pages", page.getDisplayName()
// final Versions versions = generateFromPreviewParam(preview);
//
// globalizationHelper.setSelectedLocale(new Locale(language));
//
// contentItemModel.setItemName(itemName);
// contentItemModel.setItemVersion(versions.getContentItemVersion());
//
// final String domain = uriInfo.getBaseUri().getHost();
// final Pages pages = getPages(domain);
// final Site site = pages.getSite();
// siteInfoModel.setAvailableLanguages(
// confManager
// .findConfiguration(KernelConfig.class)
// .getSupportedLanguages()
// .stream()
// .sorted()
// .collect(Collectors.toList())
// );
// siteInfoModel.setDomain(site.getDomainOfSite());
// siteInfoModel.setHost(domain);
// siteInfoModel.setName(
// Optional
// .ofNullable(site.getDisplayName())
// .orElse("")
// );
// final Category category = getCategory(domain, pages, "/");
// categoryModel.init(pages.getCategoryDomain(), category);
// final Page page = pageManager.findPageForCategory(category);
// pagePropertiesModel.setProperties(page.getProperties());
// return themesMvc.getMvcTemplate(
// uriInfo, "pages", page.getDisplayName()
// );
return getPageAsHtml(
uriInfo,
"/",
itemName,
language,
theme,
preview
);
}
@ -439,15 +464,15 @@ public class PagesController {
final String redirectTo;
if (uriInfo.getPath().endsWith("/")) {
redirectTo = String.format(
"%sindex.%s.html%s",
uriInfo.getPath(),
"%sindex.%s.html%s",
uriInfo.getPath(),
language,
buildQueryParamsStr(preview, theme)
);
} else {
final String itemPath = String.format(
"%s.%s.html%s",
itemName,
"%s.%s.html%s",
itemName,
language,
buildQueryParamsStr(preview, theme)
);
@ -492,13 +517,33 @@ public class PagesController {
final String preview
) {
final Versions versions = generateFromPreviewParam(preview);
globalizationHelper.setSelectedLocale(new Locale(language));
contentItemModel.setItemName(itemName);
final ContentItemVersion version = versions.getContentItemVersion();
contentItemModel.setItemVersion(versions.getContentItemVersion());
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Site site = pages.getSite();
siteInfoModel.setAvailableLanguages(
confManager
.findConfiguration(KernelConfig.class)
.getSupportedLanguages()
.stream()
.sorted()
.collect(Collectors.toList())
);
siteInfoModel.setDomain(site.getDomainOfSite());
siteInfoModel.setHost(domain);
siteInfoModel.setName(
Optional
.ofNullable(site.getDisplayName())
.orElse("")
);
final Category category = getCategory(domain, pages, pagePath);
categoryModel.init(pages.getCategoryDomain(), category, version);
final Page page = pageManager.findPageForCategory(category);
pagePropertiesModel.setProperties(page.getProperties());
return themesMvc.getMvcTemplate(
@ -506,6 +551,26 @@ public class PagesController {
);
}
private void initSiteInfoModel(
final Site site, final String host
) {
siteInfoModel.setAvailableLanguages(
confManager
.findConfiguration(KernelConfig.class)
.getSupportedLanguages()
.stream()
.sorted()
.collect(Collectors.toList())
);
siteInfoModel.setDomain(site.getDomainOfSite());
siteInfoModel.setHost(host);
siteInfoModel.setName(
Optional
.ofNullable(site.getDisplayName())
.orElse("")
);
}
private Site getSite(final UriInfo uriInfo) {
final String domain = Objects
.requireNonNull(uriInfo)
@ -695,48 +760,6 @@ public class PagesController {
}
private Page findPage(
final UriInfo uriInfo, final String pagePath, final String language
) {
Objects.requireNonNull(uriInfo);
Objects.requireNonNull(pagePath);
final Site site = getSite(uriInfo);
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, pagePath);
// disabled. Needs to be decided if the available languages of the
// index item or of the category are
// used to decide if a NotFoundException is thrown.
// if (!category.getTitle().hasValue(locale)) {
// throw new NotFoundException();
// }
final Locale locale = new Locale(language);
globalizationHelper.setSelectedLocale(locale);
final KernelConfig kernelConfig = confManager
.findConfiguration(KernelConfig.class);
siteInfoModel.setAvailableLanguages(
kernelConfig
.getSupportedLanguages()
.stream()
.sorted()
.collect(Collectors.toList())
);
siteInfoModel.setDomain(site.getDomainOfSite());
siteInfoModel.setHost(uriInfo.getBaseUri().getHost());
siteInfoModel.setName(site.getDisplayName());
categoryModel.setCategory(category);
categoryModel.setCategoryPath(
categoryManager.getCategoryPath(category)
);
return pageManager.findPageForCategory(category);
}
/**
* Parse the value of the {@code preview} query parameter.
*

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.pages.models;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class CategoryData {
private long categoryId;
private String uuid;
private String uniqueId;
private String name;
private String path;
private String title;
private String description;
private boolean enabled;
private boolean visible;
private boolean abstractCategory;
private boolean hasIndexItem;
CategoryData() {
super();
}
public long getCategoryId() {
return categoryId;
}
protected void setCategoryId(final long categoryId) {
this.categoryId = categoryId;
}
public String getUuid() {
return uuid;
}
protected void setUuid(final String uuid) {
this.uuid = uuid;
}
public String getUniqueId() {
return uniqueId;
}
protected void setUniqueId(final String uniqueId) {
this.uniqueId = uniqueId;
}
public String getName() {
return name;
}
protected void setName(final String name) {
this.name = name;
}
public String getTitle() {
return title;
}
protected void setTitle(final String title) {
this.title = title;
}
public String getDescription() {
return description;
}
protected void setDescription(final String description) {
this.description = description;
}
public boolean isEnabled() {
return enabled;
}
protected void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isVisible() {
return visible;
}
protected void setVisible(final boolean visible) {
this.visible = visible;
}
public boolean isAbstractCategory() {
return abstractCategory;
}
protected void setAbstractCategory(final boolean abstractCategory) {
this.abstractCategory = abstractCategory;
}
public String getPath() {
return path;
}
protected void setPath(final String path) {
this.path = path;
}
public boolean getHasIndexItem() {
return hasIndexItem;
}
protected void setHasIndexItem(final boolean hasIndexItem) {
this.hasIndexItem = hasIndexItem;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.pages.models;
import org.libreccm.categorization.Domain;
/**
* Model for the current category system/domain.
*
* @see CategoryModel
* @see Domain
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class CategoryDomainData {
private long domainId;
private String uuid;
private String domainKey;
private String uri;
private String title;
private String description;
CategoryDomainData() {
super();
}
public long getDomainId() {
return domainId;
}
protected void setDomainId(final long domainId) {
this.domainId = domainId;
}
public String getUuid() {
return uuid;
}
protected void setUuid(final String uuid) {
this.uuid = uuid;
}
public String getDomainKey() {
return domainKey;
}
protected void setDomainKey(final String domainKey) {
this.domainKey = domainKey;
}
public String getUri() {
return uri;
}
protected void setUri(final String uri) {
this.uri = uri;
}
public String getTitle() {
return title;
}
protected void setTitle(final String title) {
this.title = title;
}
public String getDescription() {
return description;
}
protected void setDescription(final String description) {
this.description = description;
}
}

View File

@ -19,17 +19,26 @@
package org.librecms.pages.models;
import org.libreccm.categorization.Category;
import org.librecms.pages.PagesController;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.categorization.Domain;
import org.libreccm.categorization.DomainRepository;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.PagesService;
import java.io.Serializable;
import java.util.Comparator;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.transaction.Transactional;
/**
* Model for MVC applications providing access to the current category. This
* model MUST be initalized by the calling application (for example
* {@link PagesController} with the current category.
* model MUST be initalized by the calling application using {@link #init(org.libreccm.categorization.Domain, org.libreccm.categorization.Category)).
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ -39,25 +48,140 @@ public class CategoryModel implements Serializable {
private static final long serialVersionUID = 1L;
private Category category;
private String categoryPath;
@Inject
private CategoryManager categoryManager;
public Category getCategory() {
@Inject
private DomainRepository domainRepository;
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private PagesService pagesService;
private CategoryDomainData domain;
private CategoryData category;
/**
* Controllers must call this method to initalize the this model.
*
* @param domain The current category domain.
* @param category The current category.
* @param version
*/
@Transactional(Transactional.TxType.REQUIRED)
public void init(
final Domain domain,
final Category category,
final ContentItemVersion version
) {
this.domain = new CategoryDomainData();
this.domain.setDescription(
globalizationHelper.getValueFromLocalizedString(
domain.getDescription()
)
);
this.domain.setDomainId(domain.getObjectId());
this.domain.setDomainKey(domain.getDomainKey());
this.domain.setTitle(
globalizationHelper.getValueFromLocalizedString(
domain.getTitle()
)
);
this.domain.setUri(domain.getUri());
this.domain.setUuid(domain.getUuid());
this.category = new CategoryData();
this.category.setAbstractCategory(category.isAbstractCategory());
this.category.setDescription(
globalizationHelper.getValueFromLocalizedString(
category.getDescription()
));
this.category.setEnabled(category.isEnabled());
this.category.setCategoryId(category.getObjectId());
this.category.setName(category.getName());
this.category.setPath(categoryManager.getCategoryPath(category));
this.category.setTitle(
globalizationHelper.getValueFromLocalizedString(
category.getTitle()
));
this.category.setUniqueId(category.getUniqueId());
this.category.setUuid(category.getUuid());
this.category.setVisible(category.isVisible());
this.category.setHasIndexItem(
//pagesService.findIndexItem(category, version).isPresent()
categoryManager.hasIndexObject(category)
);
}
public CategoryDomainData getDomain() {
return domain;
}
public CategoryData getCategory() {
return category;
}
public void setCategory(final Category category) {
this.category = category;
@Transactional(Transactional.TxType.REQUIRED)
public CategoryTreeNode getCategoryTree() {
final Domain currentDomain = domainRepository
.findById(domain.getDomainId())
.orElseThrow(
() -> new RuntimeException(
String.format(
"The category domain with the ID %d was set as current "
+ "category domain, but there is such domain in "
+ "the database.",
domain.getDomainId()
)
)
);
return buildTreeNode(currentDomain.getRoot(), null);
}
public String getCategoryPath() {
return categoryPath;
private CategoryTreeNode buildTreeNode(
final Category fromCategory,
final CategoryTreeNode parent
) {
final CategoryTreeNode node = new CategoryTreeNode();
node.setCategoryPath(categoryManager.getCategoryPath(fromCategory));
node.setDescription(
globalizationHelper.getValueFromLocalizedString(
fromCategory.getDescription())
);
node.setName(fromCategory.getName());
node.setSelected(fromCategory.getUuid().equals(category.getUuid()));
node.setParentCategory(parent);
node.setSubCategories(
fromCategory
.getSubCategories()
.stream()
.sorted(Comparator.comparing(Category::getCategoryOrder))
.map(cat -> buildTreeNode(cat, node))
.collect(Collectors.toList())
);
node.setTitle(
globalizationHelper.getValueFromLocalizedString(
fromCategory.getTitle()
)
);
node.setUuid(fromCategory.getUuid());
if (node.isSelected() && node.getParentCategory() != null) {
setSubCategoryCategorySelected(node.getParentCategory());
}
return node;
}
public void setCategoryPath(final String categoryPath) {
this.categoryPath = categoryPath;
private void setSubCategoryCategorySelected(final CategoryTreeNode node) {
node.setSubCategorySelected(true);
if (node.getParentCategory() != null) {
setSubCategoryCategorySelected(node.getParentCategory());
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.pages.models;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class CategoryTreeNode {
private String uuid;
private String name;
private String title;
private String description;
private String categoryPath;
private boolean selected;
private boolean subCategorySelected;
private CategoryTreeNode parentCategory;
private List<CategoryTreeNode> subCategories;
public String getUuid() {
return uuid;
}
protected void setUuid(final String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
protected void setName(final String name) {
this.name = name;
}
public String getTitle() {
return title;
}
protected void setTitle(final String title) {
this.title = title;
}
public String getDescription() {
return description;
}
protected void setDescription(final String description) {
this.description = description;
}
public String getCategoryPath() {
return categoryPath;
}
protected void setCategoryPath(final String categoryPath) {
this.categoryPath = categoryPath;
}
public List<CategoryTreeNode> getSubCategories() {
return Collections.unmodifiableList(subCategories);
}
protected void setSubCategories(
final List<CategoryTreeNode> subCategories
) {
this.subCategories = new ArrayList<>(subCategories);
}
public boolean isSelected() {
return selected;
}
protected void setSelected(boolean selected) {
this.selected = selected;
}
public CategoryTreeNode getParentCategory() {
return parentCategory;
}
protected void setParentCategory(final CategoryTreeNode parentCategory) {
this.parentCategory = parentCategory;
}
public boolean isSubCategorySelected() {
return subCategorySelected;
}
protected void setSubCategorySelected(final boolean subCategorySelected) {
this.subCategorySelected = subCategorySelected;
}
}

View File

@ -21,11 +21,11 @@ package org.librecms.pages.models;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.AttachmentList;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.PagesRouter;
import org.librecms.pages.PagesService;
import java.time.ZoneId;
@ -59,6 +59,9 @@ public class ContentItemModel {
@Inject
private CategoryManager categoryManager;
@Inject
private CategoryRepository categoryRepository;
@Inject
private CategoryModel categoryModel;
@ -217,11 +220,36 @@ public class ContentItemModel {
private void retrieveContentItem() {
if (itemName == null) {
contentItem = pagesService.findIndexItem(
categoryModel.getCategory(), itemVersion
categoryRepository
.findById(categoryModel.getCategory().getCategoryId())
.orElseThrow(
() -> new RuntimeException(
String.format(
"The category with the ID %d is set as current "
+ "category, but not category with that ID "
+ "can be found in the database.",
categoryModel.getCategory().getCategoryId()
)
)
),
itemVersion
);
} else {
contentItem = pagesService.findCategorizedItem(
categoryModel.getCategory(), itemName, itemVersion
categoryRepository
.findById(categoryModel.getCategory().getCategoryId())
.orElseThrow(
() -> new RuntimeException(
String.format(
"The category with the ID %d is set as current "
+ "category, but not category with that ID "
+ "can be found in the database.",
categoryModel.getCategory().getCategoryId()
)
)
),
itemName,
itemVersion
);
}
}

View File

@ -22,6 +22,7 @@ import com.arsdigita.kernel.KernelConfig;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper;
@ -54,6 +55,7 @@ import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
@ -68,6 +70,9 @@ public class ItemListModel {
@Inject
private CategoryModel categoryModel;
@Inject
private CategoryRepository categoryRepository;
@Inject
private ConfigurationManager confManager;
@ -111,7 +116,7 @@ public class ItemListModel {
public ItemListModel() {
descending = false;
limitToType = ContentItem.class.getName();
listOrder = List.of(ContentItem.class.getName());
listOrder = List.of("displayName");
pageSize = 20;
}
@ -155,11 +160,25 @@ public class ItemListModel {
return getItems().size();
}
@Transactional(Transactional.TxType.REQUIRED)
public List<? extends AbstractContentItemListItemModel> getItems() {
if (itemList == null) {
buildList(
buildLimitToType(limitToType),
collectCategories(categoryModel.getCategory()),
collectCategories(
categoryRepository
.findById(categoryModel.getCategory().getCategoryId())
.orElseThrow(
() -> new RuntimeException(
String.format(
"Category with ID %d was set as current "
+ "category, but no category with "
+ "that ID was found in the database.",
categoryModel.getCategory().getCategoryId()
)
)
)
),
listOrder,
pageSize
);
@ -205,7 +224,7 @@ public class ItemListModel {
criteriaQuery.orderBy(
listOrder
.stream()
.map(order -> builderOrder(order, from, criteriaBuilder))
.map(order -> buildOrder(order, from, criteriaBuilder))
.collect(Collectors.toList())
);
@ -225,17 +244,15 @@ public class ItemListModel {
}
private List<Category> collectCategories(final Category category) {
if (category.getSubCategories().isEmpty()) {
return Collections.emptyList();
} else {
final List<Category> categories = new ArrayList<>();
final List<Category> categories = new ArrayList<>();
categories.add(category);
if (!category.getSubCategories().isEmpty()) {
for (final Category subCategory : category.getSubCategories()) {
categories.add(subCategory);
categories.addAll(collectCategories(subCategory));
}
return categories;
}
return categories;
}
private Class<? extends ContentItem> buildLimitToType(
@ -352,7 +369,7 @@ public class ItemListModel {
return roles;
}
private Order builderOrder(
private Order buildOrder(
final String order,
final Root<? extends ContentItem> from,
final CriteriaBuilder criteriaBuilder