From ee233fefe37e58f562bc82f0d1ac1048adad45f8 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Thu, 10 Feb 2022 20:14:17 +0100 Subject: [PATCH] Fixed some problems with the edit steps for news. --- .../java/org/librecms/contenttypes/News.java | 13 +- .../documents/CmsMvcAuthoringSteps.java | 8 +- .../article/MvcArticleTextBodyStep.java | 4 - .../event/MvcEventCreateStep.java | 2 - .../contenttypes/news/MvcNewsCreateStep.java | 401 ++++++++++++ .../news/MvcNewsPropertiesStep.java | 618 ++++++++++++++++++ .../news/MvcNewsPropertiesStepModel.java | 116 ++++ .../news/MvcNewsTextBodyStep.java | 393 +++++++++++ .../news/MvcNewsTextBodyStepModel.java | 114 ++++ .../news/MvcNewsTextBodyStepResources.java | 143 ++++ .../contenttypes/news/NewsMessageBundle.java | 43 ++ .../contenttypes/news/NewsStepsConstants.java | 34 + .../article/article-text/view.xhtml | 2 +- .../ui/contenttypes/news/create-news.xhtml | 96 +++ .../news/news-basic-properties.xhtml | 214 ++++++ .../news/news-text/available-languages.xhtml | 40 ++ .../ui/contenttypes/news/news-text/edit.xhtml | 43 ++ .../ui/contenttypes/news/news-text/view.xhtml | 37 ++ .../h2/V7_0_0_29__newsdate_to_timestamp.sql | 1 + .../V7_0_0_30__news_aud_date_to_timestamp.sql | 1 + .../V7_0_0_29__newsdate_to_timestamp.sql | 1 + .../V7_0_0_30__news_aud_date_to_timestamp.sql | 1 + .../ui/contenttypes/NewsBundle.properties | 109 +++ .../ui/contenttypes/NewsBundle_de.properties | 116 ++++ .../content-sections/news-text-step.ts | 52 ++ ccm-cms/webpack.config.js | 3 +- 26 files changed, 2595 insertions(+), 10 deletions(-) create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsCreateStep.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStep.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStepModel.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStep.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepModel.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepResources.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsMessageBundle.java create mode 100644 ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsStepsConstants.java create mode 100644 ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/create-news.xhtml create mode 100644 ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-basic-properties.xhtml create mode 100644 ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/available-languages.xhtml create mode 100644 ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/edit.xhtml create mode 100644 ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/view.xhtml create mode 100644 ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_29__newsdate_to_timestamp.sql create mode 100644 ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_30__news_aud_date_to_timestamp.sql create mode 100644 ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_29__newsdate_to_timestamp.sql create mode 100644 ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_30__news_aud_date_to_timestamp.sql create mode 100644 ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle.properties create mode 100644 ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle_de.properties create mode 100644 ccm-cms/src/main/typescript/content-sections/news-text-step.ts diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/News.java b/ccm-cms/src/main/java/org/librecms/contenttypes/News.java index c2175dd48..18a9d90a0 100644 --- a/ccm-cms/src/main/java/org/librecms/contenttypes/News.java +++ b/ccm-cms/src/main/java/org/librecms/contenttypes/News.java @@ -26,6 +26,10 @@ import org.hibernate.envers.Audited; import org.libreccm.l10n.LocalizedString; import org.librecms.CmsConstants; import org.librecms.contentsection.ContentItem; +import org.librecms.ui.contentsections.documents.MvcAuthoringKit; +import org.librecms.ui.contenttypes.news.MvcNewsCreateStep; +import org.librecms.ui.contenttypes.news.MvcNewsPropertiesStep; +import org.librecms.ui.contenttypes.news.MvcNewsTextBodyStep; import java.io.Serializable; import java.util.Date; @@ -74,6 +78,13 @@ import static org.librecms.CmsConstants.*; order = 2 ) }) +@MvcAuthoringKit( + createStep = MvcNewsCreateStep.class, + authoringSteps = { + MvcNewsPropertiesStep.class, + MvcNewsTextBodyStep.class + } +) public class News extends ContentItem implements Serializable { private static final long serialVersionUID = -4939565845920227974L; @@ -96,7 +107,7 @@ public class News extends ContentItem implements Serializable { */ @Column(name = "NEWS_DATE", nullable = false) @NotNull - @Temporal(TemporalType.DATE) + @Temporal(TemporalType.TIMESTAMP) private Date releaseDate; /** diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java index fcd8672a4..b55dd20ea 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java @@ -31,6 +31,9 @@ import org.librecms.ui.contenttypes.event.MvcEventPropertiesStep; import org.librecms.ui.contenttypes.mpa.MpaSectionsResources; import org.librecms.ui.contenttypes.mpa.MvcMpaPropertiesStep; import org.librecms.ui.contenttypes.mpa.MvcMpaSectionsStep; +import org.librecms.ui.contenttypes.news.MvcNewsPropertiesStep; +import org.librecms.ui.contenttypes.news.MvcNewsTextBodyStep; +import org.librecms.ui.contenttypes.news.MvcNewsTextBodyStepResources; import java.util.Set; @@ -57,7 +60,9 @@ public class CmsMvcAuthoringSteps implements MvcAuthoringSteps { MvcEventPropertiesStep.class, MvcEventInfoStep.class, MvcMpaPropertiesStep.class, - MvcMpaSectionsStep.class + MvcMpaSectionsStep.class, + MvcNewsPropertiesStep.class, + MvcNewsTextBodyStep.class ); } @@ -68,6 +73,7 @@ public class CmsMvcAuthoringSteps implements MvcAuthoringSteps { MvcArticleTextBodyStepResources.class, MvcEventInfoStepResources.class, MpaSectionsResources.class, + MvcNewsTextBodyStepResources.class, RelatedInfoStepService.class ); } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/article/MvcArticleTextBodyStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/article/MvcArticleTextBodyStep.java index 261c4db08..d77d9df6b 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/article/MvcArticleTextBodyStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/article/MvcArticleTextBodyStep.java @@ -18,8 +18,6 @@ */ package org.librecms.ui.contenttypes.article; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import org.libreccm.configuration.ConfigurationManager; import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.LocalizedString; @@ -27,7 +25,6 @@ import org.librecms.contentsection.ContentItemRepository; import org.librecms.contenttypes.Article; import org.librecms.ui.contentsections.ItemPermissionChecker; import org.librecms.ui.contentsections.documents.AbstractMvcAuthoringStep; -import org.librecms.ui.contentsections.documents.CmsEditorLocaleVariantRow; import org.librecms.ui.contentsections.ContentSectionNotFoundException; import org.librecms.ui.contentsections.documents.CmsEditorUtil; import org.librecms.ui.contentsections.documents.DocumentNotFoundException; @@ -53,7 +50,6 @@ import javax.ws.rs.PathParam; import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef; -import java.util.StringTokenizer; /** * Authoring step for editing the main text of an {@link Article}. diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/event/MvcEventCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/event/MvcEventCreateStep.java index 298f4e985..a22d80805 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/event/MvcEventCreateStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/event/MvcEventCreateStep.java @@ -27,10 +27,8 @@ import org.librecms.contentsection.ContentItemRepository; import org.librecms.contenttypes.Event; import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; -import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Date; diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsCreateStep.java new file mode 100644 index 000000000..1b08c0231 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsCreateStep.java @@ -0,0 +1,401 @@ +/* + * 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.ui.contenttypes.news; + +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.l10n.LocalizedString; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.workflow.Workflow; +import org.librecms.contentsection.ContentItemInitializer; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contenttypes.Article; +import org.librecms.contenttypes.News; +import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.chrono.IsoChronology; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; +import javax.transaction.Transactional; + +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsNewsCreateStep") +public class MvcNewsCreateStep + extends AbstractMvcDocumentCreateStep { + + private static final String FORM_PARAM_NAME = "name"; + + private static final String FORM_PARAM_TITLE = "title"; + + private static final String FORM_PARAM_SUMMARY = "summary"; + + private static final String FORM_PARAM_INITIAL_LOCALE = "locale"; + + private static final String FORM_PARAM_SELECTED_WORKFLOW = "workflow"; + + private static final String FORM_PARAM_RELEASE_DATE = "releaseDate"; + + /** + * Provides functions for working with content items. + */ + @Inject + private ContentItemManager itemManager; + + /** + * Used to save the news. + */ + @Inject + private ContentItemRepository itemRepo; + + /** + * Provides functions for working with {@link LocalizedString}s. + */ + @Inject + private GlobalizationHelper globalizationHelper; + + /** + * Name of the news. + */ + private String name; + + /** + * Title of the news. + */ + private String title; + + /** + * Summary of the news. + */ + private String summary; + + /** + * The initial locale of the news. + */ + private String initialLocale; + + /** + * The workflow to use for the new news. + */ + private String selectedWorkflow; + + /** + * The release date of the news as ISO 8601 date/time string + */ + private String releaseDate; + + public MvcNewsCreateStep() { + super(); + + final DateTimeFormatter dateTimeFormatter + = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE) + .appendLiteral('T') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .toFormatter() + .withZone(ZoneId.systemDefault()); + releaseDate = dateTimeFormatter.format(LocalDateTime.now()); + } + + @Override + public String getDocumentType() { + return News.class.getName(); + } + + @Override + public String getDescription() { + return globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.description"); + } + + @Override + public String getBundle() { + return NewsStepsConstants.BUNDLE; + } + + public String getName() { + return name; + } + + public String getTitle() { + return title; + } + + public String getSummary() { + return summary; + } + + public String getInitialLocale() { + return initialLocale; + } + + public String getReleaseDate() { + return releaseDate; + } + + @Transactional(Transactional.TxType.REQUIRED) + public String getSelectedWorkflow() { + if (selectedWorkflow == null || selectedWorkflow.isEmpty()) { + return getContentSection() + .getContentTypes() + .stream() + .filter( + type -> type.getContentItemClass().equals( + Article.class.getName() + ) + ) + .findAny() + .map(type -> type.getDefaultWorkflow()) + .map( + workflow -> globalizationHelper.getValueFromLocalizedString( + workflow.getName() + ) + ) + .orElse(""); + } else { + return selectedWorkflow; + } + } + + @Override + public String showCreateStep() { + return "org/librecms/ui/contenttypes/news/create-news.xhtml"; + } + + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + @Override + public String createItem(final Map formParams) { + if (!formParams.containsKey(FORM_PARAM_NAME) + || formParams.get(FORM_PARAM_NAME) == null + || formParams.get(FORM_PARAM_NAME).length == 0) { + addMessage( + "danger", + globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.name.error.missing") + ); + return showCreateStep(); + } + + name = formParams.get(FORM_PARAM_NAME)[0]; + if (!name.matches("^([a-zA-Z0-9_-]*)$")) { + addMessage( + "danger", + globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.name.error.invalid") + ); + return showCreateStep(); + } + + if (!formParams.containsKey(FORM_PARAM_TITLE) + || formParams.get(FORM_PARAM_TITLE) == null + || formParams.get(FORM_PARAM_TITLE).length == 0) { + addMessage( + "danger", + globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.title.error.missing") + ); + return showCreateStep(); + } + title = formParams.get(FORM_PARAM_TITLE)[0]; + + if (!formParams.containsKey(FORM_PARAM_SUMMARY) + || formParams.get(FORM_PARAM_SUMMARY) == null + || formParams.get(FORM_PARAM_SUMMARY).length == 0) { + addMessage( + "danger", + globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.summary.error.missing") + ); + return showCreateStep(); + } + summary = formParams.get(FORM_PARAM_SUMMARY)[0]; + + if (!formParams.containsKey(FORM_PARAM_INITIAL_LOCALE) + || formParams.get(FORM_PARAM_INITIAL_LOCALE) == null + || formParams.get(FORM_PARAM_INITIAL_LOCALE).length == 0) { + addMessage( + "danger", + globalizationHelper.getLocalizedTextsUtil( + getBundle() + ).getText("createstep.initial_locale.error.missing") + ); + return showCreateStep(); + } + final Locale locale = new Locale( + formParams.get(FORM_PARAM_INITIAL_LOCALE)[0] + ); + + if (!formParams.containsKey(FORM_PARAM_RELEASE_DATE) + || formParams.get(FORM_PARAM_RELEASE_DATE) == null + || formParams.get(FORM_PARAM_RELEASE_DATE).length == 0) { + addMessage( + "danger", + globalizationHelper.getLocalizedTextsUtil( + getBundle() + ).getText("createstep.releasedate.error.missing") + ); + return showCreateStep(); + } + releaseDate = formParams.get(FORM_PARAM_RELEASE_DATE)[0]; + final DateTimeFormatter isoDateTimeFormatter + = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()); + + final LocalDateTime releaseDateTime; + try { + releaseDateTime = LocalDateTime.parse( + releaseDate, + isoDateTimeFormatter + ); + } catch (DateTimeParseException ex) { + addMessage( + "danger", + globalizationHelper + .getLocalizedTextsUtil(getBundle()) + .getText("createstep.releasedate.error.malformed") + ); + return showCreateStep(); + } + + if (!formParams.containsKey(FORM_PARAM_SELECTED_WORKFLOW) + || formParams.get(FORM_PARAM_SELECTED_WORKFLOW) == null + || formParams.get(FORM_PARAM_SELECTED_WORKFLOW).length == 0) { + addMessage( + "danger", + globalizationHelper.getLocalizedTextsUtil( + getBundle() + ).getText("createstep.workflow.none_selected") + ); + return showCreateStep(); + } + selectedWorkflow = formParams.get(FORM_PARAM_SELECTED_WORKFLOW)[0]; + + final Optional workflowResult = getContentSection() + .getWorkflowTemplates() + .stream() + .filter(template -> template.getUuid().equals(selectedWorkflow)) + .findAny(); + + if (workflowResult.isEmpty()) { + addMessage( + "danger", + globalizationHelper.getLocalizedTextsUtil( + getBundle() + ).getText("createstep.workflow.error.not_available") + ); + return showCreateStep(); + } + + if (!getMessages().isEmpty()) { + return showCreateStep(); + } + + final News news = itemManager.createContentItem( + name, + getContentSection(), + getFolder(), + workflowResult.get(), + News.class, + new NewsInitializer( + locale, + title, + summary, + Date.from( + releaseDateTime.toInstant( + ZoneId + .systemDefault() + .getRules() + .getOffset(releaseDateTime) + ) + ) + ), + locale + ); + + itemRepo.save(news); + + return String.format( + "redirect:/%s/documents/%s/%s/@news-basicproperties", + getContentSectionLabel(), + getFolderPath(), + name + ); + } + + private class NewsInitializer implements ContentItemInitializer { + + private final Locale locale; + + private final String title; + + private final String description; + + private final Date releaseDate; + + public NewsInitializer( + final Locale locale, + final String title, + final String description, + final Date releaseDate + ) { + this.locale = locale; + this.title = title; + this.description = description; + this.releaseDate = releaseDate; + } + + @Override + public void initializeValues(final News news) { + news.getTitle().putValue(locale, title); + news.getDescription().putValue(locale, description); + news.setReleaseDate(releaseDate); + } + + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStep.java new file mode 100644 index 000000000..f7ee16ea2 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStep.java @@ -0,0 +1,618 @@ +/* + * 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.ui.contenttypes.news; + +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.l10n.LocalizedString; +import org.libreccm.security.AuthorizationRequired; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.FolderManager; +import org.librecms.contenttypes.News; +import org.librecms.ui.contentsections.ContentSectionNotFoundException; +import org.librecms.ui.contentsections.ItemPermissionChecker; +import org.librecms.ui.contentsections.documents.AbstractMvcAuthoringStep; +import org.librecms.ui.contentsections.documents.DocumentNotFoundException; +import org.librecms.ui.contentsections.documents.DocumentUi; +import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.mvc.Models; +import javax.transaction.Transactional; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * Authoring step for editing the basic properties of a {@link News} item. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "news-basicproperties") +@Controller +@MvcAuthoringStepDef( + bundle = NewsStepsConstants.BUNDLE, + descriptionKey = "authoringsteps.basicproperties.description", + labelKey = "authoringsteps.basicproprties.label", + supportedDocumentType = News.class +) +public class MvcNewsPropertiesStep extends AbstractMvcAuthoringStep { + + @Inject + private NewsMessageBundle newsMessageBundle; + + /** + * Used for retrieving and saving the news. + */ + @Inject + private ContentItemRepository itemRepo; + + /** + * Provides functions for working with content items. + */ + @Inject + private ContentItemManager itemManager; + + @Inject + private DocumentUi documentUi; + + /** + * Provides functions for working with folders. + */ + @Inject + private FolderManager folderManager; + + /** + * Provides functions for working with {@link LocalizedString}s. + */ + @Inject + private GlobalizationHelper globalizationHelper; + + @Inject + private ItemPermissionChecker itemPermissionChecker; + + @Inject + private MvcNewsPropertiesStepModel newsPropertiesStepModel; + + @Inject + private Models models; + + @Override + public Class getStepClass() { + return MvcNewsPropertiesStep.class; + } + + @Override + @Transactional(Transactional.TxType.REQUIRED) + protected void init() throws ContentSectionNotFoundException, + DocumentNotFoundException { + super.init(); + + newsPropertiesStepModel.setName(getDocument().getDisplayName()); + + newsPropertiesStepModel.setTitleValues( + getDocument() + .getTitle() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + Map.Entry::getValue + ) + ) + ); + + final Set titleLocales = getDocument() + .getTitle() + .getAvailableLocales(); + newsPropertiesStepModel.setUnusedTitleLocales( + globalizationHelper + .getAvailableLocales() + .stream() + .filter(locale -> !titleLocales.contains(locale)) + .map(Locale::toString) + .collect(Collectors.toList()) + ); + + newsPropertiesStepModel.setDescriptionValues( + getDocument() + .getDescription() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + Map.Entry::getValue + ) + ) + ); + + final Set descriptionLocales = getDocument() + .getDescription() + .getAvailableLocales(); + newsPropertiesStepModel.setUnusedDescriptionLocales( + globalizationHelper + .getAvailableLocales() + .stream() + .filter(locale -> !descriptionLocales.contains(locale)) + .map(Locale::toString) + .collect(Collectors.toList()) + ); + + final News news = (News) getDocument(); + final DateTimeFormatter isoDateFormatter + = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.systemDefault()); + final DateTimeFormatter isoTimeFormatter + = DateTimeFormatter.ISO_TIME.withZone(ZoneId.systemDefault()); + final DateTimeFormatter isoDateTimeFormatter + = DateTimeFormatter.ISO_LOCAL_DATE_TIME + .withZone(ZoneId.systemDefault()); + + newsPropertiesStepModel.setReleaseDate( + Optional + .ofNullable(news.getReleaseDate()) + .map(date -> date.toInstant()) + .map(date -> isoDateTimeFormatter.format(date)) + .orElse("") + ); + newsPropertiesStepModel.setFormattedReleaseDate( + Optional + .ofNullable(news.getReleaseDate()) + .map(date -> date.toInstant()) + .map( + date -> String.format( + "%s %s", + isoDateFormatter.format(date), + isoTimeFormatter.format(date) + ) + ) + .orElse("") + ); + + } + + @GET + @Path("/") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + return "org/librecms/ui/contenttypes/news/news-basic-properties.xhtml"; + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + newsMessageBundle.getMessage("news.edit.denied") + ); + } + } + + /** + * Updates the name of the current news. + * + * @param sectionIdentifier + * @param documentPath + * @param name + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/name") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String updateName( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("name") @DefaultValue("") final String name + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + if (name.isEmpty() || name.isBlank()) { + models.put("nameMissing", true); + + return showStep(sectionIdentifier, documentPath); + } + + getDocument().setDisplayName(name); + itemRepo.save(getDocument()); + + updateDocumentPath(); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Adds a localized title to a news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated title value. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/title/@add") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String addTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("locale") final String localeParam, + @FormParam("value") final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getTitle().putValue(locale, value); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Updates a localized title of the news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated title value. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/title/@edit/{locale}") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String editTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam, + @FormParam("value") final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getTitle().putValue(locale, value); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes a localized title of the news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to remove. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/title/@remove/{locale}") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String removeTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getTitle().removeValue(locale); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Adds a localized description to the news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the description. + * @param value The description value. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/description/@add") + @Transactional(Transactional.TxType.REQUIRED) + public String addDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("locale") final String localeParam, + @FormParam("value") final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getDescription().putValue(locale, value); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Updates a localized description of the news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated description value. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/description/@edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String editDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam, + @FormParam("value") final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getDescription().putValue(locale, value); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + /** + * Removes a localized description of the news. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to remove. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/description/@remove/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String removeDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getDescription().removeValue(locale); + itemRepo.save(getDocument()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + + @POST + @Path("/releasedate") + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public String updateReleaseDate( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("releaseDate") @DefaultValue("") final String releaseDate + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getDocument())) { + if (releaseDate.isBlank()) { + models.put("releaseDateMissing", true); + + return showStep(sectionIdentifier, documentPath); + } + + final News news = (News) getDocument(); + final DateTimeFormatter isoDateTimeFormatter + = DateTimeFormatter.ISO_DATE_TIME.withZone( + ZoneId.systemDefault() + ); + + final LocalDateTime releaseDateTime; + try { + releaseDateTime = LocalDateTime.parse( + releaseDate, + isoDateTimeFormatter + ); + } catch (DateTimeParseException ex) { + models.put("releaseDateInvalid", true); + return showStep(sectionIdentifier, documentPath); + } + + news.setReleaseDate( + Date.from( + releaseDateTime.toInstant( + ZoneId.systemDefault().getRules().getOffset( + releaseDateTime + ) + ) + ) + ); + + itemRepo.save(news); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getDocument(), + getLabel() + ); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStepModel.java new file mode 100644 index 000000000..76c1989ed --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsPropertiesStepModel.java @@ -0,0 +1,116 @@ +/* + * 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.ui.contenttypes.news; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsNewsPropertiesStep") +public class MvcNewsPropertiesStepModel { + + private String name; + + private Map titleValues; + + private List unusedTitleLocales; + + private Map descriptionValues; + + private List unusedDescriptionLocales; + + private String releaseDate; + + private String formattedReleaseDate; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public Map getTitleValues() { + return Collections.unmodifiableMap(titleValues); + } + + public void setTitleValues(final Map titleValues) { + this.titleValues = new HashMap<>(titleValues); + } + + public List getUnusedTitleLocales() { + return Collections.unmodifiableList(unusedTitleLocales); + } + + public void setUnusedTitleLocales(List unusedTitleLocales) { + this.unusedTitleLocales = new ArrayList<>(unusedTitleLocales); + } + + public Map getDescriptionValues() { + return Collections.unmodifiableMap(descriptionValues); + } + + public void setDescriptionValues( + final Map descriptionValues + ) { + this.descriptionValues = new HashMap<>(descriptionValues); + } + + public List getUnusedDescriptionLocales() { + return Collections.unmodifiableList(unusedDescriptionLocales); + } + + public void setUnusedDescriptionLocales( + final List unusedDescriptionLocales + ) { + this.unusedDescriptionLocales = new ArrayList<>( + unusedDescriptionLocales + ); + } + + public String getReleaseDate() { + return releaseDate; + } + + public void setReleaseDate(final String releaseDate) { + this.releaseDate = releaseDate; + } + + public String getFormattedReleaseDate() { + return formattedReleaseDate; + } + + public void setFormattedReleaseDate(final String formattedReleaseDate) { + this.formattedReleaseDate = formattedReleaseDate; + } + + + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStep.java new file mode 100644 index 000000000..9a1626b10 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStep.java @@ -0,0 +1,393 @@ +/* + * 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.ui.contenttypes.news; + +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.l10n.LocalizedString; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contenttypes.News; +import org.librecms.ui.contentsections.ContentSectionNotFoundException; +import org.librecms.ui.contentsections.ItemPermissionChecker; +import org.librecms.ui.contentsections.documents.AbstractMvcAuthoringStep; +import org.librecms.ui.contentsections.documents.CmsEditorUtil; +import org.librecms.ui.contentsections.documents.DocumentNotFoundException; +import org.librecms.ui.contentsections.documents.DocumentUi; +import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; + +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.transaction.Transactional; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * Authoring step for editing the main text of an {@link News}. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "news-text") +@Controller +@MvcAuthoringStepDef( + bundle = NewsStepsConstants.BUNDLE, + descriptionKey = "authoringsteps.text.description", + labelKey = "authoringsteps.text.label", + supportedDocumentType = News.class +) +public class MvcNewsTextBodyStep extends AbstractMvcAuthoringStep { + + @Inject + private NewsMessageBundle newsMessageBundle; + + @Inject + private ConfigurationManager confManager; + + /** + * Used for retrieving and saving the news. + */ + @Inject + private ContentItemRepository itemRepo; + + @Inject + private DocumentUi documentUi; + + /** + * Provides functions for working with {@link LocalizedString}s. + */ + @Inject + private GlobalizationHelper globalizationHelper; + + @Inject + private ItemPermissionChecker itemPermissionChecker; + + @Inject + private MvcNewsTextBodyStepModel newsTextBodyStepModel; + + @Override + public Class getStepClass() { + return MvcNewsTextBodyStep.class; + } + + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getNews())) { + return "org/librecms/ui/contenttypes/news/news-text/available-languages.xhtml"; + } else { + return documentUi.showAccessDenied( + getContentSection(), + getNews(), + newsMessageBundle.getMessage("news.edit.denied") + ); + } + } + + /** + * View a preview of the text. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam + * + * @return The template for showing a preview of the text. + */ + @GET + @Path("/view/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String viewText( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + newsTextBodyStepModel.setSelectedLocale( + new Locale(localeParam).toString() + ); + + return "org/librecms/ui/contenttypes/news/news-text/view.xhtml"; + } + + /** + * Adds a localized main text. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/add") + @Transactional(Transactional.TxType.REQUIRED) + public String addTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @FormParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getNews())) { + final String value; + if (getNews().getText().getAvailableLocales().isEmpty()) { + value = ""; + } else { + value = globalizationHelper.getValueFromLocalizedString( + getNews().getText() + ); + } + final Locale locale = new Locale(localeParam); + getNews().getText().putValue(locale, value); + itemRepo.save(getNews()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getNews(), + getLabel() + ); + } + } + + @GET + @Path("/edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String editTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getNews())) { + newsTextBodyStepModel.setSelectedLocale( + new Locale(localeParam).toString() + ); + + return "org/librecms/ui/contenttypes/news/news-text/edit.xhtml"; + } else { + return documentUi.showAccessDenied( + getContentSection(), + getNews(), + newsMessageBundle.getMessage("news.edit.denied") + ); + } + } + + /** + * Updates a localized main text. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. + * @param value The text. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String editTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam, + @FormParam("value") final String value + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getNews())) { + final Locale locale = new Locale(localeParam); + getNews().getText().putValue(locale, value); + itemRepo.save(getNews()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getNews(), + getLabel() + ); + } + } + + /** + * Removes a localized main text. + * + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. + * + * @return A redirect to this authoring step. + */ + @POST + @Path("/remove/{locale}") + @Transactional(Transactional.TxType.REQUIRED) + public String removeTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPath, + @PathParam("locale") final String localeParam + ) { + try { + init(); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(getNews())) { + final Locale locale = new Locale(localeParam); + getNews().getText().removeValue(locale); + itemRepo.save(getNews()); + + return buildRedirectPathForStep(); + } else { + return documentUi.showAccessDenied( + getContentSection(), + getNews(), + getLabel() + ); + } + } + + @Override + protected void init() throws ContentSectionNotFoundException, + DocumentNotFoundException { + super.init(); + + if (itemPermissionChecker.canEditItem(getNews())) { + newsTextBodyStepModel.setCanEdit( + itemPermissionChecker.canEditItem(getNews()) + ); + newsTextBodyStepModel.setTitleValues( + getNews() + .getTitle() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + Map.Entry::getValue + ) + ) + ); + newsTextBodyStepModel.setTextValues( + getNews() + .getText() + .getValues() + .entrySet() + .stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), + Map.Entry::getValue + ) + ) + ); + + newsTextBodyStepModel.setVariants( + getNews() + .getText() + .getValues() + .entrySet() + .stream() + .map(CmsEditorUtil::buildVariantRow) + .collect(Collectors.toList()) + ); + + final Set locales = getNews() + .getText() + .getAvailableLocales(); + newsTextBodyStepModel.setUnusedLocales( + globalizationHelper + .getAvailableLocales() + .stream() + .filter(locale -> !locales.contains(locale)) + .map(Locale::toString) + .collect(Collectors.toList()) + ); + } + } + + private News getNews() { + return (News) getDocument(); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepModel.java new file mode 100644 index 000000000..3b633767a --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepModel.java @@ -0,0 +1,114 @@ +/* + * 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.ui.contenttypes.news; + +import org.librecms.ui.contentsections.documents.CmsEditorLocaleVariantRow; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsNewsTextBodyStep") +public class MvcNewsTextBodyStepModel { + + private boolean canEdit; + + private Map titleValues; + + private Map textValues; + + private List variants; + + private List unusedLocales; + + private String selectedLocale; + + public Map getTitleValues() { + return Collections.unmodifiableMap(titleValues); + } + + protected void setTitleValues(final Map titleValues) { + this.titleValues = new HashMap<>(titleValues); + } + + public String getTitle() { + return titleValues.get(selectedLocale); + } + + /** + * Get all localized values of the main text. + * + * @return The localized values of the main text. + */ + public Map getTextValues() { + return Collections.unmodifiableMap(textValues); + } + + protected void setTextValues(final Map textValues) { + this.textValues = new HashMap<>(textValues); + } + + /** + * Gets the locales for which the main text has not been defined yet. + * + * @return The locales for which the main text has not been defined yet. + */ + public List getVariants() { + return Collections.unmodifiableList(variants); + } + + protected void setVariants(final List variants) { + this.variants = new ArrayList<>(variants); + } + + public List getUnusedLocales() { + return Collections.unmodifiableList(unusedLocales); + } + + protected void setUnusedLocales(final List unusedLocales) { + this.unusedLocales = new ArrayList<>(unusedLocales); + } + + public String getSelectedLocale() { + return selectedLocale; + } + + protected void setSelectedLocale(final String selectedLocale) { + this.selectedLocale = selectedLocale; + } + + public boolean getCanEdit() { + return canEdit; + } + + protected void setCanEdit(final boolean canEdit) { + this.canEdit = canEdit; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepResources.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepResources.java new file mode 100644 index 000000000..178c8b4b0 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/MvcNewsTextBodyStepResources.java @@ -0,0 +1,143 @@ +/* + * 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.ui.contenttypes.news; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentSection; +import org.librecms.contenttypes.News; +import org.librecms.ui.contentsections.ContentSectionsUi; +import org.librecms.ui.contentsections.ItemPermissionChecker; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; + +import java.util.Locale; +import java.util.StringTokenizer; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; +import javax.ws.rs.ForbiddenException; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Resources used by the editor for {@link News#text}. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "news-text-resources") +public class MvcNewsTextBodyStepResources { + + /** + * Used for retrieving and saving the news. + */ + @Inject + private ContentItemRepository itemRepo; + + @Inject + private ContentSectionsUi sectionsUi; + + @Inject + private ItemPermissionChecker itemPermissionChecker; + + @GET + @Path("/variants/wordcount/{locale}") + @Produces(MediaType.TEXT_HTML) + @Transactional(Transactional.TxType.REQUIRED) + public String getWordCount( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPathParam, + @PathParam("locale") final String localeParam + ) { + final ContentSection contentSection = sectionsUi + .findContentSection(sectionIdentifier) + .orElseThrow( + () -> new NotFoundException() + ); + + final ContentItem document = itemRepo + .findByPath(contentSection, documentPathParam) + .orElseThrow( + () -> new NotFoundException() + ); + + if (!(document instanceof News)) { + throw new NotFoundException(); + } + + final News news = (News) document; + if (itemPermissionChecker.canEditItem(news)) { + final String text = news + .getText() + .getValue(new Locale(localeParam)); + final Document jsoupDoc = Jsoup.parseBodyFragment(text); + final long result = new StringTokenizer( + jsoupDoc.body().text() + ).countTokens(); + return Long.toString(result); + } else { + throw new ForbiddenException(); + } + } + + @GET + @Path("/variants/{locale}") + @Produces(MediaType.TEXT_HTML) + @Transactional(Transactional.TxType.REQUIRED) + public String viewTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME) + final String documentPathParam, + @PathParam("locale") final String localeParam + ) { + final ContentSection contentSection = sectionsUi + .findContentSection(sectionIdentifier) + .orElseThrow( + () -> new NotFoundException() + ); + + final ContentItem document = itemRepo + .findByPath(contentSection, documentPathParam) + .orElseThrow( + () -> new NotFoundException() + ); + + if (!(document instanceof News)) { + throw new NotFoundException(); + } + + final News news = (News) document; + if (itemPermissionChecker.canEditItem(news)) { + return news.getText().getValue(new Locale(localeParam)); + } else { + throw new ForbiddenException(); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsMessageBundle.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsMessageBundle.java new file mode 100644 index 000000000..1524433bb --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsMessageBundle.java @@ -0,0 +1,43 @@ +/* + * 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.ui.contenttypes.news; + +import org.libreccm.ui.AbstractMessagesBean; +import org.librecms.contenttypes.News; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * Message Bundle for the authoring steps for editing a {@link News}. + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("CmsNewsMessageBundle") +public class NewsMessageBundle extends AbstractMessagesBean { + + @Override + protected String getMessageBundle() { + return NewsStepsConstants.BUNDLE; + } + + + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsStepsConstants.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsStepsConstants.java new file mode 100644 index 000000000..b14dbbb42 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/news/NewsStepsConstants.java @@ -0,0 +1,34 @@ +/* + * 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.ui.contenttypes.news; + +/** + * Constants for the authoring steps for editing a {@link News}. + * + * @author Jens Pelzetter + */ +public final class NewsStepsConstants { + + private NewsStepsConstants() { + // Nothing + } + + public static final String BUNDLE = "org.librecms.ui.contenttypes.NewsBundle"; + +} diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/view.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/view.xhtml index 15dd43dc9..fd338cc39 100644 --- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/view.xhtml +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/view.xhtml @@ -20,7 +20,7 @@
+ href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/@edit/#{CmsArticleTextBodyStep.selectedLocale}"> #{CmsArticleMessageBundle['textstep.languages.edit']} diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/create-news.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/create-news.xhtml new file mode 100644 index 000000000..c0089e402 --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/create-news.xhtml @@ -0,0 +1,96 @@ +]> + + + + +
+

#{CmsNewsMessageBundle['createform.title']}

+ + + + + + +
+ + + + + + + + + + + + + + #{CmsNewsMessageBundle['createform.cancel']} + + + + +
+ +
+ +
+ + diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-basic-properties.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-basic-properties.xhtml new file mode 100644 index 000000000..8ef8bc6c5 --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-basic-properties.xhtml @@ -0,0 +1,214 @@ +]> + + + + + + +

#{CmsNewsMessageBundle.getMessage('basicproperties.header', [CmsNewsPropertiesStep.name])}

+ +

#{CmsNewsMessageBundle['basicproeperties.name.header']}

+
+
#{CmsNewsPropertiesStep.name}
+ + + +
+ + + + + +

#{CmsNewsMessageBundle['basicproperties.releasedate.header']}

+
+

#{CmsNewsPropertiesStep.formattedReleaseDate}

+ + + +
+ + + + + + + + + +
+ +
+ + diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/available-languages.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/available-languages.xhtml new file mode 100644 index 000000000..861f7c96b --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/available-languages.xhtml @@ -0,0 +1,40 @@ +]> + + + + + + +

#{CmsNewsMessageBundle['textstep.header']}

+ + + +
+ + + + + +
+ + + diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/edit.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/edit.xhtml new file mode 100644 index 000000000..2310070be --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/edit.xhtml @@ -0,0 +1,43 @@ +]> + + + + +
+ + + #{CmsNewsMessageBundle['textstep.back']} + +

#{CmsNewsMessageBundle.getMessage('textstep.header.edit',[CmsNewsTextBodyStep.selectedLocale])}

+
+ + + + + +
+ + + + +
+ + + diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/view.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/view.xhtml new file mode 100644 index 000000000..d76d34d9d --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/news/news-text/view.xhtml @@ -0,0 +1,37 @@ +]> + + + + +
+ + + #{CmsNewsMessageBundle['textstep.back']} + +

