diff --git a/.gitignore b/.gitignore
index 9e9d241..d420165 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ node_modules
/sci-publications/nbproject/
/sci-publications/target/
/scicms-bundle-devel-wildfly/target/
+/sci-types-project/nbproject/
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/ProjectCreateStep.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/ProjectCreateStep.java
deleted file mode 100644
index 1139abb..0000000
--- a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/ProjectCreateStep.java
+++ /dev/null
@@ -1,188 +0,0 @@
-* 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.scientificcms.contenttypes.sciproject.ui;
-
-import org.libreccm.l10n.GlobalizationHelper;
-import org.libreccm.l10n.LocalizedString;
-import org.libreccm.security.AuthorizationRequired;
-import org.libreccm.workflow.Workflow;
-import org.librecms.contentsection.ContentItemManager;
-import org.librecms.contentsection.ContentItemRepository;
-import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep;
-
-import org.scientificcms.contenttypes.sciproject.SciProject;
-
-import javax.enterprise.context.RequestScoped;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.transaction.Transactional;
-
-/**
- * Create step for a {@link SciProject}
- * @author Jens Pelzetter
- */
- @RequestScoped
- @Named("SciProjectCreateStep")
- public class ProjectCreateStep
- extends AbstractMvcDocumentCreateStep {
-
- private static final String FORM_PARAM_NAME = "name";
-
- private static final String FORM_PARAM_TITLE = "title";
-
- private static final String FORM_PARAM_STARTDATE = "startDate";
-
- private static final String FORM_PARAM_ENDDATE = "startDate";
-
- 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";
-
- /**
- * Provides functions for working with content items.
- */
- @Inject
- private ContentItemManager itemManager;
-
- /**
- * Used to save the event.
- */
- @Inject
- private ContentItemRepository itemRepo;
-
- /**
- * Provides functions for working with {@link LocalizedString}s.
- */
- @Inject
- private GlobalizationHelper globalizationHelper;
-
- /**
- * Name of the project.
- */
- private String name;
-
- /**
- * Title of the project.
- */
- private String title;
-
- /**
- * The start date of the project.
- */
- private String startDate;
-
- /**
- * The end date of the project
- */
- private String endDate;
-
- /**
- * The short description of the project.
- */
- private String shortDescription;
-
- /**
- * The initial locale of the project.
- */
- private String initialLocale;
-
- /**
- * The workflow to use for the new project.
- */
- private String selectedWorkflow;
-
- @Override
- public String getDocumentType() {
- return SciProject.class.getName();
- }
-
- @Override
- public String getDescription() {
- return globalizationHelper
- .getLocalizedTextsUtil(getBundle())
- .getText("createstep.description");
- }
-
- @Override
- public String getBundle() {
- return SciProjectStepsConstants.BUNDLE;
- }
-
- public String getName() {
- return name;
- }
-
- public String getTitle() {
- return getTitle();
- }
-
- public String getStartDate() {
- return startDate;
- }
-
- public String getEndDate() {
- return endDate;
- }
-
- public String getShortDescription() {
- return shortDescription;
- }
-
- public String getInitialLocale() {
- return initialLocale;
- }
-
- @Transactional(Transactional.TxType.REQUIRED)
- public String getSelectedWorkflow() {
- if (selectedWorkflow == null || selectedWorkflow.isEmpty()) {
- return getContentSection()
- .getContentTypes()
- .stream()
- .filter(
- type -> type.getContentItemClass().equals(
- Event.class.getName()
- )
- )
- .findAny()
- .map(type -> type.getDefaultWorkflow())
- .map(
- workflow -> globalizationHelper.getValueFromLocalizedString(
- workflow.getName()
- )
- )
- .orElse("");
- } else {
- return selectedWorkflow;
- }
- }
-
- @Override
- public String showCreateStep() {
- return "org/scientificcms/contenttypes/sciproject/ui/create-sciproject.xhtml";
- }
-
- @AuthorizationRequired
- @Transactional(Transactional.TxType.REQUIRED)
- @Override
- public String createItem(final Map formParams) {
- // ToDo
- }
- }
-
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectCreateStep.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectCreateStep.java
new file mode 100644
index 0000000..0e758cd
--- /dev/null
+++ b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectCreateStep.java
@@ -0,0 +1,377 @@
+/* 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.scientificcms.contenttypes.sciproject.ui;
+
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.l10n.LocalizedString;
+import org.libreccm.security.AuthorizationRequired;
+import org.libreccm.workflow.Workflow;
+import org.librecms.contentsection.ContentItemManager;
+import org.librecms.contentsection.ContentItemRepository;
+import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep;
+
+import org.scientificcms.contenttypes.sciproject.SciProject;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+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;
+
+/**
+ * Create step for a {@link SciProject}
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("SciProjectCreateStep")
+public class SciProjectCreateStep
+ extends AbstractMvcDocumentCreateStep {
+
+ private static final String FORM_PARAM_NAME = "name";
+
+ private static final String FORM_PARAM_TITLE = "title";
+
+ private static final String FORM_PARAM_BEGIN = "begin";
+
+ private static final String FORM_PARAM_END = "end";
+
+ 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";
+
+ /**
+ * Provides functions for working with content items.
+ */
+ @Inject
+ private ContentItemManager itemManager;
+
+ /**
+ * Used to save the event.
+ */
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ /**
+ * Provides functions for working with {@link LocalizedString}s.
+ */
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ /**
+ * Name of the project.
+ */
+ private String name;
+
+ /**
+ * Title of the project.
+ */
+ private String title;
+
+ /**
+ * The start date of the project.
+ */
+ private String beginParam;
+
+ /**
+ * The end date of the project
+ */
+ private String endParam;
+
+ /**
+ * The short description of the project.
+ */
+ private String shortDescription;
+
+ /**
+ * The initial locale of the project.
+ */
+ private String initialLocale;
+
+ /**
+ * The workflow to use for the new project.
+ */
+ private String selectedWorkflow;
+
+ @Override
+ public String getDocumentType() {
+ return SciProject.class.getName();
+ }
+
+ @Override
+ public String getDescription() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("createstep.description");
+ }
+
+ @Override
+ public String getBundle() {
+ return SciProjectStepsConstants.BUNDLE;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getBeginParam() {
+ return beginParam;
+ }
+
+ public String getEndParam() {
+ return endParam;
+ }
+
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ public String getInitialLocale() {
+ return initialLocale;
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String getSelectedWorkflow() {
+ if (selectedWorkflow == null || selectedWorkflow.isEmpty()) {
+ return getContentSection()
+ .getContentTypes()
+ .stream()
+ .filter(
+ type -> type.getContentItemClass().equals(
+ SciProject.class.getName()
+ )
+ )
+ .findAny()
+ .map(type -> type.getDefaultWorkflow())
+ .map(
+ workflow -> globalizationHelper.getValueFromLocalizedString(
+ workflow.getName()
+ )
+ )
+ .orElse("");
+ } else {
+ return selectedWorkflow;
+ }
+ }
+
+ @Override
+ public String showCreateStep() {
+ return "org/scientificcms/contenttypes/sciproject/ui/create-sciproject.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_BEGIN)
+ || formParams.get(FORM_PARAM_BEGIN) == null
+ || formParams.get(FORM_PARAM_BEGIN).length == 0) {
+ addMessage(
+ "danger",
+ globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("createstep.startdate.error.missing")
+ );
+ return showCreateStep();
+ }
+ beginParam = formParams.get(FORM_PARAM_BEGIN)[0];
+ final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ISO_DATE
+ .withZone(ZoneId.systemDefault());
+
+ final LocalDate begin;
+ try {
+ begin = LocalDate.parse(
+ beginParam,
+ isoDateFormatter
+ );
+ } catch (DateTimeParseException ex) {
+ addMessage(
+ "danger",
+ globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("createstep.begindate.error.malformed")
+ );
+ return showCreateStep();
+ }
+
+ final LocalDate end = readEndDate(formParams);
+
+ 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();
+ }
+ shortDescription = 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_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.isPresent()) {
+ addMessage(
+ "danger",
+ globalizationHelper.getLocalizedTextsUtil(
+ getBundle()
+ ).getText("createstep.workflow.error.not_available")
+ );
+ return showCreateStep();
+ }
+
+ if (!getMessages().isEmpty()) {
+ return showCreateStep();
+ }
+
+ final SciProject project = itemManager.createContentItem(
+ name,
+ getContentSection(),
+ getFolder(),
+ workflowResult.get(),
+ SciProject.class,
+ locale
+ );
+
+ project.getTitle().putValue(locale, title);
+ project.setBegin(begin);
+ if (end != null) {
+ project.setEnd(end);
+ }
+ project.getShortDescription().putValue(locale, shortDescription);
+ itemRepo.save(project);
+
+ return String.format(
+ "redirect:/%s/documents/%s/%s/@sciproject-basicproperties",
+ getContentSectionLabel(),
+ getFolderPath(),
+ name
+ );
+ }
+
+ private LocalDate readEndDate(final Map formParams) {
+ if (formParams.containsKey(FORM_PARAM_END)
+ && formParams.get(FORM_PARAM_END) != null
+ && formParams.get(FORM_PARAM_END).length > 0) {
+ endParam = formParams.get(FORM_PARAM_END)[0];
+ if (endParam != null) {
+ try {
+ return LocalDate.parse(
+ endParam,
+ DateTimeFormatter.ISO_DATE
+ );
+ } catch (DateTimeParseException ex) {
+ addMessage(
+ "danger",
+ globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("createstep.enddate.error.malformed")
+ );
+ return null;
+ }
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectDescriptionStepResources.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectDescriptionStepResources.java
new file mode 100644
index 0000000..1bda97c
--- /dev/null
+++ b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectDescriptionStepResources.java
@@ -0,0 +1,123 @@
+package org.scientificcms.contenttypes.sciproject.ui;
+
+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.ui.contentsections.ContentSectionsUi;
+import org.librecms.ui.contentsections.ItemPermissionChecker;
+import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
+import org.scientificcms.contenttypes.sciproject.SciProject;
+
+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;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAuthoringSteps.PATH_PREFIX + "sciproject-description-resources")
+public class SciProjectDescriptionStepResources {
+
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Inject
+ private ContentSectionsUi sectionsUi;
+
+ @Inject
+ private ItemPermissionChecker itemPermissionChecker;
+
+ @GET
+ @Path("/project-description/wordcount/{locale}")
+ @Produces(MediaType.TEXT_HTML)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String getProjectDescriptionWordCount(
+ @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 SciProject)) {
+ throw new NotFoundException();
+ }
+
+ final SciProject project = (SciProject) document;
+ if (itemPermissionChecker.canEditItem(project)) {
+ final String text = project
+ .getProjectDescription()
+ .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("/project-description/{locale}")
+ @Produces(MediaType.TEXT_HTML)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String viewProjectDescriptionValue(
+ @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 SciProject)) {
+ throw new NotFoundException();
+ }
+
+ final SciProject project = (SciProject) document;
+ if (itemPermissionChecker.canEditItem(project)) {
+ return project.getProjectDescription().getValue(
+ new Locale(localeParam)
+ );
+ } else {
+ throw new ForbiddenException();
+ }
+ }
+
+}
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectMessageBundle.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectMessageBundle.java
new file mode 100644
index 0000000..c48ec92
--- /dev/null
+++ b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectMessageBundle.java
@@ -0,0 +1,21 @@
+package org.scientificcms.contenttypes.sciproject.ui;
+
+import org.libreccm.ui.AbstractMessagesBean;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("SciProjectMessageBundle")
+public class SciProjectMessageBundle extends AbstractMessagesBean {
+
+ @Override
+ protected String getMessageBundle() {
+ return SciProjectStepsConstants.BUNDLE;
+ }
+
+}
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStep.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStep.java
new file mode 100644
index 0000000..847ec9a
--- /dev/null
+++ b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStep.java
@@ -0,0 +1,587 @@
+package org.scientificcms.contenttypes.sciproject.ui;
+
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.l10n.LocalizedString;
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.contentsection.ContentItemRepository;
+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 org.scientificcms.contenttypes.sciproject.SciProject;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+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;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAuthoringSteps.PATH_PREFIX + "sciproject-basicproperties")
+@Controller
+@MvcAuthoringStepDef(
+ bundle = SciProjectStepsConstants.BUNDLE,
+ descriptionKey = "authoringsteps.basicproperties.description",
+ labelKey = "authoringsteps.basicproperties.label",
+ supportedDocumentType = SciProject.class
+)
+public class SciProjectPropertiesStep extends AbstractMvcAuthoringStep {
+
+ @Inject
+ private SciProjectMessageBundle sciProjectMessageBundle;
+
+ /**
+ * Used for retrieving and saving the projects.
+ */
+ @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 SciProjectPropertiesStepModel sciProjectPropertiesStepModel;
+
+ @Inject
+ private Models models;
+
+ @Override
+ public Class getStepClass() {
+ return SciProjectPropertiesStep.class;
+ }
+
+ @Override
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void init() throws ContentSectionNotFoundException,
+ DocumentNotFoundException {
+ super.init();
+
+ sciProjectPropertiesStepModel.setName(getDocument().getDisplayName());
+
+ final Set titleLocales = getDocument()
+ .getTitle()
+ .getAvailableLocales();
+
+ sciProjectPropertiesStepModel.setTitleValues(
+ getDocument()
+ .getTitle()
+ .getValues()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue()
+ )
+ )
+ );
+
+ sciProjectPropertiesStepModel.setUnusedTitleLocales(
+ globalizationHelper
+ .getAvailableLocales()
+ .stream()
+ .filter(locale -> !titleLocales.contains(locale))
+ .map(Locale::toString)
+ .collect(Collectors.toList())
+ );
+
+ final SciProject project = (SciProject) getDocument();
+
+ sciProjectPropertiesStepModel.setShortDescriptionValues(
+ project
+ .getShortDescription()
+ .getValues()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue()
+ )
+ )
+ );
+ final Set shortDescriptionLocales = project
+ .getShortDescription()
+ .getAvailableLocales();
+
+ sciProjectPropertiesStepModel.setUnusedShortDescriptionLocales(
+ globalizationHelper
+ .getAvailableLocales()
+ .stream()
+ .filter(locale -> !shortDescriptionLocales.contains(locale))
+ .map(Locale::toString)
+ .collect(Collectors.toList())
+ );
+
+ final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ISO_DATE
+ .withZone(ZoneId.systemDefault());
+
+ sciProjectPropertiesStepModel.setBegin(
+ Optional
+ .ofNullable(project.getBegin())
+ .map(isoDateFormatter::format)
+ .orElse("")
+ );
+
+ sciProjectPropertiesStepModel.setEndDate(
+ Optional
+ .ofNullable(project.getEnd())
+ .map(isoDateFormatter::format)
+ .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/scientificcms/contenttypes/sciproject/ui/sciproject-basic-properties.xhtml";
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ sciProjectMessageBundle.getMessage("project.edit.denied")
+ );
+ }
+ }
+
+ /**
+ * Updates the name of the current project.
+ *
+ * @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.isBlank()) {
+ models.put("nameMissing", true);
+
+ return showStep(sectionIdentifier, documentPath);
+ }
+
+ getDocument().setDisplayName(name);
+ itemRepo.save(getDocument());
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ @POST
+ @Path("/begin")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String updateBegin(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath,
+ @FormParam("begin")
+ final String begin
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (DocumentNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (itemPermissionChecker.canEditItem(getDocument())) {
+ final SciProject project = (SciProject) getDocument();
+ final DateTimeFormatter isoDateFormatter
+ = DateTimeFormatter.ISO_DATE
+ .withZone(ZoneId.systemDefault());
+ project.setBegin(LocalDate.parse(begin, isoDateFormatter));
+
+ itemRepo.save(project);
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ @POST
+ @Path("/end")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String updateEnd(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath,
+ @FormParam("end")
+ final String end
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (DocumentNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (itemPermissionChecker.canEditItem(getDocument())) {
+ final SciProject project = (SciProject) getDocument();
+ final DateTimeFormatter isoDateFormatter
+ = DateTimeFormatter.ISO_DATE
+ .withZone(ZoneId.systemDefault());
+ project.setEnd(LocalDate.parse(end, isoDateFormatter));
+
+ itemRepo.save(project);
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ /**
+ * Updates a localized title of the project.
+ *
+ * @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 project.
+ *
+ * @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 project.
+ *
+ * @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 short description to the project.
+ *
+ * @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("/short-description/@add")
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String addShortDescription(
+ @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);
+ final SciProject project = (SciProject) getDocument();
+ project.getShortDescription().putValue(locale, value);
+
+ itemRepo.save(project);
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ /**
+ * Updates a localized short description to the project.
+ *
+ * @param sectionIdentifier
+ * @param documentPath
+ * @param localeParam The locale to update.
+ * @param value The description value.
+ *
+ * @return A redirect to this authoring step.
+ */
+ @POST
+ @Path("/short-description/{locale}")
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String editShortDescription(
+ @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);
+ final SciProject project = (SciProject) getDocument();
+ project.getShortDescription().putValue(locale, value);
+
+ itemRepo.save(project);
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ /**
+ * Removes a localized short description from the project.
+ *
+ * @param sectionIdentifier
+ * @param documentPath
+ * @param localeParam The locale to remove.
+ *
+ * @return A redirect to this authoring step.
+ */
+ public String removeShortDescription(
+ @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);
+ final SciProject project = (SciProject) getDocument();
+ project.getShortDescription().removeValue(locale);
+ itemRepo.save(project);
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+}
diff --git a/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStepModel.java b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStepModel.java
new file mode 100644
index 0000000..b438e74
--- /dev/null
+++ b/sci-types-project/src/main/java/org/scientificcms/contenttypes/sciproject/ui/SciProjectPropertiesStepModel.java
@@ -0,0 +1,104 @@
+package org.scientificcms.contenttypes.sciproject.ui;
+
+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("SciProjectPropertiesStep")
+public class SciProjectPropertiesStepModel {
+
+ private String name;
+
+ private Map titleValues;
+
+ private List unusedTitleLocales;
+
+ private Map shortDescriptionValues;
+
+ private List unusedShortDescriptionLocales;
+
+ private String begin;
+
+ private String endDate;
+
+ 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(final List unusedTitleLocales) {
+ this.unusedTitleLocales = new ArrayList<>(unusedTitleLocales);
+ }
+
+ public Map getShortDescriptionValues() {
+ return Collections.unmodifiableMap(shortDescriptionValues);
+ }
+
+ public void setShortDescriptionValues(
+ final Map shortDescriptionValues
+ ) {
+ this.shortDescriptionValues = new HashMap<>(shortDescriptionValues);
+ }
+
+ public List getUnusedShortDescriptionLocales() {
+ return Collections.unmodifiableList(unusedShortDescriptionLocales);
+ }
+
+ public void setUnusedShortDescriptionLocales(
+ final List unusedShortDescriptionLocales
+ ) {
+ this.unusedShortDescriptionLocales = new ArrayList<>(
+ unusedShortDescriptionLocales
+ );
+ }
+
+ public String getBegin() {
+ return begin;
+ }
+
+ public void setBegin(final String begin) {
+ this.begin = begin;
+ }
+
+ public boolean getHasStartDate() {
+ return !begin.isBlank();
+ }
+
+ public String getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(final String endDate) {
+ this.endDate = endDate;
+ }
+
+ public boolean getHasEndDate() {
+ return !endDate.isBlank();
+ }
+
+}