Fixed some problems with the edit steps for news.

pull/17/head
Jens Pelzetter 2022-02-10 20:14:17 +01:00
parent 8ca610bcc0
commit ee233fefe3
26 changed files with 2595 additions and 10 deletions

View File

@ -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;
/**

View File

@ -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
);
}

View File

@ -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}.

View File

@ -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;

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsNewsCreateStep")
public class MvcNewsCreateStep
extends AbstractMvcDocumentCreateStep<News> {
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<String, String[]> 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<Workflow> 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<News> {
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);
}
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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<MvcNewsPropertiesStep> 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<Locale> 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<Locale> 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()
);
}
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsNewsPropertiesStep")
public class MvcNewsPropertiesStepModel {
private String name;
private Map<String, String> titleValues;
private List<String> unusedTitleLocales;
private Map<String, String> descriptionValues;
private List<String> unusedDescriptionLocales;
private String releaseDate;
private String formattedReleaseDate;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Map<String, String> getTitleValues() {
return Collections.unmodifiableMap(titleValues);
}
public void setTitleValues(final Map<String, String> titleValues) {
this.titleValues = new HashMap<>(titleValues);
}
public List<String> getUnusedTitleLocales() {
return Collections.unmodifiableList(unusedTitleLocales);
}
public void setUnusedTitleLocales(List<String> unusedTitleLocales) {
this.unusedTitleLocales = new ArrayList<>(unusedTitleLocales);
}
public Map<String, String> getDescriptionValues() {
return Collections.unmodifiableMap(descriptionValues);
}
public void setDescriptionValues(
final Map<String, String> descriptionValues
) {
this.descriptionValues = new HashMap<>(descriptionValues);
}
public List<String> getUnusedDescriptionLocales() {
return Collections.unmodifiableList(unusedDescriptionLocales);
}
public void setUnusedDescriptionLocales(
final List<String> 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;
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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<MvcNewsTextBodyStep> 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<Locale> 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();
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsNewsTextBodyStep")
public class MvcNewsTextBodyStepModel {
private boolean canEdit;
private Map<String, String> titleValues;
private Map<String, String> textValues;
private List<CmsEditorLocaleVariantRow> variants;
private List<String> unusedLocales;
private String selectedLocale;
public Map<String, String> getTitleValues() {
return Collections.unmodifiableMap(titleValues);
}
protected void setTitleValues(final Map<String, String> 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<String, String> getTextValues() {
return Collections.unmodifiableMap(textValues);
}
protected void setTextValues(final Map<String, String> 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<CmsEditorLocaleVariantRow> getVariants() {
return Collections.unmodifiableList(variants);
}
protected void setVariants(final List<CmsEditorLocaleVariantRow> variants) {
this.variants = new ArrayList<>(variants);
}
public List<String> getUnusedLocales() {
return Collections.unmodifiableList(unusedLocales);
}
protected void setUnusedLocales(final List<String> 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;
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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();
}
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsNewsMessageBundle")
public class NewsMessageBundle extends AbstractMessagesBean {
@Override
protected String getMessageBundle() {
return NewsStepsConstants.BUNDLE;
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public final class NewsStepsConstants {
private NewsStepsConstants() {
// Nothing
}
public static final String BUNDLE = "org.librecms.ui.contenttypes.NewsBundle";
}

View File

@ -20,7 +20,7 @@
<c:if test="#{CmsArticleTextBodyStep.canEdit}">
<div class="text-right">
<a class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{CmsArticleTextBodyStep.selectedLocale}/@edit">
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/@edit/#{CmsArticleTextBodyStep.selectedLocale}">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.languages.edit']}</span>
</a>

View File

@ -0,0 +1,96 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
<ui:define name="main">
<div class="container">
<h1>#{CmsNewsMessageBundle['createform.title']}</h1>
<c:forEach items="#{CmsNewsCreateStep.messages.entrySet()}"
var="message">
<div class="alert alert-#{message.key}" role="alert">
#{message.value}
</div>
</c:forEach>
<form action="#{mvc.basePath}/#{CmsNewsCreateStep.contentSectionLabel}/documents/#{CmsNewsCreateStep.folderPath}@create/#{CmsNewsCreateStep.documentType}"
method="post">
<bootstrap:formGroupText
help="#{CmsNewsMessageBundle['createform.name.help']}"
inputId="name"
label="#{CmsNewsMessageBundle['createform.name.label']}"
name="name"
pattern="^([a-zA-Z0-9_-]*)$"
required="true"
value="#{CmsNewsCreateStep.name}"
/>
<bootstrap:formGroupSelect
help="#{CmsNewsMessageBundle['createform.initial_locale.help']}"
inputId="locale"
label="#{CmsNewsMessageBundle['createform.initial_locale.label']}"
name="locale"
options="#{CmsNewsCreateStep.availableLocales}"
required="true"
selectedOptions="#{[CmsNewsCreateStep.initialLocale]}"
/>
<bootstrap:formGroupText
help="#{CmsNewsMessageBundle['createform.title.help']}"
inputId="title"
label="#{CmsNewsMessageBundle['createform.title.label']}"
name="title"
required="true"
value="#{CmsNewsCreateStep.title}"
/>
<bootstrap:formGroupTextarea
cols="80"
help="#{CmsNewsMessageBundle['createform.summary.help']}"
inputId="summary"
label="#{CmsNewsMessageBundle['createform.summary.label']}"
name="summary"
required="true"
rows="16"
value="#{CmsNewsCreateStep.summary}"
/>
<bootstrap:formGroupDateTime
help="#{CmsNewsMessageBundle['createform.releasedate.help']}"
inputId="releaseDate"
label="#{CmsNewsMessageBundle['createform.releasedate.label']}"
name="releaseDate"
value="#{CmsNewsCreateStep.releaseDate}"
/>
<bootstrap:formGroupSelect
help="#{CmsNewsMessageBundle['createform.workflow.help']}"
inputId="workflow"
label="#{CmsNewsMessageBundle['createform.workflow.label']}"
name="workflow"
options="#{CmsNewsCreateStep.availableWorkflows}"
selectedOptions="#{[CmsNewsCreateStep.selectedWorkflow]}"
/>
<a class="btn btn-warning"
href="#{mvc.basePath}/#{CmsNewsCreateStep.contentSectionLabel}/documentfolders/#{CmsNewsCreateStep.folderPath}">
#{CmsNewsMessageBundle['createform.cancel']}
</a>
<button class="btn btn-success"
type="submit">
#{CmsNewsMessageBundle['createform.submit']}
</button>
</form>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,214 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:param name="authoringStep"
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties" />
<ui:define name="authoringStep">
<h2>#{CmsNewsMessageBundle.getMessage('basicproperties.header', [CmsNewsPropertiesStep.name])}</h2>
<h3>#{CmsNewsMessageBundle['basicproeperties.name.header']}</h3>
<div class="d-flex">
<pre class="mr-2">#{CmsNewsPropertiesStep.name}</pre>
<c:if test="#{CmsSelectedDocumentModel.canEdit}">
<button class="btn btn-primary btn-sm"
data-toggle="modal"
data-target="#name-edit-dialog"
type="button">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">
#{CmsNewsMessageBundle['basicproperties.name.edit']}
</span>
</button>
</c:if>
</div>
<c:if test="#{CmsSelectedDocumentModel.canEdit}">
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title"
class="modal fade"
id="name-edit-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/name"
class="modal-content"
method="post">
<div class="modal-header">
<h4 class="modal-title"
id="name-edit-dialog-title">
#{CmsNewsMessageBundle['basicproperties.name.edit.title']}
</h4>
<button aria-label="#{CmsNewsMessageBundle['basicproperties.name.edit.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x" />
</button>
</div>
<div class="modal-body">
<bootstrap:formGroupText
help="#{CmsNewsMessageBundle['basicproperties.name.help']}"
inputId="name"
label="#{CmsNewsMessageBundle['basicproperties.name.label']}"
name="name"
pattern="^([a-zA-Z0-9_-]*)$"
required="true"
value="#{CmsNewsPropertiesStep.name}"/>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsNewsMessageBundle['basicproperties.name.edit.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsNewsMessageBundle['basicproperties.name.edit.submit']}
</button>
</div>
</form>
</div>
</div>
</c:if>
<h3>#{CmsNewsMessageBundle['basicproperties.releasedate.header']}</h3>
<div class="d-flex">
<p class="mr-2">#{CmsNewsPropertiesStep.formattedReleaseDate}</p>
<c:if test="#{CmsSelectedDocumentModel.canEdit}">
<button class="btn btn-primary btn-sm"
data-toggle="modal"
data-target="#releasedate-edit-dialog"
type="button">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">
#{CmsNewsMessageBundle['basicproperties.releasedate.edit']}
</span>
</button>
</c:if>
</div>
<c:if test="#{CmsSelectedDocumentModel.canEdit}">
<div aria-hidden="true"
aria-labelledby="releasedate-edit-dialog-title"
class="modal fade"
id="releasedate-edit-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/releasedate"
class="modal-content"
method="post">
<div class="modal-header">
<h4 class="modal-title"
id="releasedate-edit-dialog-title">
#{CmsNewsMessageBundle['basicproperties.releasedate.edit.title']}
</h4>
<button aria-label="#{CmsNewsMessageBundle['basicproperties.releasedate.edit.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x" />
</button>
</div>
<div class="modal-body">
<bootstrap:formGroupDateTime
help="#{CmsNewsMessageBundle['basicproperties.releasedate.help']}"
inputId="releaseDate"
label="#{CmsNewsMessageBundle['basicproperties.releasedate.label']}"
name="releaseDate"
required="true"
value="#{CmsNewsPropertiesStep.releaseDate}"/>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsNewsMessageBundle['basicproperties.releasedate.edit.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsNewsMessageBundle['basicproperties.releasedate.edit.submit']}
</button>
</div>
</form>
</div>
</div>
</c:if>
<libreccm:localizedStringEditor
addButtonLabel="#{CmsNewsMessageBundle['basicproperties.title.add']}"
addDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.title.add.cancel']}"
addDialogLocaleSelectHelp="#{CmsNewsMessageBundle['basicproperties.title.add.locale.help']}"
addDialogLocaleSelectLabel="#{CmsNewsMessageBundle['basicproperties.title.add.locale.label']}"
addDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.title.add.submit']}"
addDialogTitle="#{CmsNewsMessageBundle['basicproperties.title.add.header']}"
addDialogValueHelp="#{CmsNewsMessageBundle['basicproperties.title.add.value.help']}"
addDialogValueLabel="#{CmsNewsMessageBundle['basicproperties.title.add.value.label']}"
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/title/@add"
editButtonLabel="#{CmsNewsMessageBundle['basicproperties.title.edit']}"
editDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.title.edit.cancel']}"
editDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.title.edit.submit']}"
editDialogTitle="#{CmsNewsMessageBundle['basicproperties.title.edit.header']}"
editDialogValueHelp="#{CmsNewsMessageBundle['basicproperties.title.edit.value.help']}"
editDialogValueLabel="#{CmsNewsMessageBundle['basicproperties.title.edit.value.label']}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/title/@edit"
editorId="title-editor"
hasUnusedLocales="#{!CmsNewsPropertiesStep.unusedTitleLocales.isEmpty()}"
headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
readOnly="#{!CmsSelectedDocumentModel.canEdit}"
removeButtonLabel="#{CmsNewsMessageBundle['basicproperties.title.remove']}"
removeDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.title.remove.cancel']}"
removeDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.title.remove.submit']}"
removeDialogText="#{CmsNewsMessageBundle['basicproperties.title.remove.text']}"
removeDialogTitle="#{CmsNewsMessageBundle['basicproperties.title.remove.header']}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/title/@remove"
title="#{CmsNewsMessageBundle['basicproperties.title.header']}"
unusedLocales="#{CmsNewsPropertiesStep.unusedTitleLocales}"
values="#{CmsNewsPropertiesStep.titleValues}"
/>
<libreccm:localizedStringEditor
addButtonLabel="#{CmsNewsMessageBundle['basicproperties.description.add']}"
addDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.description.add.cancel']}"
addDialogLocaleSelectHelp="#{CmsNewsMessageBundle['basicproperties.description.add.locale.help']}"
addDialogLocaleSelectLabel="#{CmsNewsMessageBundle['basicproperties.description.add.locale.label']}"
addDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.description.add.submit']}"
addDialogTitle="#{CmsNewsMessageBundle['basicproperties.description.add.header']}"
addDialogValueHelp="#{CmsNewsMessageBundle['basicproperties.description.add.value.help']}"
addDialogValueLabel="#{CmsNewsMessageBundle['basicproperties.description.add.value.label']}"
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/description/@add"
editButtonLabel="#{CmsNewsMessageBundle['basicproperties.description.edit']}"
editDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.description.edit.cancel']}"
editDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.description.edit.submit']}"
editDialogTitle="#{CmsNewsMessageBundle['basicproperties.description.edit.header']}"
editDialogValueHelp="#{CmsNewsMessageBundle['basicproperties.description.edit.value.help']}"
editDialogValueLabel="#{CmsNewsMessageBundle['basicproperties.description.edit.value.label']}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/description/@edit"
editorId="description-editor"
hasUnusedLocales="#{!CmsNewsPropertiesStep.unusedDescriptionLocales.isEmpty()}"
headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
readOnly="#{!CmsSelectedDocumentModel.canEdit}"
removeButtonLabel="#{CmsNewsMessageBundle['basicproperties.description.remove']}"
removeDialogCancelLabel="#{CmsNewsMessageBundle['basicproperties.description.remove.cancel']}"
removeDialogSubmitLabel="#{CmsNewsMessageBundle['basicproperties.description.remove.submit']}"
removeDialogText="#{CmsNewsMessageBundle['basicproperties.description.remove.text']}"
removeDialogTitle="#{CmsNewsMessageBundle['basicproperties.description.remove.header']}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-basicproperties/description/@remove"
title="#{CmsNewsMessageBundle['basicproperties.description.header']}"
unusedLocales="#{CmsNewsPropertiesStep.unusedDescriptionLocales}"
values="#{CmsNewsPropertiesStep.descriptionValues}"
useTextarea="true"
/>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:librecms="http://xmlns.jcp.org/jsf/composite/components/librecms"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:param name="authoringStep"
value="/libreccm/@contentsections/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text" />
<ui:define name="authoringStep">
<h2>#{CmsNewsMessageBundle['textstep.header']}</h2>
<librecms:cmsEditorVariants
addButtonLabel="#{CmsNewsMessageBundle['text.editor.add_variant']}"
addDialogLocaleSelectHelp="#{CmsNewsMessageBundle['text.editor.add.locale.help']}"
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/add"
canEdit="#{CmsSelectedDocumentModel.canEdit}"
editorPageUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/edit"
hasUnusedLocales="#{!CmsNewsTextBodyStep.unusedLocales.isEmpty()}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/remove"
sourceEditorPageUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/edit-source"
title="#{CmsNewsMessageBundle['text.editor.header']}"
unusedLocales="#{CmsNewsTextBodyStep.unusedLocales}"
variants="#{CmsNewsTextBodyStep.variants}"
viewPageUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/view"
/>
</ui:define>
<ui:define name="scripts">
<script src="#{request.contextPath}/assets/@content-sections/cms-editor.js"></script>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:librecms="http://xmlns.jcp.org/jsf/composite/components/librecms"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:define name="authoringStep">
<div class="d-flex">
<a class="btn btn-secondary btn-sm mr-2"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text">
<bootstrap:svgIcon icon="caret-left-fill" />
<span class="sr-only">#{CmsNewsMessageBundle['textstep.back']}</span>
</a>
<h2>#{CmsNewsMessageBundle.getMessage('textstep.header.edit',[CmsNewsTextBodyStep.selectedLocale])}</h2>
</div>
<c:if test="#{CmsNewsTextBodyStep.canEdit}">
<librecms:cmsEditor
backUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text"
baseUrl="#{mvc.basePath}"
canEdit="#{CmsNewsTextBodyStep.canEdit}"
contentSection="#{ContentSectionModel.sectionName}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/edit"
editorId="cms-news-text-editor"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
selectedLocale="#{CmsNewsTextBodyStep.selectedLocale}"
title="#{CmsNewsMessageBundle['text.editor.header']}"
variantUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text-resources/variants"
/>
</c:if>
</ui:define>
<ui:define name="scripts">
<script src="#{request.contextPath}/assets/@content-sections/news-text-step.js"></script>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:define name="authoringStep">
<div class="d-flex">
<a class="btn btn-secondary btn-sm algin-self-center mr-2"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text">
<bootstrap:svgIcon icon="caret-left-fill" />
<span class="sr-only">#{CmsNewsMessageBundle['textstep.back']}</span>
</a>
<h2>#{CmsNewsMessageBundle.getMessage('textstep.header.view',[CmsNewsTextBodyStep.title, CmsNewsTextBodyStep.selectedLocale])}</h2>
</div>
<c:if test="#{CmsNewsTextBodyStep.canEdit}">
<div class="text-right">
<a class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@news-text/#{CmsNewsTextBodyStep.selectedLocale}/@edit">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsNewsMessageBundle['textstep.languages.edit']}</span>
</a>
</div>
</c:if>
<div class="cms-text-preview news-text border p-2">
<h:outputText escape="false"
value="#{CmsNewsTextBodyStep.textValues.get(CmsNewsTextBodyStep.selectedLocale)}" />
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1 @@
alter table ccm_cms.news alter COLUMN news_date type timestamp;

View File

@ -0,0 +1 @@
alter table ccm_cms.news_aud alter COLUMN news_date type timestamp;

View File

@ -0,0 +1 @@
alter table ccm_cms.news alter COLUMN news_date type timestamp;

View File

@ -0,0 +1 @@
alter table ccm_cms.news_aud alter COLUMN news_date type timestamp;

View File

@ -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

View File

@ -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

View File

@ -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.")
}
});

View File

@ -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",