#{CmsNewsMessageBundle.getMessage('textstep.header.view',[CmsNewsTextBodyStep.title, CmsNewsTextBodyStep.selectedLocale])}

+
+ + + + +
+ +
+ +
+
+ + diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_29__newsdate_to_timestamp.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_29__newsdate_to_timestamp.sql new file mode 100644 index 000000000..e77cb3621 --- /dev/null +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_29__newsdate_to_timestamp.sql @@ -0,0 +1 @@ +alter table ccm_cms.news alter COLUMN news_date type timestamp; diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_30__news_aud_date_to_timestamp.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_30__news_aud_date_to_timestamp.sql new file mode 100644 index 000000000..7a38dcfa1 --- /dev/null +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_30__news_aud_date_to_timestamp.sql @@ -0,0 +1 @@ +alter table ccm_cms.news_aud alter COLUMN news_date type timestamp; diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_29__newsdate_to_timestamp.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_29__newsdate_to_timestamp.sql new file mode 100644 index 000000000..e77cb3621 --- /dev/null +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_29__newsdate_to_timestamp.sql @@ -0,0 +1 @@ +alter table ccm_cms.news alter COLUMN news_date type timestamp; diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_30__news_aud_date_to_timestamp.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_30__news_aud_date_to_timestamp.sql new file mode 100644 index 000000000..7a38dcfa1 --- /dev/null +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_30__news_aud_date_to_timestamp.sql @@ -0,0 +1 @@ +alter table ccm_cms.news_aud alter COLUMN news_date type timestamp; diff --git a/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle.properties b/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle.properties new file mode 100644 index 000000000..61a719403 --- /dev/null +++ b/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle.properties @@ -0,0 +1,109 @@ + +createstep.description=Creates a new news item. +createstep.name.error.missing=The name of the new news item is missing. +createstep.name.error.invalid=The provided name is invalid. +createstep.summary.error.missing=The summary of the new news item is missing. +createstep.title.error.missing=No title was provided for the new news item. +createstep.initial_locale.error.missing=No locale was selected. +createstep.releasedate.error.missing=The release date is missing. +createstep.releasedate.error.malformed=The release date is not a valid date/time. +createstep.workflow.none_selected=No workflow was selected. +createstep.workflow.error.not_available=The selected workflow is not available. +authoringsteps.basicproperties.description=Edit the basic properties of a news item. +authoringsteps.basicproprties.label=Basic properties +news.edit.denied=Access denied +authoringsteps.text.description=Edit the main text of a news. +authoringsteps.text.label=Main text +createform.title=Create a new news item +createform.name.help=The name of the new item. May only contain letter (a to z and A to Z, numbers, the underscore ("_") and the dash ("-"). +createform.name.label=Name +createform.initial_locale.help=The initial locale of the new news item. All localizable values provided in this form are created for the selected locale. +createform.initial_locale.label=Locale +createform.title.help=The title of the news +createform.title.label=Title +createform.summary.help=A short summary of the news. +createform.summary.label=Summary +createform.workflow.help=The workflow to use for the news. +createform.workflow.label=Workflow +createform.releasedate.help=The release date of the news. +createform.releasedate.label=Release date +createform.cancel=Cancel +createform.submit=Create news +basicproperties.header=Edit basic properties of news {0} +basicproeperties.name.header=Name +basicproperties.name.edit=Edit name +basicproperties.name.edit.title=Edit name +basicproperties.name.edit.close=Cancel +basicproperties.name.help=The name of the news item. May only contain letter (a to z and A to Z, numbers, the underscore ("_") and the dash ("-"). +basicproperties.name.label=Name +basicproperties.name.edit.submit=Submit +basicproperties.releasedate.header=Release Date +basicproperties.releasedate.edit=Edit release date +basicproperties.releasedate.edit.title=Edit release date +basicproperties.releasedate.edit.close=Cancel +basicproperties.releasedate.help=The release date of the news. +basicproperties.releasedate.label=Release date +basicproperties.releasedate.edit.submit=Submit +basicproperties.title.add=Add localized title +basicproperties.title.add.cancel=Cancel +basicproperties.title.add.locale.help=The locale of the new localized title. +basicproperties.title.add.locale.label=Title +basicproperties.title.add.submit=Add title value +basicproperties.title.add.header=Add localized title +basicproperties.title.add.value.help=The new localized title. +basicproperties.title.add.value.label=Localized title +basicproperties.title.edit=Edit +basicproperties.title.edit.cancel=Cancel +basicproperties.title.edit.submit=Save +basicproperties.title.edit.header=Edit localized title +basicproperties.title.edit.value.help=The localized title. +basicproperties.title.edit.value.label=Localized title +basicproperties.title.remove=Remove +basicproperties.title.remove.cancel=Cancel +basicproperties.title.remove.submit=Remove localized title +basicproperties.title.remove.text=Are your sure to remove the localized title for the following locale: +basicproperties.title.remove.header=Remove localized title +basicproperties.description.add=Add localized summary +basicproperties.description.add.cancel=Cancel +basicproperties.description.add.locale.help=The locale of the new localized summary. +basicproperties.description.add.locale.label=Locale +basicproperties.description.add.submit=Add localized summary +basicproperties.description.add.header=Add localized summary +basicproperties.description.add.value.help=The new localized summary. +basicproperties.description.add.value.label=Localized summary +basicproperties.description.edit=Edit +basicproperties.description.edit.cancel=Cancel +basicproperties.description.edit.submit=Save +basicproperties.description.edit.header=Edit localized summary +basicproperties.description.edit.value.help=The localized summary. +basicproperties.description.edit.value.label=Localized summary +basicproperties.description.remove=Remove +basicproperties.description.remove.cancel=Cancel +basicproperties.description.remove.submit=Remove localized summary +basicproperties.description.remove.text=Are you sure to remove the localized summary for this following locale? +basicproperties.description.remove.header=Remove localized summary +textstep.header.languages=Text of the Article - Available languages +textstep.languages.add_language.help=Select the language to add +textstep.languages.add_language.label=Add language +textstep.languages.add_language.submit=Add +textstep.languages.view=View +textstep.languages.edit=Edit +textstep.languages.remove=Remove +textstep.languages.remove.cancel=Cancel +textstep.languages.remove.confirm=Remove locale +textstep.languages.remove.title=Confirm removal of locale {0} +textstep.languages.remove.message=Are you sure to remove the text for locale {0}? +textstep.languages.th.language=Language +textstep.languages.th.actions=Actions +textstep.languages.none=No texts available +textstep.header.edit=Edit text for locale {0} +textstep.header.view=Text of Article {0} for locale {1} +textstep.back=Back to available languages +text.editor.add_variant=Add language +text.editor.add.locale.help=The language of the new variant. +text.editor.edit.value.help=The text of the article. +text.editor.edit.value.label=Text +textstep.header=Text of the News +basicproperties.title.header=Title +basicproperties.description.header=Summary +text.editor.header=Main text diff --git a/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle_de.properties b/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle_de.properties new file mode 100644 index 000000000..fa784973c --- /dev/null +++ b/ccm-cms/src/main/resources/org/librecms/ui/contenttypes/NewsBundle_de.properties @@ -0,0 +1,116 @@ + +createstep.description=Erstelle eine Neuigkeit (News) +createstep.name.error.missing=Der Name des neuen Items vom Type News wurde nicht angegeben. +createstep.name.error.invalid=Der angegebene Name is nicht zul\u00e4ssig. +createstep.summary.error.missing=Es wurde keine Zusammenfassung angegeben. +createstep.title.error.missing=Es wurde keine Titel angegeben. +createstep.initial_locale.error.missing=Es wurde keine Sprache ausgew\u00e4lt. +createstep.releasedate.error.missing=Es wurde kein Erscheinungsdatum angegeben. +createstep.releasedate.error.malformed=Das Erscheinungsdatum ist keine valide Datums-/Zeitangabe. +createstep.workflow.none_selected=Es wurde keine Arbeitsablauf ausgew\u00e4hlt. +createstep.workflow.error.not_available=Der ausgew\u00e4hlte Arbeitsablauf ist nicht verf\u00fcgbar. +authoringsteps.basicproperties.description=Bearbeiten der Basiseigenschaften einer Neuigkeit (News). +authoringsteps.basicproprties.label=Basiseigenschaften +news.edit.denied=Zugriff verweigert +authoringsteps.text.description=Haupttext einer News bearbeiten. +authoringsteps.text.label=Haupttext +createform.title=Neue Neuigkeit (News) anlegen +createform.name.help=Der Name des neuen Dokumentes. Darf nur Buchstaben (a bis z und A bis Z), Ziffern, den Unterstrich ("_") und den Bindestrich ("-") enthalten. +createform.name.label=Name +createform.initial_locale.help=Die initale Sprache der neuen News. Alle lokaliserbaren Eigenschaften aus diesem Formualar werden f\u00fcr die ausgew\u00e4hlte Sprache angelegt. +createform.initial_locale.label=Sprache +createform.title.help=Der Titel der News +createform.title.label=Titel +createform.summary.help=Eine kurze Zusammenfassung der News. +createform.summary.label=Zusammenfassung +createform.workflow.help=Der Arbeitsablauf der f\u00fcr die News genutzt wird. +createform.workflow.label=Arbeitsablauf +createform.releasedate.help=Das Erscheinungsdatum der News. +createform.releasedate.label=Erscheinungsdatum +createform.cancel=Abbrechen +createform.submit=News anlegen +basicproperties.header=Basiseigenschaften der News {0} bearbeiten +basicproeperties.name.header=Name +basicproperties.name.edit=Name bearbeiten +basicproperties.name.edit.title=Name bearbeiten +basicproperties.name.edit.close=Abbrechen +basicproperties.name.help=Der Name der News. Darf nur Buchstaben (a bis z und A bis Z), Ziffern, den Unterstrich ("_") und den Bindestrich ("-") enthalten. +basicproperties.name.label=Name +basicproperties.name.edit.submit=Speichern +basicproperties.releasedate.header=Erscheinungsdatum +basicproperties.releasedate.edit=Erscheinungsdatum bearbeiten +basicproperties.releasedate.edit.title=Erscheinungsdatum bearbeiten +basicproperties.releasedate.edit.close=Abbrechen +basicproperties.releasedate.help=Das Erscheinungsdatum der News. +basicproperties.releasedate.label=Erscheinungsdatum +basicproperties.releasedate.edit.submit=Speichern +basicproperties.title.add=Hinzuf\u00fcgen +basicproperties.title.add.cancel=Abbrechen +basicproperties.name.label=Name +basicproperties.header=Basiseigenschaften des Artikels {0} +basicproperties.name.edit.title=Name bearbeiten +basicproperties.name.edit.close=Abbrechen +basicproperties.name.edit.submit=Speichern +basicproperties.title.add=Lokalisierten Titel hinzuf\u00fcgen +basicproperties.title.add.cancel=Abbrechen +basicproperties.title.add.locale.help=The locale of the new localized title. +basicproperties.title.add.locale.label=Titel +basicproperties.title.add.submit=Titel hinzuf\u00fcgen +basicproperties.title.add.header=Lokalisierten Titel hinzuf\u00fcgen +basicproperties.title.add.value.help=Der neue lokalisierte Titel. +basicproperties.title.add.value.label=Lokalisierter Titel +basicproperties.title.edit=Bearbeiten +basicproperties.title.edit.cancel=Abbrechen +basicproperties.title.edit.submit=Speichern +basicproperties.title.edit.header=Lokalisierten Titel bearbeiten +basicproperties.title.edit.value.help=Der lokalisierte Titel. +basicproperties.title.edit.value.label=Lokalisierter Titel +basicproperties.title.remove=Entfernen +basicproperties.title.remove.cancel=Abbrechen +basicproperties.title.remove.submit=Lokalisierten Titel entfernen +basicproperties.title.remove.text=Sind Sie sicher, dass Sie den lokalisierten Titel f\u00fcr folgende Sprachen l\u00f6schen wollen? +basicproperties.title.remove.header=Lokalisierten Titel entfernen +basicproperties.description.add=Lokalisierte Zusammenfassung hinzuf\u00fcgen +basicproperties.description.add.cancel=Abbrechen +basicproperties.description.add.locale.help=Die Sprache der neuen lokalisierten Zusammenfassung. +basicproperties.description.add.locale.label=Sprache +basicproperties.description.add.submit=Lokaliserte Zusammenfassung hinzuf\u00fcgen +basicproperties.description.add.header=Lokalisierte Zusammenfassung hinzuf\u00fcgen +basicproperties.description.add.value.help=Die neue lokalisierte Beschreibung. +basicproperties.description.add.value.label=Lokalisierte Zusammenfassung +basicproperties.description.edit=Bearbeiten +basicproperties.description.edit.cancel=Abbrechen +basicproperties.description.edit.submit=Speichern +basicproperties.description.edit.header=Lokalisierte Zusammenfassung bearbeiten +basicproperties.description.edit.value.help=Die lokalisierte Zusammenfassung. +basicproperties.description.edit.value.label=Lokalisierte Zusammenfassung +basicproperties.description.remove=Entfernen +basicproperties.description.remove.cancel=Abbrechen +basicproperties.description.remove.submit=Lokalisierte Zusammenfassung entfernen +basicproperties.description.remove.text=Sind Sie sicher, dass Sie die lokalisierte Zusammenfassung f\u00fcr die folgende Sprach entfernen wollen? +basicproperties.description.remove.header=Lokalisierte Zusammenfassung entfernen +textstep.header=Haupttext des News +textstep.header.languages=Haupttext des Artikels - Sprachen +textstep.languages.add_language.help=W\u00e4hlen Sie die hinzuzuf\u00fcgende Sprache +textstep.languages.add_language.label=Sprache hinzuf\u00fcgen +textstep.languages.add_language.submit=Hinzuf\u00fcgen +textstep.languages.view=Ansehen +textstep.languages.edit=Bearbeiten +textstep.languages.remove=Entfernen +textstep.languages.remove.cancel=Abbrechen +textstep.languages.remove.confirm=Sprache entfernen +textstep.languages.remove.message=Sind Sie sicher, dass Sie den Text f\u00fcr die Sprache {0} entfernen wollen? +textstep.languages.remove.title=Entfernen der Sprache {0} best\u00e4tigen +textstep.languages.th.language=Sprache +textstep.languages.th.actions=Aktionen +textstep.languages.none=Keine Texte vorhandenen +textstep.header.edit=Text f\u00fcr Sprache {0} bearbeiten +textstep.header.view=Text des Artikels {0} f\u00fcr Sprache {1} +textstep.back=Zur\u00fcck zur Liste der verf\u00fcgbaren Sprachen +text.editor.add_variant=Sprache hinzuf\u00fcgen +text.editor.add.locale.help=Die Sprache der neuen Variante. +text.editor.edit.value.help=Der Text des Artikels. +text.editor.edit.value.label=Text +basicproperties.title.header=Titel +basicproperties.description.header=Zusammenfassung +text.editor.header=Haupttext diff --git a/ccm-cms/src/main/typescript/content-sections/news-text-step.ts b/ccm-cms/src/main/typescript/content-sections/news-text-step.ts new file mode 100644 index 000000000..a6870f18b --- /dev/null +++ b/ccm-cms/src/main/typescript/content-sections/news-text-step.ts @@ -0,0 +1,52 @@ +import { CmsEditorBuilder, CmsEditor } from "./cms-editor"; + +document.addEventListener("DOMContentLoaded", (event) => { + const editorElem = document.querySelector("#cms-news-text-editor"); + + if (editorElem) { + const saveUrl = editorElem.getAttribute("data-save-url"); + const variantUrl = editorElem.getAttribute("data-variant-url"); + + if (!saveUrl) { + console.error("saveUrl is null"); + return; + } + + if (!variantUrl) { + console.error("variantUrl is null"); + return; + } + + const builder = new CmsEditorBuilder( + editorElem as HTMLElement, + saveUrl, + variantUrl + ); + + builder + .buildEditor() + .then((editor) => { + const submitButton = document.querySelector( + ".cms-editor-save-button" + ); + + if (submitButton) { + submitButton.addEventListener("click", (event) => { + event.preventDefault(); + + console.log("HTML output of editor: "); + console.log(editor.getEditor().getHTML()); + }); + } else { + console.error("Save button not found.") + } + }) + .catch((error) => { + console.error(error); + console.trace(error); + }); + } else { + console.error("Editor element not found.") + } +}); + diff --git a/ccm-cms/webpack.config.js b/ccm-cms/webpack.config.js index 312b64450..6b2626055 100644 --- a/ccm-cms/webpack.config.js +++ b/ccm-cms/webpack.config.js @@ -11,7 +11,8 @@ module.exports = { "event-info-step-eventtype": "./src/main/typescript/content-sections/event-info-step-eventtype.ts", "event-info-step-location": "./src/main/typescript/content-sections/event-info-step-location.ts", "event-info-step-maincontributor": "./src/main/typescript/content-sections/event-info-step-maincontributor.ts", - "mpa-section-edit-text": "./src/main/typescript/content-sections/mpa-section-edit-text.ts" + "mpa-section-edit-text": "./src/main/typescript/content-sections/mpa-section-edit-text.ts", + "news-text-step": "./src/main/typescript/content-sections/news-text-step.ts" }, output: { filename: "[name].js",