diff --git a/ccm-cms-default-theme/src/main/resources/themes/librecms/templates/index-page.html.ftl b/ccm-cms-default-theme/src/main/resources/themes/librecms/templates/index-page.html.ftl index 1a68ad991..17edb4f29 100644 --- a/ccm-cms-default-theme/src/main/resources/themes/librecms/templates/index-page.html.ftl +++ b/ccm-cms-default-theme/src/main/resources/themes/librecms/templates/index-page.html.ftl @@ -4,21 +4,33 @@
-

- LibreCMS -

-

- No index item has been defined. -

- - Find out more - + <#if CmsPagesCategorizedItemModel.itemAvailable> +

+ ${CmsPagesCategorizedItemModel.title} +

+

+ ${CmsPagesCategorizedItemModel.description} +

+ + Find out more + + <#else> +

+ LibreCMS +

+

+ No index item has been defined. +

+ + Find out more + +
- <#if CmsPagesCategorizedItemModel.contentItem??> + <#if CmsPagesCategorizedItemModel.itemAvailable> Category has an index item <#else> Category has no index item @@ -34,6 +46,39 @@
view
${view!""}
+ +

From ArticleModel

+
+
Title
+
${CmsPagesArticleModel.title}
+
Description
+
${CmsPagesArticleModel.description}
+
Text
+
${CmsPagesArticleModel.text}
+
+ +

Item List

+

Item List size: ${CmsPagesItemListModel.listSize}

