diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ImageStep.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ImageStep.java
deleted file mode 100644
index 87479f3e6..000000000
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ImageStep.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.ui.authoring.assets;
-
-import com.arsdigita.bebop.Text;
-import com.arsdigita.bebop.parameters.StringParameter;
-import com.arsdigita.cms.ItemSelectionModel;
-import com.arsdigita.cms.ui.authoring.AuthoringKitWizard;
-import com.arsdigita.cms.ui.authoring.ResettableContainer;
-
-import org.librecms.CmsConstants;
-import org.librecms.ui.authoring.ContentItemAuthoringStep;
-
-/**
- *
- * @author Jens Pelzetter
- */
-@ContentItemAuthoringStep(
- labelBundle = CmsConstants.CMS_BUNDLE,
- labelKey = "image_step.label",
- descriptionBundle = CmsConstants.CMS_BUNDLE,
- descriptionKey = "image_step.description")
-public class ImageStep extends ResettableContainer {
-
- public ImageStep(final ItemSelectionModel itemSelectionModel,
- final AuthoringKitWizard authoringKitWizard,
- final StringParameter selectedLanguage) {
-
- super();
-
- super.add(new Text("Image Step placeholder"));
- }
-
-}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ItemAttachmentSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ItemAttachmentSelectionModel.java
new file mode 100644
index 000000000..6ce4cfef7
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/ItemAttachmentSelectionModel.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ui.authoring.assets;
+
+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.librecms.contentsection.ItemAttachment;
+import org.librecms.contentsection.ItemAttachmentManager;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ItemAttachmentSelectionModel implements SingleSelectionModel{
+
+ private final SingleSelectionModel model;
+
+ public ItemAttachmentSelectionModel(final LongParameter parameter) {
+ this.model = new ParameterSingleSelectionModel<>(parameter);
+ }
+
+ public ItemAttachmentSelectionModel(final String parameterName) {
+ this(new LongParameter(parameterName));
+ }
+
+ @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 ItemAttachment> getSelectedAttachment(final PageState state) {
+ final Long key = getSelectedKey(state);
+ final ItemAttachmentManager manager = CdiUtil
+ .createCdiUtil()
+ .findBean(ItemAttachmentManager.class);
+ return manager.findById(key).get();
+ }
+
+ @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/ui/authoring/assets/images/AssignedImageTableRow.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImageTableRow.java
new file mode 100644
index 000000000..1ec8e477b
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImageTableRow.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+/**
+ * A container for the data shown in the table of assigned images.
+ *
+ * @author Jens Pelzetter
+ */
+class AssignedImageTableRow {
+
+ private long attachmentId;
+ private String imageUuid;
+ private String filename;
+ private long width;
+ private long height;
+ private String type;
+ private String caption;
+
+ public long getAttachmentId() {
+ return attachmentId;
+ }
+
+ public void setAttachmentId(final long attachmentId) {
+ this.attachmentId = attachmentId;
+ }
+
+ public String getImageUuid() {
+ return imageUuid;
+ }
+
+ public void setImageUuid(final String imageUuid) {
+ this.imageUuid = imageUuid;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(final String filename) {
+ this.filename = filename;
+ }
+
+ public long getWidth() {
+ return width;
+ }
+
+ public void setWidth(final long width) {
+ this.width = width;
+ }
+
+ public long getHeight() {
+ return height;
+ }
+
+ public void setHeight(final long height) {
+ this.height = height;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ public String getCaption() {
+ return caption;
+ }
+
+ public void setCaption(final String caption) {
+ this.caption = caption;
+ }
+
+
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTable.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTable.java
new file mode 100644
index 000000000..6a179b11d
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTable.java
@@ -0,0 +1,255 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.ControlLink;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Image;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.Text;
+import com.arsdigita.bebop.event.TableActionEvent;
+import com.arsdigita.bebop.event.TableActionListener;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.bebop.table.TableCellRenderer;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableColumnModel;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.ui.authoring.assets.ItemAttachmentSelectionModel;
+import com.arsdigita.globalization.GlobalizedMessage;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.CmsConstants;
+import org.librecms.contentsection.ItemAttachment;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class AssignedImagesTable extends Table {
+
+ protected static final int COL_PREVIEW = 0;
+ protected static final int COL_IMAGE_DATA = 1;
+ protected static final int COL_CAPTION = 2;
+ protected static final int COL_MOVE = 3;
+ protected static final int COL_REMOVE = 4;
+
+ private final ItemSelectionModel itemSelectionModel;
+ private final ItemAttachmentSelectionModel moveAttachmentModel;
+ private final StringParameter selectedLanguageParam;
+
+ public AssignedImagesTable(
+ final ItemSelectionModel itemSelectionModel,
+ final ItemAttachmentSelectionModel moveAttachmentModel,
+ final StringParameter selectedLanguageParam) {
+
+ super();
+ this.itemSelectionModel = itemSelectionModel;
+ this.moveAttachmentModel = moveAttachmentModel;
+ this.selectedLanguageParam = selectedLanguageParam;
+
+ final TableColumnModel columnModel = super.getColumnModel();
+ columnModel.add(new TableColumn(
+ COL_PREVIEW,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.preview_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_IMAGE_DATA,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_IMAGE_DATA,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.caption_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_MOVE,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.move_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_IMAGE_DATA,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.remove_header",
+ CmsConstants.CMS_BUNDLE))));
+
+ super
+ .setModelBuilder(new AssignedImagesTableModelBuilder(
+ itemSelectionModel,
+ moveAttachmentModel,
+ selectedLanguageParam));
+
+ super
+ .getColumn(COL_PREVIEW)
+ .setCellRenderer(new ThumbnailCellRenderer());
+ super
+ .getColumn(COL_IMAGE_DATA)
+ .setCellRenderer(new PropertiesCellRenderer());
+ super
+ .getColumn(COL_MOVE)
+ .setCellRenderer(new MoveCellRenderer());
+ super
+ .getColumn(COL_REMOVE)
+ .setCellRenderer(new RemoveCellRenderer());
+
+ super.addTableActionListener(new TableActionListener() {
+
+ @Override
+ public void cellSelected(final TableActionEvent event)
+ throws FormProcessException {
+
+ final PageState state = event.getPageState();
+
+ final TableColumn column = getColumnModel()
+ .get(event.getColumn());
+
+ switch (column.getModelIndex()) {
+ case COL_MOVE:
+ if (moveAttachmentModel
+ .getSelectedAttachment(state) == null) {
+
+ moveAttachmentModel
+ .setSelectedKey(state,
+ Long.parseLong(column.getKey()));
+ } else {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+
+ final ItemAttachment> selectedAttachment
+ = moveAttachmentModel
+ .getSelectedAttachment(state);
+
+ final Long destId = Long
+ .parseLong((String) event.getRowKey());
+
+ controller.moveAfter(selectedAttachment, destId);
+
+ }
+ break;
+ case COL_REMOVE:
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+ controller.deleteAttachment(moveAttachmentModel
+ .getSelectedAttachment(state));
+ break;
+ }
+ }
+
+ @Override
+ public void headSelected(final TableActionEvent event) {
+ //Nothing
+ }
+
+ });
+
+ }
+
+ private class ThumbnailCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+ if (value == null) {
+ return new Text("");
+ } else {
+ final Image image = new Image((String) value, "");
+ return image;
+ }
+ }
+
+ }
+
+ private class PropertiesCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+
+ @SuppressWarnings("unchecked")
+ final ImageProperties properties = (ImageProperties) value;
+ final BoxPanel panel = new BoxPanel(BoxPanel.VERTICAL);
+
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.filename",
+ CmsConstants.CMS_BUNDLE,
+ new Object[]{properties.getFilename()})));
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.size",
+ CmsConstants.CMS_BUNDLE,
+ new Object[]{properties.getWidth(), properties.getHeight()})));
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.type",
+ CmsConstants.CMS_BUNDLE,
+ new String[]{properties.getType()})));
+
+ return panel;
+ }
+
+ }
+
+ private class MoveCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+
+ return new ControlLink((Component) value);
+ }
+
+ }
+
+ private class RemoveCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+
+ return new ControlLink((Component) value);
+ }
+
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModel.java
new file mode 100644
index 000000000..6fe788566
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModel.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.cms.CMS;
+import com.arsdigita.cms.ui.authoring.assets.ItemAttachmentSelectionModel;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.web.CCMDispatcherServlet;
+
+import org.librecms.CmsConstants;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class AssignedImagesTableModel implements TableModel {
+
+ private final PageState state;
+ private final ItemAttachmentSelectionModel moveAttachmentModel;
+
+ private final Iterator iterator;
+ private AssignedImageTableRow currentRow;
+
+ public AssignedImagesTableModel(
+ final List rows,
+ final PageState state,
+ final ItemAttachmentSelectionModel moveAttachmentModel) {
+
+ this.iterator = rows.iterator();
+ this.state = state;
+ this.moveAttachmentModel = moveAttachmentModel;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 4;
+ }
+
+ @Override
+ public boolean nextRow() {
+ if (iterator.hasNext()) {
+ currentRow = iterator.next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Object getElementAt(final int columnIndex) {
+ switch (columnIndex) {
+ case AssignedImagesTable.COL_PREVIEW:
+ return String
+ .format("%s/content-.sections/%s/images/"
+ + "uuid-%s?width=150&height=100",
+ CCMDispatcherServlet.getContextPath(),
+ CMS.getContext().getContentSection().getLabel(),
+ currentRow.getImageUuid());
+ case AssignedImagesTable.COL_IMAGE_DATA: {
+ final ImageProperties imageProperties = new ImageProperties();
+ imageProperties.setFilename(currentRow.getFilename());
+ imageProperties.setWidth(currentRow.getWidth());
+ imageProperties.setHeight(currentRow.getHeight());
+ imageProperties.setType(currentRow.getType());
+
+ return imageProperties;
+ }
+ case AssignedImagesTable.COL_CAPTION:
+ return currentRow.getCaption();
+ case AssignedImagesTable.COL_MOVE:
+ if (moveAttachmentModel.getSelectedKey(state) == null) {
+ return new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_image.link_move",
+ CmsConstants.CMS_BUNDLE));
+ } else {
+ return new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_image.link_move_below_here",
+ CmsConstants.CMS_BUNDLE));
+ }
+ case AssignedImagesTable.COL_REMOVE:
+ return new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.remove",
+ CmsConstants.CMS_BUNDLE));
+ default:
+ throw new IllegalArgumentException(String.format(
+ "Illegal column index %d.", columnIndex));
+ }
+ }
+
+ @Override
+ public Object getKeyAt(final int columnIndex) {
+ return currentRow.getAttachmentId();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModelBuilder.java
new file mode 100644
index 000000000..66f9c1464
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AssignedImagesTableModelBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.ui.authoring.assets.ItemAttachmentSelectionModel;
+import com.arsdigita.util.LockableImpl;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.contentsection.ContentItem;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Creates the {@link AssignedImagesTableModel} which is used in
+ * {@link ImageStep} for the table of assigned images. This
+ * {@link TableModelBuilder} takes the selected item and retrieves all retrieved
+ * images from the item. For each assigned image an instance
+ * {@link AssignedImageTableRow} is created. The resulting list (which might be
+ * empty) is than used to create the image model. This also means that all
+ * interaction with the database is done by this {@link TableModelBuilder}.
+ *
+ * @author Jens Pelzetter
+ */
+class AssignedImagesTableModelBuilder extends LockableImpl implements
+ TableModelBuilder {
+
+ private final ItemSelectionModel itemSelectionModel;
+ private final ItemAttachmentSelectionModel moveAttachmentModel;
+ private final StringParameter selectedLanguageParam;
+
+ /**
+ * Constructor for this {@link TableModelBuilder}.
+ *
+ * @param itemSelectionModel The model used to determine the selected
+ * {@link ContentItem}.
+ * @param selectedLanguageParam Parameter used to determine the selected
+ * language variant of the selected
+ * {@link ContentItem}.
+ */
+ protected AssignedImagesTableModelBuilder(
+ final ItemSelectionModel itemSelectionModel,
+ final ItemAttachmentSelectionModel moveAttachmentModel,
+ final StringParameter selectedLanguageParam) {
+
+ this.itemSelectionModel = itemSelectionModel;
+ this.moveAttachmentModel = moveAttachmentModel;
+ this.selectedLanguageParam = selectedLanguageParam;
+ }
+
+ @Override
+ public TableModel makeModel(final Table table, final PageState state) {
+
+ final ContentItem selectedItem = itemSelectionModel
+ .getSelectedItem(state);
+ final String selectedLanguage = (String) state
+ .getValue(selectedLanguageParam);
+ final Locale selectedLocale = new Locale(selectedLanguage);
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+
+ final List rows = controller
+ .retrieveAssignedImages(selectedItem, selectedLocale);
+
+ return new AssignedImagesTableModel(rows,
+ state,
+ moveAttachmentModel);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImageTableRow.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImageTableRow.java
new file mode 100644
index 000000000..d5d4e44d5
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImageTableRow.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+/**
+ * A container for the data shown in the table of assigned images.
+ *
+ * @author Jens Pelzetter
+ */
+class AvailableImageTableRow {
+
+ private long imageId;
+ private String imageUuid;
+ private String filename;
+ private long width;
+ private long height;
+ private String type;
+ private String caption;
+
+ public long getImageId() {
+ return imageId;
+ }
+
+ public void setImageId(final long imageId) {
+ this.imageId = imageId;
+ }
+
+ public String getImageUuid() {
+ return imageUuid;
+ }
+
+ public void setImageUuid(final String imageUuid) {
+ this.imageUuid = imageUuid;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(final String filename) {
+ this.filename = filename;
+ }
+
+ public long getWidth() {
+ return width;
+ }
+
+ public void setWidth(final long width) {
+ this.width = width;
+ }
+
+ public long getHeight() {
+ return height;
+ }
+
+ public void setHeight(final long height) {
+ this.height = height;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ public String getCaption() {
+ return caption;
+ }
+
+ public void setCaption(final String caption) {
+ this.caption = caption;
+ }
+
+
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImages.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImages.java
new file mode 100644
index 000000000..ac557099d
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImages.java
@@ -0,0 +1,231 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.ControlLink;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.PaginationModelBuilder;
+import com.arsdigita.bebop.Paginator;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.Text;
+import com.arsdigita.bebop.event.TableActionEvent;
+import com.arsdigita.bebop.event.TableActionListener;
+import com.arsdigita.bebop.form.Submit;
+import com.arsdigita.bebop.form.TextField;
+import com.arsdigita.bebop.table.TableCellRenderer;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableColumnModel;
+import com.arsdigita.globalization.GlobalizedMessage;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.CmsConstants;
+import org.librecms.assets.Image;
+import org.librecms.contentsection.AttachmentList;
+
+import java.util.List;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class AvailableImages extends BoxPanel {
+
+ protected static final int COL_PREVIEW = 0;
+ protected static final int COL_PROPERTIES = 1;
+ protected static final int COL_CAPTION = 2;
+ protected static final int COL_ADD = 3;
+
+ public AvailableImages(final AttachmentList imagesList,
+ final List assignedImages) {
+
+ super(BoxPanel.VERTICAL);
+
+ final Form filterForm = new Form("filter_available_images_form",
+ new BoxPanel(BoxPanel.HORIZONTAL));
+ final TextField filterField = new TextField("filter_available_images");
+ filterField.setLabel(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.filter_label"));
+ final Submit submitFilter = new Submit(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.submit_filter"));
+ filterForm.add(filterField);
+ filterForm.add(submitFilter);
+
+ super.add(filterForm);
+
+ final Paginator paginator = new Paginator(new PaginationModelBuilder() {
+
+ @Override
+ public int getTotalSize(final Paginator paginator,
+ final PageState state) {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+
+ return (int) controller
+ .getNumberOfAvailableImages(
+ assignedImages,
+ (String) filterField.getValue(state));
+ }
+
+ @Override
+ public boolean isVisible(final PageState state) {
+ return true;
+ }
+
+ },
+ 30);
+
+ super.add(paginator);
+
+ final Table table = new Table();
+ final TableColumnModel columnModel = table.getColumnModel();
+ columnModel.add(new TableColumn(
+ COL_PREVIEW,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.preview_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_PROPERTIES,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.properties_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_CAPTION,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.caption_header",
+ CmsConstants.CMS_BUNDLE))));
+ columnModel.add(new TableColumn(
+ COL_ADD,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.select_header",
+ CmsConstants.CMS_BUNDLE))));
+
+ table.setModelBuilder(new AvailableImagesTableModelBuilder(imagesList,
+ filterField,
+ paginator));
+
+ table
+ .getColumn(COL_PREVIEW)
+ .setCellRenderer(new ThumbnailCellRenderer());
+ table
+ .getColumn(COL_PROPERTIES)
+ .setCellRenderer(new PropertiesCellRenderer());
+ table
+ .getColumn(COL_ADD)
+ .setCellRenderer(new AddCellRenderer());
+
+ table.addTableActionListener(new TableActionListener() {
+
+ @Override
+ public void cellSelected(final TableActionEvent event)
+ throws FormProcessException {
+
+ final long imageId = (long) event.getRowKey();
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+ controller.attachImage(imagesList, imageId);
+ }
+
+ @Override
+ public void headSelected(final TableActionEvent event) {
+ //Nothing
+ }
+
+ });
+ }
+
+ private class ThumbnailCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+ if (value == null) {
+ return new Text("");
+ } else {
+ final com.arsdigita.bebop.Image image
+ = new com.arsdigita.bebop.Image(
+ (String) value, "");
+ return image;
+ }
+ }
+
+ }
+
+ private class PropertiesCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+
+ @SuppressWarnings("unchecked")
+ final ImageProperties properties = (ImageProperties) value;
+ final BoxPanel panel = new BoxPanel(BoxPanel.VERTICAL);
+
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.filename",
+ CmsConstants.CMS_BUNDLE,
+ new Object[]{properties.getFilename()})));
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.size",
+ CmsConstants.CMS_BUNDLE,
+ new Object[]{properties.getWidth(), properties.getHeight()})));
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.properties.type",
+ CmsConstants.CMS_BUNDLE,
+ new String[]{properties.getType()})));
+
+ return panel;
+ }
+
+ }
+
+ private class AddCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(final Table table,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final Object key,
+ final int row,
+ final int column) {
+
+ return new ControlLink((Component) value);
+ }
+
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModel.java
new file mode 100644
index 000000000..8036a455b
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModel.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.cms.CMS;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.web.CCMDispatcherServlet;
+
+import org.librecms.CmsConstants;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class AvailableImagesTableModel implements TableModel {
+
+ private final Iterator iterator;
+ private AvailableImageTableRow currentRow;
+
+ public AvailableImagesTableModel(final List rows) {
+ iterator = rows.iterator();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 4;
+ }
+
+ @Override
+ public boolean nextRow() {
+ if (iterator.hasNext()) {
+ currentRow = iterator.next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Object getElementAt(final int columnIndex) {
+
+ switch (columnIndex) {
+ case AvailableImages.COL_PREVIEW:
+ return String
+ .format("%s/content-.sections/%s/images/"
+ + "uuid-%s?width=150&height=100",
+ CCMDispatcherServlet.getContextPath(),
+ CMS.getContext().getContentSection().getLabel(),
+ currentRow.getImageUuid());
+ case AvailableImages.COL_PROPERTIES:
+ final ImageProperties imageProperties = new ImageProperties();
+ imageProperties.setFilename(currentRow.getFilename());
+ imageProperties.setWidth(currentRow.getWidth());
+ imageProperties.setHeight(currentRow.getHeight());
+ imageProperties.setType(currentRow.getType());
+
+ return imageProperties;
+ case AvailableImages.COL_CAPTION:
+ return currentRow.getCaption();
+ case AvailableImages.COL_ADD:
+ return new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.available_images.add",
+ CmsConstants.CMS_BUNDLE));
+ default:
+ throw new IllegalArgumentException(String.format(
+ "Illegal column index %d.", columnIndex));
+ }
+
+ }
+
+ @Override
+ public Object getKeyAt(final int columnIndex) {
+ return currentRow.getImageId();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModelBuilder.java
new file mode 100644
index 000000000..313d1b555
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/AvailableImagesTableModelBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Paginator;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.form.TextField;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.util.LockableImpl;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.assets.Image;
+import org.librecms.contentsection.AttachmentList;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class AvailableImagesTableModelBuilder
+ extends LockableImpl
+ implements TableModelBuilder {
+
+ private final AttachmentList imageList;
+ private final TextField filterField;
+ private final Paginator paginator;
+
+ public AvailableImagesTableModelBuilder(final AttachmentList imageList,
+ final TextField filterField,
+ final Paginator paginator) {
+ this.imageList = imageList;
+ this.filterField = filterField;
+ this.paginator = paginator;
+ }
+
+ @Override
+ public TableModel makeModel(final Table table,
+ final PageState state) {
+
+ final List excludedImages = imageList
+ .getAttachments()
+ .stream()
+ .map(attachment -> attachment.getAsset())
+ .filter(asset -> asset instanceof Image)
+ .map(asset -> (Image) asset)
+ .collect(Collectors.toList());
+
+ //Paginator count from 1, JPA from 0
+ final int firstResult = paginator.getFirst(state) - 1;
+ final int maxResults = paginator.getPageSize(state);
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final ImageStepController controller = cdiUtil
+ .findBean(ImageStepController.class);
+
+ final List rows = controller
+ .getAvailableImageRows(excludedImages,
+ (String) filterField.getValue(state),
+ firstResult,
+ maxResults);
+
+ return new AvailableImagesTableModel(rows);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageProperties.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageProperties.java
new file mode 100644
index 000000000..a8b8581ea
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageProperties.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class ImageProperties {
+
+ protected static final String IMAGE_DATA_FILE_NAME = "filename";
+ protected static final String IMAGE_DATA_WIDTH = "width";
+ protected static final String IMAGE_DATA_HEIGHT = "height";
+ protected static final String IMAGE_DATA_TYPE = "type";
+
+ private String filename;
+
+ private long width;
+
+ private long height;
+
+ private String type;
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(final String filename) {
+ this.filename = filename;
+ }
+
+ public long getWidth() {
+ return width;
+ }
+
+ public void setWidth(final long width) {
+ this.width = width;
+ }
+
+ public long getHeight() {
+ return height;
+ }
+
+ public void setHeight(final long height) {
+ this.height = height;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStep.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStep.java
new file mode 100644
index 000000000..862c7b478
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStep.java
@@ -0,0 +1,156 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import com.arsdigita.bebop.ActionLink;
+import com.arsdigita.bebop.ControlLink;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.Text;
+import com.arsdigita.bebop.parameters.LongParameter;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.cms.ItemSelectionModel;
+import com.arsdigita.cms.ui.GlobalNavigation;
+import com.arsdigita.cms.ui.authoring.AuthoringKitWizard;
+import com.arsdigita.cms.ui.authoring.ResettableContainer;
+import com.arsdigita.cms.ui.authoring.assets.ItemAttachmentSelectionModel;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.kernel.KernelConfig;
+
+import org.libreccm.cdi.utils.CdiUtil;
+import org.librecms.CmsConstants;
+import org.librecms.contentsection.ContentItem;
+import org.librecms.contentsection.ItemAttachment;
+import org.librecms.ui.authoring.ContentItemAuthoringStep;
+
+import java.util.Locale;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@ContentItemAuthoringStep(
+ labelBundle = CmsConstants.CMS_BUNDLE,
+ labelKey = "image_step.label",
+ descriptionBundle = CmsConstants.CMS_BUNDLE,
+ descriptionKey = "image_step.description")
+public class ImageStep extends ResettableContainer {
+
+ private final LongParameter moveAttachmentParam;
+ private final ItemAttachmentSelectionModel moveAttachmentModel;
+
+ private final Label assignedImagesHeader;
+ private final ControlLink addImageLink;
+ private final ActionLink beginLink;
+ private final Table assignedImagesTable;
+
+ public ImageStep(final ItemSelectionModel itemSelectionModel,
+ final AuthoringKitWizard authoringKitWizard,
+ final StringParameter selectedLanguageParam) {
+
+ super();
+
+ moveAttachmentParam = new LongParameter("moveAttachment");
+ moveAttachmentModel = new ItemAttachmentSelectionModel(
+ moveAttachmentParam);
+
+ assignedImagesHeader = new Label(event -> {
+ final PageState state = event.getPageState();
+ final Label target = (Label) event.getTarget();
+
+ final ContentItem selectedItem = itemSelectionModel
+ .getSelectedItem(state);
+ final String selectedLanguage = (String) state
+ .getValue(selectedLanguageParam);
+ final Locale selectedLocale = new Locale(selectedLanguage);
+ final String title;
+ if (selectedItem.getTitle().hasValue(selectedLocale)) {
+ title = selectedItem.getTitle().getValue(selectedLocale);
+ } else {
+ title = selectedItem.getTitle().getValue(KernelConfig
+ .getConfig()
+ .getDefaultLocale());
+ }
+
+ target.setLabel(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.heading",
+ CmsConstants.CMS_BUNDLE,
+ new String[]{title}));
+ });
+ super.add(assignedImagesHeader);
+
+ addImageLink = new ControlLink(new Label(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.add_image",
+ CmsConstants.CMS_BUNDLE)));
+ addImageLink.addActionListener(event -> {
+ //ToDo
+ });
+ super.add(addImageLink);
+
+ beginLink = new ActionLink(new GlobalizedMessage(
+ "cms.ui.authoring.assets.imagestep.assigned_images.move_to_beginning",
+ CmsConstants.CMS_BUNDLE));
+ add(beginLink);
+
+ beginLink.addActionListener(event -> {
+ final PageState state = event.getPageState();
+ final ItemAttachment> toMove = moveAttachmentModel
+ .getSelectedAttachment(state);
+
+ final ImageStepController controller = CdiUtil
+ .createCdiUtil()
+ .findBean(ImageStepController.class);
+
+ controller.moveToFirst(toMove);
+ });
+
+ assignedImagesTable = new AssignedImagesTable(itemSelectionModel,
+ moveAttachmentModel,
+ selectedLanguageParam);
+ super.add(assignedImagesTable);
+
+ super.add(new Text("Image Step placeholder"));
+
+ moveAttachmentModel.addChangeListener(event -> {
+
+ final PageState state = event.getPageState();
+
+ if (moveAttachmentModel.getSelectedKey(state) == null) {
+ addImageLink.setVisible(state, true);
+ beginLink.setVisible(state, false);
+ } else {
+ addImageLink.setVisible(state, false);
+ beginLink.setVisible(state, true);
+ }
+ });
+ }
+
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+
+ page.setVisibleDefault(assignedImagesHeader, true);
+ page.setVisibleDefault(addImageLink, true);
+ page.setVisibleDefault(beginLink, false);
+ page.setVisibleDefault(assignedImagesTable, true);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStepController.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStepController.java
new file mode 100644
index 000000000..259cffa68
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/assets/images/ImageStepController.java
@@ -0,0 +1,329 @@
+/*
+ * 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.ui.authoring.assets.images;
+
+import org.libreccm.l10n.LocalizedString;
+import org.librecms.assets.Image;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.AttachmentList;
+import org.librecms.contentsection.AttachmentListManager;
+import org.librecms.contentsection.ContentItem;
+import org.librecms.contentsection.ContentItemRepository;
+import org.librecms.contentsection.ItemAttachment;
+import org.librecms.contentsection.ItemAttachmentManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Root;
+import javax.transaction.Transactional;
+
+/**
+ * Encapsulates all interaction between the {@link ImageStep} and associated
+ * classes like the {@link ImageStepTableModelBuilder} and CDI beans.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+class ImageStepController {
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private AttachmentListManager attachmentListManager;
+
+ @Inject
+ private ItemAttachmentManager attachmentManager;
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected List retrieveAssignedImages(
+ final ContentItem fromContentItem, final Locale selectedLocale) {
+
+ Objects.requireNonNull(fromContentItem);
+ Objects.requireNonNull(selectedLocale);
+
+ final ContentItem item = itemRepo
+ .findById(fromContentItem.getObjectId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ContentItem with id %d in the database.",
+ fromContentItem.getObjectId())));
+
+ final List imageLists = attachmentListManager
+ .getAttachmentList(item, ".images");
+
+ if (imageLists.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List> attachments = new ArrayList<>();
+ for (final AttachmentList imageList : imageLists) {
+ for (final ItemAttachment> attachment : imageList.getAttachments()) {
+ attachments.add(attachment);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ final List rows = attachments
+ .stream()
+ .sorted((attachment1, attachment2) -> {
+ return attachment1.compareTo(attachment2);
+ })
+ .filter(attachment -> attachment.getAsset() instanceof Image)
+ .map(attachment -> (ItemAttachment) attachment)
+ .map(imageAttachment -> buildAssignedImageTableRow(imageAttachment,
+ selectedLocale))
+ .collect(Collectors.toList());
+
+ return rows;
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void moveToFirst(final ItemAttachment> attachmentToMove) {
+
+ Objects.requireNonNull(attachmentToMove);
+
+ final ItemAttachment> toMove = attachmentManager
+ .findById(attachmentToMove.getAttachmentId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ItemAttachment with ID %d in the database.",
+ attachmentToMove.getAttachmentId())));
+
+ final AttachmentList attachmentList = toMove.getAttachmentList();
+ final List> attachments = attachmentList
+ .getAttachments()
+ .stream()
+ .sorted((attachment1, attachment2) -> {
+ return attachment1.compareTo(attachment2);
+ })
+ .collect(Collectors.toList());
+
+ toMove.setSortKey(0);
+ attachments
+ .stream()
+ .filter(current -> !current.equals(toMove))
+ .forEach(current -> current.setSortKey(current.getSortKey() + 1));
+
+ attachments
+ .forEach(current -> entityManager.merge(current));
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void moveAfter(final ItemAttachment> attachmentToMove,
+ final long destId) {
+
+ Objects.requireNonNull(attachmentToMove);
+
+ final ItemAttachment> toMove = attachmentManager
+ .findById(attachmentToMove.getAttachmentId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ItemAttachment with ID %d in the database.",
+ attachmentToMove.getAttachmentId())));
+
+ final ItemAttachment> after = attachmentManager
+ .findById(destId)
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ItemAttachment with ID %d in the database.",
+ attachmentToMove.getAttachmentId())));
+
+ final AttachmentList attachmentList = toMove.getAttachmentList();
+ final List> attachments = attachmentList
+ .getAttachments()
+ .stream()
+ .sorted((attachment1, attachment2) -> {
+ return attachment1.compareTo(attachment2);
+ })
+ .collect(Collectors.toList());
+
+ if (!attachments.contains(toMove)) {
+ throw new IllegalArgumentException(String.format(
+ "ItemAttachment %d is not part of AttachmentList %d.",
+ toMove.getAttachmentId(),
+ attachmentList.getListId()));
+ }
+
+ if (!attachments.contains(after)) {
+ throw new IllegalArgumentException(String.format(
+ "ItemAttachment %d is not part of AttachmentList %d.",
+ after.getAttachmentId(),
+ attachmentList.getListId()));
+ }
+
+ final int afterIndex = attachments.indexOf(after);
+ for (int i = afterIndex + 1; i < attachments.size(); i++) {
+ final ItemAttachment> current = attachments.get(i);
+ current.setSortKey(current.getSortKey() + 1);
+ entityManager.merge(current);
+ }
+
+ toMove.setSortKey(afterIndex + 1);
+ entityManager.merge(toMove);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void deleteAttachment(final ItemAttachment> attachment) {
+
+ Objects.requireNonNull(attachment);
+
+ final ItemAttachment> toRemove = attachmentManager
+ .findById(attachment.getAttachmentId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ItemAttachment with ID %d in the database.",
+ attachment.getAttachmentId())));
+
+ entityManager.remove(toRemove);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected long getNumberOfAvailableImages(final List excluededImages,
+ final String filter) {
+
+ final CriteriaBuilder criteriaBuilder = entityManager
+ .getCriteriaBuilder();
+
+ final CriteriaQuery query = criteriaBuilder
+ .createQuery(Long.class);
+ final Root from = query.from(Image.class);
+ final Join titleJoin = from.join("title.values");
+
+ query
+ .select(criteriaBuilder.count(from));
+ if (filter == null || filter.trim().isEmpty()) {
+ query.where(criteriaBuilder.and(
+ criteriaBuilder.not(from.in(excluededImages)),
+ criteriaBuilder.like(titleJoin, String.format("&s%%", filter))));
+ } else {
+ query.where(criteriaBuilder.not(from.in(excluededImages)));
+ }
+
+ return entityManager.createQuery(query).getSingleResult();
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected List getAvailableImages(final List excludedImages,
+ final String filter,
+ final long firstImage,
+ final long maxImages) {
+ final CriteriaBuilder criteriaBuilder = entityManager
+ .getCriteriaBuilder();
+
+ final CriteriaQuery criteriaQuery = criteriaBuilder
+ .createQuery(Image.class);
+ final Root from = criteriaQuery.from(Image.class);
+ final Join titleJoin = from.join("title.values");
+
+ if (filter == null || filter.trim().isEmpty()) {
+ criteriaQuery.where(criteriaBuilder.and(
+ criteriaBuilder.not(from.in(excludedImages)),
+ criteriaBuilder.like(titleJoin, String.format("&s%%", filter))));
+ } else {
+ criteriaQuery.where(criteriaBuilder.not(from.in(excludedImages)));
+ }
+
+ final TypedQuery query = entityManager
+ .createQuery(criteriaQuery);
+ query.setFirstResult((int) firstImage);
+ query.setMaxResults((int) maxImages);
+
+ return query.getResultList();
+
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected List getAvailableImageRows(
+ final List excludedImages,
+ final String filter,
+ final long firstImage,
+ final long lastImage) {
+
+ return getAvailableImages(excludedImages, filter, firstImage, lastImage)
+ .stream()
+ .map(this::buildAvailableImageRow)
+ .collect(Collectors.toList());
+
+ }
+
+ private AvailableImageTableRow buildAvailableImageRow(final Image image) {
+ final AvailableImageTableRow row = new AvailableImageTableRow();
+ row.setImageId(image.getObjectId());
+ row.setImageUuid(image.getUuid());
+ row.setFilename(image.getFileName());
+ row.setWidth(image.getWidth());
+ row.setHeight(row.getHeight());
+ row.setType(image.getMimeType().toString());
+
+ return row;
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void attachImage(final AttachmentList attachmentList,
+ final long imageId) {
+
+ final AttachmentList addTo = attachmentListManager
+ .getAttachmentList(attachmentList.getListId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No AttachmentList with ID %d in the database.",
+ attachmentList.getListId())));
+
+ final Image image = assetRepo
+ .findById(imageId, Image.class)
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No Image with ID %d in the database.",
+ imageId)));
+
+ attachmentManager.attachAsset(image, attachmentList);
+ }
+
+ private AssignedImageTableRow buildAssignedImageTableRow(
+ final ItemAttachment imageAttachment,
+ final Locale selectedLocale) {
+
+ final AssignedImageTableRow row = new AssignedImageTableRow();
+
+ row.setAttachmentId(imageAttachment.getAttachmentId());
+
+ final Image image = imageAttachment.getAsset();
+ row.setImageUuid(image.getUuid());
+ row.setFilename(image.getFileName());
+ row.setWidth(image.getWidth());
+ row.setHeight(image.getHeight());
+ row.setType(image.getMimeType().toString());
+ row.setCaption(image.getDescription().getValue(selectedLocale));
+
+ return row;
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentList.java b/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentList.java
index a59b76157..f260541d8 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentList.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentList.java
@@ -59,6 +59,11 @@ import static org.librecms.CmsConstants.*;
@Table(name = "ATTACHMENT_LISTS", schema = DB_SCHEMA)
@Audited
@NamedQueries({
+ @NamedQuery(
+ name = "AttachmentList.findById",
+ query = "SELECT l FROM AttachmentList l WHERE l.listId = :listId"
+ )
+ ,
@NamedQuery(
name = "AttachmentList.findForItemAndName",
query = "SELECT l FROM AttachmentList l "
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentListManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentListManager.java
index 6ab9cff31..61d9da9d6 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentListManager.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/AttachmentListManager.java
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
@@ -109,6 +110,20 @@ public class AttachmentListManager {
return names;
}
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Optional getAttachmentList(final long listId) {
+
+ final TypedQuery query = entityManager
+ .createNamedQuery("AttachmentList.findById", AttachmentList.class);
+ query.setParameter("listId", listId);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
/**
* Retrieves all {@link AttachmentList}s of a {@link ContentItem} with a
* specific name.
@@ -382,5 +397,4 @@ public class AttachmentListManager {
entityManager.merge(list2.get());
}
-
}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachment.java b/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachment.java
index 629c79219..36e13e80c 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachment.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachment.java
@@ -19,9 +19,7 @@
package org.librecms.contentsection;
import org.hibernate.envers.Audited;
-import org.libreccm.core.CcmObject;
import org.libreccm.core.Identifiable;
-import org.libreccm.security.Relation;
import java.io.Serializable;
import java.util.Objects;
@@ -52,22 +50,27 @@ import static org.librecms.CmsConstants.*;
@Audited
@NamedQueries({
@NamedQuery(
- name = "ItemAttachment.countByAssetIdAndList",
- query = "SELECT COUNT(i) FROM ItemAttachment i "
- + "WHERE i.asset = :asset "
- + "AND i.attachmentList = :attachmentList")
+ name = "ItemAttachment.findById",
+ query = "SELECT i FROM ItemAttachment i "
+ + "WHERE i.attachmentId = :attachmentId")
,
@NamedQuery(
- name = "ItemAttachment.findByAssetByAndList",
- query = "SELECT i FROM ItemAttachment i "
- + "WHERE i.asset = :asset "
- + "AND i.attachmentList = :attachmentList"
+ name = "ItemAttachment.countByAssetIdAndList",
+ query = "SELECT COUNT(i) FROM ItemAttachment i "
+ + "WHERE i.asset = :asset "
+ + "AND i.attachmentList = :attachmentList")
+ ,
+ @NamedQuery(
+ name = "ItemAttachment.findByAssetByAndList",
+ query = "SELECT i FROM ItemAttachment i "
+ + "WHERE i.asset = :asset "
+ + "AND i.attachmentList = :attachmentList"
)
})
public class ItemAttachment
- implements Comparable>,
- Identifiable,
- Serializable {
+ implements Comparable>,
+ Identifiable,
+ Serializable {
private static final long serialVersionUID = -9005379413315191984L;
@@ -96,7 +99,7 @@ public class ItemAttachment
* The {@link Asset} which is linked by this attachment to the
* {@link #attachmentList}.
*/
- @ManyToOne(targetEntity = Asset.class,
+ @ManyToOne(targetEntity = Asset.class,
cascade = {CascadeType.PERSIST})
@JoinColumn(name = "ASSET_ID")
private T asset;
@@ -161,7 +164,7 @@ public class ItemAttachment
public int hashCode() {
int hash = 3;
hash
- = 71 * hash + (int) (attachmentId ^ (attachmentId >>> 32));
+ = 71 * hash + (int) (attachmentId ^ (attachmentId >>> 32));
hash = 71 * hash + Objects.hashCode(uuid);
hash = 71 * hash + Objects.hashCode(asset);
hash = 71 * hash + (int) (sortKey ^ (sortKey >>> 32));
@@ -206,11 +209,11 @@ public class ItemAttachment
public String toString(final String data) {
return String.format("%s{ "
- + "attachmentId = %d, "
- + "uuid = %s, "
- + "asset = { %s }, "
- + "sortKey = %d%s"
- + " }",
+ + "attachmentId = %d, "
+ + "uuid = %s, "
+ + "asset = { %s }, "
+ + "sortKey = %d%s"
+ + " }",
super.toString(),
attachmentId,
uuid,
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachmentManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachmentManager.java
index 249d31562..8df51dc34 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachmentManager.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ItemAttachmentManager.java
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
@@ -52,6 +53,17 @@ public class ItemAttachmentManager {
@Inject
private AssetManager assetManager;
+ public Optional> findById(final long attachmentId) {
+
+ final TypedQuery query = entityManager
+ .createNamedQuery("ItemAttachment.findById", ItemAttachment.class);
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
/**
* Adds the provided {@link Asset} to the provided {@link AttachmentList}.
*
diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java
index cd1f968a8..af9269f07 100644
--- a/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java
+++ b/ccm-cms/src/main/java/org/librecms/contenttypes/MultiPartArticleSectionManager.java
@@ -73,8 +73,6 @@ public class MultiPartArticleSectionManager {
section2.getRank()))
.collect(Collectors.toList());
- final int oldRank = section.getRank();
-
section.setRank(0);
sections
.stream()
diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources.properties
index 9a4b26dff..e62f9c3a4 100644
--- a/ccm-cms/src/main/resources/org/librecms/CmsResources.properties
+++ b/ccm-cms/src/main/resources/org/librecms/CmsResources.properties
@@ -387,3 +387,21 @@ cms.ui.authoring.file_upload.auto_detect=(Auto-Detect)
cms.ui.upload_new_content=Upload new content
cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Preview
+cms.ui.authoring.assets.imagestep.heading=Images assigned to document {0}
+cms.ui.authoring.assets.imagestep.assigned_images.preview_header=Preview
+cms.ui.authoring.assets.imagestep.assigned_images.properties_header=Properties
+cms.ui.authoring.assets.imagestep.assigned_images.caption_header=Caption
+cms.ui.authoring.assets.imagestep.assigned_images.remove_header=Remove
+cms.ui.authoring.assets.imagestep.assigned_images.properties.filename=File name: {0}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.size=Size: {0}x{1}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.type=Type: {0}
+cms.ui.authoring.assets.imagestep.assigned_image.link_move=Move
+cms.ui.authoring.assets.imagestep.assigned_image.link_move_below_here=Move below here
+cms.ui.authoring.assets.imagestep.assigned_images.move_header=Move
+cms.ui.authoring.assets.imagestep.assigned_images.move_to_beginning=Move to first position
+cms.ui.authoring.assets.imagestep.available_images.submit_filter=Submit
+cms.ui.authoring.assets.imagestep.available_images.filter_label=Filter images
+cms.ui.authoring.assets.imagestep.available_images.preview_header=Preview
+cms.ui.authoring.assets.imagestep.available_images.properties_header=Properties
+cms.ui.authoring.assets.imagestep.available_images.select_header=Add image
+cms.ui.authoring.assets.imagestep.available_images.caption_header=Caption
diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties
index 3efb7d039..981e641d1 100644
--- a/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties
+++ b/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties
@@ -384,3 +384,21 @@ cms.ui.authoring.file_upload.auto_detect=(Automatisch erkennen)
cms.ui.upload_new_content=Neuen Inhalt hochladen
cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Vorschau
+cms.ui.authoring.assets.imagestep.heading=Dokument {0} zugeordnete Bilder
+cms.ui.authoring.assets.imagestep.assigned_images.preview_header=Vorschau
+cms.ui.authoring.assets.imagestep.assigned_images.properties_header=Eigenschaften
+cms.ui.authoring.assets.imagestep.assigned_images.caption_header=Bildunterschrift
+cms.ui.authoring.assets.imagestep.assigned_images.remove_header=Entfernen
+cms.ui.authoring.assets.imagestep.assigned_images.properties.filename=Dateiname: {0}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.size=Gr\u00f6\u00dfe: {0}x{1}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.type=Typ: {0}
+cms.ui.authoring.assets.imagestep.assigned_image.link_move=Verschieben
+cms.ui.authoring.assets.imagestep.assigned_image.link_move_below_here=Hierher verschieben
+cms.ui.authoring.assets.imagestep.assigned_images.move_header=Verschieben
+cms.ui.authoring.assets.imagestep.assigned_images.move_to_beginning=An die erste Position verschieben
+cms.ui.authoring.assets.imagestep.available_images.submit_filter=Anwenden
+cms.ui.authoring.assets.imagestep.available_images.filter_label=Bilder filtern
+cms.ui.authoring.assets.imagestep.available_images.preview_header=Vorschau
+cms.ui.authoring.assets.imagestep.available_images.properties_header=Eigenschaften
+cms.ui.authoring.assets.imagestep.available_images.select_header=Hinzuf\u00fcgen
+cms.ui.authoring.assets.imagestep.available_images.caption_header=Bildunterschrift
diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties
index 38ab5b61d..eae852522 100644
--- a/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties
+++ b/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties
@@ -343,3 +343,21 @@ cms.ui.authoring.file_upload.auto_detect=(Auto-Detect)
cms.ui.upload_new_content=Upload new content
cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Preview
+cms.ui.authoring.assets.imagestep.heading=Images assigned to ContentItem {0}
+cms.ui.authoring.assets.imagestep.assigned_images.preview_header=Preview
+cms.ui.authoring.assets.imagestep.assigned_images.properties_header=Properties
+cms.ui.authoring.assets.imagestep.assigned_images.caption_header=Caption
+cms.ui.authoring.assets.imagestep.assigned_images.remove_header=Remove
+cms.ui.authoring.assets.imagestep.assigned_images.properties.filename=File name: {0}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.size=Size: {0}x{1}
+cms.ui.authoring.assets.imagestep.assigned_images.properties.type=Type: {0}
+cms.ui.authoring.assets.imagestep.assigned_image.link_move=Move
+cms.ui.authoring.assets.imagestep.assigned_image.link_move_below_here=Move below here
+cms.ui.authoring.assets.imagestep.assigned_images.move_header=Move
+cms.ui.authoring.assets.imagestep.assigned_images.move_to_beginning=Move to first position
+cms.ui.authoring.assets.imagestep.available_images.submit_filter=Submit
+cms.ui.authoring.assets.imagestep.available_images.filter_label=Filter images
+cms.ui.authoring.assets.imagestep.available_images.preview_header=Preview
+cms.ui.authoring.assets.imagestep.available_images.properties_header=Properties
+cms.ui.authoring.assets.imagestep.available_images.select_header=Add image
+cms.ui.authoring.assets.imagestep.available_images.caption_header=Caption