diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
index e60520629..94acbcd30 100644
--- a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
+++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
@@ -6,6 +6,11 @@
version="3.0">
LibreCCM Devel Bundle for Wildfly
+
+
+ ccm.develmode
+ true
+
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/FileUploadSection.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/FileUploadSection.java
new file mode 100755
index 000000000..ce5263909
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/FileUploadSection.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2001-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.ui;
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Container;
+import com.arsdigita.bebop.FormData;
+import com.arsdigita.bebop.FormSection;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.form.FileUpload;
+import com.arsdigita.bebop.form.Option;
+import com.arsdigita.bebop.form.OptionGroup;
+import com.arsdigita.bebop.form.SingleSelect;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.dispatcher.MultipartHttpServletRequest;
+import java.io.File;
+import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+import javax.activation.MimetypesFileTypeMap;
+import org.librecms.CmsConstants;
+
+/**
+ * A form section with two widgets: a mime-type selection widget and a file
+ * upload widget. The section will attempt to automatically guess the mime type
+ * from the filename (if necessary), and return the mime type.
+ *
+ * @author Stanislav Freidin (sfreidin@arsdigita.com)
+ * @author Jens Pelzetter
+ */
+public class FileUploadSection extends FormSection {
+
+ private SingleSelect mimeWidget;
+ private FileUpload fileWidget;
+ private String mimePrefix;
+ private String defaultMimeType;
+ private String parameterPrefix;
+
+ /**
+ * The mime type widget
+ */
+ public static final String MIME_TYPE = "mime_type";
+
+ /**
+ * The file upload widget
+ */
+ public static final String FILE_UPLOAD = "file_upload";
+
+ /**
+ * Automatically guess the mime type
+ */
+ public static final String GUESS_MIME = "-guess-";
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param panel The panel that is to be used to lay out the components
+ *
+ */
+ public FileUploadSection(final GlobalizedMessage mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final Container panel) {
+ this(mimeLabel, mimePrefix, defaultMimeType, "", panel);
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param panel The panel that is to be used to lay out the components
+ *
+ * @deprecated use the same constructor but with the GlobalizedMessage for
+ * the mimeLabel
+ */
+ public FileUploadSection(final String mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final Container panel) {
+ // This takes advantage of the fact that the "key" is returned
+ // when it is not present in the message bundle
+ this(new GlobalizedMessage(mimeLabel),
+ mimePrefix,
+ defaultMimeType,
+ panel);
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param parameterPrefix Prepended to MIME_TYPE and FILE_UPLOAD for
+ * parameter names so that more than 1 file upload widgets may be used per
+ * form
+ *
+ * @param panel The panel that is to be used to lay out the components
+ *
+ * @deprecated use the same constructor but with the GlobalizedMessage for
+ * the mimeLabel
+ */
+ public FileUploadSection(final String mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final String parameterPrefix,
+ final Container panel
+ ) {
+ // This takes advantage of the fact that the "key" is returned
+ // when it is not present in the message bundle
+ this(new GlobalizedMessage(mimeLabel, CmsConstants.CMS_BUNDLE),
+ mimePrefix,
+ defaultMimeType,
+ parameterPrefix,
+ panel);
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param parameterPrefix Prepended to MIME_TYPE and FILE_UPLOAD for
+ * parameter names so that more than 1 file upload widgets may be used per
+ * form
+ *
+ * @param panel The panel that is to be used to lay out the components
+ *
+ */
+ public FileUploadSection(final GlobalizedMessage mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final String parameterPrefix,
+ final Container panel) {
+
+ super(panel);
+
+ this.mimePrefix = mimePrefix;
+ this.defaultMimeType = defaultMimeType;
+ if (parameterPrefix == null) {
+ this.parameterPrefix = "";
+ } else {
+ this.parameterPrefix = parameterPrefix;
+ }
+
+ add(new Label(mimeLabel, false));
+ mimeWidget = new SingleSelect(getMimeTypeWidgetName());
+ addMimeOptions(mimeWidget, mimePrefix);
+ mimeWidget
+ .addOption(new Option(GUESS_MIME,
+ new Label(new GlobalizedMessage(
+ "cms.ui.authoring.file_upload.auto_detect",
+ CmsConstants.CMS_BUNDLE))));
+
+ mimeWidget.setDefaultValue(GUESS_MIME);
+ add(mimeWidget);
+
+ add(new Label(new GlobalizedMessage("cms.ui.upload_new_content",
+ CmsConstants.CMS_BUNDLE)));
+ fileWidget = new FileUpload(getFileUploadWidgetName());
+ add(fileWidget);
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param parameterPrefix Prepended to MIME_TYPE and FILE_UPLOAD for
+ * parameter names so that more than 1 file upload widgets may be used per
+ * form
+ *
+ */
+ public FileUploadSection(final GlobalizedMessage mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final String parameterPrefix) {
+ this(mimeLabel,
+ mimePrefix,
+ defaultMimeType,
+ parameterPrefix,
+ new ColumnPanel(2, true));
+ final ColumnPanel panel = (ColumnPanel) getPanel();
+ panel.setBorder(false);
+ panel.setPadColor("#FFFFFF");
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @param parameterPrefix Prepended to MIME_TYPE and FILE_UPLOAD for
+ * parameter names so that more than 1 file upload widgets may be used per
+ * form
+ *
+ * @deprecated use the same constructor but with the GlobalizedMessage for
+ * the mimeLabel
+ */
+ public FileUploadSection(final String mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType,
+ final String parameterPrefix) {
+ // This takes advantage of the fact that the "key" is returned
+ // when it is not present in the message bundle
+ this(new GlobalizedMessage(mimeLabel, CmsConstants.CMS_BUNDLE),
+ mimePrefix,
+ defaultMimeType,
+ parameterPrefix);
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The label for the mime type widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ * @deprecated use the same constructor but with the GlobalizedMessage for
+ * the mimeLabel
+ */
+ public FileUploadSection(final String mimeLabel,
+ final String mimePrefix,
+ final String defaultMimeType) {
+
+ // This takes advantage of the fact that the "key" is returned
+ // when it is not present in the message bundle
+ this(new GlobalizedMessage(mimeLabel, CmsConstants.CMS_BUNDLE),
+ mimePrefix,
+ defaultMimeType,
+ "");
+ }
+
+ /**
+ * Construct a new FileUploadSection
+ *
+ * @param mimeLabel The GlobalizedMessage for the label for the mime type
+ * widget
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ * @param defaultMimeType The default mime type that should be assumed if
+ * the guessing fails
+ *
+ */
+ public FileUploadSection(GlobalizedMessage mimeLabel,
+ String mimePrefix,
+ String defaultMimeType) {
+ this(mimeLabel, mimePrefix, defaultMimeType, "");
+ }
+
+ /**
+ * Try to guess the mime type from the filename, and return it. The parent
+ * form should call this method in its process listener. Note that this
+ * method may return null if the mime type could not be guessed.
+ *
+ * @param event The form section event
+ * @return The mime type of the file.
+ */
+ public MimeType getMimeType(final FormSectionEvent event) {
+
+ final FormData data = event.getFormData();
+
+ final String fileName = (String) data.get(getFileUploadWidgetName());
+ final String mimeTypeName = (String) data.get(getMimeTypeWidgetName());
+
+ // Guess the mime type from the filename
+ MimeType mimeType = null;
+ if (fileName != null) {
+ try {
+ if (GUESS_MIME.equals(mimeTypeName)) {
+ // Guess the mime type from the file extension
+ mimeType = new MimeType(MimetypesFileTypeMap
+ .getDefaultFileTypeMap()
+ .getContentType(fileName));
+ } else {
+ mimeType = new MimeType(mimeTypeName);
+ }
+ } catch (MimeTypeParseException ex) {
+ mimeType = null;
+ }
+ }
+
+ // Failed to guess it, failed to load it, fall back on the default
+ if (mimeType == null) {
+ try {
+ mimeType = new MimeType(defaultMimeType);
+ } catch (MimeTypeParseException ex) {
+ mimeType = null;
+ }
+ }
+
+ return mimeType;
+ }
+
+ /**
+ * Obtain a File object from the file upload widget. The containing form
+ * should call this method in its process listener.
+ *
+ * @param event The form section event
+ * @return
+ */
+ public File getFile(final FormSectionEvent event) {
+
+ final String fileName = getFileName(event);
+
+ if (fileName != null && fileName.length() > 0) {
+ return ((MultipartHttpServletRequest) event
+ .getPageState()
+ .getRequest())
+ .getFile(getFileUploadWidgetName());
+ }
+
+ return null;
+ }
+
+ /**
+ * Obtain a filename from the file upload widget. The containing form should
+ * call this method in its process listener.
+ *
+ * @param event The form section event
+ * @return
+ */
+ public String getFileName(final FormSectionEvent event) {
+
+ return event
+ .getFormData()
+ .getString(getFileUploadWidgetName());
+ }
+
+ /**
+ * Set the value for the mime type widget. The containing form should call
+ * this method in its init listener
+ *
+ * @param event The form section event
+ * @param mimeType The mime type to set, such as "text/html" or "img/jpeg"
+ *
+ */
+ public void setMimeType(final FormSectionEvent event,
+ final String mimeType) {
+ event
+ .getFormData()
+ .put(getMimeTypeWidgetName(), mimeType);
+ }
+
+ /**
+ * @return the mime type widget
+ */
+ public SingleSelect getMimeTypeWidget() {
+ return mimeWidget;
+ }
+
+ /**
+ * @return the file upload widget
+ */
+ public FileUpload getFileUploadWidget() {
+ return fileWidget;
+ }
+
+ /**
+ * @return the parameter name prefix
+ */
+ public String getParameterPrefix() {
+ return parameterPrefix;
+ }
+
+ /**
+ * @return the file upload parameter name
+ */
+ public String getFileUploadWidgetName() {
+ return parameterPrefix + FILE_UPLOAD;
+ }
+
+ /**
+ * @return the mime typeparameter name
+ */
+ public String getMimeTypeWidgetName() {
+ return parameterPrefix + MIME_TYPE;
+ }
+
+ /**
+ * Add mime-type options to the option group by loading all mime types which
+ * match a certain prefix from the database
+ *
+ * @param mimeTypeOptions The mime type widget to which options should be added
+ *
+ * @param mimePrefix Populate the mime type widget with all mime types that
+ * match the prefix. Some of the possible prefixes are "text", "image",
+ * "binary", etc.
+ *
+ */
+ public static void addMimeOptions(final OptionGroup mimeTypeOptions,
+ final String mimePrefix) {
+
+// MimeTypeCollection types;
+// if (mimePrefix == null || mimePrefix.equals("")) {
+// types = MimeType.getAllMimeTypes();
+// } else {
+// types = MimeType.searchMimeTypes(mimePrefix + "/");
+// }
+// while (types.next()) {
+// MimeType type = types.getMimeType();
+// mimeTypeOptions.addOption(new Option(type.getMimeType(), type.getLabel()));
+// }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java
index 1d6389925..3f4577c88 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetForm.java
@@ -353,9 +353,9 @@ public abstract class AssetForm extends Form implements FormInitListener,
final Asset asset;
if (selectedAsset.isPresent()) {
asset = selectedAsset.get();
- updateAsset(asset, state);
+ updateAsset(asset, event);
} else {
- asset = createAsset(state);
+ asset = createAsset(event);
}
asset.getTitle().addValue(getSelectedLocale(state),
@@ -388,11 +388,11 @@ public abstract class AssetForm extends Form implements FormInitListener,
protected abstract void showLocale(final PageState state);
- protected abstract Asset createAsset(final PageState state)
+ protected abstract Asset createAsset(final FormSectionEvent event)
throws FormProcessException;
protected abstract void updateAsset(final Asset asset,
- final PageState state)
+ final FormSectionEvent event)
throws FormProcessException;
@Override
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BinaryAssetForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BinaryAssetForm.java
new file mode 100644
index 000000000..e28a1dea0
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BinaryAssetForm.java
@@ -0,0 +1,216 @@
+/*
+ * 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.assets.forms;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.FormSection;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Text;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.form.TextArea;
+import com.arsdigita.cms.ui.FileUploadSection;
+import com.arsdigita.cms.ui.assets.AssetForm;
+import com.arsdigita.cms.ui.assets.AssetPane;
+import com.arsdigita.globalization.GlobalizedMessage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Optional;
+import javax.activation.MimeType;
+import org.librecms.CmsConstants;
+import org.librecms.assets.BinaryAsset;
+import org.librecms.contentsection.Asset;
+
+/**
+ * Base form for assets which extend {@link BinaryAsset}.
+ *
+ * @author Jens Pelzetter
+ */
+public abstract class BinaryAssetForm extends AssetForm {
+
+ private TextArea description;
+ private Text fileName;
+ private Text mimeType;
+ private Text size;
+ private FileUploadSection fileUpload;
+
+ public BinaryAssetForm(final AssetPane assetPane) {
+ super(assetPane);
+ }
+
+ @Override
+ protected void addWidgets() {
+
+ final BoxPanel panel = new BoxPanel(BoxPanel.VERTICAL);
+
+ panel.add(new Label(new GlobalizedMessage(
+ "cms.ui.assets.binaryasset.description",
+ CmsConstants.CMS_BUNDLE)));
+ description = new TextArea("binaryasset-description");
+ panel.add(description);
+
+ panel.add(new Label(
+ new GlobalizedMessage("cms.ui.assets.binaryasset.filename",
+ CmsConstants.CMS_BUNDLE)));
+ fileName = new Text();
+ panel.add(fileName);
+
+ panel.add(new Label(
+ new GlobalizedMessage("cms.ui.assets.binaryasset.mimetype",
+ CmsConstants.CMS_BUNDLE)));
+ mimeType = new Text();
+ panel.add(mimeType);
+
+ panel.add(new Label(
+ new GlobalizedMessage("cms.ui.assets.binaryasset.size",
+ CmsConstants.CMS_BUNDLE)));
+ size = new Text();
+ panel.add(size);
+
+ fileUpload = new FileUploadSection(
+ new GlobalizedMessage("cms.ui.assets.binaryasset.mimetype",
+ CmsConstants.CMS_BUNDLE),
+ "",
+ "");
+
+ add(panel);
+ }
+
+ @Override
+ protected void initForm(final PageState state,
+ final Optional selectedAsset) {
+
+ if (selectedAsset.isPresent()) {
+
+ if (!(selectedAsset.get() instanceof BinaryAsset)) {
+ throw new IllegalArgumentException(String.format(
+ "The provided asset must be an instanceof of class '%s' or "
+ + "an subclass but is na instanceof class '%s'.",
+ BinaryAsset.class.getName(),
+ selectedAsset.get().getClass().getName()));
+ }
+
+ final BinaryAsset binaryAsset = (BinaryAsset) selectedAsset.get();
+
+ description.setValue(state,
+ binaryAsset
+ .getDescription()
+ .getValue(getSelectedLocale(state)));
+
+ fileName.setText(binaryAsset.getFileName());
+ mimeType.setText(binaryAsset.getMimeType().toString());
+ size.setText(Long.toString(binaryAsset.getSize()));
+ }
+
+ }
+
+ @Override
+ protected void showLocale(final PageState state) {
+
+ final Optional selectedAsset = getSelectedAsset(state);
+
+ if (selectedAsset.isPresent()) {
+ if (!(getSelectedAsset(state).get() instanceof BinaryAsset)) {
+ throw new IllegalArgumentException(
+ "Selected asset is not a binary asset.");
+ }
+
+ final BinaryAsset binaryAsset = (BinaryAsset) selectedAsset.get();
+
+ description.setValue(state,
+ binaryAsset
+ .getDescription()
+ .getValue(getSelectedLocale(state)));
+ }
+
+ }
+
+ @Override
+ protected Asset createAsset(final FormSectionEvent event)
+ throws FormProcessException {
+
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
+
+ final BinaryAsset binaryAsset = createBinaryAsset(state);
+
+ binaryAsset
+ .getDescription()
+ .addValue(getSelectedLocale(state),
+ (String) description.getValue(state));
+
+ setFileData(event, binaryAsset);
+
+ return binaryAsset;
+ }
+
+ protected abstract BinaryAsset createBinaryAsset(final PageState state);
+
+ @Override
+ protected void updateAsset(final Asset asset,
+ final FormSectionEvent event)
+ throws FormProcessException {
+
+ Objects.requireNonNull(asset);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
+
+ if (!(asset instanceof BinaryAsset)) {
+ throw new IllegalArgumentException(String.format(
+ "Provided asset is not an instance of '%s' (or a sub class) "
+ + "but is an instance of class '%s'.",
+ BinaryAsset.class.getName(),
+ asset.getClass().getName()));
+ }
+
+ final BinaryAsset binaryAsset = (BinaryAsset) asset;
+
+ binaryAsset
+ .getDescription()
+ .addValue(getSelectedLocale(state),
+ (String) description.getValue(state));
+
+ setFileData(event, binaryAsset);
+ }
+
+ private void setFileData(final FormSectionEvent event,
+ final BinaryAsset binaryAsset)
+ throws FormProcessException {
+
+ final File file = fileUpload.getFile(event);
+ final Path path = file.toPath();
+ final byte[] data;
+ try {
+ data = Files.readAllBytes(path);
+ } catch (IOException ex) {
+ throw new FormProcessException(ex);
+ }
+ binaryAsset.setData(data);
+ binaryAsset.setSize(data.length);
+
+ binaryAsset.setMimeType(fileUpload.getMimeType(event));
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BookmarkForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BookmarkForm.java
index fd9a08203..31eab23d5 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BookmarkForm.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/BookmarkForm.java
@@ -134,10 +134,12 @@ public class BookmarkForm extends AssetForm {
}
@Override
- protected Asset createAsset(final PageState state)
+ protected Asset createAsset(final FormSectionEvent event)
throws FormProcessException {
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
final Bookmark bookmark = new Bookmark();
@@ -158,11 +160,14 @@ public class BookmarkForm extends AssetForm {
@Override
- protected void updateAsset(final Asset asset, final PageState state)
+ protected void updateAsset(final Asset asset,
+ final FormSectionEvent event)
throws FormProcessException {
Objects.requireNonNull(asset);
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
if (!(asset instanceof Bookmark)) {
throw new IllegalArgumentException(String.format(
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/ExternalVideoAssetForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/ExternalVideoAssetForm.java
index 428af4523..7b2f6044a 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/ExternalVideoAssetForm.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/ExternalVideoAssetForm.java
@@ -21,6 +21,7 @@ package com.arsdigita.cms.ui.assets.forms;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.cms.ui.assets.AssetPane;
import com.arsdigita.cms.ui.assets.AssetSearchWidget;
import com.arsdigita.globalization.GlobalizedMessage;
@@ -82,10 +83,12 @@ public class ExternalVideoAssetForm extends BookmarkForm {
}
@Override
- protected Asset createAsset(final PageState state)
+ protected Asset createAsset(final FormSectionEvent event)
throws FormProcessException {
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
final ExternalVideoAsset externalVideoAsset = new ExternalVideoAsset();
@@ -114,11 +117,14 @@ public class ExternalVideoAssetForm extends BookmarkForm {
}
@Override
- protected void updateAsset(final Asset asset, final PageState state) {
+ protected void updateAsset(final Asset asset,
+ final FormSectionEvent event) {
Objects.requireNonNull(asset);
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+ final PageState state = event.getPageState();
+
if (!(asset instanceof ExternalVideoAsset)) {
throw new IllegalArgumentException(String.format(
"Provided asset is not an instance of class (or sub class of) "
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/LegalMetadataForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/LegalMetadataForm.java
index 3893bdd03..0225a9cfb 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/LegalMetadataForm.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/LegalMetadataForm.java
@@ -22,6 +22,7 @@ import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.form.TextArea;
import com.arsdigita.cms.ui.assets.AssetForm;
import com.arsdigita.cms.ui.assets.AssetPane;
@@ -53,7 +54,7 @@ public class LegalMetadataForm extends AssetForm {
protected void addWidgets() {
final BoxPanel panel = new BoxPanel(BoxPanel.VERTICAL);
-
+
panel.add(new Label(new GlobalizedMessage(
"cms.ui.assets.legalmetadata.rightsholder",
CmsConstants.CMS_BUNDLE)));
@@ -77,7 +78,7 @@ public class LegalMetadataForm extends AssetForm {
CmsConstants.CMS_BUNDLE)));
creator = new TextArea("legalmetadata-creator");
panel.add(creator);
-
+
add(panel);
}
@@ -110,6 +111,7 @@ public class LegalMetadataForm extends AssetForm {
@Override
protected void showLocale(final PageState state) {
+
final Optional selectedAsset = getSelectedAsset(state);
if (selectedAsset.isPresent()) {
@@ -118,7 +120,8 @@ public class LegalMetadataForm extends AssetForm {
"Selected asset is not a legal metadata");
}
- final LegalMetadata legalMetadata = (LegalMetadata) selectedAsset.get();
+ final LegalMetadata legalMetadata = (LegalMetadata) selectedAsset.
+ get();
rights.setValue(state,
legalMetadata
@@ -128,10 +131,12 @@ public class LegalMetadataForm extends AssetForm {
}
@Override
- protected Asset createAsset(final PageState state)
+ protected Asset createAsset(final FormSectionEvent event)
throws FormProcessException {
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
final LegalMetadata legalMetadata = new LegalMetadata();
@@ -146,18 +151,20 @@ public class LegalMetadataForm extends AssetForm {
}
@Override
- protected void updateAsset(final Asset asset, final PageState state)
+ protected void updateAsset(final Asset asset,
+ final FormSectionEvent event)
throws FormProcessException {
Objects.requireNonNull(asset);
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
if (!(asset instanceof LegalMetadata)) {
throw new IllegalArgumentException(String.format(
"Provided asset is not an instance of '%s' (or a sub class) "
+ "but is an instance of class '%s'.",
- LegalMetadata.class
- .getName(),
+ LegalMetadata.class.getName(),
asset.getClass().getName()));
}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/SideNoteForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/SideNoteForm.java
index 2944caf14..19a3aa7bf 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/SideNoteForm.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/forms/SideNoteForm.java
@@ -21,6 +21,7 @@ package com.arsdigita.cms.ui.assets.forms;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.form.TextArea;
import com.arsdigita.cms.ui.assets.AssetForm;
import com.arsdigita.cms.ui.assets.AssetPane;
@@ -99,11 +100,13 @@ public class SideNoteForm extends AssetForm {
}
@Override
- protected Asset createAsset(final PageState state) throws
+ protected Asset createAsset(final FormSectionEvent event) throws
FormProcessException {
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+ final PageState state = event.getPageState();
+
final SideNote sideNote = new SideNote();
sideNote
@@ -115,11 +118,14 @@ public class SideNoteForm extends AssetForm {
}
@Override
- protected void updateAsset(final Asset asset, final PageState state)
+ protected void updateAsset(final Asset asset,
+ final FormSectionEvent event)
throws FormProcessException {
Objects.requireNonNull(asset);
- Objects.requireNonNull(state);
+ Objects.requireNonNull(event);
+
+ final PageState state = event.getPageState();
if (!(asset instanceof SideNote)) {
throw new IllegalArgumentException(String.format(
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java
index e6a46a12a..831b233be 100644
--- a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java
+++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java
@@ -45,6 +45,7 @@ import org.libreccm.web.CcmApplication;
import java.io.IOException;
import javax.enterprise.inject.spi.CDI;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@@ -142,6 +143,12 @@ public class AdminServlet extends BaseApplicationServlet {
new Label(new GlobalizedMessage("ui.admin.tab.sysinfo.title",
ADMIN_BUNDLE)),
new SystemInformationTab());
+
+ final ServletContext servletContext = getServletContext();
+ final String develMode = servletContext.getInitParameter("ccm.develmode");
+ if (develMode != null && "true".equals(develMode.toLowerCase())) {
+
+ }
//page.add(new Label("admin"));
adminPage.add(tabbedPane);
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/JpqlConsole.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/JpqlConsole.java
new file mode 100644
index 000000000..a834ad223
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/JpqlConsole.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.ui.admin;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SaveCancelSection;
+import com.arsdigita.bebop.SegmentedPanel;
+import com.arsdigita.bebop.SimpleComponent;
+import com.arsdigita.bebop.form.TextArea;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.toolbox.ui.LayoutPanel;
+import com.arsdigita.xml.Element;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class JpqlConsole extends LayoutPanel {
+
+ private static final Logger LOGGER = LogManager.getLogger(JpqlConsole.class);
+
+ private final Form queryForm;
+ private final TextArea queryArea;
+
+ public JpqlConsole() {
+ super();
+
+ final SegmentedPanel panel = new SegmentedPanel();
+
+ queryForm = new Form("jpql-console-query-form");
+
+ queryArea = new TextArea("jpql-console-query-area");
+ queryArea.setCols(132);
+ queryArea.setRows(42);
+
+ final BoxPanel hpanel = new BoxPanel(BoxPanel.HORIZONTAL);
+ hpanel.add(new Label(new GlobalizedMessage(
+ "ui.admin.jpqlconsole.query.label",
+ AdminUiConstants.ADMIN_BUNDLE)));
+ hpanel.add(queryArea);
+
+ final BoxPanel vpanel = new BoxPanel(BoxPanel.VERTICAL);
+ vpanel.add(hpanel);
+ final SaveCancelSection saveCancelSection = new SaveCancelSection();
+ saveCancelSection.getSaveButton().setLabel(
+ new GlobalizedMessage("ui.admin.jpqlconsole.query.execute",
+ AdminUiConstants.ADMIN_BUNDLE));
+ saveCancelSection.getCancelButton().setLabel(
+ new GlobalizedMessage("ui.admin.jpqlconsole.query.clear",
+ AdminUiConstants.ADMIN_BUNDLE));
+ vpanel.add(saveCancelSection);
+
+ queryForm.add(hpanel);
+
+ panel.addSegment(new Label(new GlobalizedMessage(
+ "ui.admin.jpqlconsole.query.segment.title",
+ AdminUiConstants.ADMIN_BUNDLE)),
+ queryForm);
+
+ setBody(panel);
+ }
+
+ private class QueryResults extends SimpleComponent {
+
+ private List> results;
+
+ public List> getResults() {
+ return results;
+ }
+
+ public void setResults(final List> results) {
+ this.results = results;
+ }
+
+ private List getProperties(final List> results)
+ throws IntrospectionException {
+
+ final Set propertyDescriptors = new HashSet<>();
+
+ for (final Object obj : results) {
+ final PropertyDescriptor[] properties = Introspector
+ .getBeanInfo(obj.getClass()).getPropertyDescriptors();
+
+ for (final PropertyDescriptor property : properties) {
+ propertyDescriptors.add(property);
+ }
+ }
+
+ final List propertiesList
+ = new ArrayList<>(propertyDescriptors);
+ propertiesList.sort((prop1, prop2) -> {
+ return prop1.getName().compareTo(prop2.getName());
+ });
+
+ return propertiesList;
+ }
+
+ @Override
+ public void generateXML(final PageState state,
+ final Element parent) {
+
+ final Element queryResults = parent
+ .newChildElement("bebop:query-results", BEBOP_XML_NS);
+
+ // Find all properties in the results
+ final List properties;
+ try {
+ properties = getProperties(results);
+ } catch (IntrospectionException ex) {
+ queryArea.addError(new GlobalizedMessage(
+ "ui.admin.jpqlconsole.failed_to_render_results",
+ AdminUiConstants.ADMIN_BUNDLE));
+ LOGGER.error("Failed to render results.", ex);
+ return;
+ }
+
+ final Element columns = queryResults
+ .newChildElement("bebop:query-result-columns", BEBOP_XML_NS);
+ for (final PropertyDescriptor property : properties) {
+ final Element column = columns
+ .newChildElement("bebop:query-result-column", BEBOP_XML_NS);
+ column.setText(property.getName());
+ }
+
+ final Element resultList = queryResults
+ .newChildElement("bebop:query-result-list", BEBOP_XML_NS);
+ for(final Object obj : results) {
+ for(final PropertyDescriptor property : properties) {
+ final Method readMethod = property.getReadMethod();
+
+
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
index 910fc3f90..ace0e7de7 100644
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
@@ -552,3 +552,6 @@ ui.admin.tab.users_groups_roles.role_members=Members
ui.admin.tab.users_groups_roles.role_permissions=Permissions
ui.admin.tab.users_groups_roles.role_details.save=Save
ui.admin.tab.users_groups_roles.role_details.cancel=Cancel
+ui.admin.jpqlconsole.query.label=Query
+ui.admin.jpqlconsole.query.execute=Execute
+ui.admin.jpqlconsole.query.clear=Clear
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
index c876208ca..bd7ccad64 100644
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
@@ -556,3 +556,6 @@ ui.admin.tab.users_groups_roles.role_members=Mitglieder
ui.admin.tab.users_groups_roles.role_permissions=Berechtigungen
ui.admin.tab.users_groups_roles.role_details.save=Speichern
ui.admin.tab.users_groups_roles.role_details.cancel=Abbrechen
+ui.admin.jpqlconsole.query.label=Abfrage
+ui.admin.jpqlconsole.query.execute=Ausf\u00fchren
+ui.admin.jpqlconsole.query.clear=Zur\u00fccksetzen
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
index 1f8579efe..d5acc09d7 100755
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
@@ -549,3 +549,6 @@ ui.admin.tab.users_groups_roles.role_members=Members
ui.admin.tab.users_groups_roles.role_permissions=Permissions
ui.admin.tab.users_groups_roles.role_details.save=Save
ui.admin.tab.users_groups_roles.role_details.cancel=Cancel
+ui.admin.jpqlconsole.query.label=Query
+ui.admin.jpqlconsole.query.execute=Excute
+ui.admin.jpqlconsole.query.clear=Clear
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
index 0cb6be9ec..40476d17a 100755
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
@@ -540,3 +540,6 @@ ui.admin.tab.users_groups_roles.role_members=Members
ui.admin.tab.users_groups_roles.role_permissions=Permissions
ui.admin.tab.users_groups_roles.role_details.save=Save
ui.admin.tab.users_groups_roles.role_details.cancel=Cancel
+ui.admin.jpqlconsole.query.label=Query
+ui.admin.jpqlconsole.query.execute=Execute
+ui.admin.jpqlconsole.query.clear=Clear