+
\ No newline at end of file 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 index 1c784db9e..a11d3a214 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/ArticleModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ArticleModel.java @@ -28,8 +28,6 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import javax.transaction.Transactional; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; /** * Model for getting the special properties of an {@link Article}. For general @@ -39,7 +37,7 @@ import javax.ws.rs.core.Response; */ @RequestScoped @Named("CmsPagesArticleModel") -public class ArticleModel { +public class ArticleModel implements ProcessesContentItem { @Inject private ContentItemModel contentItemModel; @@ -47,41 +45,46 @@ public class ArticleModel { @Inject private GlobalizationHelper globalizationHelper; + private boolean initialized; + private String title; private String description; private String text; + public ArticleModel() { + initialized = false; + } + @Transactional(Transactional.TxType.REQUIRED) public String getTitle() { - if (title == null) { - init(); - } + contentItemModel.init(); + return title; } @Transactional(Transactional.TxType.REQUIRED) public String getDescription() { - if (description == null) { - init(); - } + contentItemModel.init(); + return description; } @Transactional(Transactional.TxType.REQUIRED) public String getText() { - if (text == null) { - init(); - } + contentItemModel.init(); + return text; } @Transactional(Transactional.TxType.REQUIRED) - protected void init() { - final ContentItem contentItem = contentItemModel - .retrieveContentItem() - .orElse(null); + @Override + public void init(final ContentItem contentItem) { + if (initialized) { + return; + } + if (contentItem instanceof Article) { final Article article = (Article) contentItem; @@ -97,15 +100,8 @@ public class ArticleModel { .ofNullable(article.getText()) .map(globalizationHelper::getValueFromLocalizedString) .orElse(""); - } 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() - ); - } + } + initialized = true; } - + } 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 index a6a8d8ff4..09037d248 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemModel.java @@ -18,24 +18,24 @@ */ 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.core.CcmObject; import org.libreccm.l10n.GlobalizationHelper; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemVersion; +import org.librecms.pages.PagesController; import org.librecms.pages.PagesRouter; import org.librecms.pages.PagesService; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.Iterator; import java.util.Optional; import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; -import javax.persistence.EntityManager; import javax.transaction.Transactional; /** @@ -50,162 +50,348 @@ import javax.transaction.Transactional; @Named("CmsPagesCategorizedItemModel") public class ContentItemModel { - private static final Logger LOGGER = LogManager.getLogger( - ContentItemModel.class - ); - - @Inject - private CategoryManager categoryManager; - + /** + * Category repository used to retrieve the current category. + */ @Inject private CategoryRepository categoryRepository; + /** + * Category model used to get the curretn category. + */ @Inject private CategoryModel categoryModel; - @Inject - private EntityManager entityManager; - + /** + * Utility for globalization stuff. + */ @Inject private GlobalizationHelper globalizationHelper; + /** + * Provides some utility methods. + */ @Inject private PagesService pagesService; + /** + * All instances of implementations of the {@link ProcessesContentItem} + * interface. + */ + @Inject + private Instance contentItemProcessingModels; + + /** + * A {@link DateTimeFormatter} instance for converting dates and times to an + * ISO 8601 date/time string. + */ private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()); + /** + * The name of the item to be shown. + */ private String itemName; + /** + * The version of the item to be shown. + */ private ContentItemVersion itemVersion; -// private Optional contentItem; + /** + * Data of the current content item, already prepared for retrieving the + * data from a MVC template. If no item with the {@link #itemName} provided + * by the {@link PagesController} is present in the category, the + * {@link Optional} will be empty. + */ private Optional contentItem; + /** + * Gets the item name provided by the {@link PagesController}. + * + * @return The item name provided by the {@link PagesController}. + */ public String getItemName() { return itemName; } + /** + * Used by the {@link PagesController} to set the name of the requested + * content item. May be used by other controllers. + * + * @param itemName The name of the requested content item. + */ public void setItemName(final String itemName) { this.itemName = itemName; } + /** + * The requested version of the content item. + * + * @return The requested version of the content item. + */ public ContentItemVersion getItemVersion() { return itemVersion; } + /** + * Used by controllers, for example the {@link PagesController} to set the + * requested version of the content item. + * + * @param itemVersion The requested version of the content item. + */ public void setItemVersion(final ContentItemVersion itemVersion) { this.itemVersion = itemVersion; } + /** + * A convient getter for checking if a content item is available from a + * template. + * + * @return {@code true} if an item is available, {@code false} if not. + */ @Transactional(Transactional.TxType.REQUIRED) public boolean isItemAvailable() { - return getOrRetrieveContentItem().isPresent(); - } - - @Transactional(Transactional.TxType.REQUIRED) - public long getObjectId() { - return getOrRetrieveContentItem() - .map(ContentItemModelData::getObjectId) - .orElse(0L); + init(); + return contentItem.isPresent(); } + /** + * Gets the {@code objectId)} (see {@link CcmObject#objectId} of the current + * item. + * + * @return The {@code objectId} of the current item if there is an item or + * {@code -1L} if there is no item. + */ + @Transactional(Transactional.TxType.REQUIRED) + public long getObjectId() { + init(); + return contentItem + .map(ContentItemModelData::getObjectId) + .orElse(-1L); + } + + /** + * Get the UUID of the current item (see {@link CcmObject#uuid}. + * + * @return The UUID of the current item if there is an item, an empty string + * if there is not item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getUuid() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getUuid) .orElse(""); } + /** + * Gets the display name of the current item (see + * {@link CcmObject#displayName}. + * + * @return The display name of the current item, or an empty string if there + * is not current item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getDisplayName() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getDisplayName) .orElse(""); } + /** + * Gets the item UUID of the current item (see {@link ContentItem#itemUuid}. + * + * @return The item UUID of the current item, or an empty string if the is + * no current item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getItemUuid() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getItemUuid) .orElse(""); } + /** + * Gets the name of the current item {@link ContentItem#name}) for the + * current language (see {@link GlobalizationHelper#getNegotiatedLocale()}). + * + * @return The name of the the current item for the current language, or an + * empty string if there is no item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getName() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getName) .orElse(""); } + /** + * Gets the title of the current item (see {@link ContentItem#title} for the + * current language (see {@link GlobalizationHelper#getNegotiatedLocale()}). + * + * @return The title of the current item for the current language, or an + * empty string if there is no item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getTitle() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getTitle) .orElse(""); } + /** + * Gets the description of the current item (see + * {@link ContentItem#description} for the current language (see + * {@link GlobalizationHelper#getNegotiatedLocale()}). + * + * @return The description of the current item for the current language, or + * an empty string if there is no item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getDescription() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getDescription) .orElse(""); } + /** + * Gets the version of the current item (see {@link ContentItem#version}. + * + * @return The version of the current version as a string, or an empty + * string if there is not current item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getVersion() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getVersion) .orElse(""); } + /** + * Gets the creation date/time of the current item (see + * {@link ContentItem#creationDate}. + * + * @return The creation date/time of the current item as ISO 8601 date/time + * string, or an empty string if there is no item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getCreationDate() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getCreationDate) .orElse(""); } + /** + * Gets the date/time of the last modification of the current item (see + * {@link ContentItem#lastModified}. + * + * @return The date/time of the modification of the current item as ISO 8601 + * date/time string, or an empty string of there is not current + * item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getLastModified() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getLastModified) .orElse(""); } + /** + * Gets the user name of the user that created the item. + * + * @return The user name of the user that created the item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getCreationUser() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getCreationUser) .orElse(""); } + /** + * Gets the user name of the user that did the last modifications on the + * item. + * + * @return The user name of the user that did the last modifications on the + * item. + */ @Transactional(Transactional.TxType.REQUIRED) public String getLastModifyingUserName() { - return getOrRetrieveContentItem() + init(); + return contentItem .map(ContentItemModelData::getLastModifyingUserName) .orElse(""); } -// public List getAttachments() { -// throw new UnsupportedOperationException("Not implemented yet."); -// } -// private Optional getOrRetrieveContentItem() { -// if (contentItem == null) { -// retrieveContentItem(); -// } -// return contentItem; -// } + /** + * Initialize the this model and all models implementing + * {@link ProcessesContentItem#}. + * + * If this model has not been initalizied already this method retrieves the + * current content item using {@link #retrieveContentItem()}, and + * initializes {@link #contentItem} with an instance of + * {@link ContentItemModelData} build from the item using + * {@link #buildModelData(org.librecms.contentsection.ContentItem)}. + * + * After that, the method will invoke the implementation of + * {@link ProcessesContentItem#init(org.librecms.contentsection.ContentItem)} + * for all available implemetentations of {@link ProcessesContentItem} (see + * {@link #contentItemProcessingModels}). + * + * Models implementing SHOULD call this method in their getters before + * returning a value. There is not need to wrap the invocation of this + * method in an {@code if} statement, the method will only run its logic if + * the item has not been initialized. + * + * If there is no current item, the method will do nothing. + */ @Transactional(Transactional.TxType.REQUIRED) - private Optional getOrRetrieveContentItem() { - if (contentItem == null) { - contentItem = retrieveContentItem().map(this::buildModelData); + public void init() { + if (contentItem != null) { + return; + } + + final Optional item = retrieveContentItem(); + contentItem = item.map(this::buildModelData); + + final Iterator iterator + = contentItemProcessingModels.iterator(); + while (iterator.hasNext()) { + final ProcessesContentItem model = iterator.next(); + model.init(item.orElse(null)); } - return contentItem; } + /** + * Helper method for retrieving the current content item. If + * {@link #itemName} is {@code null} or {@code index} the method will return + * if the index item of the current category. If the category has not index + * item, an empty {@link Optional} is returned. + * + * If {@link #itemName} is not {@code null} or {@code index}, the + * method will try to find a content item associated to the category where + * {@link ContentItem#name} matches {@link #itemName}. + * + * @return The current content item if any, or an empyty {@link Optional}. + * + * @see PagesService#findIndexItem(org.libreccm.categorization.Category, + * org.librecms.contentsection.ContentItemVersion) + * @see + * PagesService#findCategorizedItem(org.libreccm.categorization.Category, + * java.lang.String, org.librecms.contentsection.ContentItemVersion) + */ @Transactional(Transactional.TxType.REQUIRED) - protected Optional retrieveContentItem() { + private Optional retrieveContentItem() { final Optional item; if (itemName == null || "index".equals(itemName)) { item = pagesService.findIndexItem( @@ -245,6 +431,15 @@ public class ContentItemModel { return item; } + /** + * Helper method for building an instance {@link ContentItemModelData} for a + * {@link ContentItem}. + * + * @param item The source content item. + * + * @return An instance of {@link ContentItemModelData} for the provided + * content item. + */ private ContentItemModelData buildModelData(final ContentItem item) { final ContentItemModelData data = new ContentItemModelData(); data.setObjectId(item.getObjectId()); @@ -292,6 +487,12 @@ public class ContentItemModel { return data; } + /** + * Encapsulates the data of content item provided by this model. To avoid to + * have to many fields in this model class we use an internal class to + * encapsulate the data. The getters of the model class return the values + * from an instance of this class. + */ private class ContentItemModelData { private long objectId; 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 index 81b4d10b9..e17c42f2e 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemTypeModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ContentItemTypeModel.java @@ -43,7 +43,7 @@ import javax.transaction.Transactional; */ @RequestScoped @Named("CmsPagesContentItemTypeModel") -public class ContentItemTypeModel { +public class ContentItemTypeModel implements ProcessesContentItem { @Inject private ContentItemModel contentItemModel; @@ -54,48 +54,55 @@ public class ContentItemTypeModel { private Optional contentType; public long getContentTypeId() { - return getOrRetrieveContentType() + contentItemModel.init(); + + return contentType .map(ContentItemTypeModelData::getTypeId) .orElse(0L); } public String getUuid() { - return getOrRetrieveContentType() + contentItemModel.init(); + + return contentType .map(ContentItemTypeModelData::getUuid) .orElse(""); } public String getDisplayName() { - return getOrRetrieveContentType() + contentItemModel.init(); + + return contentType .map(ContentItemTypeModelData::getDisplayName) .orElse(""); } public String getLabel() { - return getOrRetrieveContentType() + contentItemModel.init(); + + return contentType .map(ContentItemTypeModelData::getLabel) .orElse(""); } public String getDescription() { - return getOrRetrieveContentType() + contentItemModel.init(); + + return contentType .map(ContentItemTypeModelData::getDescription) .orElse(""); } - private Optional getOrRetrieveContentType() { - if (contentType == null) { - retrieveContentType(); - } - return contentType; - } - @Transactional(Transactional.TxType.REQUIRED) - private void retrieveContentType() { - contentType = contentItemModel.retrieveContentItem() + public void init(final ContentItem contentItem) { + if (contentType != null) { + return; + } + + contentType = Optional + .ofNullable(contentItem) .map(ContentItem::getContentType) .map(this::buildModelData); - } @Transactional(Transactional.TxType.REQUIRED) diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/EventModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/EventModel.java index 3c34c6b03..3e9562130 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/EventModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/EventModel.java @@ -31,8 +31,6 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import javax.transaction.Transactional; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; /** * @@ -40,7 +38,7 @@ import javax.ws.rs.core.Response; */ @RequestScoped @Named("CmsPagesEventModel") -public class EventModel { +public class EventModel implements ProcessesContentItem { @Inject private ContentItemModel contentItemModel; @@ -48,6 +46,8 @@ public class EventModel { @Inject private GlobalizationHelper globalizationHelper; + private boolean initialized; + private final DateTimeFormatter isoDateTimeFormatter; private String title; @@ -65,77 +65,67 @@ public class EventModel { private String eventType; public EventModel() { + initialized = false; isoDateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME .withZone(ZoneId.systemDefault()); } @Transactional(Transactional.TxType.REQUIRED) public String getTitle() { - if (title == null) { - init(); - } + contentItemModel.init(); return title; } @Transactional(Transactional.TxType.REQUIRED) public String getText() { - if (text == null) { - init(); - } + contentItemModel.init(); return text; } @Transactional(Transactional.TxType.REQUIRED) public String getStartDateTime() { - if (startDateTime == null) { - init(); - } + contentItemModel.init(); return startDateTime; } @Transactional(Transactional.TxType.REQUIRED) public String getEndDateTime() { - if (endDateTime == null) { - init(); - } + contentItemModel.init(); return endDateTime; } @Transactional(Transactional.TxType.REQUIRED) public String getEventDate() { - if (eventDate == null) { - init(); - } + contentItemModel.init(); + return eventDate; } @Transactional(Transactional.TxType.REQUIRED) public String getLocation() { - if (location == null) { - init(); - } + contentItemModel.init(); return location; } @Transactional(Transactional.TxType.REQUIRED) public String getEventType() { - if (eventType == null) { - init(); - } + contentItemModel.init(); - return eventDate; + return eventType; } + @Override @Transactional(Transactional.TxType.REQUIRED) - protected void init() { - final ContentItem contentItem = contentItemModel - .retrieveContentItem() - .orElse(null); + public void init(final ContentItem contentItem) { + if (initialized) { + return; + } + if (contentItem instanceof Event) { final Event event = (Event) contentItem; @@ -169,15 +159,9 @@ public class EventModel { .ofNullable(event.getEventType()) .map(globalizationHelper::getValueFromLocalizedString) .orElse(""); - } else { - throw new WebApplicationException( - "Current content item is not an event", - Response - .status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("Current content item is not an event.") - .build() - ); } + + initialized = true; } } diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/MultiPartArticleModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/MultiPartArticleModel.java index 2ca5e77ec..34d426507 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/MultiPartArticleModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/MultiPartArticleModel.java @@ -41,7 +41,7 @@ import javax.ws.rs.core.Response; */ @RequestScoped @Named("CmsPagesMultiPartArticleModel") -public class MultiPartArticleModel { +public class MultiPartArticleModel implements ProcessesContentItem { @Inject private ContentItemModel contentItemModel; @@ -49,6 +49,8 @@ public class MultiPartArticleModel { @Inject private GlobalizationHelper globalizationHelper; + private boolean initialized; + @Inject private PageUrlModel pageUrlModel; @@ -61,69 +63,62 @@ public class MultiPartArticleModel { private String currentSectionTitle; private String currentSectionText; - + private List sections; + public MultiPartArticleModel() { + initialized = false; + } + @Transactional(Transactional.TxType.REQUIRED) public String getTitle() { - if (title == null) { - init(); - } - + contentItemModel.init(); + return title; } @Transactional(Transactional.TxType.REQUIRED) public String getSummary() { - if (summary == null) { - return null; - } - + contentItemModel.init(); + return summary; } - @Transactional(Transactional.TxType.REQUIRED) public List getSectionTitles() { - if (sectionTitles == null) { - init(); - } - + contentItemModel.init(); + return Collections.unmodifiableList(sectionTitles); } @Transactional(Transactional.TxType.REQUIRED) public String getCurrentSectionTitle() { - if (currentSectionTitle == null) { - init(); - } - + contentItemModel.init(); + return currentSectionTitle; } @Transactional(Transactional.TxType.REQUIRED) public String getCurrentSectionText() { - if (currentSectionText == null) { - init(); - } - + contentItemModel.init(); + return currentSectionText; } @Transactional(Transactional.TxType.REQUIRED) public List getSections() { - if (sections == null) { - init(); - } - + contentItemModel.init(); + return Collections.unmodifiableList(sections); } + @Override @Transactional(Transactional.TxType.REQUIRED) - protected void init() { - final ContentItem contentItem = contentItemModel - .retrieveContentItem() - .orElse(null); + public void init(final ContentItem contentItem) { + if (initialized) { + return; + } + if (contentItem instanceof MultiPartArticle) { final MultiPartArticle mpa = (MultiPartArticle) contentItem; @@ -165,26 +160,20 @@ public class MultiPartArticleModel { .ofNullable(mpa.getSections().get(currentSection).getTitle()) .map(globalizationHelper::getValueFromLocalizedString) .orElse(""); - + currentSectionText = Optional .ofNullable(mpa.getSections().get(currentSection).getText()) .map(globalizationHelper::getValueFromLocalizedString) .orElse(""); - + sections = mpa .getSections() .stream() .map(this::buildSectionModel) .collect(Collectors.toList()); - } else { - throw new WebApplicationException( - "Current content item is not an MultiPartArticle", - Response - .status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("Current content item is not an MultiPartArticle.") - .build() - ); } + + initialized = true; } private int readCurrentSection() { diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/NewsModel.java b/ccm-cms/src/main/java/org/librecms/pages/models/NewsModel.java index f88c421bd..426a9aef7 100644 --- a/ccm-cms/src/main/java/org/librecms/pages/models/NewsModel.java +++ b/ccm-cms/src/main/java/org/librecms/pages/models/NewsModel.java @@ -22,7 +22,6 @@ import org.libreccm.l10n.GlobalizationHelper; import org.librecms.contentsection.ContentItem; import org.librecms.contenttypes.News; -import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; @@ -32,8 +31,6 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import javax.transaction.Transactional; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; /** * @@ -41,79 +38,79 @@ import javax.ws.rs.core.Response; */ @RequestScoped @Named("CmsPagesNewsModel") -public class NewsModel { +public class NewsModel implements ProcessesContentItem { @Inject private ContentItemModel contentItemModel; @Inject private GlobalizationHelper globalizationHelper; - + + private boolean initialized; + private final DateTimeFormatter isoDateTimeFormatter; - + private String title; - + private String description; - + private String text; - + private String releaseDateTime; - + private boolean homepage; - + public NewsModel() { + initialized = false; isoDateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME .withZone(ZoneId.systemDefault()); } @Transactional(Transactional.TxType.REQUIRED) public String getTitle() { - if (title == null) { - init(); - } - + contentItemModel.init(); + return title; } @Transactional(Transactional.TxType.REQUIRED) public String getDescription() { - if (description == null) { - init(); - } - + contentItemModel.init(); + return description; } @Transactional(Transactional.TxType.REQUIRED) public String getText() { - if (text == null) { - init(); - } + contentItemModel.init(); + return text; } @Transactional(Transactional.TxType.REQUIRED) public String getReleaseDateTime() { - if (releaseDateTime == null) { - init(); - } - + contentItemModel.init(); + return releaseDateTime; } - - @Transactional(Transactional.TxType.REQUIRED) - public boolean getHomepage() { - return homepage; - } @Transactional(Transactional.TxType.REQUIRED) - protected void init() { - final ContentItem contentItem = contentItemModel - .retrieveContentItem() - .orElse(null); + public boolean getHomepage() { + contentItemModel.init(); + + return homepage; + } + + @Override + @Transactional(Transactional.TxType.REQUIRED) + public void init(final ContentItem contentItem) { + if (initialized) { + return; + } + if (contentItem instanceof News) { - final News news = (News) contentItem; - + final News news = (News) contentItem; + title = Optional .ofNullable(news.getTitle()) .map(globalizationHelper::getValueFromLocalizedString) @@ -132,15 +129,9 @@ public class NewsModel { .map(isoDateTimeFormatter::format) .orElse(""); homepage = news.isHomepage(); - } else { - throw new WebApplicationException( - "Current content item is not a news item.", - Response - .status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("Current content item is not a new item.") - .build() - ); } + + initialized = true; } } diff --git a/ccm-cms/src/main/java/org/librecms/pages/models/ProcessesContentItem.java b/ccm-cms/src/main/java/org/librecms/pages/models/ProcessesContentItem.java new file mode 100644 index 000000000..9d3714e29 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/pages/models/ProcessesContentItem.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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.hibernate.LazyInitializationException; +import org.librecms.contentsection.ContentItem; + +import javax.transaction.Transactional; + +/** + * Models that provide data for the current content item (either the index item + * of a category or a selected content item) SHOULD implement this interface. + * + * This {@link ContentItemModel} collects all instances of classes implementing + * this interface and calls their implemententation of the + * {@link #init(org.librecms.contentsection.ContentItem)} method. + * + * For an example of an implementation please look at + * {@link ContentItemTypeModel}, {@link ArticleModel} or + * at {@link MultiPartArticleModel} for a more complex model. + * + * @author Jens Pelzetter + */ +public interface ProcessesContentItem { + + /** + * Initalizes a model providing the data of a content item for MVC + * templates. + * + * If the implementing model only provides data for specific sub classes of + * {@link ContentItem} the implementation MUST check the correct type using + * the {@code instanceof} operator. If the item is not a the correct type a + * implementation MUST gracefully ignore the item. + * + * To avoid a {@link LazyInitializationException} implementations SHOULD be + * annotated with {@link Transactional} and the + * {@link Transactional.TxType#REQUIRED}. + * + * @param item The item. + */ + void init(ContentItem item); + +}