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 2016/02/15 14:22:54 UTC

[1/2] isis git commit: ISIS-993: improving derivation of properties when layout xml exists with field sets and actions bound to those fieldsets only through @MemberOrder#name annotation.

Repository: isis
Updated Branches:
  refs/heads/ISIS-993 697bcdbc5 -> f65487e46


ISIS-993: improving derivation of properties when layout xml exists with field sets and actions bound to those fieldsets only through @MemberOrder#name annotation.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/30bfe8c6
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/30bfe8c6
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/30bfe8c6

Branch: refs/heads/ISIS-993
Commit: 30bfe8c62bbf2fa3894ccc52afb7719cf66f4bca
Parents: 697bcdb
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Feb 15 09:58:02 2016 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Feb 15 09:58:02 2016 +0000

----------------------------------------------------------------------
 .../bootstrap3/GridNormalizerServiceBS3.java    | 98 +++++++++++++-------
 .../dom/simple/SimpleObject.layout.xml          |  2 +-
 2 files changed, 68 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/30bfe8c6/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridNormalizerServiceBS3.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridNormalizerServiceBS3.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridNormalizerServiceBS3.java
index d5aed0c..283bb49 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridNormalizerServiceBS3.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridNormalizerServiceBS3.java
@@ -20,17 +20,20 @@ import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.Nullable;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.base.Strings;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
@@ -239,30 +242,67 @@ public class GridNormalizerServiceBS3 extends GridNormalizerServiceAbstract<BS3G
             propertyIds.get(surplusPropertyId).setMetadataError("No such property");
         }
 
