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:14 UTC

[isis] branch maint-1.16.1 updated (3263275 -> 26e84ce)

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

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


    from 3263275  ISIS-1589: adds documentation for .layout.fallback.xml
     new 7c495c7  ISIS-1585: adds @Action(associateWith=...)
     new 26e84ce  ISIS-1585: adds support for checkboxes in parented collections, to act as the defaults for any associated actions.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/asciidoc/guides/rgant/_rgant-Action.adoc  |   6 +
 .../guides/rgant/_rgant-Action_associateWith.adoc  |  61 ++++++++++
 .../guides/rgant/_rgant-Action_hidden.adoc         |   2 +-
 .../org/apache/isis/applib/annotation/Action.java  |  18 +++
 .../action/ActionAnnotationFacetFactory.java       |  53 +++++++++
 .../members/order/MemberOrderFacetAbstract.java    |  21 +++-
 ...va => MemberOrderFacetForActionAnnotation.java} |   9 +-
 ...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 ++
 ...tionFacetFactoryTest_NameAndSequence_parse.java |  43 +++++++
 .../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 +
 .../modules/simple/dom/impl/SimpleObject.java      |   3 +-
 .../simple/dom/impl/SimpleObject.layout.xml        |   6 +-
 37 files changed, 886 insertions(+), 98 deletions(-)
 create mode 100644 adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc
 copy core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/{MemberOrderFacetXml.java => MemberOrderFacetForActionAnnotation.java} (78%)
 create mode 100644 core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxes.java
 create mode 100644 core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/defaults/togglebox/ActionParameterDefaultsFacetViaToggleBoxesFactory.java
 create mode 100644 core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_NameAndSequence_parse.java
 create mode 100644 core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ToggledMementosProvider.java
 create mode 100644 core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java

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

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

Posted by da...@apache.org.
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.

[isis] 01/02: ISIS-1585: adds @Action(associateWith=...)

Posted by da...@apache.org.
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 7c495c7e5c3b6bbbd2126c29dfc787f71689c7ca
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Feb 9 13:49:01 2018 +0000

    ISIS-1585: adds @Action(associateWith=...)
