diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ArticleModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/ArticleModel.java new file mode 100644 index 000000000..c9c45ea33 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ArticleModel.java @@ -0,0 +1,80 @@ +/* + * 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.l10n.GlobalizationHelper; +import org.librecms.contentsection.ContentItem; +import org.librecms.contenttypes.Article; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +/** + * Model for getting the special properties of an {@link Article}. For general + * properties of an content item use {@link ContentItemModel}. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsPagesArticleModel") +public class ArticleModel { + + @Inject + private ContentItemModel contentItemModel; + + @Inject + private GlobalizationHelper globalizationHelper; + + public String getTitle() { + return getArticle() + .getTitle() + .getValue(globalizationHelper.getNegotiatedLocale()); + } + + public String getDescription() { + return getArticle() + .getDescription() + .getValue(globalizationHelper.getNegotiatedLocale()); + } + + public String getText() { + return getArticle() + .getText() + .getValue(globalizationHelper.getNegotiatedLocale()); + } + + protected Article getArticle() { + final ContentItem contentItem = contentItemModel.getContentItem(); + if (contentItem instanceof Article) { + return (Article) contentItem; + } else { + throw new WebApplicationException( + "Current content item is not an article.", + Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Current content item is not an article.") + .build() + ); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/CategoryModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/CategoryModel.java new file mode 100644 index 000000000..dd27e25ad --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/CategoryModel.java @@ -0,0 +1,63 @@ +/* + * 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.Category; +import org.librecms.pages.PagesRouter; + +import java.io.Serializable; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * Model for MVC applications providing access to the current category. This + * model MUST be initalized by the calling application (for example + * {@link PagesRouter} with the current category. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsPagesCategoryModel") +public class CategoryModel implements Serializable { + + private static final long serialVersionUID = 1L; + + private Category category; + + private String categoryPath; + + public Category getCategory() { + return category; + } + + public void setCategory(final Category category) { + this.category = category; + } + + public String getCategoryPath() { + return categoryPath; + } + + public void setCategoryPath(final String categoryPath) { + this.categoryPath = categoryPath; + } + + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemModel.java new file mode 100644 index 000000000..a6486a11a --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemModel.java @@ -0,0 +1,282 @@ +/* + * 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.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.ContentItem; +import org.librecms.contentsection.ContentItemVersion; +import org.librecms.pages.PagesRouter; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +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; + +/** + * Retrieves a categorized content item for the current category. To work, the + * {@link #itemName} property MUST be initalized by the using application (for + * example {@link PagesRouter}. The value for {@link #itemName} is usually + * determined from the requested URL. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsPagesCategorizedItemModel") +public class ContentItemModel { + + private static final Logger LOGGER = LogManager.getLogger(ContentItemModel.class + ); + + @Inject + private CategoryManager categoryManager; + + @Inject + private CategoryModel categoryModel; + + @Inject + private EntityManager entityManager; + + @Inject + private GlobalizationHelper globalizationHelper; + + private DateTimeFormatter dateTimeFormatter + = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()); + + private String itemName; + + private ContentItemVersion itemVersion; + + private Optional contentItem; + + public String getItemName() { + return itemName; + } + + public void setItemName(final String itemName) { + this.itemName = itemName; + } + + /** + * Retrieves the current content item. Depending if {@link #itemName} has + * been initalized with a value either the index item of the current + * category or the item in the category identified by {@link #itemName} will + * be retrieved. The item is only received once per request. The method will + * retrieve the item on the first call and store the result in + * {@link #contentItem}. Subsequent calls will return the value of + * {@link #contentItem}. If {@link #itemName} is not {@code null} and there + * is no content item with the requested name in the category this method + * throws a {@link NotFoundException}. + * + * @return The requested categorized item. If {@link #itemName} is + * {@code null}, and the current category has not index item, the + * method will return {@code null}. + * + * @throws NotFoundException If there is no item identified by the name in + * {@link #itemName}. + */ + public ContentItem getContentItem() { + return getOrRetrieveContentItem().orElse(null); + } + + public long getObjectId() { + return getOrRetrieveContentItem() + .map(ContentItem::getObjectId) + .orElse(0L); + } + + public String getUuid() { + return getOrRetrieveContentItem() + .map(ContentItem::getUuid) + .orElse(""); + } + + public String getDisplayName() { + return getOrRetrieveContentItem() + .map(ContentItem::getDisplayName) + .orElse(""); + } + + public String getItemUuid() { + return getOrRetrieveContentItem() + .map(ContentItem::getItemUuid) + .orElse(""); + } + + public String getName() { + return getOrRetrieveContentItem() + .map(ContentItem::getName) + .map( + localized -> localized.getValue( + globalizationHelper.getNegotiatedLocale() + ) + ) + .orElse(""); + } + + public String getTitle() { + return getOrRetrieveContentItem() + .map(ContentItem::getTitle) + .map( + localized -> localized.getValue( + globalizationHelper.getNegotiatedLocale() + ) + ).orElse(""); + } + + public String getDescription() { + return getOrRetrieveContentItem() + .map(ContentItem::getDescription) + .map( + localized -> localized.getValue( + globalizationHelper.getNegotiatedLocale() + ) + ) + .orElse(""); + } + + public String getVersion() { + return getOrRetrieveContentItem() + .map(ContentItem::getVersion) + .map(ContentItemVersion::toString) + .orElse(""); + } + + public String getCreationDate() { + return getOrRetrieveContentItem() + .map(ContentItem::getCreationDate) + .map(Date::toInstant) + .map(instant -> instant.atZone(ZoneId.systemDefault())) + .map(ZonedDateTime::toLocalDateTime) + .map(dateTimeFormatter::format) + .orElse(""); + } + + public String getLastModified() { + return getOrRetrieveContentItem() + .map(ContentItem::getLastModified) + .map(Date::toInstant) + .map(instant -> instant.atZone(ZoneId.systemDefault())) + .map(ZonedDateTime::toLocalDateTime) + .map(dateTimeFormatter::format) + .orElse(""); + } + + public String getCreationUser() { + return getOrRetrieveContentItem() + .map(ContentItem::getCreationUserName) + .orElse(""); + } + + public String getLastModifyingUserName() { + return getOrRetrieveContentItem() + .map(ContentItem::getLastModifyingUserName) + .orElse(""); + } + + private Optional getOrRetrieveContentItem() { + if (contentItem == null) { + retrieveContentItem(); + } + return contentItem; + } + + private void retrieveContentItem() { + if (itemName == null) { + retrieveIndexItem(); + } 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 criteriaQuery = builder + .createQuery(ContentItem.class); + + final Root from = criteriaQuery.from( + ContentItem.class + ); + final Join join = from.join( + "categories" + ); + + final TypedQuery query = entityManager + .createQuery(criteriaQuery + .select(from) + .where(builder.and( + builder.equal(from.get("displayName"), itemName), + builder.equal( + from.get("version"), + ContentItemVersion.DRAFT + ), + 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) + ) + ); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemTypeModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemTypeModel.java new file mode 100644 index 000000000..b61f27c66 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemTypeModel.java @@ -0,0 +1,113 @@ +/* + * 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.l10n.GlobalizationHelper; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentType; + +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; +import javax.swing.text.AbstractDocument.Content; + +/** + * MVC model for retrieving information about the content type of the current + * content item. If there is no current content item, the methods of this model + * will return an empty string. + * + * The content type is retrieved once per request cycle on the first call + * + * @see ContentItemModel + * @see ContentItemModel#getContentItem() + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsPagesContentItemTypeModel") +public class ContentItemTypeModel { + + @Inject + private ContentItemModel contentItemModel; + + @Inject + private GlobalizationHelper globalizationHelper; + + private Optional contentType; + + public ContentType getContentType() { + return getOrRetrieveContentType().orElse(null); + } + + public long getContentTypeId() { + return getOrRetrieveContentType() + .map(ContentType::getObjectId) + .orElse(0L); + } + + public String getUuid() { + return getOrRetrieveContentType() + .map(ContentType::getUuid) + .orElse(""); + } + + public String getDisplayName() { + return getOrRetrieveContentType() + .map(ContentType::getDisplayName) + .orElse(""); + } + + public String getLabel() { + return getOrRetrieveContentType() + .map(ContentType::getLabel) + .map( + label -> label.getValue( + globalizationHelper.getNegotiatedLocale()) + ) + .orElse(""); + } + + public String getDescription() { + return getOrRetrieveContentType() + .map(ContentType::getDescription) + .map( + description -> description.getValue( + globalizationHelper.getNegotiatedLocale() + ) + ) + .orElse(""); + } + + private Optional getOrRetrieveContentType() { + if (contentType == null) { + retrieveContentType(); + } + return contentType; + } + + private void retrieveContentType() { + contentType = Optional + .ofNullable(contentItemModel.getContentItem()) + .map(ContentItem::getContentType); + + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/SiteInfoModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/SiteInfoModel.java new file mode 100644 index 000000000..ed3f19a27 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/SiteInfoModel.java @@ -0,0 +1,81 @@ +/* + * 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.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * Model providing information about the current site. Must be initalized by the + * calling application. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsPagesSiteInfoModel") +public class SiteInfoModel implements Serializable { + + private static final long serialVersionUID = 1L; + + private String host; + + private String domain; + + private String name; + + private List availableLanguages; + + public String getHost() { + return host; + } + + public void setHost(final String host) { + this.host = host; + } + + public String getDomain() { + return domain; + } + + public void setDomain(final String domain) { + this.domain = domain; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public List getAvailableLanguages() { + return Collections.unmodifiableList(availableLanguages); + } + + public void setAvailableLanguages(final List availableLanguages) { + this.availableLanguages = new ArrayList<>(availableLanguages); + } + +}