diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/ResettableContainer.java b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/ResettableContainer.java
new file mode 100755
index 000000000..a0cb49645
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/ResettableContainer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.contenttypes.ui;
+
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Resettable;
+import com.arsdigita.bebop.SimpleContainer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A container which implements the {@link Resettable} interface and provides
+ * other useful methods.
+ *
+ * @author Phong Nguyen
+ * @author Jens Pelzetter
+ */
+public class ResettableContainer extends SimpleContainer implements Resettable {
+
+ /**
+ * A list of all resettable components in this container
+ */
+ private final List resettableComponents = new ArrayList<>();
+
+ /**
+ * A list of all components that are not visible by default
+ */
+ private final List componentsNotVisibleByDefault
+ = new ArrayList<>();
+
+ /**
+ * Constructs a new, empty {@code RessetableContainer}.
+ *
+ */
+ public ResettableContainer() {
+ super();
+ }
+
+ /**
+ * Constructs a new, empty {@code ResettableContainer}.
+ *
+ * @param key The key for this container.
+ *
+ */
+ public ResettableContainer(final String key) {
+ super();
+ setKey(key);
+ }
+
+ /**
+ * Constructs a new, empty {@code RessetableContainer}. The container will
+ * wrap its children in the specified tag.
+ *
+ * @param tag The name of the XML element that will be used to wrap
+ * the children of this container.
+ * @param namespace The namespace for the tag.
+ *
+ */
+ public ResettableContainer(final String tag, final String namespace) {
+ super(tag, namespace);
+ }
+
+ /**
+ * Adds a component to container.
+ *
+ * @param component The component to be added.
+ *
+ */
+ @Override
+ public void add(final Component component) {
+ add(component, true);
+ }
+
+ /**
+ * Add a component to this container
+ *
+ * @param component The component to be added.
+ * @param constraints This parameter is ignored. Child classes should
+ * override the add method if they wish to provide
+ * special handling of constraints.
+ *
+ */
+ @Override
+ public void add(final Component component, final int constraints) {
+ add(component);
+ }
+
+ /**
+ * Adds the component to this pane with the specified default visibility.
+ *
+ * @param component
+ * @param defaultVisibility The default visibility of this component
+ *
+ *
+ */
+ public void add(final Component component,
+ final boolean defaultVisibility) {
+
+ super.add(component);
+ if (component instanceof Resettable) {
+ resettableComponents.add(component);
+ }
+ if (!defaultVisibility) {
+ componentsNotVisibleByDefault.add(component);
+ }
+ }
+
+ /**
+ * Sets the visibility of all child components to false, except for the
+ * component with the specified key.
+ *
+ * @param state The state of the current request.
+ * @param key The key of the component. There will be no visibility
+ * changes if key is null.
+ *
+ */
+ public void onlyShowComponent(final PageState state,
+ final String key) {
+
+ if (key == null) {
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ final Iterator iter = children();
+ Component child;
+ while (iter.hasNext()) {
+ child = iter.next();
+ child.setVisible(state, key.equals(child.getKey()));
+ }
+ }
+
+ /**
+ * Sets the visibility of all child components to false, except for the
+ * specified component.
+ *
+ * @param state The state of the current request.
+ * @param component The key of the component. There will be no visibility
+ * changes if {@code component} is null.
+ *
+ */
+ public void onlyShowComponent(final PageState state,
+ final Component component) {
+
+ if (component == null) {
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ final Iterator iter = children();
+ Component child;
+ while (iter.hasNext()) {
+ child = iter.next();
+ child.setVisible(state, child.equals(component));
+ }
+ }
+
+ /**
+ * Resets all resettable components added to this container.
+ *
+ * @param state The state of the current request.
+ *
+ */
+ @Override
+ public void reset(final PageState state) {
+ // Reset all resettable components automatically
+ final Iterator iter = resettableComponents.iterator();
+ while (iter.hasNext()) {
+ ((Resettable) iter.next()).reset(state);
+ }
+ }
+
+ /**
+ * Registers with page that this container belongs to and sets the default
+ * visibility of child components.
+ *
+ * @param page The page this container belongs to.
+ *
+ */
+ @Override
+ public void register(final Page page) {
+ final Iterator iter = componentsNotVisibleByDefault
+ .iterator();
+ while (iter.hasNext()) {
+ page.setVisibleDefault(iter.next(), false);
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionStepController.java b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionStepController.java
new file mode 100644
index 000000000..3b8fa6a5d
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionStepController.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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 com.arsdigita.cms.contenttypes.ui.mparticle;
+
+import org.librecms.contentsection.ContentItemRepository;
+import org.librecms.contenttypes.MultiPartArticle;
+import org.librecms.contenttypes.MultiPartArticleSection;
+import org.librecms.contenttypes.MultiPartArticleSectionManager;
+import org.librecms.contenttypes.MultiPartArticleSectionRepository;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class MultiPartArticleSectionStepController {
+
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Inject
+ private MultiPartArticleSectionRepository sectionRepo;
+
+ @Inject
+ private MultiPartArticleSectionManager sectionManager;
+
+ public void moveToFirst(final MultiPartArticle article,
+ final MultiPartArticleSection section) {
+
+ final MultiPartArticle theArticle = itemRepo
+ .findById(article.getObjectId(),
+ MultiPartArticle.class)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No MultiPartArticle with ID %d in the database.",
+ article.getObjectId())));
+
+ final MultiPartArticleSection theSection = sectionRepo
+ .findById(section.getSectionId())
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No MultiPartArticleSection with ID %d in the database.",
+ section.getSectionId())));
+
+ sectionManager.moveToFirst(article, section);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionsStep.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionsStep.java.todo
new file mode 100644
index 000000000..e5b22e040
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/MultiPartArticleSectionsStep.java.todo
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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 com.arsdigita.cms.contenttypes.ui.mparticle;
+
+import com.arsdigita.bebop.ActionLink;
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Container;
+import com.arsdigita.bebop.DefaultSingleSelectionModel;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SingleSelectionModel;
+import com.arsdigita.bebop.parameters.LongParameter;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.contenttypes.ui.ResettableContainer;
+import com.arsdigita.cms.ui.GlobalNavigation;
+import com.arsdigita.cms.ui.authoring.AuthoringKitWizard;
+import com.arsdigita.globalization.GlobalizedMessage;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.CmsConstants;
+import org.librecms.contenttypes.MultiPartArticle;
+import org.librecms.contenttypes.MultiPartArticleSection;
+import org.librecms.contenttypes.MultiPartArticleSectionManager;
+
+/**
+ * Authoring kit step to manage the sections of a MultiPartArticle. Process is
+ * implemented with three main components that manipulate the currently selected
+ * MultiPartArticle and sections. The visibility of these components is managed
+ * by this class.
+ *
+ * Note: This class was originally called {@code MultiPartArticleViewSections}.
+ * Starting with version 7.0.0 all authoring step classes should end with
+ * {@code Step} to make them easily identifiable.
+ *
+ * @author Dave Turner
+ * * @author Jens Pelzetter
+ */
+public class MultiPartArticleSectionsStep extends ResettableContainer {
+
+ /* id keys for each editing panel */
+ public static final String SECTION_TABLE = "sec_tbl";
+ public static final String SECTION_EDIT = "sec_edt";
+ public static final String SECTION_PREVIEW = "sec_prv";
+ public static final String SECTION_DELETE = "sec_del";
+
+ public static final String DATA_TABLE = "dataTable";
+ public static final String ACTION_LINK = "actionLink";
+
+ private final AuthoringKitWizard authoringKitWizard;
+ private final ItemSelectionModel selectedArticleModel;
+ private final StringParameter selectedLanguageParam;
+ private SectionSelectionModel selectedSectionModel;
+ private SectionSelectionModel moveSectionModel;
+ private LongParameter moveSectionParam;
+
+ private SectionTable sectionTable;
+ private SectionEditForm sectionEditForm;
+ private SectionPreviewPanel sectionPreviewPanel;
+ private SectionDeleteForm sectionDeleteForm;
+
+ private ActionLink beginLink;
+ private Label moveSectionLabel;
+
+ private final String typeIdStr;
+
+ public MultiPartArticleSectionsStep(
+ final ItemSelectionModel selectedArticleModel,
+ final AuthoringKitWizard authoringKitWizard,
+ final StringParameter selectedLanguageParam) {
+
+ super();
+ this.selectedArticleModel = selectedArticleModel;
+ this.authoringKitWizard = authoringKitWizard;
+ this.selectedLanguageParam = selectedLanguageParam;
+
+ typeIdStr = authoringKitWizard
+ .getContentType()
+ .getContentItemClass()
+ .getName();
+
+ addWidgets();
+ }
+
+ private void addWidgets() {
+ // create the components and set default visibility
+ add(buildSectionTable(), true);
+ add(buildSectionEdit(), false);
+ add(buildSectionDelete(), false);
+ }
+
+ /**
+ * Builds a {@link Container} to hold a {@link SectionTable} and a link to
+ * add a new {@link MultiPartArticleSection}.
+ *
+ * @return A {@link Container} for the table of sections.
+ */
+ protected Container buildSectionTable() {
+
+ final ColumnPanel panel = new ColumnPanel(1);
+ panel.setKey(SECTION_TABLE + typeIdStr);
+ panel.setBorderColor("#FFFFFF");
+ panel.setPadColor("#FFFFFF");
+
+ moveSectionParam = new LongParameter("moveSection");
+ moveSectionModel = new SectionSelectionModel<>(moveSectionParam);
+
+ sectionTable = new SectionTable(selectedArticleModel,
+ moveSectionModel);
+ sectionTable.setClassAttr(DATA_TABLE);
+
+ // selected section is based on the selection in the SectionTable
+ selectedSectionModel = new SectionSelectionModel<>(sectionTable
+ .getRowSelectionModel());
+
+ sectionTable.setSectionModel(selectedSectionModel);
+
+ final Label emptyView = new Label(new GlobalizedMessage(
+ "cms.contenttypes.ui.mparticle.no_sections_yet",
+ CmsConstants.CMS_BUNDLE));
+ sectionTable.setEmptyView(this);
+
+ moveSectionLabel = new Label(new GlobalizedMessage(
+ "cms.contenttypes.ui.mparticle.section.title",
+ CmsConstants.CMS_BUNDLE));
+ panel.add(moveSectionLabel, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+
+ beginLink = new ActionLink(new GlobalizedMessage(
+ "cms.contenttypes.ui.mparticle.move_to_beginning",
+ CmsConstants.CMS_BUNDLE));
+ panel.add(beginLink);
+
+ beginLink.addActionListener(event -> {
+ final PageState state = event.getPageState();
+ final MultiPartArticle article
+ = (MultiPartArticle) selectedArticleModel
+ .getSelectedObject(state);
+ final MultiPartArticleSection section = moveSectionModel
+ .getSelectedSection(state);
+
+ final MultiPartArticleSectionStepController controller = CdiUtil
+ .createCdiUtil()
+ .findBean(MultiPartArticleSectionStepController.class);
+
+ controller.moveToFirst(article, section);
+ });
+
+
+ }
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionDeleteForm.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionDeleteForm.java.todo
new file mode 100755
index 000000000..3fe19a903
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionDeleteForm.java.todo
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.contenttypes.ui.mparticle;
+
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SaveCancelSection;
+import com.arsdigita.bebop.event.FormInitListener;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormSubmissionListener;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.contenttypes.ArticleSection;
+import com.arsdigita.cms.contenttypes.MultiPartArticle;
+import com.arsdigita.cms.contenttypes.util.MPArticleGlobalizationUtil;
+import com.arsdigita.cms.util.GlobalizationUtil;
+import com.arsdigita.util.Assert;
+import org.apache.log4j.Logger;
+
+/**
+ * A form to confirm deletion of a single section of a MultiPartArticle.
+ *
+ * @author Dave Turner
+ * @version $Id: SectionDeleteForm.java 287 2005-02-22 00:29:02Z sskracic $
+ */
+public class SectionDeleteForm extends Form
+ implements FormInitListener, FormSubmissionListener, FormProcessListener
+{
+ private final static Logger log = Logger.getLogger(SectionDeleteForm.class.getName());
+
+ protected ItemSelectionModel m_selArticle;
+ protected ItemSelectionModel m_selSection;
+ protected SaveCancelSection m_saveCancelSection;
+ private Label m_sectionNameLabel;
+
+
+ /**
+ *
+ * @param selArticle
+ * @param selSection
+ */
+ public SectionDeleteForm
+ ( ItemSelectionModel selArticle,
+ ItemSelectionModel selSection) {
+ super("SectionDeleteForm", new ColumnPanel(2));
+ m_selArticle = selArticle;
+ m_selSection = selSection;
+
+ ColumnPanel panel = (ColumnPanel)getPanel();
+ panel.setBorder(false);
+ panel.setPadColor("#FFFFFF");
+ panel.setColumnWidth(1, "20%");
+ panel.setColumnWidth(2, "80%");
+ panel.setWidth("100%");
+
+ m_sectionNameLabel = new Label ("Section Name");
+ add(m_sectionNameLabel, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+ addSaveCancelSection();
+
+ addInitListener(this);
+ addSubmissionListener(this);
+ addProcessListener(this);
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected SaveCancelSection addSaveCancelSection () {
+ m_saveCancelSection = new SaveCancelSection();
+ m_saveCancelSection.getSaveButton().setButtonLabel(
+ GlobalizationUtil.globalize("cms.ui.delete"));
+ add(m_saveCancelSection, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+ return m_saveCancelSection;
+ }
+
+ @Override
+ public void init ( FormSectionEvent event ) throws FormProcessException {
+ PageState state = event.getPageState();
+
+ ArticleSection section = (ArticleSection)m_selSection.getSelectedObject(state);
+
+ if ( section == null ) {
+ log.error("No section selected");
+ } else {
+ m_sectionNameLabel.setLabel(section.getTitle(),state);
+ }
+ }
+
+ @Override
+ public void submitted ( FormSectionEvent event ) throws FormProcessException {
+ PageState state = event.getPageState();
+
+ if ( m_saveCancelSection.getCancelButton().isSelected(state) ) {
+ throw new FormProcessException(
+ "Submission cancelled",
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.submission_cancelled")
+ );
+ }
+ }
+
+ @Override
+ public void process ( FormSectionEvent event ) throws FormProcessException {
+ PageState state = event.getPageState();
+
+ MultiPartArticle article = (MultiPartArticle)m_selArticle.getSelectedObject(state);
+ ArticleSection section = (ArticleSection)m_selSection.getSelectedObject(state);
+
+ Assert.exists(article, MultiPartArticle.class);
+ Assert.exists(section, ArticleSection.class);
+
+ article.removeSection(section);
+
+ log.info("section " + m_selSection.getSelectedKey(state) + " delete");
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionEditForm.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionEditForm.java.todo
new file mode 100755
index 000000000..0e42c1733
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionEditForm.java.todo
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.contenttypes.ui.mparticle;
+
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormData;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SaveCancelSection;
+import com.arsdigita.bebop.event.FormInitListener;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormSubmissionListener;
+import com.arsdigita.bebop.form.TextField;
+import com.arsdigita.bebop.form.CheckboxGroup;
+import com.arsdigita.bebop.form.Option;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.bebop.parameters.NotNullValidationListener;
+import com.arsdigita.bebop.parameters.TrimmedStringParameter;
+import com.arsdigita.cms.ReusableImageAsset;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.TextAsset;
+import com.arsdigita.cms.contenttypes.ArticleSection;
+import com.arsdigita.cms.contenttypes.MultiPartArticle;
+import com.arsdigita.cms.ui.CMSDHTMLEditor;
+import com.arsdigita.cms.contenttypes.util.MPArticleGlobalizationUtil;
+import com.arsdigita.domain.DataObjectNotFoundException;
+import com.arsdigita.util.UncheckedWrapperException;
+import org.apache.log4j.Logger;
+
+import java.math.BigDecimal;
+
+
+/**
+ * Form to edit an ArticleSection for a MultiPartArticle.
+ *
+ * @author Dave Turner
+ * @version $Id: SectionEditForm.java 1423 2006-12-19 22:08:04Z apevec $
+ */
+public class SectionEditForm extends Form {
+
+ private final static Logger log = Logger.getLogger(SectionEditForm.class);
+
+ private ItemSelectionModel m_selArticle;
+ private ItemSelectionModel m_selSection;
+
+ private BigDecimalParameter m_imageParam;
+ private ItemSelectionModel m_selImage;
+
+ private BigDecimalParameter m_textParam;
+ private ItemSelectionModel m_selText;
+ private MultiPartArticleViewSections m_container;
+
+ private SaveCancelSection m_saveCancelSection;
+ private ImageUploadSection m_imageUpload;
+
+
+ public static final String TITLE = "title";
+ public static final String TEXT = "text";
+ public static final String IMAGE = "image";
+ public static final String PAGE_BREAK = "pageBreak";
+
+ private static final String TEXT_PARAM = "textParam";
+ private static final String IMAGE_PARAM = "imageParam";
+
+ /**
+ * Constructor.
+ *
+ * @param selArticle the current article
+ * @param selSection the current section
+ */
+ public SectionEditForm(ItemSelectionModel selArticle,
+ ItemSelectionModel selSection) {
+ this(selArticle, selSection, null);
+ }
+ /**
+ * Constructor.
+ *
+ * @param selArticle the current article
+ * @param selSection the current section
+ * @param container container which this form is added to
+ */
+ public SectionEditForm(ItemSelectionModel selArticle,
+ ItemSelectionModel selSection,
+ MultiPartArticleViewSections container) {
+ super("SectionEditForm", new ColumnPanel(2));
+ m_selArticle = selArticle;
+ m_selSection = selSection;
+ m_container = container;
+
+ m_imageParam = new BigDecimalParameter(IMAGE_PARAM);
+ m_selImage = new ItemSelectionModel(ReusableImageAsset.class.getName(),
+ ReusableImageAsset.BASE_DATA_OBJECT_TYPE,
+ m_imageParam);
+
+ m_textParam = new BigDecimalParameter(TEXT_PARAM);
+ m_selText = new ItemSelectionModel(TextAsset.class.getName(),
+ TextAsset.BASE_DATA_OBJECT_TYPE,
+ m_textParam);
+
+ setMethod(Form.POST);
+ setEncType("multipart/form-data");
+
+ ColumnPanel panel = (ColumnPanel)getPanel();
+ panel.setBorder(false);
+ panel.setPadColor("#FFFFFF");
+ panel.setColumnWidth(1, "20%");
+ panel.setColumnWidth(2, "80%");
+ panel.setWidth("100%");
+
+ addWidgets();
+ addSaveCancelSection();
+
+ addInitListener(new SectionInitListener());
+ addSubmissionListener(new SectionSubmissionListener());
+ addProcessListener(new SectionProcessListener());
+ }
+
+ /**
+ * Instantiate and add a save/cancel section to the form.
+ *
+ * @return the SaveCancelSection that was added
+ */
+ protected SaveCancelSection addSaveCancelSection() {
+ m_saveCancelSection = new SaveCancelSection();
+ add(m_saveCancelSection, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+ return m_saveCancelSection;
+ }
+
+ /**
+ * Returns the save/cancel section from this form.
+ */
+ public SaveCancelSection getSaveCancelSection() {
+ return m_saveCancelSection;
+ }
+
+ /**
+ * Add form widgets for a Section.
+ */
+ protected void addWidgets() {
+
+ //add(new Label(MPArticleGlobalizationUtil
+ // .globalize("cms.contenttypes.ui.mparticle.section.title")));
+ TextField titleWidget = new TextField(
+ new TrimmedStringParameter(TITLE));
+ titleWidget.addValidationListener(new NotNullValidationListener());
+ titleWidget.setLabel(MPArticleGlobalizationUtil
+ .globalize("cms.contenttypes.ui.mparticle.section.title"));
+ add(titleWidget);
+
+ //add(new Label(MPArticleGlobalizationUtil
+ // .globalize("cms.contenttypes.ui.mparticle.section.text")),
+ // ColumnPanel.LEFT | ColumnPanel.FULL_WIDTH);
+ CMSDHTMLEditor textWidget =
+ new CMSDHTMLEditor(new TrimmedStringParameter(TEXT));
+ textWidget.setLabel(MPArticleGlobalizationUtil
+ .globalize("cms.contenttypes.ui.mparticle.section.text"));
+ textWidget.setRows(40);
+ textWidget.setCols(70);
+ textWidget.setWrap(CMSDHTMLEditor.SOFT);
+ add(textWidget,
+ ColumnPanel.LEFT | ColumnPanel.FULL_WIDTH);
+
+ //add(new Label(MPArticleGlobalizationUtil
+ // .globalize("cms.contenttypes.ui.mparticle.section.image")),
+ // ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+ m_imageUpload = new ImageUploadSection("image", m_selImage);
+ m_imageUpload.setLabel(MPArticleGlobalizationUtil
+ .globalize("cms.contenttypes.ui.mparticle.section.image"));
+ add(m_imageUpload, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+
+ //add(new Label());
+ CheckboxGroup pageBreak = new CheckboxGroup(PAGE_BREAK);
+ pageBreak.addOption(new Option("true",
+ new Label(MPArticleGlobalizationUtil
+ .globalize("cms.contenttypes.ui.mparticle.section.create_break")) ));
+ add(pageBreak);
+ }
+
+ /**
+ * Utility method to create a Section from the form data supplied.
+ *
+ * @param event
+ * @param article
+ * @return
+ */
+ protected ArticleSection createSection(FormSectionEvent event,
+ MultiPartArticle article) {
+
+ PageState state = event.getPageState();
+ FormData data = event.getFormData();
+
+ ArticleSection section = new ArticleSection();
+
+ section.setTitle((String)data.get(TITLE));
+ section.setName(article.getName() + ": " + (String)data.get(TITLE));
+ section.setContentSection(article.getContentSection());
+
+ return section;
+ }
+
+ /**
+ *
+ * @param p
+ */
+ @Override
+ public void register(Page p) {
+ super.register(p);
+ p.addGlobalStateParam(m_imageParam);
+ p.addGlobalStateParam(m_textParam);
+ }
+
+
+ /**
+ * Initialize the form. If there is a selected section, ie. this
+ * is an 'edit' step rather than a 'create new' step, load the data
+ * into the form fields.
+ */
+ private class SectionInitListener implements FormInitListener {
+
+ @Override
+ public void init( FormSectionEvent event )
+ throws FormProcessException {
+ PageState state = event.getPageState();
+ FormData data = event.getFormData();
+ m_selImage.setSelectedObject(state, null);
+ m_selText.setSelectedObject(state,null);
+
+
+ if ( m_selSection.getSelectedKey(state) != null ) {
+ BigDecimal id = new BigDecimal(m_selSection
+ .getSelectedKey(state).toString());
+ try {
+ // retrieve the selected Section from the persistence layer
+ ArticleSection section = new ArticleSection(id);
+
+ data.put(TITLE, section.getTitle());
+
+ TextAsset t = section.getText();
+ if ( t != null ) {
+ m_selText.setSelectedObject(state, t);
+ data.put(TEXT, t.getText());
+ }
+
+ ReusableImageAsset img = section.getImage();
+ if (img != null) {
+ m_selImage.setSelectedObject(state, img);
+ }
+
+ if (section.isPageBreak()) {
+ data.put(PAGE_BREAK, new Object[] { "true" });
+ }
+
+ } catch ( DataObjectNotFoundException ex ) {
+ log.error("Section(" + id + ") could not be found");
+ }
+ }
+
+ // Wait until the image selection model is updated before
+ // initializing the image section
+ m_imageUpload.initImageUpload(event);
+ }
+ }
+
+
+ /**
+ * Called on form submission. Check to see if the user clicked the
+ * cancel button. If they did, don't continue with the form.
+ */
+ private class SectionSubmissionListener implements FormSubmissionListener {
+
+ @Override
+ public void submitted( FormSectionEvent event )
+ throws FormProcessException {
+ PageState state = event.getPageState();
+
+ if ( m_saveCancelSection.getCancelButton()
+ .isSelected(state) && m_container != null) {
+ m_container.onlyShowComponent(
+ state, MultiPartArticleViewSections.SECTION_TABLE+
+ m_container.getTypeIDStr());
+ throw new FormProcessException(
+ "Submission cancelled",
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.submission_cancelled")
+ );
+ } else if ( m_imageUpload.getDeleteImageButton().isSelected(state) ) {
+ BigDecimal id = new BigDecimal(m_selSection
+ .getSelectedKey(state).toString());
+ log.debug("deleting image for MPA section " + id);
+ try {
+ ArticleSection section = new ArticleSection(id);
+ section.setImage(null);
+ } catch ( DataObjectNotFoundException ex ) {
+ log.error("Section(" + id + ") could not be found");
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Called after form has been validated. Create the new ArticleSection and
+ * assign it to the current MultiPartArticle.
+ */
+ private class SectionProcessListener implements FormProcessListener {
+
+ @Override
+ public void process( FormSectionEvent event )
+ throws FormProcessException {
+ PageState state = event.getPageState();
+ FormData data = event.getFormData();
+
+ // retrieve the current MultiPartArticle
+ BigDecimal id = new BigDecimal(
+ m_selArticle.getSelectedKey(state).toString());
+ MultiPartArticle article = null;
+
+ try {
+ article = new MultiPartArticle(id);
+ } catch ( DataObjectNotFoundException ex ) {
+ throw new UncheckedWrapperException(ex);
+ }
+
+ // get the selected section to update or create a new one
+ ArticleSection section = (ArticleSection)
+ m_selSection.getSelectedObject(state);
+ if ( section == null ) {
+ section = createSection(event, article);
+ article.addSection(section);
+ }
+
+ section.setTitle((String)data.get(TITLE));
+
+ Object[] pageBreakVal = (Object[])data.get(PAGE_BREAK);
+ boolean pageBreak;
+ if (pageBreakVal == null ||
+ pageBreakVal.length == 0 ||
+ !"true".equals(pageBreakVal[0])) {
+ pageBreak = false;
+ } else {
+ pageBreak = true;
+ }
+ section.setPageBreak(pageBreak);
+
+ // get the image asset
+ ReusableImageAsset reusableImageAsset =
+ m_imageUpload.processImageUpload(event);
+ if ( reusableImageAsset != null ) {
+ section.setImage(reusableImageAsset);
+ m_selImage.setSelectedObject(state, reusableImageAsset);
+ }
+
+
+ // get the text asset
+ TextAsset textAsset = (TextAsset)m_selText.getSelectedObject(state);
+ if ( textAsset == null ) {
+ textAsset = new TextAsset();
+ textAsset.setName(section.getName() + " text");
+ m_selText.setSelectedObject(state, textAsset);
+ section.setText(textAsset);
+ }
+
+ String text = (String)data.get(TEXT);
+ if ( text == null ) {
+ text = "";
+ }
+
+ textAsset.setText(text);
+ if ( m_container != null) {
+ m_container.onlyShowComponent(
+ state,
+ MultiPartArticleViewSections.SECTION_TABLE+
+ m_container.getTypeIDStr());
+ }
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionPreviewPanel.java b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionPreviewPanel.java
new file mode 100755
index 000000000..90dc0883b
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionPreviewPanel.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.contenttypes.ui.mparticle;
+
+import com.arsdigita.bebop.BoxPanel;
+
+public class SectionPreviewPanel extends BoxPanel
+{
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionSelectionModel.java
new file mode 100644
index 000000000..099bb29ad
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionSelectionModel.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 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 com.arsdigita.cms.contenttypes.ui.mparticle;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.ParameterSingleSelectionModel;
+import com.arsdigita.bebop.SingleSelectionModel;
+import com.arsdigita.bebop.event.ChangeListener;
+import com.arsdigita.bebop.parameters.LongParameter;
+import com.arsdigita.bebop.parameters.ParameterModel;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.UnexpectedErrorException;
+import org.librecms.contenttypes.MultiPartArticleSection;
+import org.librecms.contenttypes.MultiPartArticleSectionRepository;
+
+/**
+ * A {@link SingleSelectionModel} implementation for
+ * {@link MultiPartArticleSection}s.
+ *
+ * @param
+ *
+ * @author Jens Pelzetter
+ */
+class SectionSelectionModel
+ implements SingleSelectionModel {
+
+ private final Class clazz;
+ private final SingleSelectionModel model;
+
+ public SectionSelectionModel(final LongParameter parameter) {
+ this(MultiPartArticleSection.class.getName(), parameter);
+ }
+
+ public SectionSelectionModel(final String parameterName) {
+ this(MultiPartArticleSection.class.getName(),
+ new LongParameter(parameterName));
+ }
+
+ public SectionSelectionModel(final Class clazz,
+ final String parameterName) {
+ this(clazz, new LongParameter(parameterName));
+ }
+
+ @SuppressWarnings("unchecked")
+ public SectionSelectionModel(final String className,
+ final String parameterName) {
+
+ try {
+ clazz = (Class) Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ model = new ParameterSingleSelectionModel<>(
+ new LongParameter(parameterName));
+ }
+
+ public SectionSelectionModel(final Class clazz,
+ final LongParameter parameter) {
+ this(clazz, new ParameterSingleSelectionModel<>(parameter));
+ }
+
+ @SuppressWarnings("unchecked")
+ public SectionSelectionModel(final String className,
+ final LongParameter parameter) {
+
+ try {
+ clazz = (Class) Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+ model = new ParameterSingleSelectionModel<>(parameter);
+ }
+
+ public SectionSelectionModel(final Class clazz,
+ final SingleSelectionModel model) {
+ this.clazz = clazz;
+ this.model = model;
+ }
+
+ @SuppressWarnings("unchecked")
+ public SectionSelectionModel(final String className,
+ final SingleSelectionModel model) {
+
+ try {
+ clazz = (Class) Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+ this.model = model;
+ }
+
+ @Override
+ public boolean isSelected(final PageState state) {
+ return model.isSelected(state);
+ }
+
+ @Override
+ public Long getSelectedKey(final PageState state) {
+ final Object key = model.getSelectedKey(state);
+ if (key == null) {
+ return null;
+ } else if (key instanceof Long) {
+ return (Long) key;
+ } else if (key instanceof String) {
+ return Long.parseLong((String) key);
+ } else {
+ return Long.parseLong(key.toString());
+ }
+ }
+
+ @Override
+ public void setSelectedKey(final PageState state, final Long key) {
+ model.setSelectedKey(state, key);
+ }
+
+ public T getSelectedSection(final PageState state) {
+ final Long key = getSelectedKey(state);
+ final MultiPartArticleSectionRepository sectionRepo = CdiUtil
+ .createCdiUtil()
+ .findBean(MultiPartArticleSectionRepository.class);
+ @SuppressWarnings("unchecked")
+ final T object = (T) sectionRepo.findById(key).get();
+ return object;
+ }
+
+ @Override
+ public void clearSelection(final PageState state) {
+
+ model.clearSelection(state);
+ }
+
+ @Override
+ public void addChangeListener(final ChangeListener changeListener) {
+
+ model.addChangeListener(changeListener);
+ }
+
+ @Override
+ public void removeChangeListener(final ChangeListener changeListener) {
+
+ model.removeChangeListener(changeListener);
+ }
+
+ @Override
+ public ParameterModel getStateParameter() {
+
+ return model.getStateParameter();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionTable.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionTable.java.todo
new file mode 100755
index 000000000..243243a8b
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/contenttypes/ui/mparticle/SectionTable.java.todo
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.contenttypes.ui.mparticle;
+
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.ControlLink;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.event.TableActionEvent;
+import com.arsdigita.bebop.event.TableActionListener;
+import com.arsdigita.bebop.table.TableCellRenderer;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableColumnModel;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.cms.CMS;
+import com.arsdigita.cms.ContentItem;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.SecurityManager;
+import com.arsdigita.cms.contenttypes.ArticleSection;
+import com.arsdigita.cms.contenttypes.ArticleSectionCollection;
+import com.arsdigita.cms.contenttypes.MultiPartArticle;
+import com.arsdigita.cms.contenttypes.util.MPArticleGlobalizationUtil;
+import com.arsdigita.domain.DomainObjectFactory;
+import com.arsdigita.persistence.OID;
+import com.arsdigita.util.LockableImpl;
+
+import org.apache.log4j.Logger;
+
+import java.math.BigDecimal;
+
+/**
+ * A table that displays the sections for the currently
+ * selected MultiPartArticle.
+ *
+ * @author Dave Turner
+ * @version $Id: SectionTable.java 2099 2010-04-17 15:35:14Z pboy $
+ */
+public class SectionTable extends Table {
+
+ private static final Logger s_log = Logger.getLogger(SectionTable.class);
+ // match columns by (symbolic) index, makes for easier reordering
+ public static final int COL_INDEX_TITLE = 0; // "Section";
+ public static final int COL_INDEX_EDIT = 1; // "Edit";
+ public static final int COL_INDEX_MOVE = 2; // "Move";
+ public static final int COL_INDEX_DELETE = 3; // "Delete";
+ private ItemSelectionModel m_selArticle;
+ private ItemSelectionModel m_selSection;
+ private ItemSelectionModel m_moveSection;
+
+ /**
+ * Constructor. Create an instance of this class.
+ *
+ * @param selArticle a selection model that returns the MultiPartArticle
+ * which holds the sections to display.
+ * @param moveSection
+ */
+ public SectionTable(ItemSelectionModel selArticle,
+ ItemSelectionModel moveSection) {
+
+ super();
+ m_selArticle = selArticle;
+ m_moveSection = moveSection;
+
+ TableColumnModel model = getColumnModel();
+ model.add(new TableColumn(
+ COL_INDEX_TITLE,
+ new Label(MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.header_section"))));
+ model.add(new TableColumn(
+ COL_INDEX_EDIT,
+ new Label(MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.header_edit"))));
+ model.add(new TableColumn(
+ COL_INDEX_MOVE,
+ new Label(MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.header_move"))));
+ model.add(new TableColumn(
+ COL_INDEX_DELETE,
+ new Label(MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.header_delete"))));
+
+ model.get(1).setCellRenderer(new SectionTableCellRenderer(true));
+ model.get(2).setCellRenderer(new SectionTableCellRenderer(true));
+ model.get(3).setCellRenderer(new SectionTableCellRenderer(true));
+
+
+ setModelBuilder(new SectionTableModelBuilder(m_selArticle, m_moveSection));
+
+ addTableActionListener(new TableActionListener() {
+
+ @Override
+ public void cellSelected(TableActionEvent event) {
+ PageState state = event.getPageState();
+
+ TableColumn col = getColumnModel().get(event.getColumn()
+ .intValue());
+
+ if (col.getModelIndex() == COL_INDEX_MOVE) {
+ if (m_moveSection.getSelectedKey(state) == null) {
+ m_moveSection.setSelectedKey(state,
+ m_selSection
+ .getSelectedKey(state));
+ } else {
+ MultiPartArticle article = (MultiPartArticle) m_selArticle.
+ getSelectedObject(state);
+
+ BigDecimal id = (BigDecimal) m_moveSection.getSelectedKey(state);
+ ArticleSection sect = (ArticleSection) DomainObjectFactory.newInstance(
+ new OID(ArticleSection.BASE_DATA_OBJECT_TYPE, id));
+
+ BigDecimal dest =
+ new BigDecimal((String) event.getRowKey());
+ ArticleSection destSect = (ArticleSection) DomainObjectFactory.newInstance(
+ new OID(ArticleSection.BASE_DATA_OBJECT_TYPE, dest));
+
+ // if sect is lower in rank than the dest
+ // then move below is default behavior
+ int rank = destSect.getRank().intValue();
+ if (sect.getRank().intValue() > rank) {
+ // otherwise, add one to get "move below"
+ rank++;
+ }
+
+ article.changeSectionRank(sect, rank);
+ m_moveSection.setSelectedKey(state, null);
+ }
+ }
+ }
+
+ @Override
+ public void headSelected(TableActionEvent event) {
+ // do nothing
+ }
+
+ });
+ }
+
+ public void setSectionModel(ItemSelectionModel selSection) {
+ if (selSection == null) {
+ s_log.warn("null item model");
+ }
+ m_selSection = selSection;
+ }
+
+ /**
+ * The model builder to generate a suitable model for the SectionTable
+ */
+ protected class SectionTableModelBuilder extends LockableImpl
+ implements TableModelBuilder {
+
+ protected ItemSelectionModel m_selArticle;
+ protected ItemSelectionModel m_moveSection;
+
+ /**
+ * Private class constructor.
+ * @param selArticle
+ * @param moveSection
+ */
+ public SectionTableModelBuilder(ItemSelectionModel selArticle,
+ ItemSelectionModel moveSection) {
+ m_selArticle = selArticle;
+ m_moveSection = moveSection;
+ }
+
+ /**
+ *
+ * @param table
+ * @param state
+ * @return
+ */
+ @Override
+ public TableModel makeModel(Table table, PageState state) {
+ table.getRowSelectionModel().clearSelection(state);
+
+ MultiPartArticle article = (MultiPartArticle) m_selArticle
+ .getSelectedObject(state);
+
+ return new SectionTableModel(table, state, article, m_moveSection);
+ }
+
+ }
+
+ /**
+ * Internal class
+ */
+ protected class SectionTableModel implements TableModel {
+
+ private TableColumnModel m_colModel;
+ private SectionTable m_table;
+ private PageState m_state;
+ private ArticleSectionCollection m_sections;
+ private ItemSelectionModel m_moveSection;
+ private ArticleSection m_section;
+
+ /** Constructor.
+ * @param table
+ * @param state
+ * @param article
+ * @param moveSection
+ */
+ public SectionTableModel(Table table, PageState state,
+ MultiPartArticle article,
+ ItemSelectionModel moveSection) {
+ m_colModel = table.getColumnModel();
+ m_state = state;
+ m_sections = article.getSections();
+ m_table = (SectionTable) table;
+ m_moveSection = moveSection;
+ }
+
+ /** Return the number of columsn this TableModel has. */
+ @Override
+ public int getColumnCount() {
+ return m_colModel.size();
+ }
+
+ /** Move to the next row and return true if the model is now positioned
+ * on a valid row.
+ */
+ @Override
+ public boolean nextRow() {
+ if (m_sections.next()) {
+ m_section = (ArticleSection) m_sections.getArticleSection();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return the data element for the given column and the current row.
+ */
+ @Override
+ public Object getElementAt(int columnIndex) {
+
+ if (m_colModel == null) {
+ return null;
+ }
+
+ // match columns by (symbolic) index, makes for easier reordering
+ if (columnIndex == COL_INDEX_TITLE) {
+ return m_section.getTitle();
+ } else if (columnIndex == COL_INDEX_EDIT) {
+ //return "edit";
+ return new Label(
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.link_edit"));
+ } else if (columnIndex == COL_INDEX_DELETE) {
+ // return "delete";
+ return new Label(
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.link_delete"));
+ } else if (columnIndex == COL_INDEX_MOVE) {
+ if (m_moveSection.getSelectedKey(m_state) == null) {
+ // return "move";
+ return new Label(
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.link_move"));
+ } else {
+ // return "move below here";
+ return new Label(
+ MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.link_move_below"));
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the key for the given column and the current row.
+ */
+ @Override
+ public Object getKeyAt(int columnIndex) {
+ return m_section.getID();
+ }
+
+ }
+
+ /**
+ *
+ */
+ public class SectionTableCellRenderer extends LockableImpl
+ implements TableCellRenderer {
+
+ private boolean m_active;
+
+ public SectionTableCellRenderer() {
+ this(false);
+ }
+
+ public SectionTableCellRenderer(boolean active) {
+ m_active = active;
+ }
+
+ @Override
+ public Component getComponent(Table table, PageState state,
+ Object value, boolean isSelected,
+ Object key, int row, int column) {
+
+ Component ret;
+ SecurityManager sm = CMS.getSecurityManager(state);
+ ContentItem item = (ContentItem) m_selArticle.getSelectedObject(state);
+
+ boolean active = m_active && sm.canAccess(state.getRequest(),
+ SecurityManager.EDIT_ITEM,
+ item);
+
+ if (value instanceof Label) {
+ if (active) {
+ ret = new ControlLink((Component) value);
+ } else {
+ ret = (Component) value;
+ }
+
+ } else if (value instanceof String) {
+ // Backwards compatibility, should be removed asap!
+ if (active) {
+ ret = new ControlLink(value.toString());
+ } else {
+ ret = new Label(value.toString());
+ }
+ } else {
+ ret = new Label(MPArticleGlobalizationUtil.globalize(
+ "cms.contenttypes.ui.mparticle.section_table.link_not_defined"),
+ false);
+ }
+
+ return ret;
+ }
+
+ }
+}
diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticle.java b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticle.java
index 9ae7137dc..7e6478399 100644
--- a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticle.java
+++ b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticle.java
@@ -37,6 +37,7 @@ import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
+import javax.persistence.OrderBy;
import javax.persistence.Table;
import static org.librecms.CmsConstants.*;
diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSection.java b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSection.java
index 52b0064ca..f7512b853 100644
--- a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSection.java
+++ b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSection.java
@@ -33,6 +33,8 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
import javax.persistence.Table;
import static org.librecms.CmsConstants.*;
@@ -45,6 +47,17 @@ import static org.librecms.CmsConstants.*;
@Entity
@Audited
@Table(name = "MULTIPART_ARTICLE_SECTIONS", schema = DB_SCHEMA)
+@NamedQueries({
+ @NamedQuery(
+ name = "MultiPartArticleSection.findById",
+ query = "SELECT s FROM MultiPartArticleSection s "
+ + "WHERE s.sectionId = :sectionId")
+ ,
+ @NamedQuery(
+ name = "MultiPartArticleSection.findArticleOfSection",
+ query = "SELECT a FROM MultiPartArticle a "
+ + "WHERE :section MEMBER OF a.sections")
+})
public class MultiPartArticleSection implements Serializable {
private static final long serialVersionUID = 1109186628988745920L;
@@ -53,7 +66,7 @@ public class MultiPartArticleSection implements Serializable {
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "SECTION_ID")
private long sectionId;
-
+
@Embedded
@AssociationOverride(
name = "values",
@@ -83,11 +96,11 @@ public class MultiPartArticleSection implements Serializable {
public long getSectionId() {
return sectionId;
}
-
+
protected void setSectionId(final long sectionId) {
this.sectionId = sectionId;
}
-
+
public LocalizedString getTitle() {
return title;
}
@@ -121,7 +134,6 @@ public class MultiPartArticleSection implements Serializable {
}
//ToDo: Add image property
-
@Override
public int hashCode() {
int hash = 5;
diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java
new file mode 100644
index 000000000..78a56c8cb
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.contenttypes;
+
+import org.librecms.contentsection.ContentItemRepository;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class MultiPartArticleSectionManager {
+
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Inject
+ private MultiPartArticleSectionRepository sectionRepo;
+
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void addSectionToMultiPartArticle(
+ final MultiPartArticleSection section,
+ final MultiPartArticle article) {
+
+ article.addSection(section);
+ itemRepo.save(article);
+ sectionRepo.save(section);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void removeSectionFromMultiPartArticle(
+ final MultiPartArticleSection section,
+ final MultiPartArticle article) {
+
+ article.removeSection(section);
+ itemRepo.save(article);
+ sectionRepo.delete(section);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void moveToFirst(final MultiPartArticle article,
+ final MultiPartArticleSection section) {
+
+ final List sections = article
+ .getSections()
+ .stream()
+ .sorted((section1, section2) -> Integer.compare(section1.getRank(),
+ section2.getRank()))
+ .collect(Collectors.toList());
+
+ final int oldRank = section.getRank();
+
+ section.setRank(1);
+ sections
+ .stream()
+ .filter(current -> !current.equals(section))
+ .forEach(current -> current.setRank(current.getRank() + 1));
+
+ sections
+ .forEach(current -> sectionRepo.save(section));
+ }
+
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionRepository.java b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionRepository.java
new file mode 100644
index 000000000..312f2bfe7
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionRepository.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.contenttypes;
+
+import org.libreccm.auditing.AbstractAuditedEntityRepository;
+
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class MultiPartArticleSectionRepository
+ extends AbstractAuditedEntityRepository {
+
+ @Override
+ public Long getEntityId(final MultiPartArticleSection entity) {
+ return entity.getSectionId();
+ }
+
+ @Override
+ public Class getEntityClass() {
+ return MultiPartArticleSection.class;
+ }
+
+ @Override
+ public boolean isNew(final MultiPartArticleSection entity) {
+ return entity.getSectionId() == 0;
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Optional findById(
+ final long sectionId) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("MultiPartArticleSection.findById",
+ MultiPartArticleSection.class);
+ query.setParameter("sectionId", sectionId);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public MultiPartArticle findArticleOfSection(
+ final MultiPartArticleSection section) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("MultiPartArticleSection.findArticleOfSection",
+ MultiPartArticle.class);
+ query.setParameter("section", section);
+
+ return query.getSingleResult();
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java
index eefa97493..2867fa148 100644
--- a/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java
+++ b/ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java
@@ -135,7 +135,8 @@ public class CcmObjectSelectionModel
@Override
public void clearSelection(final PageState state) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+
+ model.clearSelection(state);
}
@Override
@@ -145,7 +146,7 @@ public class CcmObjectSelectionModel
@Override
public void removeChangeListener(final ChangeListener changeListener) {
- model.addChangeListener(changeListener);;
+ model.removeChangeListener(changeListener);;
}
@Override
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/AdminUIVaadin.java b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminUIVaadin.java
index 4bb157e73..aade93f75 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/AdminUIVaadin.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminUIVaadin.java
@@ -31,8 +31,6 @@ import org.apache.shiro.subject.Subject;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.PermissionChecker;
-import java.util.Locale;
-
import javax.inject.Inject;
/**