---
 .../main/asciidoc/guides/rgant/_rgant-Action.adoc  |  6 +++
 .../guides/rgant/_rgant-Action_associateWith.adoc  | 38 ++++++++++++++++
 .../guides/rgant/_rgant-Action_hidden.adoc         |  2 +-
 .../org/apache/isis/applib/annotation/Action.java  | 18 ++++++++
 .../action/ActionAnnotationFacetFactory.java       | 53 ++++++++++++++++++++++
 .../members/order/MemberOrderFacetAbstract.java    | 21 +++++++--
 .../MemberOrderFacetForActionAnnotation.java       | 34 ++++++++++++++
 ...tionFacetFactoryTest_NameAndSequence_parse.java | 43 ++++++++++++++++++
 .../modules/simple/dom/impl/SimpleObject.java      |  3 +-
 .../simple/dom/impl/SimpleObject.layout.xml        |  6 +--
 10 files changed, 212 insertions(+), 12 deletions(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action.adoc
index 3d0c422..bac04b4 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action.adoc
@@ -18,6 +18,11 @@ The table below summarizes the annotation's attributes.
 | Description
 
 
+|xref:../rgant/rgant.adoc#_rgant-Action_associateWith[`associateWith()`]
+|_name:sequence_ +
+("")
+|associates an action with another property or collection of the action.
+
 |xref:../rgant/rgant.adoc#_rgant-Action_command[`command()`]
 |`AS_CONFIGURED`, `ENABLED`, `DISABLED` +
 (`AS_CONFIGURED`)
@@ -125,6 +130,7 @@ public class ToDoItem {
 
 
 
+include::_rgant-Action_associateWith.adoc[leveloffset=+1]
 include::_rgant-Action_command.adoc[leveloffset=+1]
 include::_rgant-Action_domainEvent.adoc[leveloffset=+1]
 include::_rgant-Action_hidden.adoc[leveloffset=+1]
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
new file mode 100644
index 0000000..913d478
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_associateWith.adoc
@@ -0,0 +1,38 @@
+[[_rgant-Action_associateWith]]
+= `associateWith()`
+:Notice: 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 ag [...]
+:_basedir: ../../
+:_imagesdir: images/
+
+
+
+
+The `associateWith` attribute allows an action to be associated with other properties or collections of the same domain object.
+
+For example, an `Order` could have a collection of ``OrderItem``s, and might provide actions to add and remove items:
+
+[source,java]
+----
+public class Order {
+
+    @Collection
+    SortedSet<OrderItem> getItems() { ... }
+
+    @Action(associateWith="items:1")
+    public Order addItem(Product p, int quantity) { ... }
+
+    @Action(associateWith="items:2")
+    public Order removeItem(Product p, int quantity) { ... }
+    ...
+}
+----
+
+These actions - `addItem()` and `removeItem()` can be thought of as associated with with the `items` collection because that is the state that they primarily affect.
+
+In the user interface associated actions are rendered close to the member to which they relate.
+
+[NOTE]
+====
+The same effect can be accomplished using `@MemberOrder` or with the `.layout.xml` file.
+====
+
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_hidden.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_hidden.adoc
index 1646495..d0b2e99 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_hidden.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_hidden.adoc
@@ -7,7 +7,7 @@
 
 
 
-Actions can be hidden at the domain-level, indicating that they are not visible to the end-user.  This attribute can also be applied to xref:../rgant/rgant.adoc#_rgant-Property_hidden[properties] and xref:../rgant/rgant.adoc#_rgant-Collection_hidden[collections].
+Actions can be hidden at the domain-level, indicating that they are not visible to the end-user. This attribute can also be applied to xref:../rgant/rgant.adoc#_rgant-Property_hidden[properties] and xref:../rgant/rgant.adoc#_rgant-Collection_hidden[collections].
 
 [TIP]
 ====
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
index 17aca49..a415185 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
@@ -208,4 +208,22 @@ public @interface Action {
     RestrictTo restrictTo() default RestrictTo.NO_RESTRICTIONS;
 
 
+    // //////////////////////////////////////
+
+
+    /**
+     * Associates this action with a property or collection, specifying its id and optionally the order in the UI.
+     *
+     * <p>
+     *     This is an alternative to using {@link MemberOrder#name()}.  To specify the order (equivalent to
+     *     {@link MemberOrder#sequence()}}) add a suffix <code>:XXX</code>.
+     * </p>
+     *
+     * <p>
+     *     For example <code>@Action(associateWith="items:2.1")</code>
+     * </p>
+     */
+    String associateWith() default "";
+
+
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
index 03e95e1..a57b8ef 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
@@ -20,6 +20,9 @@
 package org.apache.isis.core.metamodel.facets.actions.action;
 
 import java.lang.reflect.Method;
+import java.util.regex.Pattern;
+
+import com.google.common.base.Strings;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionInteraction;
@@ -81,6 +84,8 @@ import org.apache.isis.core.metamodel.facets.actions.publish.PublishedActionFace
 import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacet;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
+import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
+import org.apache.isis.core.metamodel.facets.members.order.annotprop.MemberOrderFacetForActionAnnotation;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.CollectionUtils;
@@ -127,6 +132,7 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract
         processPublishing(processMethodContext);
 
         processTypeOf(processMethodContext);
+        processAssociateWith(processMethodContext);
     }
 
     void processInvocation(final ProcessMethodContext processMethodContext) {
@@ -453,6 +459,53 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract
         FacetUtil.addFacet(typeOfFacet);
     }
 
+    void processAssociateWith(final ProcessMethodContext processMethodContext) {
+
+        final Method method = processMethodContext.getMethod();
+        final FacetedMethod holder = processMethodContext.getFacetHolder();
+
+        // check for @Action(associateWith=...)
+        MemberOrderFacet memberOrderFacet = null;
+
+        final Action action = Annotations.getAnnotation(method, Action.class);
+        if (action != null) {
+            final String associateWith = action.associateWith();
+            final NameAndSequence ns = NameAndSequence.parse(associateWith);
+            if(ns != null) {
+                memberOrderFacet = new MemberOrderFacetForActionAnnotation(ns.name, ns.sequence, holder);
+            }
+        }
+
+        FacetUtil.addFacet(memberOrderFacet);
+    }
+
+    static class NameAndSequence {
+
+        private static final Pattern pattern = Pattern.compile(":");
+        static NameAndSequence parse(final String associateWith) {
+            if(Strings.isNullOrEmpty(associateWith)) {
+                return null;
+            }
+            final String[] split = pattern.split(associateWith);
+            switch (split.length) {
+            case 1:
+                return new NameAndSequence(split[0], "1");
+            case 2:
+                return new NameAndSequence(split[0], split[1]);
+            default:
+                return null;
+            }
+        }
+
+        final String name;
+        final String sequence;
+        NameAndSequence(final String name, final String sequence) {
+            this.name = name;
+            this.sequence = sequence;
+        }
+
+    }
+
     // ///////////////////////////////////////////////////////////////
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/MemberOrderFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/MemberOrderFacetAbstract.java
index c888094..edf101d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/MemberOrderFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/MemberOrderFacetAbstract.java
@@ -36,26 +36,37 @@ public abstract class MemberOrderFacetAbstract extends MultipleValueFacetAbstrac
     private final String originalName;
     private final String name;
     private final String sequence;
-    private final TranslationService translationService;
 
     public MemberOrderFacetAbstract(
             final String name,
             final String sequence,
             final TranslationService translationService,
             final FacetHolder holder) {
+        this(translatedValueElse(name, "", translationService, holder),
+             sequence,
+             holder);
+    }
+
+    public MemberOrderFacetAbstract(
+            final String name,
+            final String sequence,
+            final FacetHolder holder) {
         super(type(), holder);
-        this.translationService = translationService;
-        this.name = translatedValueElse(name, "");
+        this.name = valueElse(name, "");
         this.originalName = valueElse(name, "");
         this.sequence = valueElse(sequence, "1");
     }
 
-    private String translatedValueElse(final String name, final String defaultValue) {
+    private static String translatedValueElse(
+            final String name,
+            final String defaultValue,
+            final TranslationService translationService,
+            final FacetHolder holder) {
         final boolean nullOrEmpty = Strings.isNullOrEmpty(name);
         if (nullOrEmpty) {
             return defaultValue;
         } else {
-            final IdentifiedHolder identifiedHolder = (IdentifiedHolder) getFacetHolder();
+            final IdentifiedHolder identifiedHolder = (IdentifiedHolder) holder;
             final String context = identifiedHolder.getIdentifier().getClassName();
             return translationService.translate(context, name);
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java
new file mode 100644
index 0000000..0f5dc91
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java
@@ -0,0 +1,34 @@
+/*
+ *  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.members.order.annotprop;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacetAbstract;
+
+public class MemberOrderFacetForActionAnnotation extends MemberOrderFacetAbstract {
+
+    public MemberOrderFacetForActionAnnotation(
+            final String name,
+            final String sequence,
+            final FacetHolder holder) {
+        super(name, sequence, holder);
+    }
+
+}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_NameAndSequence_parse.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_NameAndSequence_parse.java
new file mode 100644
index 0000000..63d0152
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_NameAndSequence_parse.java
@@ -0,0 +1,43 @@
+package org.apache.isis.core.metamodel.facets.actions.action;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class ActionAnnotationFacetFactoryTest_NameAndSequence_parse {
+
+    @Test
+    public void null_or_empty() throws Exception {
+        assertNull(ActionAnnotationFacetFactory.NameAndSequence.parse(null));
+        assertNull(ActionAnnotationFacetFactory.NameAndSequence.parse(""));
+    }
+
+    @Test
+    public void too_long() throws Exception {
+        assertNull(ActionAnnotationFacetFactory.NameAndSequence.parse("abc:def:ghi"));
+    }
+
+    @Test
+    public void name_and_sequence() throws Exception {
+        final ActionAnnotationFacetFactory.NameAndSequence ns =
+                ActionAnnotationFacetFactory.NameAndSequence.parse("items:2.3");
+        assertNotNull(ns);
+
+        assertEquals("items",ns.name);
+        assertEquals("2.3",ns.sequence);
+    }
+
+    @Test
+    public void no_sequence() throws Exception {
+        final ActionAnnotationFacetFactory.NameAndSequence ns =
+                ActionAnnotationFacetFactory.NameAndSequence.parse("items");
+        assertNotNull(ns);
+
+        assertEquals("items",ns.name);
+        assertEquals("1",ns.sequence);
+    }
+
+
+}
\ No newline at end of file
diff --git a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.java b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.java
index 32ff5bd..9c598d3 100644
--- a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.java
+++ b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.java
@@ -24,6 +24,7 @@ import javax.jdo.annotations.VersionStrategy;
 import com.google.common.collect.ComparisonChain;
 
 import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Auditing;
 import org.apache.isis.applib.annotation.CommandReification;
 import org.apache.isis.applib.annotation.DomainObject;
@@ -63,7 +64,7 @@ public class SimpleObject implements Comparable<SimpleObject> {
     private String notes;
 
 
-    @Action(semantics = SemanticsOf.IDEMPOTENT, command = CommandReification.ENABLED, publishing = Publishing.ENABLED)
+    @Action(semantics = SemanticsOf.IDEMPOTENT, command = CommandReification.ENABLED, publishing = Publishing.ENABLED, associateWith = "name")
     public SimpleObject updateName(
             @Parameter(maxLength = 40)
             @ParameterLayout(named = "Name")
diff --git a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.layout.xml b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.layout.xml
index 00c97fa..955d478 100644
--- a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.layout.xml
+++ b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/dom/impl/SimpleObject.layout.xml
@@ -34,11 +34,7 @@
                                 <c:action id="delete">
                                     <c:describedAs>Deletes this object from the persistent datastore</c:describedAs>
                                 </c:action>
-                                <c:property id="name" namedEscaped="true">
-                                    <c:action id="updateName">
-                                        <c:describedAs>Updates the object's name</c:describedAs>
-                                    </c:action>
-                                </c:property>
+                                <c:property id="name" namedEscaped="true"/>
                                 <c:property id="notes" namedEscaped="true" multiLine="10" hidden="ALL_TABLES"/>
                             </c:fieldSet>
                         </bs3:col>

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