You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2019/04/26 16:09:44 UTC

[isis] 01/01: ISIS-1999: initial implementation for properties and collections on mixins

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-1999
in repository https://gitbox.apache.org/repos/asf/isis.git

commit bb04f62c70c0088ee1ff5fa325701e901908072b
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Apr 26 17:09:28 2019 +0100

    ISIS-1999: initial implementation for properties and collections on mixins
---
 .../specimpl/ObjectSpecificationAbstract.java      |   2 +-
 .../model/models/ActionPromptWithExtraContent.java |  15 ++
 .../viewer/wicket/model/models/ScalarModel.java    |  10 +-
 .../actionmenu/entityactions/LinkAndLabelUtil.java |   3 +-
 .../actionpromptsb/ActionPromptSidebar.html        |   7 +-
 .../actionpromptsb/ActionPromptSidebar.java        |  29 +++-
 .../collection/AssociatedWithActionsHelper.java    |  10 +-
 .../ui/components/collection/CollectionPanel.java  |   3 +-
 .../entity/EntityComponentFactoryAbstract.java     |   3 +-
 .../assocgroup/AssociationGroup.html}              |  40 ++---
 .../entity/assocgroup/AssociationGroup.java        | 170 +++++++++++++++++++++
 .../components/entity/fieldset/PropertyGroup.java  |   5 -
 .../linkandlabel/ActionLinkFactoryAbstract.java    |  42 +++++
 .../viewer/wicket/ui/pages/bootstrap-overrides.css |   4 +
 .../wicket/ui/panels/PromptFormAbstract.java       |  18 ++-
 15 files changed, 299 insertions(+), 62 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index b270fc8..a59c9e5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -1197,7 +1197,7 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
      * then returns an empty list.
      */
     private List<ObjectAction> createMixedInActions() {
-        if (isService() || isValue()) {
+        if (isService() || isValue() || isMixin()) {
             return Collections.emptyList();
         }
         final Set<Class<?>> mixinTypes = AppManifest.Registry.instance().getMixinTypes();
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPromptWithExtraContent.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPromptWithExtraContent.java
new file mode 100644
index 0000000..a77f0a2
--- /dev/null
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPromptWithExtraContent.java
@@ -0,0 +1,15 @@
+package org.apache.isis.viewer.wicket.model.models;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+
+/**
+ * For implementations of {@link ActionPrompt} (eg sidebars) that are also able to display extra content, eg associations of a mixin.
+ */
+public interface ActionPromptWithExtraContent extends ActionPrompt {
+
+    String getExtraContentId();
+
+    void setExtraContentPanel(final Component extraContentComponent, final AjaxRequestTarget target);
+
+}
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
index a59f89e..2d8149e 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
@@ -110,7 +110,7 @@ public class ScalarModel extends EntityModel implements LinksProvider,FormExecut
 
             @Override
             public boolean whetherHidden(final ScalarModel scalarModel, final Where where) {
-                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load();
+                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().getObject();
                 final OneToOneAssociation property = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader());
                 try {
                     final Consent visibility = property.isVisible(parentAdapter, InteractionInitiatedBy.USER, where);
@@ -122,7 +122,7 @@ public class ScalarModel extends EntityModel implements LinksProvider,FormExecut
 
             @Override
             public String whetherDisabled(final ScalarModel scalarModel, final Where where) {
-                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load();
+                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().getObject();
                 final OneToOneAssociation property = scalarModel.getPropertyMemento().getProperty(scalarModel.getSpecificationLoader());
                 try {
                     final Consent usable = property.isUsable(parentAdapter, InteractionInitiatedBy.USER, where);
@@ -308,7 +308,7 @@ public class ScalarModel extends EntityModel implements LinksProvider,FormExecut
             public void reset(ScalarModel scalarModel) {
                 final OneToOneAssociation property = scalarModel.propertyMemento.getProperty(scalarModel.getSpecificationLoader());
 
-                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().load();
+                final ObjectAdapter parentAdapter = scalarModel.getParentEntityModel().getObject();
 
                 setObjectFromPropertyIfVisible(scalarModel, property, parentAdapter);
             }
@@ -742,10 +742,8 @@ public class ScalarModel extends EntityModel implements LinksProvider,FormExecut
     }
 
     private void getAndStore(final EntityModel parentEntityModel) {
-        final ObjectAdapterMemento parentAdapterMemento = parentEntityModel.getObjectAdapterMemento();
         final OneToOneAssociation property = propertyMemento.getProperty(getSpecificationLoader());
-        final ObjectAdapter parentAdapter = parentAdapterMemento.getObjectAdapter(ConcurrencyChecking.CHECK,
-                getPersistenceSession(), getSpecificationLoader());
+        final ObjectAdapter parentAdapter = parentEntityModel.getObject();
 
         setObjectFromPropertyIfVisible(ScalarModel.this, property, parentAdapter);
     }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
index 441449c..121a8ad 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
@@ -26,7 +26,6 @@ import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
 
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
@@ -50,7 +49,7 @@ public final class LinkAndLabelUtil {
 
         final EntityModel parentEntityModel = scalarModelForAssociation.getParentEntityModel();
 
-        final ObjectAdapter parentAdapter = parentEntityModel.load(AdapterManager.ConcurrencyChecking.NO_CHECK);
+        final ObjectAdapter parentAdapter = parentEntityModel.getObject();
 
         final OneToOneAssociation oneToOneAssociation =
                 scalarModelForAssociation.getPropertyMemento().getProperty(scalarModelForAssociation.getSpecificationLoader());
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html
index 10ec1ed..98969b3 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html
@@ -35,7 +35,12 @@
                     <h4 wicket:id="header">Action Name</h4>
                 </div>
                 <div class="sidebar-content">
-                    <div wicket:id="content"></div>
+                    <div class="action-prompt">
+                        <div wicket:id="actionPrompt">[action prompt]</div>
+                    </div>
+                    <div class="extra-content">
+                        <div wicket:id="extraContent"></div>
+                    </div>
                 </div>
             </div>
         </div>
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.java
index 7dd619c..289ad64 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.java
@@ -22,12 +22,18 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.GenericPanel;
 
-import org.apache.isis.viewer.wicket.model.models.ActionPrompt;
+import org.apache.isis.viewer.wicket.model.models.ActionPromptWithExtraContent;
 
-public class ActionPromptSidebar extends GenericPanel<Void> implements ActionPrompt /* implements ActionPrompt */ {
+public class ActionPromptSidebar
+        extends GenericPanel<Void>
+        implements ActionPromptWithExtraContent {
 
     private static final long serialVersionUID = 1L;
 
+    private static final String ID_HEADER = "header";
+    private static final String ID_ACTION_PROMPT = "actionPrompt";
+    private static final String ID_EXTRA_CONTENT = "extraContent";
+
     private CloseHandler closeHandlerIfAny;
 
     public ActionPromptSidebar(final String id) {
@@ -39,6 +45,7 @@ public class ActionPromptSidebar extends GenericPanel<Void> implements ActionPro
         add(new Label(getTitleId(), "(no action)"));
 
         add(new WebMarkupContainer(getContentId()));
+        add(new WebMarkupContainer(getExtraContentId()));
     }
 
     public static ActionPromptSidebar newSidebar(String id) {
@@ -47,14 +54,18 @@ public class ActionPromptSidebar extends GenericPanel<Void> implements ActionPro
 
     @Override
     public String getTitleId() {
-        return "header";
+        return ID_HEADER;
     }
 
     @Override
     public String getContentId() {
-        return "content";
+        return ID_ACTION_PROMPT;
     }
 
+    @Override
+    public String getExtraContentId() {
+        return ID_EXTRA_CONTENT;
+    }
 
     @Override
     public void setTitle(final Component titleComponent, final AjaxRequestTarget target) {
@@ -69,6 +80,12 @@ public class ActionPromptSidebar extends GenericPanel<Void> implements ActionPro
     }
 
     @Override
+    public void setExtraContentPanel(final Component extraContentComponent, final AjaxRequestTarget target) {
+        extraContentComponent.setMarkupId(getExtraContentId());
+        addOrReplace(extraContentComponent);
+    }
+
+    @Override
     public void showPrompt(final AjaxRequestTarget target) {
         setVisible(true);
         show(target);
@@ -79,6 +96,10 @@ public class ActionPromptSidebar extends GenericPanel<Void> implements ActionPro
     public void closePrompt(final AjaxRequestTarget target) {
 
         setVisible(false);
+
+        addOrReplace(new WebMarkupContainer(getContentId()));
+        addOrReplace(new WebMarkupContainer(getExtraContentId()));
+
         if (target != null) {
             hide(target);
         }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
index 7eac1a2..3767d66 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
@@ -26,8 +26,6 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 
 import org.apache.isis.applib.filter.Filters;
-import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -35,7 +33,6 @@ import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
-import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsHelper;
 
@@ -70,12 +67,7 @@ public class AssociatedWithActionsHelper implements Serializable {
     }
 
     private ObjectSpecification getObjectSpecification(final IsisSessionFactory isisSessionFactory) {
-        final ObjectAdapterMemento parentOam = collectionModel.getParentObjectAdapterMemento();
-        final ObjectAdapter parentAdapter = parentOam.getObjectAdapter(
-                                                AdapterManager.ConcurrencyChecking.NO_CHECK,
-                                                isisSessionFactory.getCurrentSession().getPersistenceSession(),
-                                                isisSessionFactory.getSpecificationLoader());
-        return parentAdapter.getSpecification();
+        return collectionModel.getEntityModel().getTypeOfSpecification();
     }
 
     private static List<ActionType> inferActionTypes(final IsisSessionFactory isisSessionFactory) {
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
index e5724e4..a6d9425 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
@@ -30,7 +30,6 @@ import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
 import org.apache.wicket.markup.html.basic.Label;
 
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.viewer.wicket.model.common.OnSelectionHandler;
@@ -76,7 +75,7 @@ public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implem
 
         final OneToManyAssociation otma = collectionModel.getCollectionMemento().getCollection(collectionModel.getSpecificationLoader());
         final EntityModel entityModel = collectionModel.getEntityModel();
-        final ObjectAdapter adapter = entityModel.load(AdapterManager.ConcurrencyChecking.NO_CHECK);
+        final ObjectAdapter adapter = entityModel.getObject();
 
         final List<ObjectAction> associatedActions =
                 ObjectAction.Util.findForAssociation(adapter, otma, getDeploymentCategory());
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/EntityComponentFactoryAbstract.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/EntityComponentFactoryAbstract.java
index 53ed51d..09c52e9 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/EntityComponentFactoryAbstract.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/EntityComponentFactoryAbstract.java
@@ -23,7 +23,6 @@ import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
@@ -58,7 +57,7 @@ public abstract class EntityComponentFactoryAbstract extends ComponentFactoryAbs
         // is therefore not called. resulting in a concurrency exception.
         //
         // Therefore, we do the same processing here instead.
-        final ObjectAdapter adapter = entityModel.load(ConcurrencyChecking.NO_CHECK);
+        final ObjectAdapter adapter = entityModel.getObject();
         if (adapter == null) {
             // is ok;
         }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.html
similarity index 59%
copy from core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html
copy to core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.html
index 10ec1ed..bed5dd2 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionpromptsb/ActionPromptSidebar.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.html
@@ -7,9 +7,9 @@
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at
-
+  
          http://www.apache.org/licenses/LICENSE-2.0
-
+         
   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -17,30 +17,18 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:wicket="http://wicket.apache.org"
-      xml:lang="en"
-      lang="en">
-<body>
-<wicket:panel>
-<!--
-    <li class="sidebar-brand">
-    </li>
--->
-    <div class="sidebar-panel">
-        <div class="row">
-            <div class="col col-md-12">
-                <div class="sidebar-header">
-                    <h4 wicket:id="header">Action Name</h4>
-                </div>
-                <div class="sidebar-content">
-                    <div wicket:id="content"></div>
+<html xmlns:wicket="http://wicket.apache.org">
+    <body>
+        <wicket:panel>
+            <div class="associationGroup" wicket:id="associationGroup">
+                <div class="panel panel-default">
+                    <div class="associations panel-body">
+                        <div wicket:id="associations">
+                            <div wicket:id="association" class="association">[association]</div>
+                        </div>
+                    </div>
                 </div>
             </div>
-        </div>
-    </div>
-
-</wicket:panel>
-</body>
+        </wicket:panel>
+    </body>
 </html>
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.java
new file mode 100644
index 0000000..aa0e306
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/assocgroup/AssociationGroup.java
@@ -0,0 +1,170 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.wicket.ui.components.entity.assocgroup;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.repeater.RepeatingView;
+
+import org.apache.isis.applib.layout.component.CollectionLayoutData;
+import org.apache.isis.applib.layout.grid.Grid;
+import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.wicket.model.mementos.PropertyMemento;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2;
+import org.apache.isis.viewer.wicket.ui.panels.HasDynamicallyVisibleContent;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.viewer.wicket.ui.util.Components;
+
+public class AssociationGroup extends PanelAbstract<EntityModel> implements HasDynamicallyVisibleContent {
+
+    private static final String ID_ASSOCIATION_GROUP = "associationGroup";
+
+    private static final String ID_ASSOCIATIONS = "associations";
+    private static final String ID_ASSOCIATION = "association";
+
+    private final Grid grid;
+
+    private final List<ScalarPanelAbstract2> childScalarPanelAbstract2s;
+    private final List<Component> childComponents;
+
+    public AssociationGroup(final String id, final EntityModel model, final Grid grid) {
+        super(id, model);
+        this.grid = grid;
+
+        // the UI is only ever built once.
+        childComponents = buildGui();
+        childScalarPanelAbstract2s = FluentIterable.from(childComponents).filter(ScalarPanelAbstract2.class).toList();
+    }
+
+    public EntityModel getModel() {
+        return (EntityModel) getDefaultModel();
+    }
+
+
+    private List<Component> buildGui() {
+
+        final List<Component> childComponents = Lists.newArrayList();
+
+        setOutputMarkupPlaceholderTag(true);
+        setOutputMarkupId(true);
+
+        final WebMarkupContainer div = new WebMarkupContainer(ID_ASSOCIATION_GROUP);
+
+        final List<ObjectAssociation> associations = getObjectAssociations();
+
+        final RepeatingView propertyRv = new RepeatingView(ID_ASSOCIATIONS);
+        div.addOrReplace(propertyRv);
+
+        final EntityModel entityModel = getModel();
+
+        final GridFacet gridFacet = entityModel.getTypeOfSpecification().getFacet(GridFacet.class);
+        final Grid grid = gridFacet.getGrid(entityModel.getObject());
+        final Map<String, CollectionLayoutData> collectionLayoutDataById = grid.getAllCollectionsById();
+
+        for (final ObjectAssociation association : associations) {
+
+            final WebMarkupContainer associationRvContainer = new WebMarkupContainer(propertyRv.newChildId());
+            propertyRv.addOrReplace(associationRvContainer);
+
+            if(association.isOneToOneAssociation()) {
+                final OneToOneAssociation otoa = (OneToOneAssociation) association;
+
+                final PropertyMemento pm = new PropertyMemento(otoa, entityModel.getIsisSessionFactory());
+
+                final ScalarModel scalarModel =
+                        entityModel.getPropertyModel(pm, EntityModel.Mode.VIEW, EntityModel.RenderingHint.REGULAR);
+
+                final Component component = getComponentFactoryRegistry()
+                        .addOrReplaceComponent(associationRvContainer, ID_ASSOCIATION, ComponentType.SCALAR_NAME_AND_VALUE, scalarModel);
+
+                childComponents.add(component);
+            } else {
+                final String associationId = association.getId();
+
+                final CollectionLayoutData collectionLayoutData = collectionLayoutDataById.get(associationId);
+
+                // successively trample all over; not a problem
+                entityModel.setCollectionLayoutData(collectionLayoutData);
+
+                final Component component = getComponentFactoryRegistry()
+                        .addOrReplaceComponent(associationRvContainer, ID_ASSOCIATION,
+                                ComponentType.ENTITY_COLLECTION, entityModel);
+
+                childComponents.add(component);
+            }
+        }
+
+
+        // either add the built content, or hide entire
+        if(associations.isEmpty()) {
+            Components.permanentlyHide(this, div.getId());
+        } else {
+            this.addOrReplace(div);
+        }
+
+        return childComponents;
+    }
+
+    private List<ObjectAssociation> getObjectAssociations() {
+        return this.getModel().getObject().getSpecification().getAssociations(Contributed.INCLUDED);
+    }
+
+    @Override
+    public void onConfigure() {
+        for (final ScalarPanelAbstract2 childComponent : childScalarPanelAbstract2s) {
+            childComponent.configure();
+        }
+        super.onConfigure();
+    }
+
+    @Override
+    public boolean isVisible() {
+
+        // HACK: there are some components that are not ScalarPanelAbstract2's, eg the pdfjsviewer.
+        // In this case, don't ever hide.
+
+        // TODO: should remove this hack.  We need some sort of SPI for ScalarPanelAbstract2's and any other component,
+        // (eg PdfJsViewer) that can implement.  It's "probably" just a matter of having PdfJsViewer do its work in the
+        // correct Wicket callback (probably onConfigure).
+        if(childComponents.size() > childScalarPanelAbstract2s.size()) {
+            return true;
+        }
+        // HACK:END
+
+        for (final ScalarPanelAbstract2 childComponent : childScalarPanelAbstract2s) {
+            if(childComponent.isVisibilityAllowed()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
index 2e7373d..4faf74a 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
@@ -70,7 +70,6 @@ public class PropertyGroup extends PanelAbstract<EntityModel> implements HasDyna
     private static final String ID_PROPERTY = "property";
 
     private final FieldSet fieldSet;
-    private final boolean visible;
     private final List<ScalarPanelAbstract2> childScalarPanelAbstract2s;
     private final List<Component> childComponents;
 
@@ -81,9 +80,6 @@ public class PropertyGroup extends PanelAbstract<EntityModel> implements HasDyna
         // the UI is only ever built once.
         childComponents = buildGui();
         childScalarPanelAbstract2s = FluentIterable.from(childComponents).filter(ScalarPanelAbstract2.class).toList();
-
-        final ImmutableList<ObjectAssociation> associations = getObjectAssociations();
-        this.visible = !associations.isEmpty();
     }
 
     public EntityModel getModel() {
@@ -136,7 +132,6 @@ public class PropertyGroup extends PanelAbstract<EntityModel> implements HasDyna
                     panelHeading, ID_ASSOCIATED_ACTION_LINKS_PANEL_DROPDOWN,
                     actionsPanelDropDown,
                     AdditionalLinksPanel.Style.DROPDOWN);
-
         }
 
         // either add the built content, or hide entire
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
index 4cd00b6..40ffaea 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
@@ -37,13 +37,17 @@ import org.apache.wicket.model.AbstractReadOnlyModel;
 import org.apache.wicket.request.cycle.RequestCycle;
 
 import org.apache.isis.applib.annotation.PromptStyle;
+import org.apache.isis.applib.layout.grid.Grid;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Grid;
 import org.apache.isis.applib.services.metamodel.MetaModelService2;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.postprocessors.param.ActionParameterDefaultsFacetFromAssociatedCollection;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
@@ -54,6 +58,7 @@ import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.model.models.ActionPrompt;
 import org.apache.isis.viewer.wicket.model.models.ActionPromptProvider;
+import org.apache.isis.viewer.wicket.model.models.ActionPromptWithExtraContent;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.models.FormExecutor;
 import org.apache.isis.viewer.wicket.model.models.InlinePromptContext;
@@ -64,6 +69,8 @@ import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistryAccessor;
 import org.apache.isis.viewer.wicket.ui.components.actions.ActionFormExecutorStrategy;
 import org.apache.isis.viewer.wicket.ui.components.actions.ActionParametersPanel;
+import org.apache.isis.viewer.wicket.ui.components.entity.assocgroup.AssociationGroup;
+import org.apache.isis.viewer.wicket.ui.components.layout.bs3.BS3GridPanel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistryAccessor;
@@ -203,6 +210,41 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
                 actionParametersPanel.setActionPrompt(prompt);
                 prompt.showPrompt(target);
 
+                if(prompt instanceof ActionPromptWithExtraContent) {
+                    final ActionPromptWithExtraContent promptWithExtraContent =
+                            (ActionPromptWithExtraContent) prompt;
+
+                    final ObjectAction action = actionModel.getActionMemento().getAction(getSpecificationLoader());
+                    if(action instanceof ObjectActionMixedIn) {
+                        final ObjectActionMixedIn actionMixedIn = (ObjectActionMixedIn) action;
+                        final ObjectSpecification mixinType = actionMixedIn.getMixinType();
+
+                        if(mixinType.isViewModel()) {
+
+                            final ObjectAdapter targetAdapterForMixin = action.realTargetAdapter(actionModel.getTargetAdapter());
+                            final EntityModel entityModelForMixin = new EntityModel(targetAdapterForMixin);
+
+                            final GridFacet facet = mixinType.getFacet(GridFacet.class);
+                            final Grid gridForMixin = facet.getGrid(targetAdapterForMixin);
+
+                            final String extraContentId = promptWithExtraContent.getExtraContentId();
+
+                            if(gridForMixin instanceof BS3Grid) {
+                                final BS3Grid bs3Grid = (BS3Grid) gridForMixin;
+                                final BS3GridPanel gridPanel = new BS3GridPanel(extraContentId, entityModelForMixin, bs3Grid);
+                                promptWithExtraContent.setExtraContentPanel(gridPanel, target);
+                            } else {
+
+                                final AssociationGroup associationGroup = new AssociationGroup(
+                                        extraContentId, entityModelForMixin, gridForMixin);
+                                promptWithExtraContent.setExtraContentPanel(associationGroup, target);
+                            }
+                        }
+
+
+                    }
+                }
+
                 return prompt;
 
             } else {
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/bootstrap-overrides.css b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/bootstrap-overrides.css
index 2f4828f..1def935 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/bootstrap-overrides.css
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/bootstrap-overrides.css
@@ -1056,3 +1056,7 @@ ul.sidebar-nav {
     padding-bottom: 62px;
     margin-bottom: 62px;
 }
+
+div.sidebar-panel div.extra-content {
+    margin-top: 30px;
+}
\ No newline at end of file
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
index fcc531f..b89b978 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
@@ -188,9 +188,14 @@ public abstract class PromptFormAbstract<T extends BookmarkableModel<ObjectAdapt
 
     protected void closePromptIfAny(final AjaxRequestTarget target) {
 
-        final ActionPromptProvider promptProvider = ActionPromptProvider.Util.getFrom(parentPanel);
-        if(promptProvider != null) {
-            promptProvider.closePrompt(target);
+        try {
+            final ActionPromptProvider promptProvider = ActionPromptProvider.Util.getFrom(parentPanel);
+            if(promptProvider != null) {
+                promptProvider.closePrompt(target);
+            }
+        } catch (org.apache.wicket.WicketRuntimeException ex) {
+            // if "No Page found for component"
+            // do nothing
         }
     }
 
@@ -207,7 +212,12 @@ public abstract class PromptFormAbstract<T extends BookmarkableModel<ObjectAdapt
     }
 
     private UiHintContainer getPageUiHintContainerIfAny() {
-        Page page = getPage();
+        final Page page;
+        try {
+            page = getPage();
+        } catch(org.apache.wicket.WicketRuntimeException ex) {
+            return null;
+        }
         if (page instanceof EntityPage) {
             EntityPage entityPage = (EntityPage) page;
             return entityPage.getUiHintContainerIfAny();