-        final Map<String, List<OneToOneAssociation>> boundAssociationsByFieldSetId = Maps.newHashMap();
-        if(!missingPropertyIds.isEmpty()) {
-            final List<String> unboundPropertyIds = Lists.newArrayList(missingPropertyIds);
-            for (String missingPropertyId : missingPropertyIds) {
-                final OneToOneAssociation otoa = oneToOneAssociationById.get(missingPropertyId);
-                final MemberOrderFacet memberOrderFacet = otoa.getFacet(MemberOrderFacet.class);
-                if(memberOrderFacet != null) {
-                    final String id = asId(memberOrderFacet.name());
-                    if(fieldSetIds.containsKey(id)) {
-                        List<OneToOneAssociation> boundAssociations = boundAssociationsByFieldSetId.get(id);
-                        if(boundAssociations == null) {
-                            boundAssociations = Lists.newArrayList();
-                            boundAssociationsByFieldSetId.put(id, boundAssociations);
-                        }
-                        boundAssociations.add(otoa);
-                        unboundPropertyIds.remove(missingPropertyId);
+        // catalog which associations are bound to an existing field set
+        // so that (below) we can determine which missing property ids are not unbound vs which should be included
+        // in the fieldset that they are bound to.
+        final Map<String, Set<String>> boundAssociationIdsByFieldSetId = Maps.newHashMap();
+
+        // all those explicitly in the grid
+        for (FieldSet fieldSet : fieldSetIds.values()) {
+            final String fieldSetId = fieldSet.getId();
+            Set<String> boundAssociationIds = boundAssociationIdsByFieldSetId.get(fieldSetId);
+            if(boundAssociationIds == null) {
+                boundAssociationIds = Sets.newLinkedHashSet();
+                boundAssociationIds.addAll(
+                        FluentIterable.from(fieldSet.getProperties()).transform(
+                                new Function<PropertyLayoutData, String>() {
+                                    @Override
+                                    public String apply(@Nullable final PropertyLayoutData propertyLayoutData) {
+                                        return propertyLayoutData.getId();
+                                    }
+                                }).toList());
+                boundAssociationIdsByFieldSetId.put(fieldSetId, boundAssociationIds);
+            }
+        }
+        // along with any specified by existing metadata
+        for (OneToOneAssociation otoa : oneToOneAssociationById.values()) {
+            final MemberOrderFacet memberOrderFacet = otoa.getFacet(MemberOrderFacet.class);
+            if(memberOrderFacet != null) {
+                final String id = asId(memberOrderFacet.name());
+                if(fieldSetIds.containsKey(id)) {
+                    Set<String> boundAssociationIds = boundAssociationIdsByFieldSetId.get(id);
+                    if(boundAssociationIds == null) {
+                        boundAssociationIds = Sets.newLinkedHashSet();
+                        boundAssociationIdsByFieldSetId.put(id, boundAssociationIds);
                     }
+                    boundAssociationIds.add(otoa.getId());
                 }
             }
+        }
+
+        if(!missingPropertyIds.isEmpty()) {
+
+            final List<String> unboundPropertyIds = Lists.newArrayList(missingPropertyIds);
+
+            for (final String fieldSetId : boundAssociationIdsByFieldSetId.keySet()) {
+                final Set<String> boundPropertyIds = boundAssociationIdsByFieldSetId.get(fieldSetId);
+                unboundPropertyIds.removeAll(boundPropertyIds);
+            }
 
-            for (String fieldSetId : boundAssociationsByFieldSetId.keySet()) {
+            for (final String fieldSetId : boundAssociationIdsByFieldSetId.keySet()) {
                 final FieldSet fieldSet = fieldSetIds.get(fieldSetId);
-                final List<OneToOneAssociation> associations =
-                        boundAssociationsByFieldSetId.get(fieldSetId);
+                final Set<String> associationIds =
+                        boundAssociationIdsByFieldSetId.get(fieldSetId);
+
+                final List<OneToOneAssociation> associations = Lists.newArrayList(
+                        FluentIterable.from(associationIds)
+                        .transform(new Function<String, OneToOneAssociation>() {
+                            @Nullable @Override public OneToOneAssociation apply(final String propertyId) {
+                                return oneToOneAssociationById.get(propertyId);
+                            }
+                        })
+                        .filter(Predicates.<OneToOneAssociation>notNull())
+                );
 
                 associations.sort(byMemberOrderSequence());
                 addPropertiesTo(fieldSet,
@@ -341,18 +381,14 @@ public class GridNormalizerServiceBS3 extends GridNormalizerServiceAbstract<BS3G
                                 if (oneToManyAssociationById.containsKey(id)) {
                                     return false;
                                 }
-                                // if the @MemberOrder references a field set, then don't mark it as missing, but
-                                // instead explicitly add it to the list of actions of that fieldset.
-                                // (note we only do this provided we already know that
-                                // there is at least one property for said fieldset)
-                                if(boundAssociationsByFieldSetId.containsKey(id)) {
-                                    final List<OneToOneAssociation> oneToOneAssociations =
-                                            boundAssociationsByFieldSetId.get(id);
-                                    if(!oneToOneAssociations.isEmpty()) {
-                                        final ActionLayoutData actionLayoutData = new ActionLayoutData(actionId);
-                                        actionLayoutData.setPosition(ActionLayout.Position.PANEL_DROPDOWN);
-                                        fieldSetIds.get(id).getActions().add(actionLayoutData);
-                                    }
+                                // if the @MemberOrder for the action references a field set (that has bound
+                                // associations), then don't mark it as missing, butinstead explicitly add it to the
+                                // list of actions of that fieldset.
+                                final Set<String> boundAssociationIds = boundAssociationIdsByFieldSetId.get(id);
+                                if(boundAssociationIds != null && !boundAssociationIds.isEmpty()) {
+                                    final ActionLayoutData actionLayoutData = new ActionLayoutData(actionId);
+                                    actionLayoutData.setPosition(ActionLayout.Position.PANEL_DROPDOWN);
+                                    fieldSetIds.get(id).getActions().add(actionLayoutData);
                                     return false;
                                 }
                                 // is missing after all.

http://git-wip-us.apache.org/repos/asf/isis/blob/30bfe8c6/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.layout.xml
----------------------------------------------------------------------
diff --git a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.layout.xml b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.layout.xml
index 5072933..9012b51 100644
--- a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.layout.xml
+++ b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.layout.xml
@@ -21,7 +21,7 @@
                 <bs3:tab name="Misc">
                     <bs3:row>
                         <bs3:col span="12">
-                            <c:fieldSet name="Misc">
+                            <c:fieldSet name="Metadata">
                                 <c:property id="versionSequence"/>
                             </c:fieldSet>
                         </bs3:col>


[2/2] isis git commit: ISIS-993: dynamically evaluate visibility of tabpanel, tab, row, col, fieldset and collection (suppress parent containers if there would be nothing to show).

Posted by da...@apache.org.
ISIS-993: dynamically evaluate visibility of tabpanel, tab, row, col, fieldset and collection (suppress parent containers if there would be nothing to show).


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/f65487e4
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/f65487e4
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/f65487e4

Branch: refs/heads/ISIS-993
Commit: f65487e46008680f6368afc186e5f43513dfc50b
Parents: 30bfe8c
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Feb 15 13:22:02 2016 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Feb 15 13:22:02 2016 +0000

----------------------------------------------------------------------
 .../ActionResultResponseType.java               |  20 +++-
 .../collection/EntityCollectionPanel.java       | 109 +++++++++++--------
 .../entity/fieldset/PropertyGroup.html          |   2 +-
 .../entity/fieldset/PropertyGroup.java          |  73 ++++++++++---
 .../ui/components/layout/bs3/col/Col.java       |  92 +++++++++++-----
 ...eatingViewWithDynamicallyVisibleContent.java |  54 +++++++++
 .../ui/components/layout/bs3/row/Row.java       |  32 ++++--
 .../layout/bs3/tabs/TabGroupPanel.java          |  23 +++-
 .../ui/components/layout/bs3/tabs/TabPanel.java |  49 +++++++--
 .../ui/panels/HasDynamicallyVisibleContent.java |  26 +++++
 .../java/domainapp/dom/simple/SimpleObject.java |   7 ++
 11 files changed, 375 insertions(+), 112 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
index 606d969..6b98431 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
@@ -19,12 +19,15 @@ package org.apache.isis.viewer.wicket.ui.actionresponse;
 import java.net.URL;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.Callable;
+
 import com.google.common.collect.Lists;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -136,10 +139,23 @@ public enum ActionResultResponseType {
         return IsisContext.getPersistenceSession().adapterFor(pojo);
     }
 
-    private static ActionResultResponse toEntityPage(final ActionModel model, final ObjectAdapter actualAdapter, ConcurrencyException exIfAny) {
+    private static ActionResultResponse toEntityPage(final ActionModel model, final ObjectAdapter actualAdapter, final ConcurrencyException exIfAny) {
         // this will not preserve the URL (because pageParameters are not copied over)
         // but trying to preserve them seems to cause the 302 redirect to be swallowed somehow
-        final EntityPage entityPage = new EntityPage(actualAdapter, exIfAny);
+        final EntityPage entityPage =
+
+                // disabling concurrency checking after the layout XML (grid) feature
+                // was throwing an exception when rebuild grid after invoking action
+                // not certain why that would be the case, but think it should be
+                // safe to simply disable while recreating the page to re-render back to user.
+                AdapterManager.ConcurrencyChecking.executeWithConcurrencyCheckingDisabled(
+                    new Callable<EntityPage>() {
+                        @Override public EntityPage call() throws Exception {
+                            return new EntityPage(actualAdapter, exIfAny);
+                        }
+                    }
+            );
+
         return ActionResultResponse.toPage(entityPage);
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
index f5a6c16..0fa887d 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
@@ -26,7 +26,10 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.Model;
 
+import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
@@ -39,14 +42,16 @@ import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.Addi
 import org.apache.isis.viewer.wicket.ui.components.collection.CollectionPanel;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorHelper;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel;
+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;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 
 /**
  * {@link PanelAbstract Panel} representing the properties of an entity, as per
  * the provided {@link EntityModel}.
  */
-public class EntityCollectionPanel extends PanelAbstract<EntityModel> {
+public class EntityCollectionPanel extends PanelAbstract<EntityModel> implements HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
 
@@ -59,81 +64,99 @@ public class EntityCollectionPanel extends PanelAbstract<EntityModel> {
 
     private final ScopedSessionAttribute<Integer> selectedItemSessionAttribute;
 
+    final WebMarkupContainer div;
+
     public EntityCollectionPanel(final String id, final EntityModel entityModel) {
         super(id, entityModel);
 
         selectedItemSessionAttribute = ScopedSessionAttribute.create(
                 entityModel, this,  EntityCollectionModel.SESSION_ATTRIBUTE_SELECTED_ITEM);
 
+        div = buildGui();
     }
 
     /**
-     * Build UI only after added to parent.
+     * Attach UI only after added to parent.
      */
     public void onInitialize() {
         super.onInitialize();
-        buildGui();
-    }
 
-    private void buildGui() {
+        final WebMarkupContainer panel = this;
+        if(contentDynamicallyVisible) {
+            panel.add(div);
+        } else {
+            Components.permanentlyHide(panel, div.getId());
+        }
+
+    }
 
-        final WebMarkupContainer collectionRvContainer = this;
+    private WebMarkupContainer buildGui() {
+        final WebMarkupContainer div = new WebMarkupContainer(ID_COLLECTION_GROUP);
 
         final EntityCollectionModel entityCollectionModel = EntityCollectionModel.createParented(getModel());
         final OneToManyAssociation association = entityCollectionModel.getCollectionMemento().getCollection();
+        final ObjectAdapter objectAdapter = getModel().getObject();
+        final Consent visibility = association.isVisible(objectAdapter, InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
 
-        final CssClassFacet facet = association.getFacet(CssClassFacet.class);
-        if(facet != null) {
-            final ObjectAdapter objectAdapter = getModel().getObject();
-            final String cssClass = facet.cssClass(objectAdapter);
-            CssClassAppender.appendCssClassTo(collectionRvContainer, cssClass);
-        }
+        if(visibility.isAllowed()) {
 
-        final WebMarkupContainer fieldSet = new WebMarkupContainer(ID_COLLECTION_GROUP);
-        collectionRvContainer.add(fieldSet);
+            contentDynamicallyVisible = true;
 
-        final CollectionPanel collectionPanel = new CollectionPanel(ID_COLLECTION, entityCollectionModel);
+            final CssClassFacet facet = association.getFacet(CssClassFacet.class);
+            if(facet != null) {
+                final String cssClass = facet.cssClass(objectAdapter);
+                CssClassAppender.appendCssClassTo(div, cssClass);
+            }
 
-        Label labelComponent = collectionPanel.createLabel(ID_COLLECTION_NAME, association.getName());
-        final NamedFacet namedFacet = association.getFacet(NamedFacet.class);
-        labelComponent.setEscapeModelStrings(namedFacet == null || namedFacet.escaped());
-        fieldSet.add(labelComponent);
+            final CollectionPanel collectionPanel = new CollectionPanel(ID_COLLECTION, entityCollectionModel);
 
-        final String description = association.getDescription();
-        if(description != null) {
-            labelComponent.add(new AttributeAppender("title", Model.of(description)));
-        }
+            Label labelComponent = collectionPanel.createLabel(ID_COLLECTION_NAME, association.getName());
+            final NamedFacet namedFacet = association.getFacet(NamedFacet.class);
+            labelComponent.setEscapeModelStrings(namedFacet == null || namedFacet.escaped());
+            div.add(labelComponent);
 
-        final List<LinkAndLabel> links = entityCollectionModel.getLinks();
-        AdditionalLinksPanel.addAdditionalLinks (fieldSet,ID_ADDITIONAL_LINKS, links, AdditionalLinksPanel.Style.INLINE_LIST);
+            final String description = association.getDescription();
+            if(description != null) {
+                labelComponent.add(new AttributeAppender("title", Model.of(description)));
+            }
 
-        final CollectionSelectorHelper selectorHelper =
-                new CollectionSelectorHelper(entityCollectionModel, getComponentFactoryRegistry(),
-                        selectedItemSessionAttribute);
+            final List<LinkAndLabel> links = entityCollectionModel.getLinks();
+            AdditionalLinksPanel.addAdditionalLinks (div,ID_ADDITIONAL_LINKS, links, AdditionalLinksPanel.Style.INLINE_LIST);
 
-        final List<ComponentFactory> componentFactories = selectorHelper.getComponentFactories();
+            final CollectionSelectorHelper selectorHelper =
+                    new CollectionSelectorHelper(entityCollectionModel, getComponentFactoryRegistry(),
+                            selectedItemSessionAttribute);
 
-        if (componentFactories.size() <= 1) {
-            permanentlyHide(ID_SELECTOR_DROPDOWN);
-        } else {
-            CollectionSelectorPanel selectorDropdownPanel =
-                    new CollectionSelectorPanel(ID_SELECTOR_DROPDOWN,
-                    entityCollectionModel, selectedItemSessionAttribute);
+            final List<ComponentFactory> componentFactories = selectorHelper.getComponentFactories();
 
-            final Model<ComponentFactory> componentFactoryModel = new Model<>();
+            if (componentFactories.size() <= 1) {
+                permanentlyHide(ID_SELECTOR_DROPDOWN);
+            } else {
+                CollectionSelectorPanel selectorDropdownPanel =
+                        new CollectionSelectorPanel(ID_SELECTOR_DROPDOWN,
+                                entityCollectionModel, selectedItemSessionAttribute);
 
-            final int selected = selectorHelper.honourViewHintElseDefault(selectorDropdownPanel);
+                final Model<ComponentFactory> componentFactoryModel = new Model<>();
 
-            ComponentFactory selectedComponentFactory = componentFactories.get(selected);
-            componentFactoryModel.setObject(selectedComponentFactory);
+                final int selected = selectorHelper.honourViewHintElseDefault(selectorDropdownPanel);
 
-            this.setOutputMarkupId(true);
-            fieldSet.addOrReplace(selectorDropdownPanel);
+                ComponentFactory selectedComponentFactory = componentFactories.get(selected);
+                componentFactoryModel.setObject(selectedComponentFactory);
 
-            collectionPanel.setSelectorDropdownPanel(selectorDropdownPanel);
+                this.setOutputMarkupId(true);
+                div.addOrReplace(selectorDropdownPanel);
+
+                collectionPanel.setSelectorDropdownPanel(selectorDropdownPanel);
+            }
+            div.addOrReplace(collectionPanel);
         }
-        fieldSet.addOrReplace(collectionPanel);
+        return div;
     }
 
+    private boolean contentDynamicallyVisible = false;
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.html
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.html
index 38245a0..5b402cb 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.html
@@ -20,7 +20,7 @@
 <html xmlns:wicket="http://wicket.apache.org">
     <body>
         <wicket:panel>
-            <fieldset class="memberGroup">
+            <fieldset class="memberGroup" wicket:id="memberGroup">
                 <div class="panel panel-default">
                     <div class="panel-heading">
                         <span wicket:id="memberGroupName" class="panel-title">[group name]</span>

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
----------------------------------------------------------------------
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 7cb2052..fac77cf 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
@@ -20,6 +20,12 @@ package org.apache.isis.viewer.wicket.ui.components.entity.fieldset;
 
 import java.util.List;
 
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -44,10 +50,13 @@ 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.actionmenu.entityactions.AdditionalLinksPanel;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.EntityActionUtil;
+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 PropertyGroup extends PanelAbstract<EntityModel> {
+public class PropertyGroup extends PanelAbstract<EntityModel> implements HasDynamicallyVisibleContent {
 
+    private static final String ID_MEMBER_GROUP = "memberGroup";
     private static final String ID_MEMBER_GROUP_NAME = "memberGroupName";
 
     private static final String ID_ASSOCIATED_ACTION_LINKS_PANEL = "associatedActionLinksPanel";
@@ -70,30 +79,43 @@ public class PropertyGroup extends PanelAbstract<EntityModel> {
     }
 
     private void buildGui() {
-        String groupName = fieldSet.getName();
+
+        final List<PropertyLayoutData> properties = fieldSet.getProperties();
+
         // changed to NO_CHECK because more complex BS3 layouts trip concurrency exception (haven't investigated as to why).
         final ObjectAdapter adapter = getModel().load(AdapterManager.ConcurrencyChecking.NO_CHECK);
 
-        add(new Label(ID_MEMBER_GROUP_NAME, groupName));
+        final WebMarkupContainer div = new WebMarkupContainer(ID_MEMBER_GROUP);
 
-        final List<LinkAndLabel> memberGroupActions = Lists.newArrayList();
+        String groupName = fieldSet.getName();
 
-        final RepeatingView propertyRv = new RepeatingView(ID_PROPERTIES);
-        add(propertyRv);
+        div.add(new Label(ID_MEMBER_GROUP_NAME, groupName));
 
-        final List<PropertyLayoutData> properties = fieldSet.getProperties();
-        for (PropertyLayoutData property : properties) {
-            final ObjectAssociation association = adapter.getSpecification().getAssociation(property.getId());
-            final Consent visibility = association.isVisible(adapter, InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
-            if(visibility.isVetoed()) {
-                continue;
-            }
+        final List<LinkAndLabel> memberGroupActions = Lists.newArrayList();
 
+        final RepeatingView propertyRv = new RepeatingView(ID_PROPERTIES);
+        div.add(propertyRv);
+
+        final ImmutableList<ObjectAssociation> visibleAssociations = FluentIterable.from(properties)
+                .transform(new Function<PropertyLayoutData, ObjectAssociation>() {
+                    @Override public ObjectAssociation apply(final PropertyLayoutData propertyLayoutData) {
+                        return adapter.getSpecification().getAssociation(propertyLayoutData.getId());
+                    }
+                })
+                .filter(new Predicate<ObjectAssociation>() {
+                    @Override public boolean apply(@Nullable final ObjectAssociation objectAssociation) {
+                        final Consent visibility =
+                                objectAssociation .isVisible(adapter, InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
+                        return visibility.isAllowed();
+                    }
+                })
+                .toList();
+
+        for (final ObjectAssociation association : visibleAssociations) {
             final WebMarkupContainer propertyRvContainer = new WebMarkupContainer(propertyRv.newChildId());
             propertyRv.add(propertyRvContainer);
-
-            addPropertyToForm(getModel(), (OneToOneAssociation) association, propertyRvContainer,
-                    memberGroupActions);
+            addPropertyToForm(getModel(), (OneToOneAssociation) association, propertyRvContainer, memberGroupActions);
+            contentDynamicallyVisible = true;
         }
 
         final List<LinkAndLabel> actionsPanel = LinkAndLabel
@@ -102,13 +124,20 @@ public class PropertyGroup extends PanelAbstract<EntityModel> {
                 .positioned(memberGroupActions, ActionLayout.Position.PANEL_DROPDOWN);
 
         AdditionalLinksPanel.addAdditionalLinks(
-                this, ID_ASSOCIATED_ACTION_LINKS_PANEL,
+                div, ID_ASSOCIATED_ACTION_LINKS_PANEL,
                 actionsPanel,
                 AdditionalLinksPanel.Style.INLINE_LIST);
         AdditionalLinksPanel.addAdditionalLinks(
-                this, ID_ASSOCIATED_ACTION_LINKS_PANEL_DROPDOWN,
+                div, ID_ASSOCIATED_ACTION_LINKS_PANEL_DROPDOWN,
                 actionsPanelDropDown,
                 AdditionalLinksPanel.Style.DROPDOWN);
+
+        // either add the built content, or hide entire
+        if(!contentDynamicallyVisible) {
+            Components.permanentlyHide(this, div.getId());
+        } else {
+            this.add(div);
+        }
     }
 
     private void addPropertyToForm(
@@ -128,4 +157,12 @@ public class PropertyGroup extends PanelAbstract<EntityModel> {
         entityActions.addAll(
                 EntityActionUtil.asLinkAndLabelsForAdditionalLinksPanel(entityModel, associatedActions));
     }
+
+
+    private boolean contentDynamicallyVisible = false;
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
index 057ea4b..8f99ad1 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
@@ -30,16 +30,18 @@ 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.grid.bootstrap3.BS3Col;
-import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
-import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
-import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
+import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.layout.component.ActionLayoutData;
 import org.apache.isis.applib.layout.component.CollectionLayoutData;
 import org.apache.isis.applib.layout.component.DomainObjectLayoutData;
 import org.apache.isis.applib.layout.component.FieldSet;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Col;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.runtime.system.DeploymentType;
 import org.apache.isis.core.runtime.system.context.IsisContext;
@@ -54,11 +56,12 @@ import org.apache.isis.viewer.wicket.ui.components.entity.fieldset.PropertyGroup
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.Util;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.row.Row;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.tabs.TabGroupPanel;
+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;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 
-public class Col extends PanelAbstract<EntityModel> {
+public class Col extends PanelAbstract<EntityModel> implements HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
 
@@ -96,8 +99,6 @@ public class Col extends PanelAbstract<EntityModel> {
         CssClassAppender.appendCssClassTo(div, bs3Col.toCssClass());
         Util.appendCssClass(div, bs3Col, ID_COL);
 
-        this.addOrReplace(div);
-
         // icon/title
         final DomainObjectLayoutData domainObject = bs3Col.getDomainObject();
 
@@ -115,6 +116,8 @@ public class Col extends PanelAbstract<EntityModel> {
             actionOwner = entityHeaderPanel;
             actionIdToUse = "entityActions";
             actionIdToHide = "actions";
+
+            contentDynamicallyVisible = true;
         } else {
             Components.permanentlyHide(div, ID_ENTITY_HEADER_PANEL);
             actionOwner = div;
@@ -126,19 +129,28 @@ public class Col extends PanelAbstract<EntityModel> {
         // actions
         // (rendering depends on whether also showing the icon/title)
         final List<ActionLayoutData> actionLayoutDatas = bs3Col.getActions();
-            final List<ObjectAction> objectActions =
-                FluentIterable.from(actionLayoutDatas)
-                        .transform(new Function<ActionLayoutData, ObjectAction>() {
-                            @Nullable @Override public ObjectAction apply(@Nullable final ActionLayoutData actionLayoutData) {
-                                return getModel().getTypeOfSpecification().getObjectAction(actionLayoutData.getId());
-                            }
-                        })
-                        .filter(Predicates.<ObjectAction>notNull())
-                        .toList();
-        final List<LinkAndLabel> entityActionLinks = EntityActionUtil.asLinkAndLabelsForAdditionalLinksPanel(getModel(), objectActions);
+        final List<ObjectAction> visibleActions =
+            FluentIterable.from(actionLayoutDatas)
+                    .transform(new Function<ActionLayoutData, ObjectAction>() {
+                        @Nullable @Override public ObjectAction apply(@Nullable final ActionLayoutData actionLayoutData) {
+                            return getModel().getTypeOfSpecification().getObjectAction(actionLayoutData.getId());
+                        }
+                    })
+                    .filter(Predicates.<ObjectAction>notNull())
+                    .filter(new Predicate<ObjectAction>() {
+                        @Override public boolean apply(@Nullable final ObjectAction objectAction) {
+                            final Consent visibility = objectAction
+                                    .isVisible(getModel().getObject(), InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
+                            return visibility.isAllowed();
+                        }
+                    })
+                    .toList();
+        final List<LinkAndLabel> entityActionLinks =
+                EntityActionUtil.asLinkAndLabelsForAdditionalLinksPanel(getModel(), visibleActions);
 
         if(!entityActionLinks.isEmpty()) {
             AdditionalLinksPanel.addAdditionalLinks(actionOwner, actionIdToUse, entityActionLinks, AdditionalLinksPanel.Style.INLINE_LIST);
+            contentDynamicallyVisible = true;
         } else {
             Components.permanentlyHide(actionOwner, actionIdToUse);
         }
@@ -151,8 +163,9 @@ public class Col extends PanelAbstract<EntityModel> {
         // rows
         final List<BS3Row> rows = Lists.newArrayList(this.bs3Col.getRows());
         if(!rows.isEmpty()) {
-            final RepeatingView rowsRv = buildRows(ID_ROWS, rows);
+            final RepeatingViewWithDynamicallyVisibleContent rowsRv = buildRows(ID_ROWS, rows);
             div.add(rowsRv);
+            contentDynamicallyVisible = contentDynamicallyVisible || rowsRv.isContentDynamicallyVisible();
         } else {
             Components.permanentlyHide(div, ID_ROWS);
         }
@@ -172,7 +185,8 @@ public class Col extends PanelAbstract<EntityModel> {
                             }
                         }).toList();
         if(!tabGroupsWithNonEmptyTabs.isEmpty()) {
-            final RepeatingView tabGroupRv = new RepeatingView(ID_TAB_GROUPS);
+            final RepeatingViewWithDynamicallyVisibleContent tabGroupRv =
+                    new RepeatingViewWithDynamicallyVisibleContent(ID_TAB_GROUPS);
 
             for (BS3TabGroup bs3TabGroup : tabGroupsWithNonEmptyTabs) {
 
@@ -190,7 +204,7 @@ public class Col extends PanelAbstract<EntityModel> {
                     final BS3Tab bs3Tab = tabs.get(0);
                     // render the rows of the one-and-only tab of this tab group.
                     final List<BS3Row> tabRows = bs3Tab.getRows();
-                    final RepeatingView rowsRv = buildRows(id, tabRows);
+                    final RepeatingViewWithDynamicallyVisibleContent rowsRv = buildRows(id, tabRows);
                     tabGroupRv.add(rowsRv);
                     break;
                 default:
@@ -204,6 +218,7 @@ public class Col extends PanelAbstract<EntityModel> {
 
             }
             div.add(tabGroupRv);
+            contentDynamicallyVisible = contentDynamicallyVisible || tabGroupRv.isContentDynamicallyVisible();
         } else {
             Components.permanentlyHide(div, ID_TAB_GROUPS);
         }
@@ -218,58 +233,77 @@ public class Col extends PanelAbstract<EntityModel> {
                     }
                 }).toList();
         if(!fieldSetsWithProperties.isEmpty()) {
-            final RepeatingView fieldSetRv = new RepeatingView(ID_FIELD_SETS);
+            final RepeatingViewWithDynamicallyVisibleContent fieldSetRv =
+                    new RepeatingViewWithDynamicallyVisibleContent(ID_FIELD_SETS);
 
             for (FieldSet fieldSet : fieldSetsWithProperties) {
 
                 final String id = fieldSetRv.newChildId();
                 final EntityModel entityModelWithHints = getModel().cloneWithLayoutMetadata(fieldSet);
 
-                final WebMarkupContainer propertyGroup = new PropertyGroup(id, entityModelWithHints);
+                final PropertyGroup propertyGroup = new PropertyGroup(id, entityModelWithHints);
                 fieldSetRv.add(propertyGroup);
             }
             div.add(fieldSetRv);
+            contentDynamicallyVisible = contentDynamicallyVisible || fieldSetRv.isContentDynamicallyVisible();
         } else {
             Components.permanentlyHide(div, ID_FIELD_SETS);
         }
 
 
-
         // collections
         final List<CollectionLayoutData> collections = bs3Col.getCollections();
         if(!collections.isEmpty()) {
-            final RepeatingView collectionRv = new RepeatingView(ID_COLLECTIONS);
+            final RepeatingViewWithDynamicallyVisibleContent collectionRv =
+                    new RepeatingViewWithDynamicallyVisibleContent(ID_COLLECTIONS);
 
             for (CollectionLayoutData collection : collections) {
 
                 final String id = collectionRv.newChildId();
                 final EntityModel entityModelWithHints = getModel().cloneWithLayoutMetadata(collection);
 
-                final WebMarkupContainer collectionPanel = new EntityCollectionPanel(id, entityModelWithHints);
+                final EntityCollectionPanel collectionPanel = new EntityCollectionPanel(id, entityModelWithHints);
                 collectionRv.add(collectionPanel);
             }
             div.add(collectionRv);
+            contentDynamicallyVisible = contentDynamicallyVisible || collectionRv.isContentDynamicallyVisible();
         } else {
             Components.permanentlyHide(div, ID_COLLECTIONS);
         }
 
+
+        final WebMarkupContainer panel = this;
+        if(contentDynamicallyVisible) {
+            panel.add(div);
+        } else {
+            Components.permanentlyHide(panel, div.getId());
+        }
+
     }
 
-    private RepeatingView buildRows(final String owningId, final List<BS3Row> rows) {
-        final RepeatingView rowRv = new RepeatingView(owningId);
+    private RepeatingViewWithDynamicallyVisibleContent buildRows(final String owningId, final List<BS3Row> rows) {
+        final RepeatingViewWithDynamicallyVisibleContent rowRv =
+                new RepeatingViewWithDynamicallyVisibleContent(owningId);
 
         for(final BS3Row bs3Row: rows) {
 
             final String id = rowRv.newChildId();
             final EntityModel entityModelWithHints = getModel().cloneWithLayoutMetadata(bs3Row);
 
-            final WebMarkupContainer row = new Row(id, entityModelWithHints);
+            final Row row = new Row(id, entityModelWithHints);
 
             rowRv.add(row);
         }
         return rowRv;
     }
 
+
+    private boolean contentDynamicallyVisible = false;
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
+    }
+
     ///////////////////////////////////////////////////////
     // Dependencies (from context)
     ///////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/RepeatingViewWithDynamicallyVisibleContent.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/RepeatingViewWithDynamicallyVisibleContent.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/RepeatingViewWithDynamicallyVisibleContent.java
new file mode 100644
index 0000000..b61e912
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/RepeatingViewWithDynamicallyVisibleContent.java
@@ -0,0 +1,54 @@
+/*
+ *  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.layout.bs3.col;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.repeater.RepeatingView;
+
+import org.apache.isis.viewer.wicket.ui.panels.HasDynamicallyVisibleContent;
+
+public class RepeatingViewWithDynamicallyVisibleContent extends RepeatingView implements HasDynamicallyVisibleContent {
+
+    public RepeatingViewWithDynamicallyVisibleContent(final String id) {
+        super(id);
+    }
+
+    @Override
+    public MarkupContainer add(final Component... children) {
+        final MarkupContainer component = super.add(children);
+        for (Component child : children) {
+            if(child instanceof HasDynamicallyVisibleContent) {
+                final HasDynamicallyVisibleContent hasDynamicallyVisibleContent = (HasDynamicallyVisibleContent) child;
+                contentDynamicallyVisible = contentDynamicallyVisible || hasDynamicallyVisibleContent.isContentDynamicallyVisible();
+            } else {
+                contentDynamicallyVisible = true;
+            }
+        }
+        return component;
+    }
+
+    private boolean contentDynamicallyVisible = false;
+
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/row/Row.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/row/Row.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/row/Row.java
index 7aac0fe..56d0e05 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/row/Row.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/row/Row.java
@@ -19,7 +19,6 @@
 package org.apache.isis.viewer.wicket.ui.components.layout.bs3.row;
 
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.repeater.RepeatingView;
 
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3ClearFix;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Col;
@@ -31,9 +30,12 @@ import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.Util;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.clearfix.ClearFix;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.col.Col;
+import org.apache.isis.viewer.wicket.ui.components.layout.bs3.col.RepeatingViewWithDynamicallyVisibleContent;
+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 Row extends PanelAbstract<EntityModel> {
+public class Row extends PanelAbstract<EntityModel> implements HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
 
@@ -54,9 +56,8 @@ public class Row extends PanelAbstract<EntityModel> {
 
     private void buildGui() {
 
-        Util.appendCssClass(this, bs3Row, "row");
-
-        final RepeatingView rv = new RepeatingView(ID_ROW_CONTENTS);
+        final RepeatingViewWithDynamicallyVisibleContent rv =
+                new RepeatingViewWithDynamicallyVisibleContent(ID_ROW_CONTENTS);
 
         for(final BS3RowContent bs3RowContent: bs3Row.getCols()) {
 
@@ -65,7 +66,10 @@ public class Row extends PanelAbstract<EntityModel> {
 
             final WebMarkupContainer rowContent;
             if(bs3RowContent instanceof BS3Col) {
-                rowContent = new Col(id, entityModelWithHints);
+                Col col = new Col(id, entityModelWithHints);
+                contentDynamicallyVisible = contentDynamicallyVisible || col.isContentDynamicallyVisible();
+                rowContent = col;
+
             } else if (bs3RowContent instanceof BS3ClearFix) {
                 rowContent = new ClearFix(id, entityModelWithHints);
             } else {
@@ -75,7 +79,21 @@ public class Row extends PanelAbstract<EntityModel> {
             rv.add(rowContent);
         }
 
-        add(rv);
+        final WebMarkupContainer panel = this;
+        if(contentDynamicallyVisible) {
+            Util.appendCssClass(panel, bs3Row, "row");
+            panel.add(rv);
+        } else {
+            Components.permanentlyHide(panel, rv.getId());
+        }
+
+    }
+
+
+    private boolean contentDynamicallyVisible = false;
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
     }
 
     ///////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabGroupPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabGroupPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabGroupPanel.java
index b350b6c..14db996 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabGroupPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabGroupPanel.java
@@ -20,6 +20,7 @@ package org.apache.isis.viewer.wicket.ui.components.layout.bs3.tabs;
 
 import java.util.List;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 
@@ -33,10 +34,12 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.util.ScopedSessionAttribute;
+import org.apache.isis.viewer.wicket.ui.components.layout.bs3.col.RepeatingViewWithDynamicallyVisibleContent;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
 
-public class TabGroupPanel extends AjaxBootstrapTabbedPanel {
+// hmmm... not sure how to make this implement HasDynamicallyVisibleContent
+public class TabGroupPanel extends AjaxBootstrapTabbedPanel  {
 
     public static final String SESSION_ATTR_SELECTED_TAB = "selectedTab";
 
@@ -53,12 +56,18 @@ public class TabGroupPanel extends AjaxBootstrapTabbedPanel {
                 .toList();
 
         for (final BS3Tab bs3Tab : tablist) {
+            final RepeatingViewWithDynamicallyVisibleContent rv = TabPanel.newRows(entityModel, bs3Tab);
             tabs.add(new AbstractTab(Model.of(bs3Tab.getName())) {
                 private static final long serialVersionUID = 1L;
 
                 @Override
                 public Panel getPanel(String panelId) {
-                    return new TabPanel(panelId, entityModel, bs3Tab);
+                    return new TabPanel(panelId, entityModel, bs3Tab, rv);
+                }
+
+                @Override
+                public boolean isVisible() {
+                    return rv.isContentDynamicallyVisible();
                 }
             });
         }
@@ -94,4 +103,14 @@ public class TabGroupPanel extends AjaxBootstrapTabbedPanel {
             }
         }
     }
+
+    @Override
+    public boolean isVisible() {
+        return FluentIterable.<AbstractTab>from(getTabs()).anyMatch(new Predicate<AbstractTab>() {
+            @Override
+            public boolean apply(final AbstractTab tab) {
+                return tab.isVisible();
+            }
+        });
+    }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabPanel.java
index 61c81e7..48277ae 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/tabs/TabPanel.java
@@ -1,17 +1,19 @@
 package org.apache.isis.viewer.wicket.ui.components.layout.bs3.tabs;
 
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.repeater.RepeatingView;
 
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
 import org.apache.isis.viewer.wicket.model.hints.HasUiHintDisambiguator;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.Util;
+import org.apache.isis.viewer.wicket.ui.components.layout.bs3.col.RepeatingViewWithDynamicallyVisibleContent;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.row.Row;
+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 TabPanel extends PanelAbstract implements HasUiHintDisambiguator {
+public class TabPanel extends PanelAbstract implements HasUiHintDisambiguator, HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
 
@@ -21,10 +23,14 @@ public class TabPanel extends PanelAbstract implements HasUiHintDisambiguator {
     private final BS3Tab bs3Tab;
 
     public TabPanel(String id, final EntityModel model, final BS3Tab bs3Tab) {
+        this(id, model, bs3Tab, null);
+    }
+
+    public TabPanel(String id, final EntityModel model, final BS3Tab bs3Tab, final RepeatingViewWithDynamicallyVisibleContent repeatingViewWithDynamicallyVisibleContent) {
         super(id);
 
         this.bs3Tab = bs3Tab;
-        buildGui(model, bs3Tab);
+        buildGui(model, bs3Tab, repeatingViewWithDynamicallyVisibleContent);
     }
 
     /**
@@ -35,24 +41,47 @@ public class TabPanel extends PanelAbstract implements HasUiHintDisambiguator {
         return bs3Tab.getName();
     }
 
-    protected void buildGui(final EntityModel model, final BS3Tab bs3Tab) {
+    protected void buildGui(final EntityModel model, final BS3Tab bs3Tab, final RepeatingViewWithDynamicallyVisibleContent rvIfAny) {
+
+        final WebMarkupContainer div = new WebMarkupContainer(ID_TAB_PANEL);
 
-        final WebMarkupContainer container = new WebMarkupContainer(ID_TAB_PANEL);
+        final RepeatingViewWithDynamicallyVisibleContent rv = rvIfAny != null ? rvIfAny : newRows(model, bs3Tab);
+        div.add(rv);
+        contentDynamicallyVisible = contentDynamicallyVisible || rv.isContentDynamicallyVisible();
+
+        final WebMarkupContainer panel = this;
+        if(contentDynamicallyVisible) {
+            Util.appendCssClassIfRequired(panel, bs3Tab);
+            panel.add(div);
+        } else {
+            Components.permanentlyHide(panel, div.getId());
+        }
 
-        Util.appendCssClassIfRequired(this, bs3Tab);
+    }
 
-        final RepeatingView rv = new RepeatingView(ID_ROWS);
+    public static RepeatingViewWithDynamicallyVisibleContent newRows(final EntityModel model, final BS3Tab bs3Tab) {
+        final RepeatingViewWithDynamicallyVisibleContent rv = new RepeatingViewWithDynamicallyVisibleContent(ID_ROWS);
 
         for(final BS3Row bs3Row: bs3Tab.getRows()) {
 
             final String newChildId = rv.newChildId();
             final EntityModel entityModelWithHints = model.cloneWithLayoutMetadata(bs3Row);
 
-            final WebMarkupContainer row = new Row(newChildId, entityModelWithHints);
+            final Row row = new Row(newChildId, entityModelWithHints);
 
             rv.add(row);
         }
-        container.add(rv);
-        add(container);
+        return rv;
+    }
+
+    private boolean contentDynamicallyVisible = false;
+    @Override
+    public boolean isContentDynamicallyVisible() {
+        return contentDynamicallyVisible;
+    }
+
+    @Override
+    public boolean isVisible() {
+        return isContentDynamicallyVisible();
     }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/HasDynamicallyVisibleContent.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/HasDynamicallyVisibleContent.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/HasDynamicallyVisibleContent.java
new file mode 100644
index 0000000..d5440fd
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/HasDynamicallyVisibleContent.java
@@ -0,0 +1,26 @@
+/*
+ *  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.panels;
+
+public interface HasDynamicallyVisibleContent {
+
+    boolean isContentDynamicallyVisible();
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f65487e4/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
----------------------------------------------------------------------
diff --git a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
index 7f2702e..18abff2 100644
--- a/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
+++ b/example/application/simpleapp/dom/src/main/java/domainapp/dom/simple/SimpleObject.java
@@ -28,6 +28,7 @@ import javax.jdo.annotations.VersionStrategy;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.BookmarkPolicy;
+import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.applib.annotation.Editing;
@@ -139,6 +140,9 @@ public class SimpleObject implements Comparable<SimpleObject> {
         return (java.sql.Timestamp) JDOHelper.getVersion(this);
     }
 
+    public boolean hideVersionSequence() {
+        return getName().contains("ob");
+    }
 
     @Override
     public int compareTo(final SimpleObject other) {
@@ -155,6 +159,9 @@ public class SimpleObject implements Comparable<SimpleObject> {
         return simpleObjects.findByName(getName().substring(0,1));
     }
 
+    @CollectionLayout(
+            defaultView = "table"
+    )
     public List<SimpleObject> getOthers() {
         return simpleObjects.listAll();
     }