diff --git a/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/ArticleInCollectedVolumeRow.java b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/ArticleInCollectedVolumeRow.java
new file mode 100644
index 0000000..1e5c20a
--- /dev/null
+++ b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/ArticleInCollectedVolumeRow.java
@@ -0,0 +1,41 @@
+package org.scientificcms.publications.ui.contenttypes;
+
+import java.util.Comparator;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ArticleInCollectedVolumeRow
+ implements Comparable {
+
+ private String title;
+
+ private String chapter;
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ public String getChapter() {
+ return chapter;
+ }
+
+ public void setChapter(final String chapter) {
+ this.chapter = chapter;
+ }
+
+ @Override
+ public int compareTo(final ArticleInCollectedVolumeRow other) {
+ return Comparator
+ .nullsFirst(
+ Comparator.comparing(ArticleInCollectedVolumeRow::getChapter)
+ )
+ .compare(this, other);
+ }
+
+}
diff --git a/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStep.java b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStep.java
new file mode 100644
index 0000000..98fca9c
--- /dev/null
+++ b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStep.java
@@ -0,0 +1,310 @@
+package org.scientificcms.publications.ui.contenttypes;
+
+import org.libreccm.api.Identifier;
+import org.libreccm.api.IdentifierParser;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.ui.contentsections.ContentSectionNotFoundException;
+import org.librecms.ui.contentsections.ItemPermissionChecker;
+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.publications.ArticleInCollectedVolume;
+import org.scientificcms.publications.CollectedVolume;
+import org.scientificcms.publications.CollectedVolumeManager;
+import org.scientificcms.publications.PublicationRepository;
+import org.scientificcms.publications.contenttypes.CollectedVolumeItem;
+import org.scientificcms.publications.contenttypes.MonographItem;
+import org.scientificcms.publications.ui.SciPublicationsUiConstants;
+import org.scientificcms.publications.ui.SciPublicationsUiMessageBundle;
+
+import java.util.Objects;
+import java.util.Optional;
+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.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
+ + CollectedVolumePropertiesStep.EDIT_STEP_URL_FRAGMENT)
+@Controller
+@MvcAuthoringStepDef(
+ bundle = SciPublicationsUiConstants.BUNDLE,
+ descriptionKey = "authoringsteps.basicproperties.description",
+ labelKey = "authoringsteps.basicproperties.label",
+ supportedDocumentType = MonographItem.class
+)
+public class CollectedVolumePropertiesStep
+ extends AbstractPublicationWithPublisherPropertiesStep {
+
+ public static final String EDIT_STEP_URL_FRAGMENT
+ = "collectedvolume-basicproperties";
+
+ @Inject
+ private DocumentUi documentUi;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ @Inject
+ private IdentifierParser identifierParser;
+
+ @Inject
+ private ItemPermissionChecker itemPermissionChecker;
+
+ @Inject
+ private CollectedVolumeManager collectedVolumeManager;
+
+ @Inject
+ private CollectedVolumePropertiesStepModel propertiesStepModel;
+
+ @Inject
+ private Models models;
+
+ @Inject
+ private PublicationRepository publicationRepo;
+
+ @Inject
+ private SciPublicationsUiMessageBundle messageBundle;
+
+ @Override
+ public Class getStepClass() {
+ return CollectedVolumePropertiesStep.class;
+ }
+
+ @Override
+ protected String getEditStepUrlFragment() {
+ return EDIT_STEP_URL_FRAGMENT;
+ }
+
+ @Override
+ public Class getPublicationClass() {
+ return CollectedVolume.class;
+ }
+
+ @Override
+ protected String getStepTemplatePath() {
+ return "org/scientificcms/contenttypes/ui/collectedvolume/edit-collectedvolume.xhtml";
+ }
+
+ @Override
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void init() throws ContentSectionNotFoundException,
+ DocumentNotFoundException {
+ super.init();
+
+ propertiesStepModel.setArticles(
+ getPublication()
+ .getArticles()
+ .stream()
+ .map(this::buildArticleInCollectedVolumeRow)
+ .collect(Collectors.toList())
+ );
+ }
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ @Override
+ public String showStep(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath
+ ) {
+ return super.showStep(sectionIdentifier, documentPath);
+ }
+
+ @POST()
+ @Path("/properties")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ @Override
+ public String updateProperties(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath,
+ @FormParam("yearOfPublication")
+ final String yearOfPublicationParam
+ ) {
+ return super.updateProperties(
+ sectionIdentifier,
+ documentPath,
+ yearOfPublicationParam
+ );
+ }
+
+ @POST
+ @Path("/articles")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String addArticle(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath,
+ @FormParam("articleIdentifier")
+ final String articleIdentifier
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (DocumentNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (itemPermissionChecker.canEditItem(getDocument())) {
+ final Identifier identifier = identifierParser.parseIdentifier(
+ articleIdentifier
+ );
+ final Optional articleResult;
+ switch (identifier.getType()) {
+ case ID:
+ articleResult = publicationRepo.findByIdAndType(
+ Long.parseLong(identifier.getIdentifier()),
+ ArticleInCollectedVolume.class
+ );
+ break;
+ case UUID:
+ articleResult = publicationRepo.findByUuidAndType(
+ identifier.getIdentifier(),
+ ArticleInCollectedVolume.class
+ );
+ break;
+ default:
+ articleResult = Optional.empty();
+ break;
+ }
+
+ if (articleResult.isEmpty()) {
+ return showArticleNotFound(
+ sectionIdentifier,
+ documentPath,
+ articleIdentifier
+ );
+ }
+
+ final ArticleInCollectedVolume article = articleResult.get();
+ collectedVolumeManager.addArticleToCollectedVolume(
+ article,
+ getPublication()
+ );
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ @POST
+ @Path("/articles/{articleUuid}/remove")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeArticle(
+ @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
+ final String documentPath,
+ @PathParam("articleUuid")
+ final String articleUuid
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (DocumentNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (itemPermissionChecker.canEditItem(getDocument())) {
+ final Optional articleResult
+ = getPublication()
+ .getArticles()
+ .stream()
+ .filter(
+ article -> Objects.equals(
+ article.getUuid(),
+ articleUuid
+ )
+ )
+ .findAny();
+
+ if (articleResult.isEmpty()) {
+ return showArticleNotPartOfCollectedVolume(
+ sectionIdentifier,
+ documentPath,
+ articleUuid
+ );
+ }
+
+ final ArticleInCollectedVolume article = articleResult.get();
+ collectedVolumeManager.removeArticleFromCollectedVolume(
+ article,
+ getPublication()
+ );
+
+ return buildRedirectPathForStep();
+ } else {
+ return documentUi.showAccessDenied(
+ getContentSection(),
+ getDocument(),
+ getLabel()
+ );
+ }
+ }
+
+ private ArticleInCollectedVolumeRow buildArticleInCollectedVolumeRow(
+ final ArticleInCollectedVolume article
+ ) {
+ final ArticleInCollectedVolumeRow row
+ = new ArticleInCollectedVolumeRow();
+ row.setTitle(
+ globalizationHelper.getValueFromLocalizedString(
+ article.getTitle()
+ )
+ );
+ row.setChapter(article.getChapter());
+
+ return row;
+ }
+
+ private String showArticleNotFound(
+ final String sectionIdentifier,
+ final String documentPath,
+ final String articleIdentifier
+ ) {
+ models.put("articleNotFound", articleIdentifier);
+ return showStep(sectionIdentifier, documentPath);
+ }
+
+ private String showArticleNotPartOfCollectedVolume(
+ final String sectionIdentifier,
+ final String documentPath,
+ final String articleIdentifier
+ ) {
+ models.put("articleNotPartOfCollectedVolume", articleIdentifier);
+ return showStep(sectionIdentifier, documentPath);
+ }
+
+}
diff --git a/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStepModel.java b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStepModel.java
new file mode 100644
index 0000000..41e684d
--- /dev/null
+++ b/sci-publications/src/main/java/org/scientificcms/publications/ui/contenttypes/CollectedVolumePropertiesStepModel.java
@@ -0,0 +1,30 @@
+package org.scientificcms.publications.ui.contenttypes;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("SciCmsCollectedVolumePropertiesStepModel")
+public class CollectedVolumePropertiesStepModel {
+
+ private List articles;
+
+ public List getArticles() {
+ return Collections.unmodifiableList(articles);
+ }
+
+ public void setArticles(final List articles) {
+ this.articles = new ArrayList<>(articles);
+ }
+
+
+
+}