Pages Application refactoring for MVC

pull/10/head
Jens Pelzetter 2021-11-13 17:56:44 +01:00
parent 6383aec674
commit 27b18d8888
4 changed files with 322 additions and 103 deletions

View File

@ -31,17 +31,25 @@ import org.libreccm.theming.ThemeInfo;
import org.libreccm.theming.ThemeVersion;
import org.libreccm.theming.Themes;
import org.libreccm.theming.mvc.ThemesMvc;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.models.CategoryModel;
import org.librecms.pages.models.ContentItemModel;
import org.librecms.pages.models.SiteInfoModel;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
@ -173,6 +181,12 @@ public class PagesController {
@Inject
private ConfigurationManager confManager;
@Inject
private ContentItemManager contentItemManager;
@Inject
private ContentItemL10NManager contentItemL10NManager;
@Inject
private ContentItemModel contentItemModel;
@ -182,6 +196,9 @@ public class PagesController {
@Inject
private PagesRepository pagesRepo;
@Inject
private PagesService pagesService;
@Inject
private SiteInfoModel siteInfoModel;
@ -206,13 +223,27 @@ public class PagesController {
@GET
@Path("/")
@Transactional(Transactional.TxType.REQUIRED)
public Response redirectToIndexPage(@Context final UriInfo uriInfo) {
public Response redirectToIndexPage(
@Context
final UriInfo uriInfo,
@QueryParam("theme")
@DefaultValue(ThemesMvc.DEFAULT_THEME_PARAM)
final String theme,
@QueryParam("preview")
@DefaultValue("")
final String preview
) {
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/");
final String language = determineLanguage(category);
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
final String indexPage = String.format("/index.%s.html", language);
final String indexPage = String.format(
"/index.%s.html%s",
language,
buildQueryParamsStr(preview, theme)
);
final URI uri = uriInfo.getBaseUriBuilder().path(indexPage).build();
return Response.temporaryRedirect(uri).build();
}
@ -221,16 +252,27 @@ public class PagesController {
@Path("/{name:[\\w\\-]+}")
@Transactional(Transactional.TxType.REQUIRED)
public Response getRootPage(
@Context final UriInfo uriInfo,
@PathParam("name") final String itemName
@Context
final UriInfo uriInfo,
@PathParam("name") final String itemName,
@QueryParam("theme")
@DefaultValue(ThemesMvc.DEFAULT_THEME_PARAM)
final String theme,
@QueryParam("preview")
@DefaultValue("")
final String preview
) {
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/");
final String language = determineLanguage(category);
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
final String itemPage = String.format(
"/%s.%s.html", itemName, language
"/%s.%s.html%s",
itemName,
language,
buildQueryParamsStr(preview, theme)
);
final URI uri = uriInfo.getBaseUriBuilder().path(itemPage).build();
return Response.temporaryRedirect(uri).build();
@ -241,12 +283,20 @@ public class PagesController {
@Transactional(Transactional.TxType.REQUIRED)
public Response getRootPageAsHtml(
@Context final UriInfo uriInfo,
@PathParam("name") final String itemName) {
@QueryParam("theme")
@DefaultValue(ThemesMvc.DEFAULT_THEME_PARAM)
final String theme,
@QueryParam("preview")
@DefaultValue("")
final String preview,
@PathParam("name")
final String itemName
) {
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/");
final String language = determineLanguage(category);
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, itemName, versions);
final String itemPage = String.format(
"/%s.%s.html", itemName, language
@ -284,7 +334,14 @@ public class PagesController {
contentItemModel.setItemName(itemName);
contentItemModel.setItemVersion(versions.getContentItemVersion());
return themesMvc.getMvcTemplate(uriInfo, "pages", "page");
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/");
final Page page = pageManager.findPageForCategory(category);
page.getDisplayName();
return themesMvc.getMvcTemplate(
uriInfo, "pages", page.getDisplayName()
);
}
/**
@ -297,6 +354,8 @@ public class PagesController {
* @param uriInfo
* @param page
* @param itemName
* @param theme
* @param preview
*
* @return
*/
@ -306,12 +365,21 @@ public class PagesController {
public Response getPage(
@Context final UriInfo uriInfo,
@PathParam("page") final String page,
@PathParam("name") final String itemName
@PathParam("name") final String itemName,
@QueryParam("theme")
@DefaultValue("--DEFAULT--")
final String theme,
@QueryParam("preview")
@DefaultValue("")
final String preview
) {
// ToDo Check!!!
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, page);
final String language = determineLanguage(category);
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
final String redirectTo;
if (uriInfo.getPath().endsWith("/")) {
@ -346,12 +414,21 @@ public class PagesController {
public Response getPageAsHtml(
@Context final UriInfo uriInfo,
@PathParam("page") final String page,
@PathParam("name") final String itemName
@PathParam("name") final String itemName,
@QueryParam("theme")
@DefaultValue("--DEFAULT--")
final String theme,
@QueryParam("preview")
@DefaultValue("")
final String preview
) {
//ToDo Check!
final String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, page);
final String language = determineLanguage(category);
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
final String redirectTo;
if (uriInfo.getPath().endsWith("/")) {
@ -461,20 +538,91 @@ public class PagesController {
);
}
private String determineLanguage(final Category category) {
private String determineLanguage(
final Category category, final Versions versions
) {
final Locale negoiatedLocale = globalizationHelper
.getNegotiatedLocale();
final String language;
if (category.getTitle().hasValue(negoiatedLocale)) {
language = negoiatedLocale.toString();
} else if (category.getTitle().hasValue(defaultLocale)) {
language = defaultLocale.toString();
if (categoryManager.hasIndexObject(category)) {
final ContentItem indexItem = getIndexObject(category, versions)
.get();
if (contentItemL10NManager.hasLanguage(indexItem, negoiatedLocale)) {
return negoiatedLocale.toString();
} else {
return confManager
.findConfiguration(KernelConfig.class)
.getDefaultLanguage();
}
} else {
throw new NotFoundException();
if (category.getTitle().hasValue(negoiatedLocale)) {
return negoiatedLocale.toString();
} else {
return defaultLocale.toString();
}
}
}
return language;
private String determineLanguage(
final Category category,
final String itemName,
final Versions versions
) {
final Locale negoiatedLocale = globalizationHelper
.getNegotiatedLocale();
final ContentItem contentItem = pagesService.findCategorizedItem(
category, itemName, versions.getContentItemVersion()
)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No item %s found in category %s.",
itemName,
categoryManager.getCategoryPath(category)
)
)
);
final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class
);
if (contentItemL10NManager.hasLanguage(contentItem, negoiatedLocale)) {
return negoiatedLocale.toString();
} else if (contentItemL10NManager.hasLanguage(contentItem, kernelConfig
.getDefaultLocale())) {
return kernelConfig.getDefaultLanguage();
} else {
throw new NotFoundException(
String.format(
"No item %s found in category %s.",
itemName,
categoryManager.getCategoryPath(category)
)
);
}
}
private String buildQueryParamsStr(
final String previewParam, final String themeParam
) {
return List
.of(
Optional
.of(themeParam)
.filter(String::isBlank)
.map(param -> String.format("theme=%s", param))
.orElse(""),
Optional
.of(previewParam)
.filter(String::isBlank)
.map(param -> String.format("preview=%s", param))
.orElse("")
)
.stream()
.filter(String::isBlank)
.collect(Collectors.joining("&", "?", ""));
}
private ThemeInfo getTheme(
@ -508,6 +656,26 @@ public class PagesController {
}
}
private Optional<ContentItem> getIndexObject(
final Category category, final Versions versions
) {
final Predicate<ContentItem> filter;
if (versions.getContentItemVersion() == ContentItemVersion.DRAFT) {
filter = (ContentItem item) -> !contentItemManager.isLive(item);
} else {
filter = (ContentItem item) -> contentItemManager.isLive(item);
}
return categoryManager
.getIndexObject(category)
.stream()
.filter(object -> object instanceof ContentItem)
.map(object -> (ContentItem) object)
.filter(filter)
.findFirst();
}
private Page findPage(
final UriInfo uriInfo, final String pagePath, final String language
) {

View File

@ -0,0 +1,106 @@
/*
* 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;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemVersion;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
public class PagesService {
@Inject
private CategoryManager categoryManager;
@Inject
private EntityManager entityManager;
@Transactional(Transactional.TxType.REQUIRED)
public Optional<ContentItem> findIndexItem(
final Category category, final ContentItemVersion version
) {
return categoryManager
.getIndexObject(category)
.stream()
.filter(object -> object instanceof ContentItem)
.map(object -> (ContentItem) object)
.filter(item -> item.getVersion() == version)
.findFirst();
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<ContentItem> findCategorizedItem(
final Category category,
final String itemName,
final ContentItemVersion version
) {
final CriteriaBuilder builder = entityManager
.getCriteriaBuilder();
final CriteriaQuery<ContentItem> criteriaQuery = builder
.createQuery(ContentItem.class);
final Root<ContentItem> from = criteriaQuery.from(
ContentItem.class
);
final Join<ContentItem, Categorization> join = from.join(
"categories"
);
final TypedQuery<ContentItem> query = entityManager
.createQuery(
criteriaQuery
.select(from)
.where(
builder.and(
builder.equal(from.get("displayName"), itemName),
builder.equal(
from.get("version"),
version
),
builder.equal(join.get("category"), category)
)
)
);
try {
return Optional.of(query.getSingleResult());
} catch (NoResultException ex) {
return Optional.empty();
}
}
}

View File

@ -20,33 +20,25 @@ package org.librecms.pages.models;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
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;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;
import javax.ws.rs.NotFoundException;
/**
@ -77,6 +69,9 @@ public class ContentItemModel {
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private PagesService pagesService;
private final DateTimeFormatter dateTimeFormatter
= DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault());
@ -93,11 +88,11 @@ public class ContentItemModel {
public void setItemName(final String itemName) {
this.itemName = itemName;
}
public ContentItemVersion getItemVersion() {
return itemVersion;
}
public void setItemVersion(final ContentItemVersion itemVersion) {
this.itemVersion = itemVersion;
}
@ -221,66 +216,13 @@ public class ContentItemModel {
private void retrieveContentItem() {
if (itemName == null) {
retrieveIndexItem();
contentItem = pagesService.findIndexItem(
categoryModel.getCategory(), itemVersion
);
} else {
retrieveCategorizedItem();
}
}
private void retrieveIndexItem() {
final Category category = categoryModel.getCategory();
contentItem = categoryManager
.getIndexObject(category)
.stream()
.filter(object -> object instanceof ContentItem)
.map(object -> (ContentItem) object)
.filter(item -> item.getVersion() == itemVersion)
.findFirst();
}
private void retrieveCategorizedItem() {
final Category category = categoryModel.getCategory();
final CriteriaBuilder builder = entityManager
.getCriteriaBuilder();
final CriteriaQuery<ContentItem> criteriaQuery = builder
.createQuery(ContentItem.class);
final Root<ContentItem> from = criteriaQuery.from(
ContentItem.class
);
final Join<ContentItem, Categorization> join = from.join(
"categories"
);
final TypedQuery<ContentItem> query = entityManager
.createQuery(criteriaQuery
.select(from)
.where(builder.and(
builder.equal(from.get("displayName"), itemName),
builder.equal(
from.get("version"),
itemVersion
),
builder.equal(join.get("category"), category)
)));
try {
contentItem = Optional.of(query.getSingleResult());
} catch (NoResultException ex) {
LOGGER.warn(
"No ContentItem with name \"{}\" in Category \"{}\".",
itemName,
Objects.toString(category)
);
throw new NotFoundException(
String.format(
"No ContentItem with name \"%s\" in Category \"%s\".",
itemName,
Objects.toString(category)
)
contentItem = pagesService.findCategorizedItem(
categoryModel.getCategory(), itemName, itemVersion
);
}
}
}

View File

@ -49,6 +49,8 @@ import javax.ws.rs.core.UriInfo;
@RequestScoped
public class ThemesMvc {
public static final String DEFAULT_THEME_PARAM = "--DEFAULT--";
@Inject
private Models models;
@ -89,15 +91,15 @@ public class ThemesMvc {
);
} else {
final Site site = getSite(uriInfo);
final String theme = parseThemeParam(uriInfo);
themeVersion = parsePreviewParam(uriInfo);
themeInfo = getTheme(
site,
theme,
themeVersion
);
final String theme = parseThemeParam(uriInfo);
themeVersion = parsePreviewParam(uriInfo);
themeInfo = getTheme(
site,
theme,
themeVersion
);
}
final ThemeManifest manifest = themeInfo.getManifest();
final Map<String, String> views = manifest.getViewsOfApplication(
application
@ -203,7 +205,7 @@ public class ThemesMvc {
final Site site,
final String theme,
final ThemeVersion themeVersion) {
if ("--DEFAULT--".equals(theme)) {
if (DEFAULT_THEME_PARAM.equals(theme)) {
return themes
.getTheme(site.getDefaultTheme(), themeVersion)
.orElseThrow(
@ -239,13 +241,14 @@ public class ThemesMvc {
* @param uriInfo Information about the current URI.
*
* @return The value of the {@link theme} query parameter if present, or
* {@code --DEFAULT--} if the query parameter is not present.
* {@link #DEFAULT_THEME_PARAM} if the query parameter is not
* present.
*/
private String parseThemeParam(final UriInfo uriInfo) {
if (uriInfo.getQueryParameters().containsKey("theme")) {
return uriInfo.getQueryParameters().getFirst("theme");
} else {
return "--DEFAULT--";
return DEFAULT_THEME_PARAM;
}
}