From c269df5d6461aa0f26f49ca057238a3623b43ee0 Mon Sep 17 00:00:00 2001 From: jensp Date: Thu, 5 Apr 2018 17:04:31 +0000 Subject: [PATCH] CCM NG: Styleable containers for PageModel (part 1) git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5364 8810af33-2d31-482b-a856-94f89814c4df --- .../pagemodels/PageModelsController.java | 13 +- .../layout/FlexLayoutComponentForm.java | 411 ------------------ .../ui/AbstractPageModelComponentEditor.java | 7 +- .../libreccm/admin/ui/PageModelDetails.java | 19 +- .../admin/ui/PageModelsController.java | 74 ++-- .../main/java/org/libreccm/core/CcmCore.java | 12 +- .../pagemodel/AbstractPageRenderer.java | 37 +- .../libreccm/pagemodel/ComponentModel.java | 27 +- .../pagemodel/ComponentModelRepository.java | 5 +- .../libreccm/pagemodel/ContainerModel.java | 263 +++++++++++ .../pagemodel/ContainerModelManager.java | 144 ++++++ .../pagemodel/ContainerModelRepository.java | 102 +++++ .../org/libreccm/pagemodel/PageModel.java | 33 +- .../libreccm/pagemodel/PageModelManager.java | 227 ++++++---- .../libreccm/pagemodel/layout/FlexBox.java | 173 -------- .../pagemodel/layout/FlexBoxRepository.java | 67 --- .../libreccm/pagemodel/layout/FlexLayout.java | 141 ------ .../pagemodel/layout/FlexLayoutManager.java | 136 ------ .../pagemodel/layout/FlexLayoutRenderer.java | 102 ----- .../pagemodel/styles/CssProperty.java | 133 ++++++ .../libreccm/pagemodel/styles/Dimension.java | 116 +++++ .../libreccm/pagemodel/styles/MediaQuery.java | 199 +++++++++ .../libreccm/pagemodel/styles/MediaRule.java | 169 +++++++ .../MediaType.java} | 12 +- .../org/libreccm/pagemodel/styles/Rule.java | 169 +++++++ .../org/libreccm/pagemodel/styles/Styles.java | 196 +++++++++ .../org/libreccm/pagemodel/styles/Unit.java | 36 ++ 27 files changed, 1787 insertions(+), 1236 deletions(-) delete mode 100644 ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/layout/FlexLayoutComponentForm.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModel.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelManager.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelRepository.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBox.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBoxRepository.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayout.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutManager.java delete mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutRenderer.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/CssProperty.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/Dimension.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaQuery.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaRule.java rename ccm-core/src/main/java/org/libreccm/pagemodel/{layout/FlexDirection.java => styles/MediaType.java} (84%) create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/Rule.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/Styles.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/styles/Unit.java diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/PageModelsController.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/PageModelsController.java index a910738f8..8375f43ed 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/PageModelsController.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/PageModelsController.java @@ -244,9 +244,10 @@ class PageModelsController implements Serializable { pageModelId))); final List components = new ArrayList<>(); - for (final ComponentModel component : model.getComponents()) { - components.add(component); - } +// ToDo +// for (final ComponentModel component : model.getComponents()) { +// components.add(component); +// } return components; } @@ -268,7 +269,8 @@ class PageModelsController implements Serializable { .format("No PageModel with ID %d in the database.", pageModelId))); - pageModelManager.addComponentModel(model, componentModel); +// ToDo +// pageModelManager.addComponentModel(model, componentModel); } @@ -296,7 +298,8 @@ class PageModelsController implements Serializable { .format("No ComponentModel with ID %d in the database.", componentModelId))); - pageModelManager.removeComponentModel(model, componentModel); +// ToDo +// pageModelManager.removeComponentModel(model, componentModel); } } diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/layout/FlexLayoutComponentForm.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/layout/FlexLayoutComponentForm.java deleted file mode 100644 index 9f5ed9b7c..000000000 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/pagemodels/layout/FlexLayoutComponentForm.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodels.layout; - -import com.arsdigita.bebop.BoxPanel; -import com.arsdigita.bebop.Component; -import com.arsdigita.bebop.FormData; -import com.arsdigita.bebop.FormProcessException; -import com.arsdigita.bebop.Label; -import com.arsdigita.bebop.List; -import com.arsdigita.bebop.PageState; -import com.arsdigita.bebop.ParameterSingleSelectionModel; -import com.arsdigita.bebop.PropertySheet; -import com.arsdigita.bebop.PropertySheetModel; -import com.arsdigita.bebop.PropertySheetModelBuilder; -import com.arsdigita.bebop.event.FormSectionEvent; -import com.arsdigita.bebop.form.Option; -import com.arsdigita.bebop.form.SingleSelect; -import com.arsdigita.bebop.list.ListCellRenderer; -import com.arsdigita.bebop.list.ListModel; -import com.arsdigita.bebop.list.ListModelBuilder; -import com.arsdigita.globalization.GlobalizedMessage; -import com.arsdigita.ui.admin.AdminUiConstants; -import com.arsdigita.ui.admin.pagemodels.AbstractComponentModelForm; -import com.arsdigita.ui.admin.pagemodels.PageModelsTab; -import com.arsdigita.util.LockableImpl; -import com.arsdigita.xml.Element; - -import org.libreccm.cdi.utils.CdiUtil; -import org.libreccm.core.UnexpectedErrorException; -import org.libreccm.pagemodel.ComponentModelRepository; -import org.libreccm.pagemodel.layout.FlexBox; -import org.libreccm.pagemodel.layout.FlexDirection; -import org.libreccm.pagemodel.layout.FlexLayout; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.Objects; - -/** - * - * @author Jens Pelzetter - */ -public class FlexLayoutComponentForm - extends AbstractComponentModelForm { - - private final ParameterSingleSelectionModel selectedComponentId; - - private SingleSelect directionSelect; -// private FlexBoxesPanel boxesPanel; - - private List boxesList; - - public FlexLayoutComponentForm( - final PageModelsTab pageModelsTab, - final ParameterSingleSelectionModel selectedModelId, - final ParameterSingleSelectionModel selectedComponentId) { - - super("FlexLayoutComponentForm", - pageModelsTab, - selectedModelId, - selectedComponentId); - - Objects.requireNonNull(pageModelsTab); - Objects.requireNonNull(selectedModelId); - Objects.requireNonNull(selectedComponentId); - - this.selectedComponentId = selectedComponentId; - } - - @Override - protected void addWidgets() { - - directionSelect = new SingleSelect("directionSelect"); - directionSelect.setLabel(new GlobalizedMessage( - AdminUiConstants.ADMIN_BUNDLE, - "ui.admin.pagelayout.flexlayout.direction.label")); - directionSelect - .addOption(new Option( - FlexDirection.HORIZONTAL.toString(), - new Label(new GlobalizedMessage( - AdminUiConstants.ADMIN_BUNDLE, - "ui.admin.pagelayout.flexlayout.direction" - + ".option.horizontal")))); - directionSelect - .addOption(new Option( - FlexDirection.VERTICAL.toString(), - new Label(new GlobalizedMessage( - AdminUiConstants.ADMIN_BUNDLE, - "ui.admin.pagelayout.flexlayout.direction" - + ".option.vertical")))); - add(directionSelect); - -// boxesPanel = new FlexBoxesPanel(); -// add(boxesPanel); - boxesList - = new BoxesList(new BoxesListModelBuilder(selectedComponentId)); - boxesList.setCellRenderer(new BoxesListCellRenderer()); - boxesList.setEmptyView(new Label(new GlobalizedMessage( - "ui.admin.pagelayout.flexlayout.no_boxes", - AdminUiConstants.ADMIN_BUNDLE))); - add(boxesList); - } - - @Override - protected FlexLayout createComponentModel() { - return new FlexLayout(); - } - - @Override - protected void updateComponentModel(final FlexLayout componentModel, - final PageState state, - final FormData data) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void init(final FormSectionEvent event) throws FormProcessException { - - super.init(event); - - final PageState state = event.getPageState(); - - final FlexLayout layout = getComponentModel(); - - if (layout == null) { - directionSelect.setValue(state, FlexDirection.VERTICAL.toString()); - } else { - directionSelect.setValue(state, layout.getDirection().toString()); - } - } - -// private class FlexBoxesPanel extends SimpleContainer { -// -// private FlexDirection direction; -// -// public FlexBoxesPanel() { -// super("flexLayout", "bebop"); -// -// direction = FlexDirection.VERTICAL; -// } -// -// public FlexDirection getDirection() { -// return direction; -// } -// -// public void setFlexDirection(final FlexDirection direction) { -// this.direction = direction; -// } -// -// @Override -// protected Element generateParent(final Element parent) { -// -// final Element element = super.generateParent(parent); -// element.addAttribute("direction", direction -// .toString() -// .toLowerCase()); -// return element; -// } -// -// } - private class FlexBoxPanel extends BoxPanel { - - private final FlexBox box; - - public FlexBoxPanel(final FlexBox box) { - - super(BoxPanel.VERTICAL); - - this.box = box; - - final PropertySheet propertySheet = new PropertySheet( - new FlexBoxPropertySheetModelBuilder(box)); - super.add(propertySheet); - } - - } - - private class FlexBoxPropertySheetModelBuilder - extends LockableImpl - implements PropertySheetModelBuilder { - - private final FlexBox box; - - public FlexBoxPropertySheetModelBuilder(final FlexBox box) { - this.box = box; - } - - @Override - public PropertySheetModel makeModel(final PropertySheet sheet, - final PageState state) { - - return new FlexBoxPropertySheetModel(box); - } - - } - - private class FlexBoxPropertySheetModel implements PropertySheetModel { - - private final FlexBox box; - private final Iterator propertyIterator; - private FlexBoxProperty currentProperty; - - public FlexBoxPropertySheetModel(final FlexBox box) { - this.box = box; - propertyIterator = Arrays - .asList(FlexBoxProperty.values()) - .iterator(); - } - - @Override - public boolean nextRow() { - - if (box == null) { - return false; - } - - if (propertyIterator.hasNext()) { - currentProperty = propertyIterator.next(); - return true; - } else { - return false; - } - } - - @Override - public String getLabel() { - return currentProperty.toString(); - } - - @Override - public GlobalizedMessage getGlobalizedLabel() { - - final String key = String - .format("ui.admin.pagemodels.flexlayout.box.properties.%s", - currentProperty.toString().toLowerCase()); - return new GlobalizedMessage(key, AdminUiConstants.ADMIN_BUNDLE); - } - - @Override - public String getValue() { - - switch (currentProperty) { - case ORDER: - return Integer.toString(box.getOrder()); - case SIZE: - return Integer.toString(box.getSize()); - case COMPONENT: - if (box.getComponent() == null) { - return ""; - } else { - return box.getComponent().getClass().getName(); - } - default: - throw new UnexpectedErrorException(String - .format("Invalid \"%s\" for property of FlexBox.", - currentProperty.toString())); - } - } - } - - private static enum FlexBoxProperty { - ORDER, - SIZE, - COMPONENT - } - - private class BoxesList extends List { - - private FlexLayout flexLayout; - - public BoxesList(final ListModelBuilder listModelBuilder) { - super(listModelBuilder); - } - - public FlexLayout getFlexLayout() { - return flexLayout; - } - - public void setFlexLayout(final FlexLayout flexLayout) { - this.flexLayout = flexLayout; - } - - @Override - protected String getTagName() { - return "flexLayoutBoxesList"; - } - - @Override - protected void exportLayoutAttribute(final Element list) { - - if (flexLayout.getDirection() == FlexDirection.HORIZONTAL) { - list.addAttribute("layout", "horizontal"); - } else { - list.addAttribute("layout", "vertical"); - } - } - - } - - private class BoxesListModelBuilder - extends LockableImpl - implements ListModelBuilder { - - private final ParameterSingleSelectionModel selectedComponentId; - - public BoxesListModelBuilder( - final ParameterSingleSelectionModel selectedComponentId) { - - super(); - - Objects.requireNonNull(selectedComponentId); - - this.selectedComponentId = selectedComponentId; - } - - @Override - public ListModel makeModel(final List list, - final PageState state) { - - if (selectedComponentId.isSelected(state)) { - - final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); - - final ComponentModelRepository componentModelRepo = cdiUtil - .findBean(ComponentModelRepository.class); - - final String componentModelId = selectedComponentId - .getSelectedKey(state); - - final FlexLayout flexLayout = (FlexLayout) componentModelRepo - .findById(Long.parseLong(componentModelId)) - .orElseThrow(() -> new IllegalArgumentException(String - .format("No ComponentModel with ID %s in the database", - componentModelId))); - - return new BoxesListModel(flexLayout.getBoxes()); - - } else { - return new BoxesListModel(Collections.emptyList()); - } - } - - } - - private class BoxesListModel implements ListModel { - - private final Iterator iterator; - private FlexBox currentBox; - - public BoxesListModel(final java.util.List boxes) { - this.iterator = boxes.iterator(); - } - - @Override - public boolean next() { - if (iterator.hasNext()) { - currentBox = iterator.next(); - return true; - } else { - return false; - } - } - - @Override - public Object getElement() { - return currentBox; - } - - @Override - public String getKey() { - return Long.toString(currentBox.getBoxId()); - } - - } - - private class BoxesListCellRenderer implements ListCellRenderer { - - @Override - public Component getComponent(final List list, - final PageState state, - final Object value, - final String key, - final int index, - final boolean isSelected) { - - final FlexBox box = (FlexBox) value; - - return new FlexBoxPanel(box); - } - - } - -} 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 index 1d844090c..251c24d95 100644 --- a/ccm-core/src/main/java/org/libreccm/admin/ui/AbstractPageModelComponentEditor.java +++ b/ccm-core/src/main/java/org/libreccm/admin/ui/AbstractPageModelComponentEditor.java @@ -137,9 +137,10 @@ public abstract class AbstractPageModelComponentEditor componentModel = createComponentModel(); componentModel.setKey(key); updateComponentModel(); - controller - .getPageModelsController() - .addComponentModel(pageModel, componentModel); +// ToDo +// controller +// .getPageModelsController() +// .addComponentModel(pageModel, componentModel); } else { componentModel.setKey(key); updateComponentModel(); 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 27d36e1f9..ed6280280 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 @@ -389,15 +389,16 @@ class PageModelDetails extends Window { ValoTheme.BUTTON_DANGER); 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); +// ToDo +// 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/PageModelsController.java b/ccm-core/src/main/java/org/libreccm/admin/ui/PageModelsController.java index 21eab54e1..6e9c9d97b 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 @@ -134,42 +134,44 @@ class PageModelsController implements Serializable { pageModelsTableDataProvider.refreshAll(); } - @Transactional(Transactional.TxType.REQUIRED) - protected void addComponentModel(final PageModel pageModel, - final ComponentModel componentModel) { - - Objects.requireNonNull(pageModel); - Objects.requireNonNull(componentModel); - - final PageModel toPageModel = pageModelRepo - .findById(pageModel.getPageModelId()) - .orElseThrow(() -> new IllegalArgumentException(String - .format("No PageModel with ID %d in the database.", - pageModel.getPageModelId()))); - - pageModelManager.addComponentModel(toPageModel, componentModel); - } +// ToDo +// @Transactional(Transactional.TxType.REQUIRED) +// protected void addComponentModel(final PageModel pageModel, +// final ComponentModel componentModel) { +// +// Objects.requireNonNull(pageModel); +// Objects.requireNonNull(componentModel); +// +// final PageModel toPageModel = pageModelRepo +// .findById(pageModel.getPageModelId()) +// .orElseThrow(() -> new IllegalArgumentException(String +// .format("No PageModel with ID %d in the database.", +// pageModel.getPageModelId()))); +// +// pageModelManager.addComponentModel(toPageModel, componentModel); +// } - @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); - } +// ToDo +// @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/core/CcmCore.java b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java index 03eeee24f..cc516ef62 100644 --- a/ccm-core/src/main/java/org/libreccm/core/CcmCore.java +++ b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java @@ -18,7 +18,6 @@ */ package org.libreccm.core; -import com.arsdigita.ui.admin.pagemodels.layout.FlexLayoutComponentForm; import com.arsdigita.ui.admin.AdminServlet; import com.arsdigita.ui.admin.AdminUiConstants; import com.arsdigita.ui.admin.applications.AdminApplicationCreator; @@ -38,8 +37,6 @@ import org.libreccm.modules.InstallEvent; import org.libreccm.modules.Module; import org.libreccm.modules.ShutdownEvent; import org.libreccm.modules.UnInstallEvent; -import org.libreccm.pagemodel.PageModelComponentModel; -import org.libreccm.pagemodel.layout.FlexLayout; import org.libreccm.security.SystemUsersSetup; import org.libreccm.web.ApplicationType; @@ -50,7 +47,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; -import static com.arsdigita.ui.admin.AdminUiConstants.*; /** * @@ -75,13 +71,7 @@ import static com.arsdigita.ui.admin.AdminUiConstants.*; creator = AdminJsfApplicationCreator.class, servletPath = "/admin-jsf/admin.xhtml")}, pageModelComponentModels = { - @PageModelComponentModel( - modelClass = FlexLayout.class, - editor = FlexLayoutComponentForm.class, - descBundle = ADMIN_BUNDLE, - titleKey = "ui.pagemodel.components.flexlayout.title", - descKey = "ui.pagemodel.components.flexlayout.desc" - ) + }, configurations = { com.arsdigita.bebop.BebopConfig.class, diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageRenderer.java b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageRenderer.java index dab5feacc..f98a419b1 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageRenderer.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/AbstractPageRenderer.java @@ -18,6 +18,7 @@ */ package org.libreccm.pagemodel; +import java.util.HashMap; import java.util.Map; import javax.inject.Inject; @@ -58,20 +59,38 @@ public abstract class AbstractPageRenderer implements PageRenderer { final Map page = renderPage(parameters); - for (final ComponentModel componentModel : pageModel.getComponents()) { - final Optional component = renderComponent( - componentModel, - componentModel.getClass(), - parameters); - if (component.isPresent()) { - page.put(componentModel.getKey(), - component.get()); - } + for (final ContainerModel containerModel : pageModel.getContainers()) { + + final Map container = renderContainer( + containerModel, parameters); + page.put(containerModel.getKey(), container); } return page; } + protected Map renderContainer( + final ContainerModel containerModel, + final Map parameters) { + + final Map container = new HashMap<>(); + + container.put("key", containerModel.getKey()); + container.put("styles", containerModel.getStyles().toCss()); + + for (final ComponentModel componentModel : containerModel + .getComponents()) { + + renderComponent(componentModel, + componentModel.getClass(), + parameters) + .ifPresent(component -> container.put(componentModel.getKey(), + component)); + } + + return container; + } + /** * Helper method for rendering the components. * diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModel.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModel.java index 8f98bbbd1..a4235be65 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModel.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModel.java @@ -77,12 +77,9 @@ public class ComponentModel implements Serializable { @NotNull private String modelUuid; - /** - * The {@link PageModel} to which the {@code ComponentModel} belongs. - */ @ManyToOne - @JoinColumn(name = "PAGE_MODEL_ID") - private PageModel pageModel; + @JoinColumn(name = "CONTAINER_ID") + private ContainerModel container; /** * ID of the component. Must be unique inside a {@link PageModel}. @@ -138,12 +135,12 @@ public class ComponentModel implements Serializable { this.modelUuid = modelUuid; } - public PageModel getPageModel() { - return pageModel; + public ContainerModel getContainer() { + return container; } - protected void setPageModel(final PageModel pageModel) { - this.pageModel = pageModel; + protected void setContainer(final ContainerModel container) { + this.container = container; } public String getIdAttribute() { @@ -184,7 +181,7 @@ public class ComponentModel implements Serializable { hash = 53 * hash + (int) (componentModelId ^ (componentModelId >>> 32)); hash = 53 * hash + Objects.hashCode(uuid); - hash = 53 * hash + Objects.hashCode(pageModel); + hash = 53 * hash + Objects.hashCode(container); hash = 53 * hash + Objects.hashCode(idAttribute); hash = 53 * hash + Objects.hashCode(classAttribute); hash = 53 * hash + Objects.hashCode(styleAttribute); @@ -220,10 +217,12 @@ public class ComponentModel implements Serializable { if (!Objects.equals(styleAttribute, other.getStyleAttribute())) { return false; } - if (!Objects.equals(key, other.getKey())) { + + if (!Objects.equals(container, other.getContainer())) { return false; } - return Objects.equals(pageModel, other.getPageModel()); + + return Objects.equals(key, other.getKey()); } public boolean canEqual(final Object obj) { @@ -238,7 +237,7 @@ public class ComponentModel implements Serializable { public String toString(final String data) { return String.format("%s{ " + "componentModelId = %d, " - + "pageModel = %s, " + + "container = %s, " + "idAttribute = \"%s\", " + "classAttribute = \"%s\", " + "styleAttribute = \"%s\", " @@ -246,7 +245,7 @@ public class ComponentModel implements Serializable { + " }", super.hashCode(), componentModelId, - pageModel, + container, idAttribute, classAttribute, styleAttribute, diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelRepository.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelRepository.java index b35032174..e060e1d05 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelRepository.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ComponentModelRepository.java @@ -87,6 +87,7 @@ public class ComponentModelRepository */ @Override public void initNewEntity(final ComponentModel componentModel) { + final String uuid = UUID.randomUUID().toString(); componentModel.setUuid(uuid); @@ -97,8 +98,8 @@ public class ComponentModelRepository } @Transactional(Transactional.TxType.REQUIRED) - public Optional findById(final long modelId, - final Class modelClass) { + public Optional findById( + final long modelId, final Class modelClass) { return Optional.ofNullable(getEntityManager().find(modelClass, modelId)); diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModel.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModel.java new file mode 100644 index 000000000..f340223b6 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModel.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2018 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.pagemodel; + +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.libreccm.core.CoreConstants; +import org.libreccm.pagemodel.styles.Styles; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.OrderBy; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +/** + * A {@code ContainerModel} for grouping {@link ComponentModel}s. Each + * {@link PageModel} contains a least one container. A container also contains + * styles which allow it to provide the theme engine with CSS for aligning the + * components in the container. Please note that theme developers can ignore the + * information. + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "PAGE_MODEL_CONTAINER_MODELS", schema = CoreConstants.DB_SCHEMA) +@NamedQueries({ + @NamedQuery(name = "ContainerModel.findByKeyAndPage", + query = "SELECT c FROM Container c " + + "WHERE c.key = :key " + + "AND c.pageModel = :pageModel") +}) +public class ContainerModel implements Serializable { + + private static final long serialVersionUID = -7472858443655353588L; + + /** + * ID of the ContainerModel in the database. + */ + @Id + @Column(name = "CONTAINER_ID") + private long containerId; + + /** + * The UUID of this version of the container. + */ + @Column(name = "UUID", length = 255, nullable = false) + @NotNull + private String uuid; + + /** + * The UUID of the container which is the same in all versions. + */ + @Column(name = "CONTAINER_UUID", length = 255, nullable = false) + @NotNull + private String containerUuid; + + /** + * A key for identifying the container inside a {@link PageModel}. May be + * used for the value of the {@code id} or {@code class} attribute in HTML. + * It is recommended the use semantic names. + */ + @Column(name = "COMPONENT_KEY", length = 255) + private String key; + + /** + * Styles for this container. This should be limited to CSS which describes + * the layout of the components in the container. Colours etc. are the + * responsibility of the theme. A theme might also alter the styles stored + * here. + */ + @OneToOne + @JoinColumn(name = "STYLE_ID") + @Cascade(CascadeType.ALL) + private Styles styles; + + @ManyToOne + @JoinColumn(name = "PAGE_MODEL_ID") + private PageModel pageModel; + + /** + * The components in this this container. + */ + @OneToMany(mappedBy = "container") + @OrderBy("key ASC") + private List components; + + public ContainerModel() { + this.styles = new Styles(); + this.components = new ArrayList<>(); + } + + public long getContainerId() { + return containerId; + } + + protected void setContainerId(final long containerId) { + this.containerId = containerId; + } + + public String getUuid() { + return uuid; + } + + protected void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getContainerUuid() { + return containerUuid; + } + + protected void setContainerUuid(final String containerUuid) { + this.containerUuid = containerUuid; + } + + public String getKey() { + return key; + } + + public void setKey(final String key) { + this.key = key; + } + + public Styles getStyles() { + return styles; + } + + protected void setStyles(final Styles styles) { + this.styles = styles; + } + + public PageModel getPageModel() { + return pageModel; + } + + protected void setPageModel(final PageModel pageModel) { + this.pageModel = pageModel; + } + + public List getComponents() { + return Collections.unmodifiableList(components); + } + + protected void setComponents(final List components) { + this.components = new ArrayList<>(components); + } + + protected void addComponent(final ComponentModel component) { + components.add(component); + } + + protected void removeComponent(final ComponentModel component) { + components.remove(component); + } + + protected void clearComponents() { + components.clear(); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + (int) (containerId ^ (containerId >>> 32)); + hash = 97 * hash + Objects.hashCode(uuid); + hash = 97 * hash + Objects.hashCode(containerUuid); + hash = 97 * hash + Objects.hashCode(key); + hash = 97 * hash + Objects.hashCode(styles); + return hash; + } + + @Override + public boolean equals(final Object obj) { + + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ContainerModel)) { + return false; + } + final ContainerModel other = (ContainerModel) obj; + if (!other.canEqual(this)) { + return false; + } + + if (containerId != other.getContainerId()) { + return false; + } + if (!Objects.equals(uuid, other.getUuid())) { + return false; + } + if (!Objects.equals(containerUuid, other.getContainerUuid())) { + return false; + } + + if (!Objects.equals(styles, other.getStyles())) { + return false; + } + + return Objects.equals(key, other.getKey()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof ContainerModel; + } + + public String toString(final String data) { + + return String.format("%s{ " + + "containerId = %d, " + + "uuid = %s, " + + "containerUuid = %s, " + + "key = \"%s\", " + + "styles = %s%s" + + " }", + super.toString(), + containerId, + uuid, + containerUuid, + key, + Objects.toString(styles), + data); + } + + @Override + public final String toString() { + + return toString(""); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelManager.java new file mode 100644 index 000000000..3ffebebfd --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelManager.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 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.pagemodel; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.modules.CcmModule; +import org.libreccm.modules.Module; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class ContainerModelManager implements Serializable { + + private static final long serialVersionUID = -2793646505397414050L; + + private static final Logger LOGGER = LogManager + .getLogger(ContainerModelManager.class); + + @Inject + private ContainerModelRepository containerRepo; + + @Inject + private ComponentModelRepository componentModelRepo; + + private final Map components + = new HashMap<>(); + + /** + * Called by CDI after an instance of this class is created. Initialises the + * {@link #components} by retrieving the data about all available + * {@link ComponentModel}s. + */ + @PostConstruct + private void init() { + LOGGER.debug("Initalising {}...", + ContainerModelManager.class.getName()); + + final ServiceLoader modules = ServiceLoader.load( + CcmModule.class); + + for (CcmModule module : modules) { + final Module moduleData = module.getClass().getAnnotation( + Module.class); + + final PageModelComponentModel[] models = moduleData + .pageModelComponentModels(); + + for (PageModelComponentModel model : models) { + components.put(model.modelClass().getName(), + model); + } + } + LOGGER.debug("Initalised {}. Found {} ComponentModels.", + ContainerModelManager.class.getName(), + components.size()); + } + + /** + * Adds a {@link ComponentModel} to a {@link ContainerModel}. + * + * @param container The {@link ContainerModel} to which component model + * is added. + * @param componentModel The {@link ComponentModel} to add. + */ + @Transactional(Transactional.TxType.REQUIRED) + public void addComponentModel(final ContainerModel container, + final ComponentModel componentModel) { + + if (container == null) { + throw new IllegalArgumentException( + "Can't add a component model to page model null."); + } + + if (componentModel == null) { + throw new IllegalArgumentException( + "Can't add component model null to a page model."); + } + + container.addComponent(componentModel); + componentModel.setContainer(container); + + containerRepo.save(container); + componentModelRepo.save(componentModel); + } + + /** + * Removes a {@link ComponentModel} from a {@link ContainerModel}. + * + * @param container The {@link ContainerModel} from which the + * {@link ComponentModel} is removed. + * @param componentModel The {@link ComponentModel} to remove. The component + * model is also removed from the database. + */ + @Transactional(Transactional.TxType.REQUIRED) + public void removeComponentModel(final ContainerModel container, + final ComponentModel componentModel) { + + if (container == null) { + throw new IllegalArgumentException( + "Can't remove a component model from page model null."); + } + + if (componentModel == null) { + throw new IllegalArgumentException( + "Can't remove component model null from a page model."); + } + + container.removeComponent(componentModel); + componentModel.setContainer(null); + + containerRepo.save(container); + componentModelRepo.delete(componentModel); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelRepository.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelRepository.java new file mode 100644 index 000000000..49ebf861d --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ContainerModelRepository.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 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.pagemodel; + +import org.libreccm.core.AbstractEntityRepository; +import org.libreccm.core.CoreConstants; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.RequiresPrivilege; + +import java.util.Optional; +import java.util.UUID; + +import javax.enterprise.context.RequestScoped; +import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class ContainerModelRepository + extends AbstractEntityRepository { + + private static final long serialVersionUID = 6613005988522263867L; + + @Override + public Class getEntityClass() { + return ContainerModel.class; + } + + @Override + public String getIdAttributeName() { + return "containerId"; + } + + @Override + public Long getIdOfEntity(final ContainerModel container) { + return container.getContainerId(); + } + + @Override + public boolean isNew(final ContainerModel container) { + return container.getContainerId() == 0; + } + + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + @Override + public void save(final ContainerModel container) { + + super.save(container); + } + + @Override + public void initNewEntity(final ContainerModel container) { + + final String uuid = UUID.randomUUID().toString(); + container.setUuid(uuid); + + if (container.getContainerUuid() == null + || container.getContainerUuid().isEmpty()) { + + container.setContainerUuid(uuid); + } + } + + @Transactional(Transactional.TxType.REQUIRED) + public Optional findContainerByKeyAndPageModel( + final String key, final PageModel pageModel) { + + final TypedQuery query = getEntityManager() + .createNamedQuery("ContainerModel.findByKeyAndPage", ContainerModel.class); + query.setParameter("key", key); + query.setParameter("pageModel", pageModel); + + try { + return Optional.of(query.getSingleResult()); + } catch(NoResultException ex) { + return Optional.empty(); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModel.java b/ccm-core/src/main/java/org/libreccm/pagemodel/PageModel.java index e35367194..11911714e 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModel.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/PageModel.java @@ -237,17 +237,14 @@ public class PageModel implements Serializable { @NotNull private String type; - /** - * The components of the page described by this {@code PageModel}. - */ @OneToMany(mappedBy = "pageModel") @OrderBy("key ASC") - private List components; + private List containers; public PageModel() { title = new LocalizedString(); description = new LocalizedString(); - components = new ArrayList<>(); + containers = new ArrayList<>(); } public long getPageModelId() { @@ -324,24 +321,24 @@ public class PageModel implements Serializable { this.type = type; } - public List getComponents() { - return Collections.unmodifiableList(components); + public List getContainers() { + return Collections.unmodifiableList(containers); } - protected void setComponents(final List components) { - this.components = new ArrayList<>(components); + protected void setContainers(final List containers) { + this.containers = new ArrayList<>(containers); } - - protected void addComponent(final ComponentModel component) { - components.add(component); + + protected void addContainer(final ContainerModel container) { + containers.add(container); } - - protected void removeComponent(final ComponentModel component) { - components.remove(component); + + protected void removeContainer(final ContainerModel container) { + containers.remove(container); } - - protected void clearComponents() { - components.clear(); + + protected void clearContainers() { + containers.clear(); } @Override diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelManager.java index 47e69d6c8..59b3477f1 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelManager.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/PageModelManager.java @@ -24,6 +24,11 @@ import org.libreccm.core.CoreConstants; import org.libreccm.core.UnexpectedErrorException; import org.libreccm.modules.CcmModule; import org.libreccm.modules.Module; +import org.libreccm.pagemodel.styles.CssProperty; +import org.libreccm.pagemodel.styles.MediaQuery; +import org.libreccm.pagemodel.styles.MediaRule; +import org.libreccm.pagemodel.styles.Rule; +import org.libreccm.pagemodel.styles.Styles; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; import org.libreccm.web.CcmApplication; @@ -50,6 +55,7 @@ import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; +import java.util.stream.Collectors; /** * Provides several methods for managing {@link PageModel}s. @@ -66,10 +72,16 @@ public class PageModelManager { private EntityManager entityManager; @Inject - private PageModelRepository pageModelRepo; + private ComponentModelRepository componentModelRepo; @Inject - private ComponentModelRepository componentModelRepo; + private ContainerModelRepository containerModelRepo; + + @Inject + private ContainerModelManager containerModelManager; + + @Inject + private PageModelRepository pageModelRepo; private final Map components = new HashMap<>(); @@ -291,19 +303,104 @@ public class PageModelManager { liveModel.setApplication(draftModel.getApplication()); liveModel.setType(draftModel.getType()); - LOGGER.debug("Publishing ComponentModels of PageModel \"{}\"...", + LOGGER.debug("Publishing ContainerModels of PageModel \"{}\"...", draftModel.getName()); - liveModel.clearComponents(); - for (final ComponentModel draft : draftModel.getComponents()) { - final ComponentModel live = publishComponentModel(draft); - addComponentModel(liveModel, live); - } + liveModel.clearContainers(); + + draftModel + .getContainers() + .stream() + .map(this::publishContainerModel) + .forEach(liveContainerModel -> addContainerModel(pageModel, + liveContainerModel)); LOGGER.debug("Successfully published PageModel \"{}\".", liveModel.getName()); return liveModel; } + private ContainerModel publishContainerModel( + final ContainerModel draftModel) { + + Objects.requireNonNull(draftModel); + + final ContainerModel liveModel = new ContainerModel(); + liveModel.setKey(draftModel.getKey()); + liveModel.setContainerUuid(draftModel.getContainerUuid()); + + final Styles draftStyles = draftModel.getStyles(); + final Styles liveStyles = new Styles(); + liveStyles.setStyleName(draftStyles.getStyleName()); + liveStyles.setRules(draftStyles + .getRules() + .stream() + .map(this::publishRule) + .collect(Collectors.toList())); + liveStyles.setMediaRules(draftStyles + .getMediaRules() + .stream() + .map(this::publishMediaRule) + .collect(Collectors.toList())); + + draftModel + .getComponents() + .stream() + .map(this::publishComponentModel) + .forEach( + liveComponentModel -> containerModelManager + .addComponentModel(liveModel, liveComponentModel)); + + return liveModel; + } + + private MediaRule publishMediaRule(final MediaRule draftMediaRule) { + + Objects.requireNonNull(draftMediaRule); + + final MediaRule liveMediaRule = new MediaRule(); + final MediaQuery liveMediaQuery = new MediaQuery(); + liveMediaQuery + .setMaxWidth(draftMediaRule.getMediaQuery().getMaxWidth()); + liveMediaQuery + .setMediaType(draftMediaRule.getMediaQuery().getMediaType()); + liveMediaQuery + .setMinWidth(draftMediaRule.getMediaQuery().getMinWidth()); + + liveMediaRule.setRules(draftMediaRule + .getRules() + .stream() + .map(this::publishRule) + .collect(Collectors.toList())); + + return liveMediaRule; + } + + private Rule publishRule(final Rule draftRule) { + + Objects.requireNonNull(draftRule); + + final Rule liveRule = new Rule(); + liveRule.setSelector(draftRule.getSelector()); + liveRule.setProperties(draftRule + .getProperties() + .stream() + .map(this::publishCssProperty) + .collect(Collectors.toList())); + + return liveRule; + } + + private CssProperty publishCssProperty(final CssProperty draftProperty) { + + Objects.requireNonNull(draftProperty); + + final CssProperty liveProperty = new CssProperty(); + liveProperty.setName(draftProperty.getName()); + liveProperty.setValue(draftProperty.getValue()); + + return liveProperty; + } + /** * Helper method for coping the {@link ComponentModel}s from the draft * version to the live version. @@ -369,8 +466,8 @@ public class PageModelManager { target.addAll(source); writeMethod.invoke(draftModel, target); } catch (IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { + | IllegalArgumentException + | InvocationTargetException ex) { throw new UnexpectedErrorException(ex); } @@ -384,8 +481,8 @@ public class PageModelManager { source = (Map) readMethod.invoke(draftModel); target = (Map) readMethod.invoke(liveModel); } catch (IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { + | IllegalArgumentException + | InvocationTargetException ex) { throw new UnexpectedErrorException(ex); } @@ -401,8 +498,8 @@ public class PageModelManager { source = (Set) readMethod.invoke(draftModel); target = (Set) readMethod.invoke(liveModel); } catch (IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { + | IllegalArgumentException + | InvocationTargetException ex) { throw new UnexpectedErrorException(ex); } @@ -413,8 +510,8 @@ public class PageModelManager { value = readMethod.invoke(draftModel); writeMethod.invoke(liveModel, value); } catch (IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { + | IllegalArgumentException + | InvocationTargetException ex) { throw new UnexpectedErrorException(ex); } } @@ -454,95 +551,37 @@ public class PageModelManager { return result; } - /** - * Returns all available {@link ComponentModel}. - * - * @return A list of all available {@link ComponentModel}s. - */ - public List findAvailableComponents() { - final List list = new ArrayList<>(components - .values()); - list.sort((component1, component2) -> { - return component1.modelClass().getName().compareTo( - component2.modelClass().getName()); - }); + @Transactional(Transactional.TxType.REQUIRED) + public void addContainerModel(final PageModel pageModel, + final ContainerModel container) { - return list; - } + Objects.requireNonNull(pageModel); + Objects.requireNonNull(container); - /** - * Finds the description specific {@link ComponentModel}. - * - * @param className The fully qualified name of the {@link ComponentModel} - * class. - * - * @return An {@link Optional} containing the description of the - * {@link ComponentModel} if there is a {@link ComponentModel} with - * the specified {@code name}. Otherwise an empty {@link Optional}. - */ - public Optional findComponentModel( - final String className) { - - if (components.containsKey(className)) { - return Optional.of(components.get(className)); - } else { - return Optional.empty(); - } - } - - /** - * Adds a {@link ComponentModel} to a {@link PageModel}. - * - * @param pageModel The {@link PageModel} to which component model is - * added. - * @param componentModel The {@link ComponentModel} to add. - */ - public void addComponentModel(final PageModel pageModel, - final ComponentModel componentModel) { - - if (pageModel == null) { - throw new IllegalArgumentException( - "Can't add a component model to page model null."); - } - - if (componentModel == null) { - throw new IllegalArgumentException( - "Can't add component model null to a page model."); - } - - pageModel.addComponent(componentModel); - componentModel.setPageModel(pageModel); + pageModel.addContainer(container); + container.setPageModel(pageModel); pageModelRepo.save(pageModel); - componentModelRepo.save(componentModel); + containerModelRepo.save(container); } - /** - * Removes a {@link ComponentModel} from a {@link PageModel}. - * - * @param pageModel The {@link PageModel} from which the - * {@link ComponentModel} is removed. - * @param componentModel The {@link ComponentModel} to remove. The component - * model is also removed from the database. - */ - public void removeComponentModel(final PageModel pageModel, - final ComponentModel componentModel) { + @Transactional(Transactional.TxType.REQUIRED) + public void removeContainerModel(final PageModel pageModel, + final ContainerModel container) { - if (pageModel == null) { - throw new IllegalArgumentException( - "Can't remove a component model from page model null."); + Objects.requireNonNull(pageModel); + Objects.requireNonNull(container); + + if (!container.getComponents().isEmpty()) { + + throw new IllegalArgumentException("Container is not empty."); } - if (componentModel == null) { - throw new IllegalArgumentException( - "Can't remove component model null from a page model."); - } - - pageModel.removeComponent(componentModel); - componentModel.setPageModel(null); + pageModel.removeContainer(container); + container.setPageModel(null); pageModelRepo.save(pageModel); - componentModelRepo.delete(componentModel); + containerModelRepo.delete(container); } } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBox.java b/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBox.java deleted file mode 100644 index 5f5e68a81..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBox.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodel.layout; - -import org.libreccm.core.CoreConstants; -import org.libreccm.pagemodel.ComponentModel; - -import java.io.Serializable; -import java.util.Objects; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToOne; -import javax.persistence.Table; - -/** - * A box in a {@link FlexLayout}. - * - * @author Jens Pelzetter - */ -@Entity -@Table(name = "FLEX_LAYOUT_BOXES", schema = CoreConstants.DB_SCHEMA) -@NamedQueries( -@NamedQuery(name = "FlexBox.findBoxesForLayout", - query = "SELECT b FROM FlexBox b WHERE b.layout = :layout")) -public class FlexBox implements Serializable { - - private static final long serialVersionUID = -6085798536072937899L; - - @Id - @Column(name = "BOX_ID") - @GeneratedValue(strategy = GenerationType.AUTO) - private long boxId; - - @ManyToOne - @JoinColumn(name = "LAYOUT_ID") - private FlexLayout layout; - - @Column(name = "BOX_ORDER") - private int order; - - @Column(name = "BOX_SIZE") - private int size; - - @OneToOne - @JoinColumn(name = "COMPONENT_ID") - private ComponentModel component; - - public long getBoxId() { - return boxId; - } - - public void setBoxId(final long boxId) { - this.boxId = boxId; - } - - public FlexLayout getLayout() { - return layout; - } - - protected void setLayout(final FlexLayout layout) { - this.layout = layout; - } - - public int getOrder() { - return order; - } - - public void setOrder(final int order) { - this.order = order; - } - - public int getSize() { - return size; - } - - public void setSize(final int size) { - this.size = size; - } - - public ComponentModel getComponent() { - return component; - } - - public void setComponent(final ComponentModel component) { - this.component = component; - } - - @Override - public int hashCode() { - int hash = 5; - hash = 89 * hash + (int) (boxId ^ (boxId >>> 32)); - hash = 89 * hash + order; - hash = 89 * hash + size; - hash = 89 * hash + Objects.hashCode(component); - return hash; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof FlexBox)) { - return false; - } - final FlexBox other = (FlexBox) obj; - if (!other.canEqual(this)) { - return false; - } - if (boxId != other.getBoxId()) { - return false; - } - if (order != other.getOrder()) { - return false; - } - if (size != other.getSize()) { - return false; - } - return Objects.equals(component, other.getComponent()); - } - - public boolean canEqual(final Object obj) { - return obj instanceof FlexBox; - } - - @Override - public final String toString() { - return toString(""); - } - - public String toString(final String data) { - return String.format("%s{ " - + "columnId = %d, " - + "order = %d" - + "size = %d, " - + "component = %s%s" - + " }", - super.toString(), - boxId, - order, - size, - Objects.toString(component), - data); - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBoxRepository.java b/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBoxRepository.java deleted file mode 100644 index f417572b4..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexBoxRepository.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodel.layout; - -import org.libreccm.core.AbstractEntityRepository; - -import java.util.List; - -import javax.enterprise.context.RequestScoped; -import javax.persistence.TypedQuery; -import javax.transaction.Transactional; - -/** - * - * @author Jens Pelzetter - */ -@RequestScoped -public class FlexBoxRepository extends AbstractEntityRepository { - - private static final long serialVersionUID = -5321887349687319620L; - - @Override - public Class getEntityClass() { - return FlexBox.class; - } - - @Override - public String getIdAttributeName() { - return "boxId"; - } - - @Override - public Long getIdOfEntity(final FlexBox entity) { - return entity.getBoxId(); - } - - @Override - public boolean isNew(final FlexBox entity) { - return entity.getBoxId() == 0; - } - - @Transactional(Transactional.TxType.REQUIRED) - public List getBoxesForLayout(final FlexLayout layout) { - - final TypedQuery query = getEntityManager() - .createNamedQuery("FlexBox.findBoxesForLayout", FlexBox.class); - query.setParameter("layout", layout); - - return query.getResultList(); - } -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayout.java b/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayout.java deleted file mode 100644 index f65e1a9ef..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayout.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodel.layout; - -import org.libreccm.core.CoreConstants; -import org.libreccm.pagemodel.ComponentModel; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; - -/** - * A layout component container which provides the information for laying - * out other components using the Flex Layout properties in CSS. - * - * The component contains a collection of {@code FlexBox} objects which contain - * another component. A theme should use the Flex Layout properties in CSS to - * create the real layout. - * - * @author Jens Pelzetter - */ -@Entity -@Table(name = "FLEX_LAYOUT_COMPONENTS", schema = CoreConstants.DB_SCHEMA) -public class FlexLayout extends ComponentModel { - - private static final long serialVersionUID = 1977244351125227610L; - - /** - * The direction in which the components are shown. This is really only a - * hint for the theme, usually for the layout on wide screens. On small - * screens is is most likely that the boxes are stacked vertically. - */ - @Column(name = "DIRECTION") - @Enumerated(EnumType.STRING) - private FlexDirection direction; - - /** - * The boxes containing the components. - */ - @OneToMany(mappedBy = "layout") - @OrderBy(value = "order") - private List boxes; - - public FlexLayout() { - boxes = new ArrayList<>(); - } - - public FlexDirection getDirection() { - return direction; - } - - public void setDirection(final FlexDirection direction) { - this.direction = direction; - } - - public List getBoxes() { - return Collections.unmodifiableList(boxes); - } - - protected void setBoxes(final List boxes) { - this.boxes = new ArrayList<>(boxes); - } - - protected void addBox(final FlexBox box) { - boxes.add(box); - } - - protected void removeBox(final FlexBox box) { - boxes.remove(box); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 67 * hash + Objects.hashCode(direction); - hash = 67 * hash + Objects.hashCode(boxes); - return hash; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof FlexLayout)) { - return false; - } - final FlexLayout other = (FlexLayout) obj; - if (!other.canEqual(this)) { - return false; - } - if (direction != other.getDirection()) { - return false; - } - return Objects.equals(boxes, other.getBoxes()); - } - - @Override - public boolean canEqual(final Object obj) { - return obj instanceof FlexLayout; - } - - @Override - public String toString(final String data) { - return super.toString(String.format(", " - + "direction = %s, " - + "layoutCells = %s", - direction, - Objects.toString(boxes))); - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutManager.java b/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutManager.java deleted file mode 100644 index e51857c9f..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutManager.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodel.layout; - -import org.libreccm.pagemodel.ComponentModelRepository; - -import java.io.Serializable; -import java.util.Objects; - -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; - -/** - * - * @author Jens Pelzetter - */ -@RequestScoped -public class FlexLayoutManager implements Serializable { - - private static final long serialVersionUID = 6697380241367469301L; - - @Inject - private ComponentModelRepository componentModelRepo; - - @Inject - private FlexBoxRepository flexBoxRepo; - - public void addBoxToLayout(final FlexBox box, final FlexLayout layout) { - - Objects.requireNonNull(box); - Objects.requireNonNull(layout); - - layout.addBox(box); - box.setLayout(layout); - - componentModelRepo.save(layout); - flexBoxRepo.save(box); - } - - public void removeBoxFromLayout(final FlexBox box, - final FlexLayout layout) { - - Objects.requireNonNull(box); - Objects.requireNonNull(layout); - - layout.removeBox(box); - box.setLayout(null); - - componentModelRepo.save(layout); - flexBoxRepo.delete(box); - } - - public void decreaseBoxOrder(final FlexLayout layout, - final FlexBox box) { - - Objects.requireNonNull(box); - Objects.requireNonNull(layout); - - final int currentPosition = layout.getBoxes().indexOf(box); - - if (currentPosition < 0) { - throw new IllegalArgumentException(String - .format("The FlexBox with ID %d is not part " - + "of the FlexLayout \"%s\".", - box.getBoxId(), - layout.getUuid())); - } - - final FlexBox prevBox; - if ((currentPosition - 1) > 0) { - prevBox = layout.getBoxes().get(currentPosition -1); - } else { - // No previous box, return silently. - return; - } - - final int prevPosition = prevBox.getOrder(); - - prevBox.setOrder(currentPosition); - box.setOrder(prevPosition); - - flexBoxRepo.save(box); - flexBoxRepo.save(prevBox); - } - - public void increaseBoxOrder(final FlexLayout layout, - final FlexBox box) { - - Objects.requireNonNull(box); - Objects.requireNonNull(layout); - - final int currentPosition = layout.getBoxes().indexOf(box); - - if (currentPosition < 0) { - throw new IllegalArgumentException(String - .format("The FlexBox with ID %d is not part " - + "of the FlexLayout \"%s\".", - box.getBoxId(), - layout.getUuid())); - } - - final FlexBox nextBox; - if ((currentPosition + 1) < layout.getBoxes().size()) { - nextBox = layout.getBoxes().get(currentPosition + 1); - } else { - // No previous box, return silently. - return; - } - - final int nextPosition = nextBox.getOrder(); - - nextBox.setOrder(currentPosition); - box.setOrder(nextPosition); - - flexBoxRepo.save(box); - flexBoxRepo.save(nextBox); - - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutRenderer.java b/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutRenderer.java deleted file mode 100644 index c7809640a..000000000 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexLayoutRenderer.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2018 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.pagemodel.layout; - -import org.libreccm.pagemodel.ComponentModel; -import org.libreccm.pagemodel.ComponentRenderer; -import org.libreccm.pagemodel.ComponentRendererManager; -import org.libreccm.pagemodel.RendersComponent; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; - -/** - * - * @author Jens Pelzetter - */ -@RequestScoped -@RendersComponent(componentModel = FlexLayout.class) -public class FlexLayoutRenderer implements ComponentRenderer { - - @Inject - private ComponentRendererManager componentRendererManager; - - @Override - public Map renderComponent( - final FlexLayout componentModel, - final Map parameters) { - - Objects.requireNonNull(componentModel); - Objects.requireNonNull(parameters); - - final Map result = new HashMap<>(); - result.put("type", FlexLayout.class.getName()); - result.put("direction", componentModel.getDirection().toString()); - - result.put("boxes", - componentModel - .getBoxes() - .stream() - .map(box -> renderBox(box, parameters)) - .collect(Collectors.toList())); - - return result; - } - - private Map renderBox( - final FlexBox box, - final Map parameters) { - - final Map result = new HashMap<>(); - - result.put("order", box.getOrder()); - result.put("size", box.getSize()); - - result.put("component", - renderComponent(box.getComponent(), - box.getComponent().getClass(), - parameters)); - - return result; - } - - private Object renderComponent( - final ComponentModel componentModel, - final Class componentModelClass, - final Map parameters) { - - final Optional> renderer = componentRendererManager - .findComponentRenderer(componentModelClass); - - if (renderer.isPresent()) { - @SuppressWarnings("unchecked") - final M model = (M) componentModel; - return renderer.get().renderComponent(model, parameters); - } else { - return null; - } - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/CssProperty.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/CssProperty.java new file mode 100644 index 000000000..8f07f4d72 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/CssProperty.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import java.io.Serializable; +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * A single CSS property like {@code font-weight: bold}. + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "STYLE_PROPERTIES") +public class CssProperty implements Serializable { + + private static final long serialVersionUID = -4697757123207731769L; + + @Id + @Column(name = "PROPERTY_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private long propertyId; + + @Column(name = "NAME", length = 256) + private String name; + + @Column(name = "PROPERTY_VALUE", length = 4096) + private String value; + + public long getPropertyId() { + return propertyId; + } + + protected void setPropertyId(long propertyId) { + this.propertyId = propertyId; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(final String value) { + this.value = value; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + (int) (propertyId ^ (propertyId >>> 32)); + hash = 17 * hash + Objects.hashCode(name); + hash = 17 * hash + Objects.hashCode(value); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof CssProperty)) { + return false; + } + final CssProperty other = (CssProperty) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + return Objects.equals(value, other.getValue()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof CssProperty; + } + + @Override + public String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "name = \"%s\", " + + "value = \"%s\"%s" + + " }", + super.toString(), + name, + value, + data); + } + + public String toCss() { + return String.format("%s: %s", + name, + value); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Dimension.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Dimension.java new file mode 100644 index 000000000..2b4a68a9e --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Dimension.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import java.io.Serializable; +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; + +/** + * + * @author Jens Pelzetter + */ +@Embeddable +public class Dimension implements Serializable { + + private static final long serialVersionUID = 44299305931240403L; + + @Column(name = "DIMENSION_VALUE") + private float value; + + @Column(name = "UNIT") + @Enumerated(EnumType.STRING) + private Unit unit; + + public float getValue() { + return value; + } + + public void setValue(final float value) { + this.value = value; + } + + public Unit getUnit() { + return unit; + } + + public void setUnit(final Unit unit) { + this.unit = unit; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + Float.floatToIntBits(value); + hash = 37 * hash + Objects.hashCode(unit); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Dimension)) { + return false; + } + final Dimension other = (Dimension) obj; + if (!other.canEqual(this)) { + return false; + } + + if (Float.floatToIntBits(value) != Float + .floatToIntBits(other.getValue())) { + return false; + } + return unit == other.getUnit(); + } + + public boolean canEqual(final Object obj) { + return obj instanceof Dimension; + } + + public String toString(final String data) { + return String.format("%s{ " + + "value = %f, " + + "unit = \"%s\"%s" + + " }", + super.toString(), + value, + Objects.toString(unit), + data); + } + + @Override + public final String toString() { + + return toString(""); + } + + public String toCss() { + return String.format("%s%s", value, unit.toString().toLowerCase()); + } +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaQuery.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaQuery.java new file mode 100644 index 000000000..a647a39a5 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaQuery.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import org.libreccm.core.CoreConstants; + +import java.io.Serializable; +import java.util.Objects; + +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "STYLE_MEDIA_QUERIES", schema = CoreConstants.DB_SCHEMA) +public class MediaQuery implements Serializable { + + private static final long serialVersionUID = 8047120379515301590L; + + @Id + @Column(name = "MEDIA_QUERY_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private long mediaQueryId; + + @Column(name = "MEDIA_TYPE") + @Enumerated(EnumType.STRING) + private MediaType mediaType; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "value", + column = @Column(name = "MIN_WIDTH_VALUE")) + , + @AttributeOverride(name = "unit", + column = @Column(name = "MIN_WIDTH_UNIT")) + }) + private Dimension minWidth; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "value", + column = @Column(name = "MAX_WIDTH_VALUE")) + , + @AttributeOverride(name = "unit", + column = @Column(name = "MAX_WIDTH_UNIT")) + }) + private Dimension maxWidth; + + public long getMediaQueryId() { + return mediaQueryId; + } + + protected void setMediaQueryId(long mediaQueryId) { + this.mediaQueryId = mediaQueryId; + } + + public MediaType getMediaType() { + return mediaType; + } + + public void setMediaType(final MediaType mediaType) { + this.mediaType = mediaType; + } + + public Dimension getMinWidth() { + return minWidth; + } + + public void setMinWidth(final Dimension minWidth) { + this.minWidth = minWidth; + } + + public Dimension getMaxWidth() { + return maxWidth; + } + + public void setMaxWidth(final Dimension maxWidth) { + this.maxWidth = maxWidth; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + (int) (mediaQueryId ^ (mediaQueryId >>> 32)); + hash = 31 * hash + Objects.hashCode(mediaType); + hash = 31 * hash + Objects.hashCode(minWidth); + hash = 31 * hash + Objects.hashCode(maxWidth); + return hash; + } + + @Override + public boolean equals(final Object obj) { + + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MediaQuery)) { + return false; + } + final MediaQuery other = (MediaQuery) obj; + if (!other.canEqual(this)) { + return false; + } + if (mediaQueryId != other.getMediaQueryId()) { + return false; + } + if (mediaType != other.getMediaType()) { + return false; + } + if (!Objects.equals(minWidth, other.getMinWidth())) { + return false; + } + return Objects.equals(maxWidth, other.getMaxWidth()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof MediaQuery; + } + + public String toString(final String data) { + + return String.format("%s{ " + + "mediaQueryId = %d, " + + "mediaType = \"%s\", " + + "minWidth = %s, " + + "maxWidth = %s%s" + + " }", + super.toString(), + mediaQueryId, + Objects.toString(mediaType), + Objects.toString(minWidth), + Objects.toString(maxWidth), + data); + } + + @Override + public final String toString() { + return toString(""); + } + + public String toCss() { + + final StringBuilder builder = new StringBuilder("@media"); + + if (mediaType != null) { + builder.append(" ").append(mediaType.toString()).append(" "); + } + + if (minWidth != null) { + if (builder.length() > "@media".length()) { + builder.append(" and "); + } + + builder.append(String.format("(min-width: %s", minWidth.toCss())); + } + + if (maxWidth != null) { + if (builder.length() > "@media".length()) { + builder.append(" and "); + } + + builder.append(String.format("(max-width: %s", maxWidth.toCss())); + } + + + return builder.toString(); + } +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaRule.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaRule.java new file mode 100644 index 000000000..c58621ce6 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaRule.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.libreccm.core.CoreConstants; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +/** + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "STYLE_MEDIA_RULES", schema = CoreConstants.DB_SCHEMA) +public class MediaRule implements Serializable { + + private static final long serialVersionUID = -5776387865481417402L; + + @Id + @Column(name = "MEDIA_RULE_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private long mediaRuleId; + + @OneToOne + @JoinColumn(name = "MEDIA_QUERY_ID") + @Cascade(CascadeType.ALL) + private MediaQuery mediaQuery; + + @OneToMany + @JoinColumn(name = "STYLE_ID") + @Cascade(CascadeType.ALL) + private List rules; + + public long getMediaRuleId() { + return mediaRuleId; + } + + protected void setMediaRuleId(final long mediaRuleId) { + this.mediaRuleId = mediaRuleId; + } + + public MediaQuery getMediaQuery() { + return mediaQuery; + } + + public void setMediaQuery(final MediaQuery mediaQuery) { + this.mediaQuery = mediaQuery; + } + + public List getRules() { + return Collections.unmodifiableList(rules); + } + + public void setRules(final List rules) { + this.rules = new ArrayList<>(rules); + } + + public void addRule(final Rule rule) { + rules.add(rule); + } + + public void removeRule(final Rule rule) { + rules.remove(rule); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + (int) (mediaRuleId ^ (mediaRuleId >>> 32)); + hash = 67 * hash + Objects.hashCode(mediaQuery); + hash = 67 * hash + Objects.hashCode(rules); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MediaRule)) { + return false; + } + final MediaRule other = (MediaRule) obj; + if (!other.canEqual(this)) { + return false; + } + if (mediaRuleId != other.getMediaRuleId()) { + return false; + } + if (!Objects.equals(mediaQuery, other.getMediaQuery())) { + return false; + } + return Objects.equals(rules, other.getRules()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof MediaRule; + } + + public String toString(final String data) { + + return String.format("%s{ " + + "mediaRuleId = %d, " + + "mediaQuery = %s, " + + "rules = %s%s" + + " }", + super.toString(), + mediaRuleId, + Objects.toString(mediaQuery), + Objects.toString(rules), + data); + } + + @Override + public final String toString() { + return toString(""); + } + + public String toCss() { + + final String rulesCss = rules + .stream() + .map(Rule::toCss) + .collect(Collectors.joining(";\n%t")); + + return String.format("%s {%n" + + "%s%n" + + "}%n", + mediaQuery.toCss(), + rulesCss); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexDirection.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaType.java similarity index 84% rename from ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexDirection.java rename to ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaType.java index fc972b347..d2fb66597 100644 --- a/ccm-core/src/main/java/org/libreccm/pagemodel/layout/FlexDirection.java +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/MediaType.java @@ -16,15 +16,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ -package org.libreccm.pagemodel.layout; +package org.libreccm.pagemodel.styles; /** - * + * Media types for CSS. Only the well supported ones are supported + * * @author Jens Pelzetter */ -public enum FlexDirection { +public enum MediaType { - HORIZONTAL, - VERTICAL + ALL, + PRINT, + SCREEN, } diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Rule.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Rule.java new file mode 100644 index 000000000..ef0d13752 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Rule.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.libreccm.core.CoreConstants; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +/** + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "STYLE_RULES", schema = CoreConstants.DB_SCHEMA) +public class Rule implements Serializable { + + private static final long serialVersionUID = -4027217790520373364L; + + @Id + @Column(name = "RULE_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private long ruleId; + + @Column(name = "SELECTOR", length = 2048) + private String selector; + + @OneToMany + @JoinColumn(name = "RULE_ID") + @Cascade({CascadeType.ALL}) + private List properties; + + public Rule() { + properties = new ArrayList<>(); + } + + public long getRuleId() { + return ruleId; + } + + protected void setRuleId(long ruleId) { + this.ruleId = ruleId; + } + + public String getSelector() { + return selector; + } + + public void setSelector(final String selector) { + this.selector = selector; + } + + public List getProperties() { + return Collections.unmodifiableList(properties); + } + + public void setProperties(final List properties) { + this.properties = new ArrayList<>(properties); + } + + public void addProperty(final CssProperty property) { + properties.add(property); + } + + public void removeProperties(final CssProperty property) { + properties.remove(property); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 29 * hash + (int) (ruleId ^ (ruleId >>> 32)); + hash = 29 * hash + Objects.hashCode(selector); + hash = 29 * hash + Objects.hashCode(properties); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Rule)) { + return false; + } + final Rule other = (Rule) obj; + if (!other.canEqual(this)) { + return false; + } + if (ruleId != other.getRuleId()) { + return false; + } + if (!Objects.equals(selector, other.getSelector())) { + return false; + } + return Objects.equals(properties, other.getProperties()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof Rule; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "ruleId = %d, " + + "selector = \"%s\", " + + "properties = %s%s" + + " }", + super.toString(), + ruleId, + selector, + Objects.toString(properties), + data); + } + + public String toCss() { + + final String propertiesCss = properties + .stream() + .map(CssProperty::toCss) + .collect(Collectors.joining(";\n\t")); + + return String.format("%s {%n" + + "%s%n" + + "}%n", + selector, + propertiesCss); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Styles.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Styles.java new file mode 100644 index 000000000..d051420d0 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Styles.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.libreccm.core.CoreConstants; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +/** + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "STYLES", schema = CoreConstants.DB_SCHEMA) +public class Styles implements Serializable { + + private static final long serialVersionUID = -6166372396205730453L; + + @Id + @Column(name = "STYLE_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private long styleId; + + @Column(name = "STYLENAME") + private String styleName; + + @OneToMany + @JoinColumn(name = "STYLE_ID") + @Cascade(CascadeType.ALL) + private List rules; + + @OneToMany + @JoinColumn(name = "STYLE_ID") + @Cascade(CascadeType.ALL) + private List mediaRules; + + public long getStyleId() { + return styleId; + } + + public void setStyleId(long styleId) { + this.styleId = styleId; + } + + public String getStyleName() { + return styleName; + } + + public void setStyleName(final String styleName) { + this.styleName = styleName; + } + + public List getRules() { + return Collections.unmodifiableList(rules); + } + + public void setRules(final List rules) { + this.rules = new ArrayList<>(rules); + } + + public void addRule(final Rule rule) { + rules.add(rule); + } + + public void removeRule(final Rule rule) { + rules.remove(rule); + } + + public List getMediaRules() { + return Collections.unmodifiableList(mediaRules); + } + + public void setMediaRules(final List mediaRules) { + this.mediaRules = new ArrayList<>(mediaRules); + } + + public void addMediaRule(final MediaRule mediaRule) { + mediaRules.add(mediaRule); + } + + public void removeMediaRule(final MediaRule mediaRule) { + mediaRules.remove(mediaRule); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + (int) (styleId ^ (styleId >>> 32)); + hash = 97 * hash + Objects.hashCode(styleName); + hash = 97 * hash + Objects.hashCode(rules); + hash = 97 * hash + Objects.hashCode(mediaRules); + return hash; + } + + @Override + public boolean equals(final Object obj) { + + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Styles)) { + return false; + } + final Styles other = (Styles) obj; + if (!other.canEqual(this)) { + return false; + } + + if (styleId != other.getStyleId()) { + return false; + } + if (!Objects.equals(styleName, other.getStyleName())) { + return false; + } + return Objects.equals(rules, other.getRules()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof Styles; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "styleId = %d, " + + "styleName = \"%s\", " + + "rules = %s, " + + "mediaRules = %s%s" + + " }", + super.toString(), + styleId, + styleName, + Objects.toString(rules), + Objects.toString(mediaRules), + data); + } + + public String toCss() { + + final String rulesCss = rules + .stream() + .map(Rule::toCss) + .collect(Collectors.joining(";\n\n")); + final String mediaRulesCss = mediaRules + .stream() + .map(MediaRule::toCss) + .collect(Collectors.joining(";\n\n")); + + final StringBuilder builder = new StringBuilder(); + + return builder + .append(rulesCss) + .append(mediaRulesCss) + .toString(); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Unit.java b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Unit.java new file mode 100644 index 000000000..4799ea87c --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/styles/Unit.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 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.pagemodel.styles; + +/** + * Common units used in CSS for length values. + * + * @author Jens Pelzetter + */ +public enum Unit { + + EM, + EX, + PERCENT, + PX, + REM, + VH, + VW, + +}