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 2018/02/09 23:59:16 UTC

[isis] 02/02: ISIS-1585: adds support for checkboxes in parented collections, to act as the defaults for any associated actions.

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

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

commit 26e84ce74c9260ac5f9ec0fac9c8d919743e9e4f
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Feb 9 23:21:42 2018 +0000

    ISIS-1585: adds support for checkboxes in parented collections, to act as the defaults for any associated actions.
---
 .../guides/rgant/_rgant-Action_associateWith.adoc  |  25 +++-
 ...ActionParameterDefaultsFacetViaToggleBoxes.java |  63 ++++++++++
 ...arameterDefaultsFacetViaToggleBoxesFactory.java | 127 +++++++++++++++++++++
 .../core/metamodel/spec/feature/ObjectAction.java  |  49 ++++++++
 .../spec/feature/ObjectActionParameter.java        |  30 +++++
 .../metamodel/specloader/SpecificationLoader.java  |  34 ++++--
 .../dflt/ProgrammingModelFacetsJava5.java          |   7 ++
 .../background/CommandExecutorServiceDefault.java  |   3 +-
 .../system/session/IsisSessionFactoryBuilder.java  |   2 +-
 .../viewer/wicket/model/models/ActionPrompt.java   |   6 +
 .../wicket/model/models/EntityCollectionModel.java |   3 +-
 .../model/models/ToggledMementosProvider.java      |  13 +++
 .../entityactions/EntityActionLinkFactory.java     |   8 +-
 .../actionmenu/entityactions/LinkAndLabelUtil.java |  12 +-
 .../actionmenu/serviceactions/CssMenuItem.java     |   2 +-
 .../serviceactions/ServiceActionLinkFactory.java   |   7 +-
 .../actionprompt/ActionPromptModalWindow.java      |   2 +
 .../collection/AssociatedWithActionsHelper.java    |  87 ++++++++++++++
 .../ui/components/collection/CollectionPanel.java  |  99 +++++++++++++++-
 .../collection/bulk/BulkActionsHelper.java         |  56 +++++----
 .../collection/bulk/BulkActionsLinkFactory.java    |   4 +-
 .../collection/bulk/BulkActionsProvider.java       |   6 +-
 .../CollectionContentsAsAjaxTablePanel.java        |   2 +-
 .../StandaloneCollectionPanel.java                 |  42 ++++---
 .../components/widgets/bootstrap/ModalDialog.java  |  10 ++
 .../widgets/linkandlabel/ActionLinkFactory.java    |   5 +-
 .../linkandlabel/ActionLinkFactoryAbstract.java    |  78 ++++++++++++-
 .../viewer/wicket/ui/pages/bootstrap-overrides.css |   5 +
 28 files changed, 705 insertions(+), 82 deletions(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc
index 913d478..38d0c94 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc
@@ -22,7 +22,7 @@ public class Order {
     public Order addItem(Product p, int quantity) { ... }
 
     @Action(associateWith="items:2")
-    public Order removeItem(Product p, int quantity) { ... }
+    public Order removeItem(OrderItem item) { ... }
     ...
 }
 ----
@@ -36,3 +36,26 @@ In the user interface associated actions are rendered close to the member to whi
 The same effect can be accomplished using `@MemberOrder` or with the `.layout.xml` file.
 ====
 
+
+== Collection Parameters
+
+If the action that has collection parameters is associated with a collection, then the Wicket viewer will render the collection with checkboxes, and these checkboxes can be used to select the items of the action parameter.
+
+For example, suppose we have a "removeItems(...)" action:
+
+[source,java]
+----
+public class Order {
+
+    @Collection
+    SortedSet<OrderItem> getItems() { ... }
+
+    ...
+
+    @Action(associateWith="items:2")
+    public Order removeItems(SortedSet<OrderItem> items) { ... }
+    public SortedSet<OrderItem> choices0RemoveItems() { return getItems(); }
+}
+----
+
+The Wicket viewer will then render the "items" collection with checkboxes, and any selected items will be used as the pre-selected set of items if the action is invoked.
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxes.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxes.java
new file mode 100644
index 0000000..f9ad50e
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxes.java
@@ -0,0 +1,63 @@
+/*
+ *  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.core.metamodel.facets.param.defaults.togglebox;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.param.defaults.ActionParameterDefaultsFacetAbstract;
+
+public class ActionParameterDefaultsFacetViaToggleBoxes extends ActionParameterDefaultsFacetAbstract {
+
+    private static ThreadLocal<List<Object>> selectedPojos = new ThreadLocal<List<Object>>() {
+        @Override protected List<Object> initialValue() {
+            return Collections.emptyList();
+        }
+    };
+
+    public interface SerializableRunnable<T> extends Callable<T>, Serializable {}
+
+    public static <T> T withSelected(final List<Object> objects, final SerializableRunnable<T> callable) {
+        try {
+            selectedPojos.set(objects);
+            return callable.call();
+        } catch (Exception e) {
+            throw new ApplicationException(e);
+        } finally {
+            selectedPojos.set(Collections.emptyList());
+        }
+    }
+
+    public ActionParameterDefaultsFacetViaToggleBoxes(final FacetHolder holder) {
+        super(holder);
+    }
+
+    @Override
+    public Object getDefault(final ObjectAdapter target, List<ObjectAdapter> argumentsIfAvailable) {
+        return selectedPojos.get();
+    }
+
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxesFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxesFactory.java
new file mode 100644
index 0000000..3af6f2b
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxesFactory.java
@@ -0,0 +1,127 @@
+/*
+ *  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.core.metamodel.facets.param.defaults.togglebox;
+
+import java.util.List;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.filter.Filters;
+import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
+import org.apache.isis.core.metamodel.deployment.DeploymentCategoryProvider;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+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.ObjectActionParameter;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+/**
+ * Sets up all the {@link Facet}s for an action in a single shot.
+ */
+public class ActionParameterDefaultsFacetViaToggleBoxesFactory extends FacetFactoryAbstract {
+
+    private DeploymentCategoryProvider deploymentCategoryProvider;
+
+    /**
+     * Note that the {@link Facet}s registered are the generic ones from
+     * noa-architecture (where they exist)
+     */
+    public ActionParameterDefaultsFacetViaToggleBoxesFactory() {
+        super(ImmutableList.of(FeatureType.OBJECT_POST_PROCESSING));
+    }
+
+    @Override
+    public void process(final ProcessClassContext processClassContext) {
+        final ObjectSpecification objectSpecification = getSpecificationLoader()
+                .loadSpecification(processClassContext.getCls());
+        postProcess(objectSpecification);
+
+    }
+
+    /**
+     * NOT API.
+     * Called as special case in SpecificationLoader.
+     */
+    public void postProcess(final ObjectSpecification objectSpecification) {
+
+        // all the actions of this type
+        final List<ActionType> actionTypes = inferActionTypes();
+        List<ObjectAction> objectActions = objectSpecification.getObjectActions(actionTypes, Contributed.INCLUDED, Filters.<ObjectAction>any());
+
+        // and all the collections of this type
+        final List<OneToManyAssociation> oneToManyAssociations =
+                objectSpecification.getCollections(Contributed.INCLUDED);
+
+        // for each collection, ...
+        for (final OneToManyAssociation otma : oneToManyAssociations) {
+
+            // ... see if any of its actions has a collection parameter of the same type
+            //
+            // eg Order#getItems() and Order#removeItems(List<OrderItem>)
+            //
+            final String collectionId = otma.getId();
+            final ObjectSpecification specification = otma.getSpecification();
+            final ImmutableList<ObjectAction> actions = FluentIterable.from(objectActions)
+                    .filter(
+                        ObjectAction.Predicates.associatedWithAndWithCollectionParameterFor(collectionId, specification))
+                    .toList();
+
+            //
+            // ... for the matching actions, install the default facet populated using toggle boxes
+            //
+            final ObjectActionParameter.Predicates.CollectionParameter whetherCollectionParamOfType =
+                    new ObjectActionParameter.Predicates.CollectionParameter(specification);
+            for (final ObjectAction action : actions) {
+                final List<ObjectActionParameter> parameters = action.getParameters();
+                final ImmutableList<ObjectActionParameter> collectionParams = FluentIterable.from(parameters)
+                        .filter(whetherCollectionParamOfType).toList();
+                for (final ObjectActionParameter collectionParam : collectionParams) {
+                    FacetUtil.addFacet(new ActionParameterDefaultsFacetViaToggleBoxes(collectionParam));
+                }
+            }
+        }
+    }
+
+    private List<ActionType> inferActionTypes() {
+        final List<ActionType> actionTypes = Lists.newArrayList();
+        actionTypes.add(ActionType.USER);
+        final DeploymentCategory deploymentCategory = deploymentCategoryProvider.getDeploymentCategory();
+        if ( !deploymentCategory.isProduction()) {
+            actionTypes.add(ActionType.PROTOTYPE);
+        }
+        return actionTypes;
+    }
+
+
+    @Override
+    public void setServicesInjector(final ServicesInjector servicesInjector) {
+        super.setServicesInjector(servicesInjector);
+        deploymentCategoryProvider = servicesInjector.getDeploymentCategoryProvider();
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
index 6ec249d..e2b8bd1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
@@ -21,8 +21,12 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
+import javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 
 import org.apache.isis.applib.Identifier;
@@ -413,6 +417,51 @@ public interface ObjectAction extends ObjectMember {
         public static Predicate<ObjectAction> memberOrderOf(ObjectAssociation association) {
             return org.apache.isis.applib.filter.Filters.asPredicate(Filters.memberOrderOf(association));
         }
+
+        public static Predicate<ObjectAction> associatedWithAndWithCollectionParameterFor(
+                final String collectionName,
+                final ObjectSpecification collectionTypeOfSpec) {
+
+            return com.google.common.base.Predicates.and(
+                    new AssociatedWith(collectionName),
+                    new HasParameterMatching(
+                        new ObjectActionParameter.Predicates.CollectionParameter(collectionTypeOfSpec)
+                    )
+            );
+        }
+
+        public static class AssociatedWith implements Predicate<ObjectAction> {
+            private final String memberNameAssociatedWith;
+            public AssociatedWith(final String memberNameAssociatedWith) {
+                this.memberNameAssociatedWith = memberNameAssociatedWith;
+            }
+
+            @Override
+            public boolean apply(final ObjectAction objectAction) {
+                final MemberOrderFacet memberOrderFacet = objectAction.getFacet(MemberOrderFacet.class);
+                if(memberOrderFacet == null) {
+                    return false;
+                }
+                final String name = memberNameAssociatedWith;
+                final String memberOrderName = memberOrderFacet.untranslatedName();
+                return name != null && memberOrderName != null &&
+                       Objects.equal(name.toLowerCase(), memberOrderName.toLowerCase());
+            }
+        }
+
+        public static class HasParameterMatching implements Predicate<ObjectAction> {
+            private final Predicate<ObjectActionParameter> parameterPredicate;
+            public HasParameterMatching(final Predicate<ObjectActionParameter> parameterPredicate) {
+                this.parameterPredicate = parameterPredicate;
+            }
+
+            @Override
+            public boolean apply(@Nullable final ObjectAction objectAction) {
+                return FluentIterable
+                        .from(objectAction.getParameters())
+                        .anyMatch(parameterPredicate);
+            }
+        }
     }
 
     //endregion
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
index 0ddbec1..fce2438 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionParameter.java
@@ -19,13 +19,17 @@
 
 package org.apache.isis.core.metamodel.spec.feature;
 
+import javax.annotation.Nullable;
+
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 
 import org.apache.isis.applib.filter.Filter;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
 import org.apache.isis.core.metamodel.interactions.ActionArgValidityContext;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 /**
  * Analogous to {@link ObjectAssociation}.
@@ -155,4 +159,30 @@ public interface ObjectActionParameter extends ObjectFeature, CurrentHolder {
         private Functions(){}
 
     }
+
+    public static class Predicates {
+        private Predicates(){}
+
+        public static class CollectionParameter implements Predicate<ObjectActionParameter> {
+
+            private final ObjectSpecification elementSpecification;
+
+            public CollectionParameter(final ObjectSpecification elementSpecification) {
+                this.elementSpecification = elementSpecification;
+            }
+
+            @Override
+            public boolean apply(@Nullable final ObjectActionParameter objectActionParameter) {
+                if (!(objectActionParameter instanceof OneToManyActionParameter)) {
+                    return false;
+                }
+
+                final OneToManyActionParameter otmap =
+                        (OneToManyActionParameter) objectActionParameter;
+                final ObjectSpecification specification = otmap.getSpecification();
+                final ObjectSpecification typeOfSpecification = this.elementSpecification;
+                return specification == typeOfSpecification;
+            }
+        }
+    }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index 07b06bb..8142104 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -42,6 +42,7 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facets.FacetFactory;
 import org.apache.isis.core.metamodel.facets.object.autocomplete.AutoCompleteFacet;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
+import org.apache.isis.core.metamodel.facets.param.defaults.togglebox.ActionParameterDefaultsFacetViaToggleBoxesFactory;
 import org.apache.isis.core.metamodel.layoutmetadata.LayoutMetadataReader;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
@@ -358,9 +359,6 @@ public class SpecificationLoader implements ApplicationScopedComponent {
             return spec;
         }
         final ObjectSpecification specification = createSpecification(type, natureOfService);
-        if (specification == null) {
-            throw new IsisException("Failed to create specification for class " + typeName);
-        }
 
         // put into the cache prior to introspecting, to prevent
         // infinite loops
@@ -439,17 +437,18 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         final ObjectSpecificationAbstract.IntrospectionState introspectionState = specSpi.getIntrospectionState();
 
         // REVIEW: can't remember why this is done in multiple passes, could it be simplified?
-        if (introspectionState == ObjectSpecificationAbstract.IntrospectionState.NOT_INTROSPECTED) {
+        switch (introspectionState) {
+        case NOT_INTROSPECTED:
 
             specSpi.setIntrospectionState(ObjectSpecificationAbstract.IntrospectionState.BEING_INTROSPECTED);
             introspect(specSpi);
-
-        } else if (introspectionState == ObjectSpecificationAbstract.IntrospectionState.BEING_INTROSPECTED) {
-
+            break;
+        case BEING_INTROSPECTED:
             introspect(specSpi);
-
-        } else if (introspectionState == ObjectSpecificationAbstract.IntrospectionState.INTROSPECTED) {
+            break;
+        case INTROSPECTED:
             // nothing to do
+            break;
         }
         return spec;
     }
@@ -461,6 +460,23 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     }
 
+    public void postProcess() {
+
+        //
+        // HMM.  Not possible to add this as a facet factory, because of
+        // inifinite loop (can't lookup actions of spec until fully processed).
+        // so, instead, calling as a one-off special-case in SpecificationLoader
+        //
+        final ActionParameterDefaultsFacetViaToggleBoxesFactory factory =
+                new ActionParameterDefaultsFacetViaToggleBoxesFactory();
+        factory.setServicesInjector(getServicesInjector());
+
+        final Collection<ObjectSpecification> specs = allSpecifications();
+        for (final ObjectSpecification spec : specs) {
+            factory.postProcess(spec);
+        }
+    }
+
     //endregion
 
     //region > allSpecifications
diff --git a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
index 4435bca..302a8f8 100644
--- a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
+++ b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
@@ -280,6 +280,13 @@ public final class ProgrammingModelFacetsJava5 extends ProgrammingModelAbstract
         addFactory(new ActionDefaultsFacetViaMethodFactory());
         addFactory(new ActionParameterDefaultsFacetViaMethodFactory());
 
+        //
+        // HMM.  Not possible to add this as a facet factory, because of
+        // inifinite loop (can't lookup actions of spec until fully processed).
+        // so, instead, calling as a one-off special-case in SpecificationLoader
+        //
+        //addFactory(new ActionParameterDefaultsFacetViaToggleBoxesFactory());
+
         // members in general
         
         addFactory(new NamedFacetStaticMethodFactory());
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/CommandExecutorServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/CommandExecutorServiceDefault.java
index 63737b5..cb29cf0 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/CommandExecutorServiceDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/CommandExecutorServiceDefault.java
@@ -148,11 +148,10 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
             // thrown then we would have a command with only completedAt, which is inconsistent.
             // Therefore instead we copy down from the backgroundInteraction (similar to how we populate the
             // completedAt at the end)
-            final Interaction.Execution priorExecution = interaction.getPriorExecution();
             final Interaction.Execution currentExecution = interaction.getCurrentExecution();
 
             final Timestamp startedAt = currentExecution != null
-                    ? priorExecution.getStartedAt()
+                    ? currentExecution.getStartedAt()
                     : clockService.nowAsJavaSqlTimestamp();
 
             commandWithDto.setStartedAt(startedAt);
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
index c834152..5c9e0b4 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
@@ -212,6 +212,7 @@ public class IsisSessionFactoryBuilder {
                     new Runnable() {
                         @Override
                         public void run() {
+                            specificationLoader.postProcess();
                             try {
                                 specificationLoader.validateAndAssert();
 
@@ -223,7 +224,6 @@ public class IsisSessionFactoryBuilder {
                                 }
                                 IsisContext.setMetaModelInvalidException(ex);
                             }
-
                         }
                     }
             );
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPrompt.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPrompt.java
index 0e2747a..f2e03a7 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPrompt.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionPrompt.java
@@ -68,4 +68,10 @@ public interface ActionPrompt extends Serializable {
      * @param target The current Ajax request handler
      */
     void closePrompt(AjaxRequestTarget target);
+
+    public interface CloseHandler extends Serializable {
+        public void close(final AjaxRequestTarget target);
+    }
+
+    void setOnClose(CloseHandler closeHandler);
 }
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
index 923980a..a49e365 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
@@ -487,7 +487,7 @@ public class EntityCollectionModel extends ModelAbstract<List<ObjectAdapter>> im
     }
     
     public List<ObjectAdapterMemento> getToggleMementosList() {
-        return Collections.unmodifiableList(this.toggledMementosList);
+        return Collections.unmodifiableList(Lists.newArrayList(this.toggledMementosList));
     }
 
     public void clearToggleMementosList() {
@@ -495,6 +495,7 @@ public class EntityCollectionModel extends ModelAbstract<List<ObjectAdapter>> im
     }
 
     public void addLinkAndLabels(List<LinkAndLabel> linkAndLabels) {
+        this.linkAndLabels.clear();
         this.linkAndLabels.addAll(linkAndLabels);
     }
 
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ToggledMementosProvider.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ToggledMementosProvider.java
new file mode 100644
index 0000000..eb1ef2e
--- /dev/null
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ToggledMementosProvider.java
@@ -0,0 +1,13 @@
+package org.apache.isis.viewer.wicket.model.models;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+
+import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
+
+public interface ToggledMementosProvider extends Serializable {
+    List<ObjectAdapterMemento> getToggles();
+    void clearToggles(final AjaxRequestTarget target);
+}
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
index bb4c32c..e680565 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/EntityActionLinkFactory.java
@@ -27,6 +27,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel.ActionLinkFactoryAbstract;
 
 public final class EntityActionLinkFactory extends ActionLinkFactoryAbstract {
@@ -42,7 +43,8 @@ public final class EntityActionLinkFactory extends ActionLinkFactoryAbstract {
     @Override
     public LinkAndLabel newLink(
             final ObjectAction objectAction,
-            final String linkId) {
+            final String linkId,
+            final ToggledMementosProvider toggledMementosProviderIfAny) {
 
         final ObjectAdapter objectAdapter = this.targetEntityModel.load(ConcurrencyChecking.NO_CHECK);
         
@@ -65,7 +67,7 @@ public final class EntityActionLinkFactory extends ActionLinkFactoryAbstract {
         // }
 
         
-        final AbstractLink link = newLink(linkId, objectAction);
+        final AbstractLink link = newLink(linkId, objectAction, toggledMementosProviderIfAny);
 
         // similarly for whether disabled, done at point of rendering
 
@@ -84,4 +86,6 @@ public final class EntityActionLinkFactory extends ActionLinkFactoryAbstract {
     }
 
 
+
+
 }
\ No newline at end of file
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 6d8c9af..441449c 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
@@ -33,6 +33,7 @@ import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel.ActionLinkFactory;
 
 public final class LinkAndLabelUtil {
@@ -76,6 +77,15 @@ public final class LinkAndLabelUtil {
             final List<ObjectAction> objectActions,
             final ScalarModel scalarModelForAssociationIfAny) {
 
+        return asActionLinksForAdditionalLinksPanel(parentEntityModel, objectActions, scalarModelForAssociationIfAny, null);
+    }
+
+    public static List<LinkAndLabel> asActionLinksForAdditionalLinksPanel(
+            final EntityModel parentEntityModel,
+            final List<ObjectAction> objectActions,
+            final ScalarModel scalarModelForAssociationIfAny,
+            final ToggledMementosProvider toggledMementosProviderIfAny) {
+
         final ActionLinkFactory linkFactory = new EntityActionLinkFactory(parentEntityModel, scalarModelForAssociationIfAny);
 
         return FluentIterable.from(objectActions)
@@ -83,7 +93,7 @@ public final class LinkAndLabelUtil {
 
                     @Override
                     public LinkAndLabel apply(ObjectAction objectAction) {
-                        return linkFactory.newLink(objectAction, AdditionalLinksPanel.ID_ADDITIONAL_LINK);
+                        return linkFactory.newLink(objectAction, AdditionalLinksPanel.ID_ADDITIONAL_LINK,toggledMementosProviderIfAny);
                     }
                 })
                 .filter(Predicates.<LinkAndLabel>notNull())
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/CssMenuItem.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/CssMenuItem.java
index 877b4ea..41e2f9a 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/CssMenuItem.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/CssMenuItem.java
@@ -375,7 +375,7 @@ class CssMenuItem implements Serializable {
         final String descriptionIfAny = describedAsFacet != null ? describedAsFacet.value() : null;
 
         // build the link
-        final LinkAndLabel linkAndLabel = actionLinkFactory.newLink(objectAction, PageAbstract.ID_MENU_LINK);
+        final LinkAndLabel linkAndLabel = actionLinkFactory.newLink(objectAction, PageAbstract.ID_MENU_LINK, null);
         if (linkAndLabel == null) {
             // can only get a null if invisible, so this should not happen given the visibility guard above
             return null;
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/ServiceActionLinkFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/ServiceActionLinkFactory.java
index e96bc54..f36f540 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/ServiceActionLinkFactory.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/serviceactions/ServiceActionLinkFactory.java
@@ -26,6 +26,7 @@ import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChec
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel.ActionLinkFactoryAbstract;
 
 class ServiceActionLinkFactory extends ActionLinkFactoryAbstract {
@@ -38,11 +39,13 @@ class ServiceActionLinkFactory extends ActionLinkFactoryAbstract {
 
     @Override
     public LinkAndLabel newLink(
-            final ObjectAction objectAction, final String linkId) {
+            final ObjectAction objectAction,
+            final String linkId,
+            final ToggledMementosProvider toggledMementosProviderIfAny) {
         
         ObjectAdapter objectAdapter = this.targetEntityModel.load(ConcurrencyChecking.NO_CHECK);
 
-        final AbstractLink link = newLink(linkId, objectAction);
+        final AbstractLink link = newLink(linkId, objectAction, toggledMementosProviderIfAny);
 
         return newLinkAndLabel(objectAdapter, objectAction, link, null);
     }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
index 782a3e9..b3880c9 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
@@ -16,6 +16,7 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.actionprompt;
 
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
@@ -66,4 +67,5 @@ public class ActionPromptModalWindow extends ModalDialog<Void> {
         response.render(OnDomReadyHeaderItem.forScript(
                 String.format("Wicket.Event.publish(Isis.Topic.FOCUS_FIRST_PARAMETER, '%s')", getMarkupId())));
     }
+
 }
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
new file mode 100644
index 0000000..65bf609
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
@@ -0,0 +1,87 @@
+/*
+ *  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.collection;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+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;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+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;
+
+/**
+ * See also {@link BulkActionsHelper}.
+ */
+public class AssociatedWithActionsHelper implements Serializable {
+    private final EntityCollectionModel collectionModel;
+
+    public AssociatedWithActionsHelper(final EntityCollectionModel collectionModel) {
+        this.collectionModel = collectionModel;
+    }
+
+    public List<ObjectAction> getAssociatedActions(final IsisSessionFactory isisSessionFactory) {
+
+        if(collectionModel.isStandalone()) {
+            return Collections.emptyList();
+        }
+        final ObjectSpecification objectSpec = getObjectSpecification(isisSessionFactory);
+
+        final List<ActionType> actionTypes = inferActionTypes(isisSessionFactory);
+        List<ObjectAction> objectActions = objectSpec.getObjectActions(actionTypes, Contributed.INCLUDED, Filters.<ObjectAction>any());
+
+        return FluentIterable.from(objectActions)
+                .filter(ObjectAction.Predicates.associatedWithAndWithCollectionParameterFor(
+                            collectionModel.getName(),
+                            collectionModel.getTypeOfSpecification()))
+                .toList();
+    }
+
+    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();
+    }
+
+    private static List<ActionType> inferActionTypes(final IsisSessionFactory isisSessionFactory) {
+        final List<ActionType> actionTypes = Lists.newArrayList();
+        actionTypes.add(ActionType.USER);
+        final DeploymentCategory deploymentCategory = isisSessionFactory.getDeploymentCategory();
+        if ( !deploymentCategory.isProduction()) {
+            actionTypes.add(ActionType.PROTOTYPE);
+        }
+        return actionTypes;
+    }
+
+}
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 b479039..cdbf1e5 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
@@ -19,11 +19,13 @@
 
 package org.apache.isis.viewer.wicket.ui.components.collection;
 
+import java.io.Serializable;
 import java.util.List;
 
 import com.google.common.collect.Lists;
 
 import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
 import org.apache.wicket.markup.html.basic.Label;
 
@@ -31,13 +33,18 @@ 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;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.LinkAndLabelUtil;
+import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsProvider;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorProvider;
+import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ObjectAdapterToggleboxColumn;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
@@ -47,7 +54,8 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel
  * Panel for rendering entity collection; analogous to (any concrete subclass
  * of) {@link ScalarPanelAbstract2}.
  */
-public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implements CollectionSelectorProvider {
+public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implements CollectionSelectorProvider,
+        BulkActionsProvider {
 
     private static final long serialVersionUID = 1L;
 
@@ -57,6 +65,8 @@ public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implem
 
     private Label label;
 
+    private final AssociatedWithActionsHelper associatedWithActionsHelper;
+
     public CollectionPanel(
             final String id,
             final EntityCollectionModel collectionModel) {
@@ -67,14 +77,22 @@ 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 List<ObjectAction> associatedActions =
                 ObjectAction.Util.findForAssociation(adapter, otma, getDeploymentCategory());
 
+        associatedWithActionsHelper = new AssociatedWithActionsHelper(collectionModel);
+
+        final ToggledMementosProvider toggledMementosProvider =
+                new MyToggledMementosProvider(collectionModel, this, this);
+
         entityActionLinks.addAll(
-                LinkAndLabelUtil.asActionLinksForAdditionalLinksPanel(entityModel, associatedActions, null));
+                LinkAndLabelUtil
+                        .asActionLinksForAdditionalLinksPanel(
+                                entityModel, associatedActions, null, toggledMementosProvider));
 
         collectionModel.addLinkAndLabels(entityActionLinks);
+
     }
 
     @Override
@@ -87,6 +105,8 @@ public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implem
         collectionContents = getComponentFactoryRegistry().addOrReplaceComponent(this, ComponentType.COLLECTION_CONTENTS, getModel());
 
         addOrReplace(new NotificationPanel(ID_FEEDBACK, collectionContents, new ComponentFeedbackMessageFilter(collectionContents)));
+
+        setOutputMarkupId(true);
     }
 
     public Label createLabel(final String id, final String collectionName) {
@@ -106,6 +126,79 @@ public class CollectionPanel extends PanelAbstract<EntityCollectionModel> implem
     public void setSelectorDropdownPanel(CollectionSelectorPanel selectorDropdownPanel) {
         this.selectorDropdownPanel = selectorDropdownPanel;
     }
+
+    //endregion
+
+    //region > BulkActionsProvider
+    ObjectAdapterToggleboxColumn toggleboxColumn;
+
+    @Override
+    public ObjectAdapterToggleboxColumn getToggleboxColumn() {
+
+        if(toggleboxColumn == null) {
+            final List<ObjectAction> associatedActions =
+                    associatedWithActionsHelper.getAssociatedActions(getIsisSessionFactory());
+
+            final EntityCollectionModel entityCollectionModel = getModel();
+            if(associatedActions.isEmpty() || entityCollectionModel.isStandalone()) {
+                return null;
+            }
+
+            toggleboxColumn = new ObjectAdapterToggleboxColumn();
+            final OnSelectionHandler handler = new OnSelectionHandler() {
+
+                private static final long serialVersionUID = 1L;
+
+                @Override
+                public void onSelected(
+                        final Component context,
+                        final ObjectAdapter selectedAdapter,
+                        final AjaxRequestTarget ajaxRequestTarget) {
+                    getModel().toggleSelectionOn(selectedAdapter);
+                }
+
+            };
+            toggleboxColumn.setOnSelectionHandler(handler);
+        }
+
+        return toggleboxColumn;
+    }
+
+
+    @Override
+    public void configureBulkActions(final ObjectAdapterToggleboxColumn toggleboxColumn) {
+    }
+
+    private static class MyToggledMementosProvider implements ToggledMementosProvider, Serializable {
+        private final EntityCollectionModel collectionModel;
+        private final BulkActionsProvider bulkActionsProvider;
+        private final CollectionPanel collectionPanel;
+
+        MyToggledMementosProvider(
+                final EntityCollectionModel collectionModel,
+                final BulkActionsProvider bulkActionsProvider,
+                final CollectionPanel collectionPanel) {
+            this.collectionModel = collectionModel;
+            this.bulkActionsProvider = bulkActionsProvider;
+            this.collectionPanel = collectionPanel;
+        }
+
+        @Override
+        public List<ObjectAdapterMemento> getToggles() {
+            return collectionModel.getToggleMementosList();
+        }
+
+        @Override
+        public void clearToggles(final AjaxRequestTarget target) {
+            collectionModel.clearToggleMementosList();
+
+            final ObjectAdapterToggleboxColumn toggleboxColumn = bulkActionsProvider.getToggleboxColumn();
+            toggleboxColumn.clearToggles();
+
+            target.add(collectionPanel);
+        }
+    }
+
     //endregion
 
 
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsHelper.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsHelper.java
index 723f85b..39fd75a 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsHelper.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsHelper.java
@@ -22,8 +22,7 @@ import java.io.Serializable;
 import java.util.Collections;
 import java.util.List;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 
 import org.apache.isis.applib.filter.Filters;
@@ -34,52 +33,49 @@ import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.ui.components.collection.AssociatedWithActionsHelper;
 
+/**
+ * See also {@link AssociatedWithActionsHelper}.
+ */
 public class BulkActionsHelper implements Serializable {
 
-    private final EntityCollectionModel model;
+    private final EntityCollectionModel collectionModel;
 
     private static final long serialVersionUID = 1L;
 
-    public BulkActionsHelper(final EntityCollectionModel model) {
-        this.model = model;
-    }
-
-    private EntityCollectionModel getModel() {
-        return model;
+    public BulkActionsHelper(final EntityCollectionModel collectionModel) {
+        this.collectionModel = collectionModel;
     }
 
     public List<ObjectAction> getBulkActions(final IsisSessionFactory isisSessionFactory) {
-        final EntityCollectionModel model = getModel();
 
-        if(model.isParented()) {
+        if(collectionModel.isParented()) {
             return Collections.emptyList();
         }
 
-        final ObjectSpecification typeSpec = model.getTypeOfSpecification();
-
-        List<ObjectAction> objectActions = typeSpec.getObjectActions(ActionType.USER, Contributed.INCLUDED, Filters.<ObjectAction>any());
+        final ObjectSpecification objectSpec = getObjectSpecification(isisSessionFactory);
 
-        final DeploymentCategory deploymentCategory = isisSessionFactory.getDeploymentCategory();
-        if ( !deploymentCategory.isProduction()) {
-            List<ObjectAction> prototypeActions   = typeSpec.getObjectActions(ActionType.PROTOTYPE, Contributed.INCLUDED, Filters.<ObjectAction>any());
-            objectActions.addAll(prototypeActions);
-        }
+        final List<ActionType> actionTypes = inferActionTypes(isisSessionFactory);
+        List<ObjectAction> objectActions = objectSpec.getObjectActions(actionTypes, Contributed.INCLUDED, Filters.<ObjectAction>any());
 
-        List<ObjectAction> flattenedActions = objectActions;
-
-        return Lists.newArrayList(Iterables.filter(flattenedActions, BULK));
+        return FluentIterable.from(objectActions)
+                .filter(ObjectAction.Predicates.bulk())
+                .toList();
     }
 
+    private ObjectSpecification getObjectSpecification(final IsisSessionFactory isisSessionFactory) {
+        return collectionModel.getTypeOfSpecification();
+    }
 
-    @SuppressWarnings("deprecation")
-    private static final Predicate<ObjectAction> BULK = Filters.asPredicate(ObjectAction.Filters.bulk());
-
-    /**
-     * Protected so can be overridden in testing if required.
-     */
-    protected boolean isDebugMode() {
-        return true;
+    private List<ActionType> inferActionTypes(final IsisSessionFactory isisSessionFactory) {
+        final List<ActionType> actionTypes = Lists.newArrayList();
+        actionTypes.add(ActionType.USER);
+        final DeploymentCategory deploymentCategory = isisSessionFactory.getDeploymentCategory();
+        if ( !deploymentCategory.isProduction()) {
+            actionTypes.add(ActionType.PROTOTYPE);
+        }
+        return actionTypes;
     }
 
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
index 4a6a82d..a407aeb 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
@@ -52,6 +52,7 @@ import org.apache.isis.viewer.wicket.model.mementos.ActionMemento;
 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.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponse;
 import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponseType;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ObjectAdapterToggleboxColumn;
@@ -79,7 +80,8 @@ public final class BulkActionsLinkFactory implements ActionLinkFactory {
     @Override
     public LinkAndLabel newLink(
             final ObjectAction objectAction,
-            final String linkId) {
+            final String linkId,
+            final ToggledMementosProvider toggledMementosProviderIfAny) {
 
         final ActionMemento actionMemento = new ActionMemento(objectAction);
         final AbstractLink link = new Link<Object>(linkId) {
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java
index 39f2b96..335b8dc 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java
@@ -18,11 +18,13 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.collection.bulk;
 
+import java.io.Serializable;
+
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ObjectAdapterToggleboxColumn;
 
-public interface BulkActionsProvider {
+public interface BulkActionsProvider extends Serializable {
 
     void configureBulkActions(ObjectAdapterToggleboxColumn toggleboxColumn);
 
-    ObjectAdapterToggleboxColumn createToggleboxColumn();
+    ObjectAdapterToggleboxColumn getToggleboxColumn();
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
index 700998f..8093034 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
@@ -90,7 +90,7 @@ public class CollectionContentsAsAjaxTablePanel
         ObjectAdapterToggleboxColumn toggleboxColumn = null;
         if(bulkActionsProvider != null) {
 
-            toggleboxColumn = bulkActionsProvider.createToggleboxColumn();
+            toggleboxColumn = bulkActionsProvider.getToggleboxColumn();
             if(toggleboxColumn != null) {
                 columns.add(toggleboxColumn);
             }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
index 242b58a..eba0bb0 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
@@ -138,31 +138,35 @@ public class StandaloneCollectionPanel extends PanelAbstract<EntityCollectionMod
 
     //region > BulkActionsProvider
 
+    ObjectAdapterToggleboxColumn toggleboxColumn;
+
     @Override
-    public ObjectAdapterToggleboxColumn createToggleboxColumn() {
+    public ObjectAdapterToggleboxColumn getToggleboxColumn() {
 
-        final List<ObjectAction> bulkActions = bulkActionsHelper.getBulkActions(getIsisSessionFactory());
+        if (toggleboxColumn == null) {
+            final List<ObjectAction> bulkActions = bulkActionsHelper.getBulkActions(getIsisSessionFactory());
 
-        final EntityCollectionModel entityCollectionModel = getModel();
-        if(bulkActions.isEmpty() || entityCollectionModel.isParented()) {
-            return null;
-        }
+            final EntityCollectionModel entityCollectionModel = getModel();
+            if(bulkActions.isEmpty() || entityCollectionModel.isParented()) {
+                return null;
+            }
 
-        final ObjectAdapterToggleboxColumn toggleboxColumn = new ObjectAdapterToggleboxColumn();
-        final OnSelectionHandler handler = new OnSelectionHandler() {
+            toggleboxColumn = new ObjectAdapterToggleboxColumn();
+            final OnSelectionHandler handler = new OnSelectionHandler() {
 
-            private static final long serialVersionUID = 1L;
+                private static final long serialVersionUID = 1L;
 
-            @Override
-            public void onSelected(
-                    final Component context,
-                    final ObjectAdapter selectedAdapter,
-                    final AjaxRequestTarget ajaxRequestTarget) {
-                getModel().toggleSelectionOn(selectedAdapter);
-            }
+                @Override
+                public void onSelected(
+                        final Component context,
+                        final ObjectAdapter selectedAdapter,
+                        final AjaxRequestTarget ajaxRequestTarget) {
+                    getModel().toggleSelectionOn(selectedAdapter);
+                }
 
-        };
-        toggleboxColumn.setOnSelectionHandler(handler);
+            };
+            toggleboxColumn.setOnSelectionHandler(handler);
+        }
 
         return toggleboxColumn;
     }
@@ -181,7 +185,7 @@ public class StandaloneCollectionPanel extends PanelAbstract<EntityCollectionMod
         List<LinkAndLabel> linkAndLabels = Lists.transform(bulkActions, new Function<ObjectAction, LinkAndLabel>(){
             @Override
             public LinkAndLabel apply(ObjectAction objectAction) {
-                return linkFactory.newLink(objectAction, ID_ADDITIONAL_LINK);
+                return linkFactory.newLink(objectAction, ID_ADDITIONAL_LINK, null);
             }
         });
 
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/bootstrap/ModalDialog.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/bootstrap/ModalDialog.java
index f3a5e07..a0ffd4d 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/bootstrap/ModalDialog.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/bootstrap/ModalDialog.java
@@ -34,6 +34,8 @@ import de.agilecoders.wicket.extensions.markup.html.bootstrap.behavior.Draggable
  */
 public class ModalDialog<T> extends Modal<T> implements ActionPrompt {
 
+    private CloseHandler closeHandlerIfAny;
+
     public ModalDialog(String markupId) {
         this(markupId, null);
     }
@@ -82,6 +84,14 @@ public class ModalDialog<T> extends Modal<T> implements ActionPrompt {
             close(target);
         }
         setVisible(false);
+        if(closeHandlerIfAny != null) {
+            closeHandlerIfAny.close(target);
+        }
+    }
+
+    @Override
+    public void setOnClose(final CloseHandler closeHandlerIfAny) {
+        this.closeHandlerIfAny = closeHandlerIfAny;
     }
 
     @Override
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactory.java
index 4dced29..092838a 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactory.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactory.java
@@ -20,8 +20,10 @@
 package org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel;
 
 import java.io.Serializable;
+
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 
 public interface ActionLinkFactory extends Serializable {
 
@@ -31,5 +33,6 @@ public interface ActionLinkFactory extends Serializable {
      */
     LinkAndLabel newLink(
             final ObjectAction objectAction,
-            final String linkId);
+            final String linkId,
+            final ToggledMementosProvider toggledMementosProviderIfAny);
 }
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 a1ffa24..e705eba 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
@@ -17,8 +17,16 @@
 
 package org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel;
 
+import java.util.List;
 import java.util.concurrent.Callable;
 
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
 import org.apache.wicket.Application;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
@@ -29,13 +37,16 @@ import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.facets.param.defaults.togglebox.ActionParameterDefaultsFacetViaToggleBoxes;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 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;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettingsAccessor;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+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;
@@ -43,6 +54,7 @@ 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;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistryAccessor;
@@ -72,7 +84,8 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
 
     protected ActionLink newLink(
             final String linkId,
-            final ObjectAction action) {
+            final ObjectAction action,
+            final ToggledMementosProvider toggledMementosProviderIfAny) {
 
         final ActionModel actionModel = ActionModel.create(this.targetEntityModel, action);
 
@@ -80,9 +93,58 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
                 new ActionLink(linkId, actionModel, action) {
                     private static final long serialVersionUID = 1L;
 
-                    protected void doOnClick(AjaxRequestTarget target) {
+                    protected void doOnClick(final AjaxRequestTarget target) {
+
+                        if(toggledMementosProviderIfAny != null) {
+
+                            final PersistenceSession persistenceSession = getIsisSessionFactory()
+                                    .getCurrentSession().getPersistenceSession();
+                            final SpecificationLoader specificationLoader =
+                                    getIsisSessionFactory().getSpecificationLoader();
+
+                            final List<ObjectAdapterMemento> selectedMementos =
+                                    toggledMementosProviderIfAny.getToggles();
+
+                            final ImmutableList<Object> selectedPojos = FluentIterable.from(selectedMementos)
+                                    .transform(new Function<ObjectAdapterMemento, Object>() {
+                                        @Nullable @Override
+                                        public Object apply(@Nullable final ObjectAdapterMemento input) {
+                                            if(input == null) {
+                                                return null;
+                                            }
+                                            final ObjectAdapter objectAdapter = input.getObjectAdapter(
+                                                    AdapterManager.ConcurrencyChecking.NO_CHECK,
+                                                    persistenceSession, specificationLoader);
+                                            return objectAdapter != null ? objectAdapter.getObject() : null;
+                                        }
+                                    })
+                                    .filter(Predicates.notNull())
+                                    .toList();
+
+                            final ActionPrompt actionPrompt = ActionParameterDefaultsFacetViaToggleBoxes.withSelected(
+                                    selectedPojos,
+                                    new ActionParameterDefaultsFacetViaToggleBoxes.SerializableRunnable<ActionPrompt>() {
+                                        public ActionPrompt call() {
+                                            return performOnClick(target);
+                                        }
+                                    }
+                            );
+                            if(actionPrompt != null) {
+                                actionPrompt.setOnClose(new ActionPrompt.CloseHandler() {
+                                    @Override
+                                    public void close(final AjaxRequestTarget target) {
+                                        toggledMementosProviderIfAny.clearToggles(target);
+                                    }
+                                });
+                            }
+
+                        } else {
+                            performOnClick(target);
+                        }
+                    }
 
-                        ActionLinkFactoryAbstract.this.onClick(this, target);
+                    private ActionPrompt performOnClick(final AjaxRequestTarget target) {
+                        return ActionLinkFactoryAbstract.this.onClick(this, target);
                     }
 
                 };
@@ -91,8 +153,10 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
         return link;
     }
 
-
-    private void onClick(
+    /**
+     * @return the prompt, if not inline prompt
+     */
+    private ActionPrompt onClick(
             final ActionLink actionLink,
             final AjaxRequestTarget target) {
 
@@ -135,6 +199,8 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
                 actionParametersPanel.setActionPrompt(prompt);
                 prompt.showPrompt(target);
 
+                return prompt;
+
             } else {
 
 
@@ -202,6 +268,8 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
 
             target.add(scalarTypeContainer);
         }
+
+        return null;
     }
 
 
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 f8e4f4a..18981b6 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
@@ -753,6 +753,11 @@ div.referencePanel.scalarNameAndValueComponentType {
 tr.headers th form input {
     margin-left: -8px;
 }
+/*
+tbody td.togglebox-column form {
+    margin-left: 6px;
+}
+*/
 
 #aboutLink {
     padding-right: 30px;

-- 
To stop receiving notification emails like this one, please contact
danhaywood@apache.org.