diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/AbstractContentItemComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/AbstractContentItemComponentEditor.java
new file mode 100644
index 000000000..dd497ecf3
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/AbstractContentItemComponentEditor.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 org.librecms.ui.pagemodel;
+
+import com.vaadin.ui.TextField;
+import org.libreccm.admin.ui.AbstractPageModelComponentEditor;
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.CmsConstants;
+import org.librecms.pagemodel.ContentItemComponent;
+
+/**
+ *
+ * @author Jens Pelzetter
+ * @param
+ */
+public abstract class AbstractContentItemComponentEditor
+ extends AbstractPageModelComponentEditor {
+
+ private static final long serialVersionUID = -4872408582648018134L;
+
+ private final PageModelComponentEditorController controller;
+
+ private TextField modeField;
+
+ public AbstractContentItemComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ public AbstractContentItemComponentEditor(
+ final PageModel pageModel,
+ final T componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ private void addWidgets() {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(CmsConstants.CMS_BUNDLE);
+
+ modeField = new TextField(textsUtil
+ .getText("cms.ui.pagemodel.contentitem_component_form.mode.label"));
+ addComponent(modeField);
+ }
+
+ @Override
+ protected void initWidgets() {
+
+ final T component = getComponentModel();
+
+ if (component != null) {
+ modeField.setValue(component.getMode());
+ }
+ }
+
+ @Override
+ protected void updateComponentModel() {
+
+ final T component = getComponentModel();
+
+ component.setMode(modeField.getValue());
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategorizedItemComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategorizedItemComponentEditor.java
new file mode 100644
index 000000000..2387b2323
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategorizedItemComponentEditor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.pagemodel;
+
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.pagemodel.CategorizedItemComponent;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class CategorizedItemComponentEditor
+ extends AbstractContentItemComponentEditor {
+
+ private static final long serialVersionUID = 7641211643041787151L;
+
+ public CategorizedItemComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+ }
+
+ public CategorizedItemComponentEditor(
+ final PageModel pageModel,
+ final CategorizedItemComponent componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+ }
+
+ @Override
+ protected boolean validate() {
+ //Nothing to validate here.
+ return true;
+ }
+
+ @Override
+ protected CategorizedItemComponent createComponentModel() {
+
+ return new CategorizedItemComponent();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategoryTreeComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategoryTreeComponentEditor.java
new file mode 100644
index 000000000..7f9011de6
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/CategoryTreeComponentEditor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.pagemodel;
+
+import com.vaadin.ui.CheckBox;
+import org.libreccm.admin.ui.AbstractPageModelComponentEditor;
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.CmsConstants;
+import org.librecms.pagemodel.CategoryTreeComponent;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class CategoryTreeComponentEditor
+ extends AbstractPageModelComponentEditor {
+
+ private static final long serialVersionUID = -6162769539698324778L;
+
+ private final PageModelComponentEditorController controller;
+
+ private CheckBox showFullTreeCheckBox;
+
+ public CategoryTreeComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModelInfo,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModelInfo, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ public CategoryTreeComponentEditor(
+ final PageModel pageModel,
+ final CategoryTreeComponent componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ private void addWidgets() {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(CmsConstants.CMS_BUNDLE);
+
+ showFullTreeCheckBox = new CheckBox(textsUtil
+ .getText(
+ "cms.ui.pagemodel.category_tree_component_form.show_full_tree.label"));
+
+ }
+
+ @Override
+ protected void initWidgets() {
+
+ final CategoryTreeComponent component = getComponentModel();
+
+ showFullTreeCheckBox.setValue(component.isShowFullTree());
+ }
+
+ @Override
+ protected boolean validate() {
+
+ //Nothing to validate here
+ return true;
+ }
+
+ @Override
+ protected CategoryTreeComponent createComponentModel() {
+ return new CategoryTreeComponent();
+ }
+
+ @Override
+ protected void updateComponentModel() {
+
+ final CategoryTreeComponent component = getComponentModel();
+
+ component.setShowFullTree(showFullTreeCheckBox.getValue());
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/FixedContentItemComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/FixedContentItemComponentEditor.java
new file mode 100644
index 000000000..573974c0b
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/FixedContentItemComponentEditor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.pagemodel;
+
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.pagemodel.FixedContentItemComponent;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class FixedContentItemComponentEditor
+ extends AbstractContentItemComponentEditor {
+
+ private static final long serialVersionUID = 7641211643041787151L;
+
+ public FixedContentItemComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+ }
+
+ public FixedContentItemComponentEditor(
+ final PageModel pageModel,
+ final FixedContentItemComponent componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+
+
+ }
+
+ @Override
+ protected boolean validate() {
+ //Nothing to validate here.
+ return true;
+ }
+
+ @Override
+ protected FixedContentItemComponent createComponentModel() {
+
+ return new FixedContentItemComponent();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/GreetingItemComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/GreetingItemComponentEditor.java
new file mode 100644
index 000000000..18a7fea6b
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/GreetingItemComponentEditor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.pagemodel;
+
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.pagemodel.GreetingItemComponent;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class GreetingItemComponentEditor
+ extends AbstractContentItemComponentEditor {
+
+ private static final long serialVersionUID = 7641211643041787151L;
+
+ public GreetingItemComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+ }
+
+ public GreetingItemComponentEditor(
+ final PageModel pageModel,
+ final GreetingItemComponent componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+ }
+
+ @Override
+ protected boolean validate() {
+ //Nothing to validate here.
+ return true;
+ }
+
+ @Override
+ protected GreetingItemComponent createComponentModel() {
+
+ return new GreetingItemComponent();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/pagemodel/ItemListComponentEditor.java b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/ItemListComponentEditor.java
new file mode 100644
index 000000000..23843adc4
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/pagemodel/ItemListComponentEditor.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.pagemodel;
+
+import com.vaadin.server.UserError;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.TextArea;
+import com.vaadin.ui.TextField;
+import org.libreccm.admin.ui.AbstractPageModelComponentEditor;
+import org.libreccm.admin.ui.PageModelComponentEditorController;
+import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.librecms.CmsConstants;
+import org.librecms.pagemodel.ItemListComponent;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ItemListComponentEditor
+ extends AbstractPageModelComponentEditor {
+
+ private static final long serialVersionUID = 8607871974091248260L;
+
+ private final PageModelComponentEditorController controller;
+
+ private CheckBox descendingCheckBox;
+ private TextField limitToTypeField;
+ private TextField pageSizeField;
+ private TextArea listOrderArea;
+
+ public ItemListComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModelInfo,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModelInfo, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ public ItemListComponentEditor(
+ final PageModel pageModel,
+ final ItemListComponent componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super(pageModel, componentModel, controller);
+
+ this.controller = controller;
+
+ addWidgets();
+ }
+
+ private void addWidgets() {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(CmsConstants.CMS_BUNDLE);
+
+ descendingCheckBox = new CheckBox(textsUtil
+ .getText(
+ "cms.ui.pagemodel.itemlist_component_form.descending.label"));
+ addComponent(descendingCheckBox);
+
+ limitToTypeField = new TextField(textsUtil
+ .getText(
+ "cms.ui.pagemodel.itemlist_component_form.limit_to_type.label"));
+ addComponent(limitToTypeField);
+
+ pageSizeField = new TextField(textsUtil
+ .getText("cms.ui.pagemodel.itemlist_component_form.page_size.label"));
+ addComponent(pageSizeField);
+
+ listOrderArea = new TextArea(textsUtil
+ .getText("cms.ui.pagemodel.itemlist_component_form.list_order.label"));
+ addComponent(listOrderArea);
+ }
+
+ @Override
+ protected void initWidgets() {
+
+ final ItemListComponent itemListComponent = getComponentModel();
+
+ if (itemListComponent != null) {
+ descendingCheckBox.setValue(itemListComponent.isDescending());
+ }
+
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ protected boolean validate() {
+
+ final String pageSizeValue = pageSizeField.getValue();
+ if (pageSizeValue != null
+ && !pageSizeValue.isEmpty()
+ && !pageSizeValue.matches("\\d*")) {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(CmsConstants.CMS_BUNDLE);
+
+ pageSizeField.setComponentError(new UserError(textsUtil
+ .getText(
+ "cms.ui.pagemodel.itemlist_component_form.page_size.error.not_a_number")));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ protected ItemListComponent createComponentModel() {
+ return new ItemListComponent();
+ }
+
+ @Override
+ protected void updateComponentModel() {
+
+ final ItemListComponent component = getComponentModel();
+
+ final boolean descending = descendingCheckBox.getValue();
+ final String limitToType = limitToTypeField.getValue();
+ final int pageSize = Integer.parseInt(pageSizeField.getValue());
+ final List listOrder = Arrays
+ .stream(listOrderArea.getValue().split("\n"))
+ .collect(Collectors.toList());
+
+ component.setDescending(descending);
+ component.setLimitToType(limitToType);
+ component.setPageSize(pageSize);
+ component.setListOrder(listOrder);
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/AbstractPageModelComponentEditor.java b/ccm-core/src/main/java/org/libreccm/admin/ui/AbstractPageModelComponentEditor.java
new file mode 100644
index 000000000..c0abd8e9c
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/AbstractPageModelComponentEditor.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import com.arsdigita.ui.admin.AdminUiConstants;
+
+import com.vaadin.server.UserError;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.themes.ValoTheme;
+import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+
+/**
+ *
+ * @author Jens Pelzetter
+ * @param
+ */
+public abstract class AbstractPageModelComponentEditor
+ extends Window {
+
+ private static final long serialVersionUID = 7347805088308841378L;
+
+ private final PageModelComponentEditorController controller;
+ private final PageModel pageModel;
+ private PageModelComponentModel componentModelInfo;
+ private T componentModel;
+
+ private final FormLayout formLayout;
+
+ private TextField keyField;
+
+ public AbstractPageModelComponentEditor(
+ final PageModel pageModel,
+ final PageModelComponentModel componentModelInfo,
+ final PageModelComponentEditorController controller) {
+
+ super();
+
+ this.controller = controller;
+ this.pageModel = pageModel;
+ this.componentModelInfo = componentModelInfo;
+
+ formLayout = new FormLayout();
+
+ createWidgets();
+ }
+
+ public AbstractPageModelComponentEditor(
+ final PageModel pageModel,
+ final T componentModel,
+ final PageModelComponentEditorController controller) {
+
+ super();
+
+ this.pageModel = pageModel;
+ this.componentModel = componentModel;
+ this.controller = controller;
+
+ formLayout = new FormLayout();
+
+ createWidgets();
+
+ keyField.setValue(componentModel.getKey());
+ }
+
+ private void createWidgets() {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ keyField = new TextField(textsUtil.getText(
+ "ui.admin.pagemodels.components.key.label"));
+ addComponent(keyField);
+
+ final Button saveButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.components.save"));
+ saveButton.addStyleName(ValoTheme.BUTTON_PRIMARY);
+ saveButton.addClickListener(this::saveButtonClicked);
+
+ final Button cancelButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.components.cancel"));
+ cancelButton.addStyleName(ValoTheme.BUTTON_DANGER);
+ cancelButton.addClickListener(event -> close());
+
+ final HorizontalLayout buttonsLayout = new HorizontalLayout(saveButton,
+ cancelButton);
+
+ setContent(new VerticalLayout(formLayout, buttonsLayout));
+ }
+
+ private void saveButtonClicked(final Button.ClickEvent event) {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ final String key = keyField.getValue();
+ if (key == null
+ || key.isEmpty()
+ || key.matches("\\s*")) {
+
+ keyField.setComponentError(new UserError(textsUtil
+ .getText("ui.admin.pagemodels.components.key.error.not_empty")));
+ return;
+ }
+
+ if (!validate()) {
+ return;
+ }
+
+ if (componentModel == null) {
+ componentModel = createComponentModel();
+ componentModel.setKey(key);
+ updateComponentModel();
+ controller.getPageModelManager().addComponentModel(pageModel,
+ componentModel);
+ } else {
+
+ componentModel.setKey(key);
+ updateComponentModel();
+ controller.getComponentModelRepository().save(componentModel);
+ }
+ }
+
+ protected PageModelComponentEditorController getController() {
+ return controller;
+ }
+
+ protected PageModelComponentModel getComponentModelInfo() {
+ return componentModelInfo;
+ }
+
+ protected T getComponentModel() {
+ return componentModel;
+ }
+
+ protected final void addComponent(final Component component) {
+
+ formLayout.addComponent(component);
+ }
+
+ protected abstract void initWidgets();
+
+ /**
+ *
+ * @return {@code true} if form is validate, {@code false} if not.
+ */
+ protected abstract boolean validate();
+
+ protected abstract T createComponentModel();
+
+ protected abstract void updateComponentModel();
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentEditorController.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentEditorController.java
new file mode 100644
index 000000000..f97bfabf0
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentEditorController.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.pagemodel.ComponentModelRepository;
+import org.libreccm.pagemodel.PageModelManager;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class PageModelComponentEditorController {
+
+ @Inject
+ private ComponentModelRepository componentModelRepository;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ @Inject
+ private PageModelManager pageModelManager;
+
+ public ComponentModelRepository getComponentModelRepository() {
+ return componentModelRepository;
+ }
+
+ public GlobalizationHelper getGlobalizationHelper() {
+ return globalizationHelper;
+ }
+
+ public PageModelManager getPageModelManager() {
+ return pageModelManager;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelTypesDataProvider.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelTypesDataProvider.java
new file mode 100644
index 000000000..cc386ce03
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelTypesDataProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import com.vaadin.cdi.ViewScoped;
+import com.vaadin.data.provider.AbstractDataProvider;
+import com.vaadin.data.provider.Query;
+import org.libreccm.pagemodel.ComponentModels;
+import org.libreccm.pagemodel.PageModelComponentModel;
+
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@ViewScoped
+class PageModelComponentModelTypesDataProvider
+ extends AbstractDataProvider {
+
+ private static final long serialVersionUID = -27393177360237040L;
+
+ @Inject
+ private ComponentModels componentModels;
+
+ @Override
+ public boolean isInMemory() {
+ return true;
+ }
+
+ @Override
+ public int size(final Query query) {
+
+ return componentModels
+ .findAvailableComponentModels()
+ .size();
+ }
+
+ @Override
+ public Stream fetch(
+ final Query query) {
+
+ return componentModels
+ .findAvailableComponentModels()
+ .stream();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelsTableDataProvider.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelsTableDataProvider.java
new file mode 100644
index 000000000..8387cc401
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelComponentModelsTableDataProvider.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import com.vaadin.cdi.ViewScoped;
+import com.vaadin.data.provider.AbstractBackEndDataProvider;
+import com.vaadin.data.provider.Query;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelRepository;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@ViewScoped
+class PageModelComponentModelsTableDataProvider
+ extends AbstractBackEndDataProvider {
+
+ private static final long serialVersionUID = -8880329002442808769L;
+
+ @Inject
+ private PageModelRepository pageModelRepo;
+
+ private PageModel pageModel;
+
+ protected PageModel getPageModel() {
+ return pageModel;
+ }
+
+ protected void setPageModel(final PageModel pageModel) {
+ Objects.requireNonNull(pageModel);
+ this.pageModel = pageModel;
+ }
+
+ @Override
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected Stream fetchFromBackEnd(
+ final Query query) {
+
+ return retrievePageModel()
+ .getComponents()
+ .stream();
+ }
+
+ @Override
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected int sizeInBackEnd(final Query query) {
+
+ return retrievePageModel().getComponents().size();
+ }
+
+ private PageModel retrievePageModel() {
+
+ return pageModelRepo
+ .findById(pageModel.getPageModelId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No PageModel with ID %d in the database.",
+ pageModel.getPageModelId())));
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelDetails.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelDetails.java
index 3826a6e6a..57633d92c 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelDetails.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelDetails.java
@@ -21,16 +21,35 @@ package org.libreccm.admin.ui;
import com.arsdigita.kernel.KernelConfig;
import com.arsdigita.ui.admin.AdminUiConstants;
+import com.vaadin.icons.VaadinIcons;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Component;
import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
+import com.vaadin.ui.components.grid.HeaderCell;
+import com.vaadin.ui.components.grid.HeaderRow;
import org.libreccm.configuration.ConfigurationManager;
+import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ComponentModels;
import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
+import org.libreccm.ui.ConfirmDialog;
+import org.libreccm.web.CcmApplication;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
+import java.util.Optional;
/**
*
@@ -40,11 +59,27 @@ class PageModelDetails extends Window {
private static final long serialVersionUID = -3617001410191320596L;
+ private static final String COL_KEY = "key";
+ private static final String COL_TYPE = "type";
+ private static final String COL_EDIT = "edit";
+ private static final String COL_DEL = "del";
+
+ private final AdminViewController controller;
+ private final CcmApplication application;
+ private final PageModel pageModel;
+
+ private final NativeSelect componentModelTypeSelect;
+
PageModelDetails(final PageModel pageModel,
+ final CcmApplication application,
final AdminViewController controller) {
super();
+ this.controller = controller;
+ this.application = application;
+ this.pageModel = pageModel;
+
final GlobalizationHelper globalizationHelper = controller
.getGlobalizationHelper();
final LocalizedTextsUtil textsUtil = globalizationHelper
@@ -81,9 +116,262 @@ class PageModelDetails extends Window {
final FormLayout propertiesSheetLayout = new FormLayout(
nameLabel, titleLabel, applicationLabel, descLabel);
-
-
+ final Button editPropertiesButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.edit_basic_properties"));
+ editPropertiesButton.setIcon(VaadinIcons.EDIT);
+ editPropertiesButton
+ .addClickListener(this::editBasicPropertiesButtonClicked);
+
+ final PageModelsController pageModelsController = controller
+ .getPageModelsController();
+
+ final Grid componentsModelGrid = new Grid<>();
+ final PageModelComponentModelsTableDataProvider dataProvider
+ = pageModelsController
+ .getComponentModelsTableDataProvider();
+ dataProvider.setPageModel(pageModel);
+ componentsModelGrid.setDataProvider(dataProvider);
+ componentsModelGrid
+ .addColumn(ComponentModel::getKey)
+ .setCaption(textsUtil
+ .getText("ui.admin.pagemodels.componentmodels.cols.key.heading"))
+ .setId(COL_KEY);
+ componentsModelGrid
+ .addColumn(this::getComponentModelType)
+ .setCaption(textsUtil
+ .getText("ui.admin.pagemodels.componentmodels.cols.type.heading"))
+ .setId(COL_TYPE);
+ componentsModelGrid
+ .addComponentColumn(this::buildEditButton)
+ .setCaption(textsUtil
+ .getText("ui.admin.pagemodels.componentmodels.cols.edit.heading"))
+ .setId(COL_EDIT);
+ componentsModelGrid
+ .addComponentColumn(this::buildDeleteButton)
+ .setCaption(textsUtil
+ .getText(
+ "ui.admin.pagemodels.componentmodels.cols.delete.heading"))
+ .setId(COL_DEL);
+ componentsModelGrid.setWidth("100%");
+
+ componentModelTypeSelect = new NativeSelect<>(
+ textsUtil.getText("ui.admin.pagemodels.add_new_component.type"),
+ pageModelsController.getComponentModelTypesDataProvider());
+ componentModelTypeSelect
+ .setItemCaptionGenerator(this::generateComponentModelTypeCaption);
+ final Button addComponentModelButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.add_new_component.submit"));
+ addComponentModelButton.setIcon(VaadinIcons.PLUS_CIRCLE_O);
+ addComponentModelButton
+ .addClickListener(this::addComponentButtonClicked);
+ final HeaderRow headerRow = componentsModelGrid.prependHeaderRow();
+ final HeaderCell headerCell = headerRow.join(COL_KEY,
+ COL_TYPE,
+ COL_EDIT,
+ COL_DEL);
+ headerCell.setComponent(new HorizontalLayout(componentModelTypeSelect,
+ addComponentModelButton));
+
super.setContent(new VerticalLayout(propertiesSheetLayout));
}
+ @SuppressWarnings("unchecked")
+ private void addComponentButtonClicked(final Button.ClickEvent event) {
+
+ final PageModelComponentModel componentModelInfo
+ = componentModelTypeSelect.getValue();
+
+ final String bebopFormClassName = componentModelInfo
+ .editor()
+ .getName();
+
+ final PageModelsController pageModelsController = controller
+ .getPageModelsController();
+
+ final String editorName = bebopFormClassName
+ .replace("com.arsdigita.cms", "org.librecms")
+ .replace("Form", "Editor");
+
+ final Class extends AbstractPageModelComponentEditor>> editorClass;
+ try {
+ editorClass
+ = (Class extends AbstractPageModelComponentEditor>>) Class
+ .forName(editorName);
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final Constructor extends AbstractPageModelComponentEditor>> constructor;
+ try {
+ constructor = editorClass
+ .getDeclaredConstructor(PageModel.class,
+ PageModelComponentModel.class,
+ PageModelComponentEditorController.class);
+ } catch (NoSuchMethodException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final AbstractPageModelComponentEditor> editor;
+ try {
+ editor = constructor.newInstance(
+ pageModel,
+ componentModelInfo,
+ pageModelsController.getComponentEditorController());
+ } catch (InstantiationException
+ | IllegalAccessException
+ | InvocationTargetException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ editor.setModal(true);
+ editor.setWidth("50%");
+ editor.setHeight("40%");
+
+ UI.getCurrent().addWindow(editor);
+ }
+
+ private void editBasicPropertiesButtonClicked(final Button.ClickEvent event) {
+
+ final PageModelForm pageModelForm = new PageModelForm(pageModel,
+ application,
+ controller);
+ pageModelForm.setModal(true);
+ pageModelForm.setWidth("40%");
+ pageModelForm.setHeight("30%");
+
+ UI.getCurrent().addWindow(pageModelForm);
+ }
+
+ private String getComponentModelType(final ComponentModel model) {
+
+ return controller
+ .getPageModelsController()
+ .getComponentModelTitle(model.getClass());
+
+ }
+
+ private String generateComponentModelTypeCaption(
+ final PageModelComponentModel item) {
+
+ final GlobalizationHelper globalizationHelper = controller
+ .getGlobalizationHelper();
+ final LocalizedTextsUtil textsUtil = globalizationHelper
+ .getLocalizedTextsUtil(item.descBundle());
+
+ return textsUtil.getText(item.titleKey());
+ }
+
+ private Component buildEditButton(final ComponentModel componentModel) {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ final Button editButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.components.edit"));
+ editButton.setIcon(VaadinIcons.EDIT);
+ editButton.addClickListener(event -> editComponentModel(componentModel));
+
+ return editButton;
+ }
+
+ @SuppressWarnings(
+ "unchecked")
+ private void editComponentModel(final ComponentModel componentModel) {
+
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ final PageModelsController pageModelsController = controller
+ .getPageModelsController();
+ final ComponentModels componentModels = pageModelsController
+ .getComponentModels();
+
+ final Optional componentModelInfo
+ = componentModels
+ .getComponentModelInfo(componentModel.getClass());
+ if (componentModelInfo.isPresent()) {
+
+ final String bebopFormClassName = componentModelInfo
+ .get()
+ .editor()
+ .getName();
+ final String editorName = bebopFormClassName
+ .replace("com.arsdigita.cms", "org.librecms")
+ .replace("Form", "Editor");
+
+ final Class extends AbstractPageModelComponentEditor>> editorClass;
+ try {
+ editorClass
+ = (Class extends AbstractPageModelComponentEditor>>) Class
+ .forName(editorName);
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final Constructor extends AbstractPageModelComponentEditor>> constructor;
+
+ try {
+ constructor = editorClass
+ .getDeclaredConstructor(PageModel.class,
+ ComponentModel.class,
+ PageModelComponentEditorController.class
+ );
+ } catch (NoSuchMethodException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final AbstractPageModelComponentEditor> editor;
+ try {
+ editor = constructor.newInstance(
+ pageModel,
+ componentModel,
+ pageModelsController.getComponentEditorController());
+ } catch (InstantiationException
+ | IllegalAccessException
+ | InvocationTargetException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ editor.setModal(true);
+ editor.setWidth("50%");
+ editor.setHeight("40%");
+
+ UI.getCurrent().addWindow(editor);
+ } else {
+ Notification.show(textsUtil
+ .getText("ui.admin.pageModels.no_info_for_component",
+ new String[]{componentModel.getClass().getName()}),
+ Notification.Type.ERROR_MESSAGE);
+ }
+ }
+
+ private Component buildDeleteButton(final ComponentModel componentModel) {
+
+ final PageModelsController pageModelsController = controller
+ .getPageModelsController();
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ final Button deleteButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.components.delete"));
+ deleteButton.setIcon(VaadinIcons.EDIT);
+ deleteButton.addClickListener(event -> {
+
+ final ConfirmDialog confirmDialog = new ConfirmDialog(() -> {
+ pageModelsController.removeComponentModel(pageModel,
+ componentModel);
+ return null;
+ });
+ confirmDialog.setMessage(textsUtil.getText(
+ "ui.admin.pagemodels.componentmodels.cols.delete.confirmation"));
+ confirmDialog.setModal(true);
+ UI.getCurrent().addWindow(confirmDialog);
+ });
+
+ return deleteButton;
+ }
+
}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelForm.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelForm.java
new file mode 100644
index 000000000..25fde1696
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelForm.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import com.arsdigita.ui.admin.AdminUiConstants;
+
+import com.vaadin.server.UserError;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.themes.ValoTheme;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.l10n.LocalizedTextsUtil;
+import org.libreccm.l10n.ui.LocalizedStringEditor;
+import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelManager;
+import org.libreccm.pagemodel.PageModelRepository;
+import org.libreccm.web.CcmApplication;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+class PageModelForm extends Window {
+
+ private static final long serialVersionUID = -8618363472800298648L;
+
+ private final AdminViewController controller;
+ private final CcmApplication application;
+ private PageModel pageModel;
+
+ private TextField nameField;
+ private LocalizedStringEditor titleEditor;
+ private LocalizedStringEditor descriptionEditor;
+
+ public PageModelForm(final AdminViewController controller,
+ final CcmApplication application) {
+
+ super();
+
+ this.controller = controller;
+ this.application = application;
+
+ addWidgets();
+ }
+
+ public PageModelForm(final PageModel pageModel,
+ final CcmApplication application,
+ final AdminViewController controller) {
+
+ super();
+
+ this.controller = controller;
+ this.application = application;
+ this.pageModel = pageModel;
+
+ addWidgets();
+
+ nameField.setValue(pageModel.getName());
+ }
+
+ private void addWidgets() {
+
+ final GlobalizationHelper globalizationHelper = controller
+ .getGlobalizationHelper();
+ final LocalizedTextsUtil textsUtil = globalizationHelper
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ nameField = new TextField(textsUtil.getText("ui.admin.pagemodels.name"));
+ nameField.setRequiredIndicatorVisible(true);
+
+ titleEditor = new LocalizedStringEditor(pageModel.getTitle(),
+ globalizationHelper);
+ titleEditor.setCaption(textsUtil.getText("ui.admin.pagemodels.title"));
+
+ descriptionEditor = new LocalizedStringEditor(
+ pageModel.getDescription(), globalizationHelper);
+ descriptionEditor
+ .setCaption(textsUtil.getText("ui.admin.pagemodels.desc"));
+
+ final FormLayout formLayout = new FormLayout(nameField,
+ titleEditor,
+ descriptionEditor);
+
+ final Button saveButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.buttons.save"));
+ saveButton.addStyleName(ValoTheme.BUTTON_PRIMARY);
+ saveButton.addClickListener(this::saveButtonClicked);
+ final Button cancelButton = new Button(textsUtil
+ .getText("ui.admin.pagemodels.buttons.cancel"));
+ cancelButton.addStyleName(ValoTheme.BUTTON_DANGER);
+ cancelButton.addClickListener(event -> close());
+ final HorizontalLayout buttonsLayout = new HorizontalLayout(saveButton,
+ cancelButton);
+
+ final VerticalLayout layout = new VerticalLayout(formLayout,
+ buttonsLayout);
+ setContent(layout);
+ }
+
+ private void saveButtonClicked(final Button.ClickEvent event) {
+
+ final PageModelsController pageModelsController = controller
+ .getPageModelsController();
+ final PageModelManager pageModelManager = pageModelsController
+ .getPageModelManager();
+ final LocalizedTextsUtil textsUtil = controller
+ .getGlobalizationHelper()
+ .getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
+
+ final String name = nameField.getValue();
+
+ if (name == null
+ || name.isEmpty()
+ || name.matches("\\s*")) {
+
+ nameField
+ .setComponentError(new UserError(
+ textsUtil.getText("ui.admin.pagemodels.name.error.empty")));
+ return;
+ }
+
+ if (pageModel == null) {
+
+ pageModel = pageModelManager.createPageModel(name, application);
+ }
+ pageModel.setName(name);
+
+ final PageModelRepository pageModelRepo = pageModelsController
+ .getPageModelRepo();
+ pageModelRepo.save(pageModel);
+
+ close();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsController.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsController.java
index a5b7e93ca..9c5e9e078 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsController.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsController.java
@@ -19,11 +19,18 @@
package org.libreccm.admin.ui;
import com.vaadin.cdi.ViewScoped;
+import org.libreccm.pagemodel.ComponentModel;
+import org.libreccm.pagemodel.ComponentModelRepository;
+import org.libreccm.pagemodel.ComponentModels;
import org.libreccm.pagemodel.PageModel;
+import org.libreccm.pagemodel.PageModelComponentModel;
import org.libreccm.pagemodel.PageModelManager;
import org.libreccm.pagemodel.PageModelRepository;
import java.io.Serializable;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.transaction.Transactional;
@@ -37,6 +44,15 @@ class PageModelsController implements Serializable {
private static final long serialVersionUID = 6204724295214879943L;
+ @Inject
+ private ComponentModelRepository componentModelRepo;
+
+ @Inject
+ private ComponentModels componentModels;
+
+ @Inject
+ private PageModelComponentEditorController componentEditorController;
+
@Inject
private PageModelManager pageModelManager;
@@ -46,6 +62,20 @@ class PageModelsController implements Serializable {
@Inject
private PageModelsTableDataProvider pageModelsTableDataProvider;
+ @Inject
+ private PageModelComponentModelsTableDataProvider componentModelsTableDataProvider;
+
+ @Inject
+ private PageModelComponentModelTypesDataProvider componentModelTypesDataProvider;
+
+ protected ComponentModels getComponentModels() {
+ return componentModels;
+ }
+
+ protected PageModelComponentEditorController getComponentEditorController() {
+ return componentEditorController;
+ }
+
protected PageModelManager getPageModelManager() {
return pageModelManager;
}
@@ -58,6 +88,39 @@ class PageModelsController implements Serializable {
return pageModelsTableDataProvider;
}
+ protected PageModelComponentModelsTableDataProvider getComponentModelsTableDataProvider() {
+ return componentModelsTableDataProvider;
+ }
+
+ protected PageModelComponentModelTypesDataProvider getComponentModelTypesDataProvider() {
+ return componentModelTypesDataProvider;
+ }
+
+ /**
+ * Retrieves the localised title of the {@link ComponentModel}.
+ *
+ * @param clazz The class of the {@link ComponentModel}.
+ *
+ * @return The localised title of the {@link ComponentModel}.
+ */
+ protected String getComponentModelTitle(
+ final Class extends ComponentModel> clazz) {
+
+ Objects.requireNonNull(clazz);
+
+ final Optional info = componentModels
+ .getComponentModelInfo(clazz);
+
+ if (info.isPresent()) {
+ final ResourceBundle bundle = ResourceBundle
+ .getBundle(info.get().descBundle());
+
+ return bundle.getString(info.get().titleKey());
+ } else {
+ return clazz.getName();
+ }
+ }
+
@Transactional(Transactional.TxType.REQUIRED)
protected void deletePageModel(final long pageModelId) {
@@ -71,4 +134,26 @@ class PageModelsController implements Serializable {
pageModelsTableDataProvider.refreshAll();
}
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void removeComponentModel(final PageModel pageModel,
+ final ComponentModel componentModel) {
+
+ Objects.requireNonNull(pageModel);
+ Objects.requireNonNull(componentModel);
+
+ final PageModel fromPageModel = pageModelRepo
+ .findById(pageModel.getPageModelId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No PageModel with ID %d in the database.",
+ pageModel.getPageModelId())));
+
+ final ComponentModel theComponentModel = componentModelRepo
+ .findById(componentModel.getComponentModelId())
+ .orElseThrow(() -> new IllegalArgumentException(String
+ .format("No ComponentModel with ID %d in the database.",
+ componentModel.getComponentModelId())));
+
+ pageModelManager.removeComponentModel(fromPageModel, theComponentModel);
+ }
+
}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTab.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTab.java
index 0b5f4feb1..912555f5c 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTab.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTab.java
@@ -25,15 +25,19 @@ import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.Grid;
+import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Label;
import com.vaadin.ui.Tree;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.components.grid.HeaderCell;
+import com.vaadin.ui.components.grid.HeaderRow;
import com.vaadin.ui.themes.ValoTheme;
import org.libreccm.l10n.LocalizedTextsUtil;
import org.libreccm.pagemodel.PageModel;
import org.libreccm.ui.ConfirmDialog;
+import org.libreccm.web.CcmApplication;
/**
*
@@ -50,12 +54,15 @@ class PageModelsTab extends CustomComponent {
private static final String COL_EDIT = "edit";
private static final String COL_DELETE = "delete";
+ private final Tree applicationTree;
+ private final Grid pageModelsGrid;
+
protected PageModelsTab(final AdminViewController adminViewController) {
super();
- final Tree applicationTree = new Tree<>(
- adminViewController.getApplicationTreeDataProvider());
+ applicationTree = new Tree<>(adminViewController
+ .getApplicationTreeDataProvider());
applicationTree.setItemCaptionGenerator(ApplicationTreeNode::getTitle);
applicationTree.setItemCollapseAllowedProvider(node -> {
return !node.getNodeType().equals(ApplicationTreeNodeType.ROOT_NODE);
@@ -65,7 +72,7 @@ class PageModelsTab extends CustomComponent {
.getGlobalizationHelper()
.getLocalizedTextsUtil(AdminUiConstants.ADMIN_BUNDLE);
- final Grid pageModelsGrid = new Grid<>();
+ pageModelsGrid = new Grid<>();
pageModelsGrid.setDataProvider(adminViewController
.getPageModelsController()
.getPageModelsTableDataProvider());
@@ -100,6 +107,30 @@ class PageModelsTab extends CustomComponent {
pageModelsGrid.setVisible(false);
pageModelsGrid.setWidth("100%");
+ final Button addPageModelButton = new Button(localizedTextsUtil
+ .getText("ui.admin.pagemodels.create_new"));
+ addPageModelButton.setIcon(VaadinIcons.PLUS_CIRCLE_O);
+ addPageModelButton.addClickListener(event -> {
+ final CcmApplication application
+ = ((PageModelsTableDataProvider) pageModelsGrid
+ .getDataProvider()).getApplication();
+ final PageModelForm pageModelForm = new PageModelForm(
+ adminViewController, application);
+ pageModelForm.setModal(true);
+ pageModelForm.setWidth("40%");
+ pageModelForm.setHeight("30%");
+
+ UI.getCurrent().addWindow(pageModelForm);
+ });
+ final HeaderRow headerRow = pageModelsGrid.prependHeaderRow();
+ final HeaderCell headerCell = headerRow.join(COL_NAME,
+ COL_TITLE,
+ COL_DESC,
+ COL_LIVE,
+ COL_EDIT,
+ COL_DELETE);
+ headerCell.setComponent(new HorizontalLayout(addPageModelButton));
+
final Label placeholder = new Label(localizedTextsUtil.getText(
"ui.admin.pagemodels.select_application"));
@@ -114,7 +145,7 @@ class PageModelsTab extends CustomComponent {
if (nodeType == ApplicationTreeNodeType.APPLICATION_NODE
|| nodeType
- == ApplicationTreeNodeType.SINGLETON_APPLICATION_NODE) {
+ == ApplicationTreeNodeType.SINGLETON_APPLICATION_NODE) {
final PageModelsTableDataProvider dataProvider
= (PageModelsTableDataProvider) pageModelsGrid
.getDataProvider();
@@ -128,7 +159,7 @@ class PageModelsTab extends CustomComponent {
});
final VerticalLayout treeLayout = new VerticalLayout(applicationTree);
-
+
final HorizontalSplitPanel panel = new HorizontalSplitPanel(
treeLayout, layout);
panel.setSplitPosition(20.0f);
@@ -155,8 +186,12 @@ class PageModelsTab extends CustomComponent {
.format("No PageModel with ID %d in the database.",
row.getPageModelId())));
+ final CcmApplication application
+ = ((PageModelsTableDataProvider) pageModelsGrid
+ .getDataProvider()).getApplication();
+
final PageModelDetails pageModelDetails = new PageModelDetails(
- pageModel, controller);
+ pageModel, application, controller);
pageModelDetails.center();
pageModelDetails.setModal(true);
pageModelDetails.setWidth("90%");
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTableDataProvider.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTableDataProvider.java
index c4ad435ca..6748da3a6 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTableDataProvider.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsTableDataProvider.java
@@ -50,7 +50,7 @@ class PageModelsTableDataProvider
@Inject
private ApplicationRepository applicationRepo;
-
+
@Inject
private EntityManager entityManager;
@@ -70,7 +70,7 @@ class PageModelsTableDataProvider
this.application = application;
refreshAll();
}
-
+
@Transactional(Transactional.TxType.REQUIRED)
public void setApplicationUuid(final String uuid) {
application = applicationRepo
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoController.java b/ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoController.java
similarity index 99%
rename from ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoController.java
rename to ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoController.java
index 8f832e7cc..2fa8c1cf9 100644
--- a/ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoController.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoController.java
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
-package org.libreccm.ui.admin.sysinfo;
+package org.libreccm.admin.ui.sysinfo;
import org.xml.sax.SAXException;
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoProperty.java b/ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoProperty.java
similarity index 96%
rename from ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoProperty.java
rename to ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoProperty.java
index 059d79d57..d865f9b32 100644
--- a/ccm-core/src/main/java/org/libreccm/ui/admin/sysinfo/SysInfoProperty.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/sysinfo/SysInfoProperty.java
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
-package org.libreccm.ui.admin.sysinfo;
+package org.libreccm.admin.ui.sysinfo;
/**
*
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/usersgroupsroles/RolesController.java b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/RolesController.java
similarity index 98%
rename from ccm-core/src/main/java/org/libreccm/ui/admin/usersgroupsroles/RolesController.java
rename to ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/RolesController.java
index ea83631fe..7a47bf114 100644
--- a/ccm-core/src/main/java/org/libreccm/ui/admin/usersgroupsroles/RolesController.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/RolesController.java
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
-package org.libreccm.ui.admin.usersgroupsroles;
+package org.libreccm.admin.ui.usersgroupsroles;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;