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.ThemeVersion;
import org.libreccm.theming.Themes; import org.libreccm.theming.Themes;
import org.libreccm.theming.mvc.ThemesMvc; 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.contentsection.ContentItemVersion;
import org.librecms.pages.models.CategoryModel; import org.librecms.pages.models.CategoryModel;
import org.librecms.pages.models.ContentItemModel; import org.librecms.pages.models.ContentItemModel;
import org.librecms.pages.models.SiteInfoModel; import org.librecms.pages.models.SiteInfoModel;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -173,6 +181,12 @@ public class PagesController {
@Inject @Inject
private ConfigurationManager confManager; private ConfigurationManager confManager;
@Inject
private ContentItemManager contentItemManager;
@Inject
private ContentItemL10NManager contentItemL10NManager;
@Inject @Inject
private ContentItemModel contentItemModel; private ContentItemModel contentItemModel;
@ -182,6 +196,9 @@ public class PagesController {
@Inject @Inject
private PagesRepository pagesRepo; private PagesRepository pagesRepo;
@Inject
private PagesService pagesService;
@Inject @Inject
private SiteInfoModel siteInfoModel; private SiteInfoModel siteInfoModel;
@ -206,13 +223,27 @@ public class PagesController {
@GET @GET
@Path("/") @Path("/")
@Transactional(Transactional.TxType.REQUIRED) @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 String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain); final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/"); 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(); final URI uri = uriInfo.getBaseUriBuilder().path(indexPage).build();
return Response.temporaryRedirect(uri).build(); return Response.temporaryRedirect(uri).build();
} }
@ -221,16 +252,27 @@ public class PagesController {
@Path("/{name:[\\w\\-]+}") @Path("/{name:[\\w\\-]+}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public Response getRootPage( public Response getRootPage(
@Context final UriInfo uriInfo, @Context
@PathParam("name") final String itemName 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 String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain); final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/"); 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( 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(); final URI uri = uriInfo.getBaseUriBuilder().path(itemPage).build();
return Response.temporaryRedirect(uri).build(); return Response.temporaryRedirect(uri).build();
@ -241,12 +283,20 @@ public class PagesController {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public Response getRootPageAsHtml( public Response getRootPageAsHtml(
@Context final UriInfo uriInfo, @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 String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain); final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, "/"); 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( final String itemPage = String.format(
"/%s.%s.html", itemName, language "/%s.%s.html", itemName, language
@ -284,7 +334,14 @@ public class PagesController {
contentItemModel.setItemName(itemName); contentItemModel.setItemName(itemName);
contentItemModel.setItemVersion(versions.getContentItemVersion()); 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 uriInfo
* @param page * @param page
* @param itemName * @param itemName
* @param theme
* @param preview
* *
* @return * @return
*/ */
@ -306,12 +365,21 @@ public class PagesController {
public Response getPage( public Response getPage(
@Context final UriInfo uriInfo, @Context final UriInfo uriInfo,
@PathParam("page") final String page, @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 String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain); final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, page); 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; final String redirectTo;
if (uriInfo.getPath().endsWith("/")) { if (uriInfo.getPath().endsWith("/")) {
@ -346,12 +414,21 @@ public class PagesController {
public Response getPageAsHtml( public Response getPageAsHtml(
@Context final UriInfo uriInfo, @Context final UriInfo uriInfo,
@PathParam("page") final String page, @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 String domain = uriInfo.getBaseUri().getHost();
final Pages pages = getPages(domain); final Pages pages = getPages(domain);
final Category category = getCategory(domain, pages, page); 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; final String redirectTo;
if (uriInfo.getPath().endsWith("/")) { 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 final Locale negoiatedLocale = globalizationHelper
.getNegotiatedLocale(); .getNegotiatedLocale();
final String language; if (categoryManager.hasIndexObject(category)) {
if (category.getTitle().hasValue(negoiatedLocale)) { final ContentItem indexItem = getIndexObject(category, versions)
language = negoiatedLocale.toString(); .get();
} else if (category.getTitle().hasValue(defaultLocale)) { if (contentItemL10NManager.hasLanguage(indexItem, negoiatedLocale)) {
language = defaultLocale.toString(); return negoiatedLocale.toString();
} else { } else {
throw new NotFoundException(); return confManager
.findConfiguration(KernelConfig.class)
.getDefaultLanguage();
}
} else {
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( 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( private Page findPage(
final UriInfo uriInfo, final String pagePath, final String language 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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.CategoryManager;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.AttachmentList; import org.librecms.contentsection.AttachmentList;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemVersion; import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.PagesRouter; import org.librecms.pages.PagesRouter;
import org.librecms.pages.PagesService;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.persistence.EntityManager; 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; import javax.ws.rs.NotFoundException;
/** /**
@ -77,6 +69,9 @@ public class ContentItemModel {
@Inject @Inject
private GlobalizationHelper globalizationHelper; private GlobalizationHelper globalizationHelper;
@Inject
private PagesService pagesService;
private final DateTimeFormatter dateTimeFormatter private final DateTimeFormatter dateTimeFormatter
= DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()); = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault());
@ -221,66 +216,13 @@ public class ContentItemModel {
private void retrieveContentItem() { private void retrieveContentItem() {
if (itemName == null) { if (itemName == null) {
retrieveIndexItem(); contentItem = pagesService.findIndexItem(
categoryModel.getCategory(), itemVersion
);
} else { } else {
retrieveCategorizedItem(); contentItem = pagesService.findCategorizedItem(
} categoryModel.getCategory(), itemName, itemVersion
}
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)
)
); );
} }
} }
} }

View File

@ -49,6 +49,8 @@ import javax.ws.rs.core.UriInfo;
@RequestScoped @RequestScoped
public class ThemesMvc { public class ThemesMvc {
public static final String DEFAULT_THEME_PARAM = "--DEFAULT--";
@Inject @Inject
private Models models; private Models models;
@ -203,7 +205,7 @@ public class ThemesMvc {
final Site site, final Site site,
final String theme, final String theme,
final ThemeVersion themeVersion) { final ThemeVersion themeVersion) {
if ("--DEFAULT--".equals(theme)) { if (DEFAULT_THEME_PARAM.equals(theme)) {
return themes return themes
.getTheme(site.getDefaultTheme(), themeVersion) .getTheme(site.getDefaultTheme(), themeVersion)
.orElseThrow( .orElseThrow(
@ -239,13 +241,14 @@ public class ThemesMvc {
* @param uriInfo Information about the current URI. * @param uriInfo Information about the current URI.
* *
* @return The value of the {@link theme} query parameter if present, or * @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) { private String parseThemeParam(final UriInfo uriInfo) {
if (uriInfo.getQueryParameters().containsKey("theme")) { if (uriInfo.getQueryParameters().containsKey("theme")) {
return uriInfo.getQueryParameters().getFirst("theme"); return uriInfo.getQueryParameters().getFirst("theme");
} else { } else {
return "--DEFAULT--"; return DEFAULT_THEME_PARAM;
} }
} }