You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2026/01/21 11:19:58 UTC

(causeway) branch 3960-row.action.visibility created (now c4940342b2a)

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

ahuber pushed a change to branch 3960-row.action.visibility
in repository https://gitbox.apache.org/repos/asf/causeway.git


      at c4940342b2a CAUSEWAY-3960: fixes Actions in Tables have wrong Usability/Visibility Logic

This branch includes the following new commits:

     new c4940342b2a CAUSEWAY-3960: fixes Actions in Tables have wrong Usability/Visibility Logic

The 1 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.



(causeway) 01/01: CAUSEWAY-3960: fixes Actions in Tables have wrong Usability/Visibility Logic

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3960-row.action.visibility
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit c4940342b2ac07e3e309ba3d261a05ca21b790e1
Author: andi-huber <ah...@apache.org>
AuthorDate: Wed Jan 21 12:19:47 2026 +0100

    CAUSEWAY-3960: fixes Actions in Tables have wrong Usability/Visibility
    Logic
    
    Task-Url: https://issues.apache.org/jira/browse/CAUSEWAY-3960
---
 .../viewer/commons/model/action/UiActionForm.java  | 14 ++++----
 .../viewer/wicket/model/models/ActionModel.java    | 16 ++++-----
 .../interaction/act/ActionInteractionWkt.java      | 12 +++----
 .../ActionLinksAsButtonInlinePanel.java            |  2 +-
 .../entityactions/ActionLinksAsDropDownPanel.java  |  4 +--
 .../entityactions/ActionLinksPanel.java            | 30 +++++++++--------
 .../ui/components/attributes/AttributePanel.java   | 39 +++++++++-------------
 .../parented/ParentedCollectionPanel.java          |  2 +-
 .../present/ajaxtable/columns/ActionColumn.java    | 10 +++---
 .../wicket/ui/components/layout/bs/col/Col.java    |  3 +-
 .../components/object/fieldset/PropertyGroup.java  | 15 ++++-----
 .../object/header/ObjectHeaderPanel.java           |  3 +-
 .../components/widgets/actionlink/ActionLink.java  | 27 ++++++++++++---
 13 files changed, 93 insertions(+), 84 deletions(-)

diff --git a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/action/UiActionForm.java b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/action/UiActionForm.java
index 2ec15693118..ec39064ff24 100644
--- a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/action/UiActionForm.java
+++ b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/action/UiActionForm.java
@@ -42,32 +42,30 @@ public interface UiActionForm
 
     // -- USABILITY
 
-    default Consent getUsabilityConsent() {
+    default Consent getUsabilityConsent(final Where where) {
         return getAction().isUsable(
                 getActionOwner(),
                 InteractionInitiatedBy.USER,
-                Where.OBJECT_FORMS);
+                where);
     }
 
     // -- VISABILITY
 
-    default Consent getVisibilityConsent() {
+    default Consent getVisibilityConsent(final Where where) {
 
         // guard against missing action owner
         var actionOwner = getActionOwner();
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(actionOwner)) {
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(actionOwner))
             return Veto.DEFAULT; // veto, so we don't render the action
-        }
 
         // check whether action owner type is hidden
-        if (getActionOwner().objSpec().isHidden()) {
+        if (getActionOwner().objSpec().isHidden())
             return Veto.DEFAULT;
-        }
 
         return getAction().isVisible(
                 actionOwner,
                 InteractionInitiatedBy.USER,
-                Where.OBJECT_FORMS);
+                where);
     }
 
     // -- VALIDITY
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ActionModel.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ActionModel.java
index ac8f51d2fa2..a6a4539588b 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ActionModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ActionModel.java
@@ -134,7 +134,7 @@ public static ActionModel forEntityFromActionColumn(
         return ActionModel.forEntity(
                         parentEntityModel,
                         action.getFeatureIdentifier(),
-                        Where.OBJECT_FORMS,
+                        Where.ALL_TABLES,
                         columnActionModifier,
                         null, null, null);
     }
@@ -153,8 +153,8 @@ public static ActionModel forCollection(
     public static ActionModel forPropertyOrParameter(
             final ObjectAction action,
             final UiAttributeWkt attributeModel) {
-        return attributeModel instanceof PropertyModel
-                ? forProperty(action, (PropertyModel)attributeModel)
+        return attributeModel instanceof PropertyModel p
+                ? forProperty(action, p)
                 : forParameter(action, (ParameterModel)attributeModel);
     }
 
@@ -175,14 +175,13 @@ public static ActionModel forParameter(
         //XXX[CAUSEWAY-3080] only supported, when parameter type is a singular composite value-type
         var param = parameterModel.getMetaModel();
         if(param.isSingular()
-                && param.getElementType().isCompositeValue()) {
+                && param.getElementType().isCompositeValue())
             return ActionModel.forEntity(
                             parameterModel.getParentUiModel(),
                             action.getFeatureIdentifier(),
                             Where.OBJECT_FORMS,
                             ColumnActionModifier.NONE,
                             null, parameterModel, null);
-        }
         return null;
     }
 
@@ -262,11 +261,11 @@ public Optional<ParameterModel> getAssociatedParameter() {
     }
 
     public boolean isVisible() {
-        return getVisibilityConsent().isAllowed();
+        return getVisibilityConsent(delegate.where()).isAllowed();
     }
 
     public boolean isEnabled() {
-        return getUsabilityConsent().isAllowed();
+        return getUsabilityConsent(delegate.where()).isAllowed();
     }
 
     @Override
@@ -309,11 +308,10 @@ public String toString() {
 
     private static void guardAgainstNotBookmarkable(final ManagedObject objectAdapter) {
         var isIdentifiable = ManagedObjects.isIdentifiable(objectAdapter);
-        if (!isIdentifiable) {
+        if (!isIdentifiable)
             throw new IllegalArgumentException(String.format(
                     "Object '%s' is not identifiable (has no identifier).",
                     objectAdapter.getTitle()));
-        }
     }
 
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
index 2cf1497ae99..2a72d233e0a 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
@@ -23,7 +23,6 @@
 import java.util.stream.Stream;
 
 import org.apache.wicket.model.ChainingModel;
-
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.Identifier;
@@ -46,6 +45,9 @@
 import org.apache.causeway.viewer.wicket.model.models.interaction.BookmarkedObjectWkt;
 import org.apache.causeway.viewer.wicket.model.models.interaction.HasBookmarkedOwnerAbstract;
 
+import lombok.Getter;
+import lombok.experimental.Accessors;
+
 /**
  * The parent (container) model of multiple <i>parameter models</i> which implement
  * {@link ChainingModel}.
@@ -71,7 +73,7 @@ public class ActionInteractionWkt
     private static final long serialVersionUID = 1L;
 
     private final String memberId;
-    private final Where where;
+    @Getter @Accessors(fluent=true) private final Where where;
 
     /**
      * memoize, so if we only need the meta-model,
@@ -153,16 +155,14 @@ protected ActionInteraction load() {
                     associatedWithParameterIfAny.getParameterNegotiationModel(), paramIndex, memberId, where);
         }
 
-        if(associatedWithCollectionIfAny!=null) {
+        if(associatedWithCollectionIfAny!=null)
             return ActionInteraction.startWithMultiselect(getBookmarkedOwner(), memberId, where,
                     associatedWithCollectionIfAny.getDataTableModel());
-        }
 
-        if(associatedWithPropertyIfAny!=null) {
+        if(associatedWithPropertyIfAny!=null)
             // supports composite-value-types via mixin
             return ActionInteraction.startAsBoundToProperty(
                     associatedWithPropertyIfAny.getManagedProperty(), memberId, where);
-        }
 
         return ActionInteraction.start(getBookmarkedOwner(), memberId, where);
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsButtonInlinePanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsButtonInlinePanel.java
index 65a0f6da821..ae573434888 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsButtonInlinePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsButtonInlinePanel.java
@@ -26,6 +26,6 @@ class ActionLinksAsButtonInlinePanel extends ActionLinksPanel {
     private static final long serialVersionUID = 1L;
 
     public ActionLinksAsButtonInlinePanel(final String id, final Can<ActionLink> links) {
-        super(id, links, Style.INLINE_LIST);
+        super(id, links, ActionPanelStyle.INLINE_LIST);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsDropDownPanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsDropDownPanel.java
index 736c4def7c5..528dadb3784 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsDropDownPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksAsDropDownPanel.java
@@ -25,7 +25,7 @@ class ActionLinksAsDropDownPanel extends ActionLinksPanel {
 
     private static final long serialVersionUID = 1L;
 
-    public ActionLinksAsDropDownPanel(final String id, final Can<ActionLink> links) {
-        super(id, links, Style.DROPDOWN);
+    public ActionLinksAsDropDownPanel(final String id, final Can<ActionLink> links, final ActionPanelStyle style) {
+        super(id, links, style);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksPanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksPanel.java
index 4de7779200c..bd3c5c119e5 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/actionlinks/entityactions/ActionLinksPanel.java
@@ -20,11 +20,11 @@
 
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.wicket.MarkupContainer;
 
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.viewer.commons.model.decorators.ActionDecorators.ActionStyle;
 import org.apache.causeway.viewer.wicket.model.models.ActionModel;
@@ -36,7 +36,7 @@
 
 import lombok.RequiredArgsConstructor;
 
-public class ActionLinksPanel
+public abstract class ActionLinksPanel
 extends MenuablePanelAbstract {
 
     private static final long serialVersionUID = 1L;
@@ -46,7 +46,7 @@ public class ActionLinksPanel
     private static final String ID_ADDITIONAL_LINK_TITLE = "additionalLinkTitle";
 
     @RequiredArgsConstructor
-    public enum Style {
+    public enum ActionPanelStyle {
         INLINE_LIST(ActionStyle.BUTTON) {
             @Override
             public ActionLinksPanel newPanel(final String id, final Can<ActionLink> links) {
@@ -56,7 +56,7 @@ public ActionLinksPanel newPanel(final String id, final Can<ActionLink> links) {
         DROPDOWN(ActionStyle.MENU_ITEM) {
             @Override
             public ActionLinksPanel newPanel(final String id, final Can<ActionLink> links) {
-                return new ActionLinksAsDropDownPanel(id, links);
+                return new ActionLinksAsDropDownPanel(id, links, DROPDOWN);
             }
         };
         abstract ActionLinksPanel newPanel(String id, Can<ActionLink> links);
@@ -71,8 +71,9 @@ public static ActionLinksPanel addActionLinks(
             final MarkupContainer markupContainer,
             final String id,
             final Can<ActionModel> links,
-            final Style style) {
-        var panel = actionLinks(id, links, style);
+            final ActionPanelStyle style,
+            final Where renderWhere) {
+        var panel = actionLinks(id, links, style, renderWhere);
         if(panel.isEmpty()) {
             WktComponents.permanentlyHide(markupContainer, id);
             return null;
@@ -82,18 +83,19 @@ public static ActionLinksPanel addActionLinks(
 
     public static Optional<ActionLinksPanel> actionLinks(
             final String id,
-            final Can<ActionModel> links,
-            final Style style) {
-        if(links.isEmpty()) {
-            return Optional.empty();
-        }
-        return Optional.of(style.newPanel(id, links.map(ActionLink::create)));
+            final Can<ActionModel> actionModels,
+            final ActionPanelStyle style,
+            final Where renderWhere) {
+
+        return actionModels.isEmpty()
+            ? Optional.empty()
+            : Optional.of(style.newPanel(id, actionModels.map(act->ActionLink.create(act, renderWhere))));
     }
 
     protected ActionLinksPanel(
             final String id,
             final Can<ActionLink> actionLinks,
-            final Style style) {
+            final ActionPanelStyle style) {
         super(id, actionLinks);
         setOutputMarkupId(true);
 
@@ -122,7 +124,7 @@ protected final Stream<ActionLink> streamActionLinks() {
     }
 
     protected final List<ActionLink> listOfActionLinks() {
-        return streamActionLinks().collect(Collectors.toList());
+        return streamActionLinks().toList();
     }
 
     public final boolean hasAnyVisibleLink() {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/attributes/AttributePanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/attributes/AttributePanel.java
index bc249ff4bef..f92ffb62287 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/attributes/AttributePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/attributes/AttributePanel.java
@@ -39,6 +39,7 @@
 
 import org.apache.causeway.applib.annotation.ActionLayout;
 import org.apache.causeway.applib.annotation.LabelPosition;
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.collections.ImmutableEnumSet;
 import org.apache.causeway.commons.internal.base._Casts;
@@ -147,24 +148,19 @@ public boolean isViewingAndCanEditAny() {
 
         static RenderScenario inferFrom(final AttributePanel scalarPanel) {
             var attributeModel = scalarPanel.attributeModel();
-            if(attributeModel.getRenderingHint().isInTable()) {
+            if(attributeModel.getRenderingHint().isInTable())
                 return COMPACT;
-            }
-            if(attributeModel.isParameter()) {
+            if(attributeModel.isParameter())
                 return _Util.canParameterEnterNestedEdit(attributeModel)
                         ? EDITING_WITH_LINK_TO_NESTED // nested/embedded dialog
                         : EDITING; // for params always EDITING even if editing is vetoed
-            }
             // at this point we are processing a property (not a parameter)
-            if(attributeModel.isEditingMode()) {
+            if(attributeModel.isEditingMode())
                 return EDITING;
-            }
-            if(_Util.canPropertyEnterInlineEditDirectly(attributeModel)) {
+            if(_Util.canPropertyEnterInlineEditDirectly(attributeModel))
                 return CAN_EDIT_INLINE;
-            }
-            if(_Util.lookupPropertyActionForInlineEdit(attributeModel).isPresent()) {
+            if(_Util.lookupPropertyActionForInlineEdit(attributeModel).isPresent())
                 return CAN_EDIT_INLINE_VIA_ACTION;
-            }
             return attributeModel.disabledReason().isPresent()
                     ? READONLY
                     : CAN_EDIT;
@@ -520,7 +516,8 @@ public void addChangeListener(final AttributeModelChangeListener listener) {
     protected final <T extends Behavior> void addOrReplaceBehavoir(
             final @NonNull Class<T> behaviorClass, final @NonNull Supplier<T> factory) {
         var validationFeedbackReceiver = getValidationFeedbackReceiver();
-        if(validationFeedbackReceiver == null) { return; }
+        if(validationFeedbackReceiver == null)
+            return;
         for (var behavior : validationFeedbackReceiver.getBehaviors(behaviorClass)) {
             validationFeedbackReceiver.remove(behavior);
         }
@@ -559,9 +556,8 @@ protected final void scalarNameLabelAddTo(final MarkupContainer container, final
         helper.visibleLabelId.ifPresent(visibleLabelId->{
 
             final Label scalarNameLabel = Wkt.labelAdd(container, visibleLabelId, labelCaption);
-            if(_Strings.isNullOrEmpty(labelCaption.getObject())) {
+            if(_Strings.isNullOrEmpty(labelCaption.getObject()))
                 return;
-            }
 
             WktDecorators.formLabel()
                 .decorate(scalarNameLabel, FormLabelDecorationModel
@@ -601,9 +597,8 @@ void hideHiddenLabels(final MarkupContainer container) {
     protected abstract Component getValidationFeedbackReceiver();
 
     private void addFeedbackOnlyTo(final MarkupContainer markupContainer, final Component component) {
-        if(component==null) {
+        if(component==null)
             return;
-        }
         markupContainer.addOrReplace(
                 RegularFrame.FEEDBACK.createComponent(id->
                     new NotificationPanel(id, component, new ComponentFeedbackMessageFilter(component))));
@@ -617,13 +612,13 @@ private void addActionLinksBelowAndRight(
                 .filter(ActionModel.isPositionedAt(ActionLayout.Position.BELOW));
         ActionLinksPanel.addActionLinks(
                 labelIfRegular, RegularFrame.ASSOCIATED_ACTION_LINKS_BELOW.getContainerId(),
-                linksBelow, ActionLinksPanel.Style.INLINE_LIST);
+                linksBelow, ActionLinksPanel.ActionPanelStyle.INLINE_LIST, Where.OBJECT_FORMS);
 
         var linksRight = actionModels
                 .filter(ActionModel.isPositionedAt(ActionLayout.Position.RIGHT));
         ActionLinksPanel.addActionLinks(
                 labelIfRegular, RegularFrame.ASSOCIATED_ACTION_LINKS_RIGHT.getContainerId(),
-                linksRight, ActionLinksPanel.Style.DROPDOWN);
+                linksRight, ActionLinksPanel.ActionPanelStyle.DROPDOWN, Where.OBJECT_FORMS);
     }
 
     /**
@@ -698,18 +693,16 @@ public Repaint updateIfNecessary(
            onMakeNotEditable(usabilityConsent.getReasonAsString().orElse(null));
        }
 
-       if (visibilityBefore != visibilityAfter) {
-           // repaint the param panel if visibility has changed
+       if (visibilityBefore != visibilityAfter)
+        // repaint the param panel if visibility has changed
            return visibilityAfter
                    ? Repaint.REQUIRED_ON_PARENT
                    : Repaint.REQUIRED;
-       }
 
        if (usabilityBefore != usabilityAfter
-               && visibilityAfter) {
-           // repaint the param panel if usability has changed, but only if visible
+               && visibilityAfter)
+        // repaint the param panel if usability has changed, but only if visible
            return Repaint.REQUIRED;
-       }
 
        return Repaint.OPTIONAL;
    }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/parented/ParentedCollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/parented/ParentedCollectionPanel.java
index 840e9ef5a81..62d93e33304 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/parented/ParentedCollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/parented/ParentedCollectionPanel.java
@@ -152,7 +152,7 @@ private void buildGui() {
 
             final Can<ActionModel> links = collectionModel.getLinks();
             ActionLinksPanel.addActionLinks(
-                    div, ID_ADDITIONAL_LINKS, links, ActionLinksPanel.Style.INLINE_LIST);
+                    div, ID_ADDITIONAL_LINKS, links, ActionLinksPanel.ActionPanelStyle.INLINE_LIST, Where.OBJECT_FORMS);
 
             createSelectorDropdownPanel(collectionModel);
             collectionPanel.setSelectorDropdownPanel(selectorDropdownPanel);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/present/ajaxtable/columns/ActionColumn.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/present/ajaxtable/columns/ActionColumn.java
index 0f07f6af678..d1deb0b53cf 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/present/ajaxtable/columns/ActionColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collection/present/ajaxtable/columns/ActionColumn.java
@@ -21,21 +21,21 @@
 import java.util.Optional;
 
 import org.apache.wicket.Component;
+import org.jspecify.annotations.NonNull;
 
 import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
 import org.apache.causeway.viewer.wicket.model.models.ActionModel;
 import org.apache.causeway.viewer.wicket.model.models.ActionModel.ColumnActionModifier;
-import org.apache.causeway.viewer.wicket.model.models.coll.DataRowWkt;
-import org.apache.causeway.viewer.wicket.model.models.coll.CollectionModel.Variant;
 import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
+import org.apache.causeway.viewer.wicket.model.models.coll.CollectionModel.Variant;
+import org.apache.causeway.viewer.wicket.model.models.coll.DataRowWkt;
 import org.apache.causeway.viewer.wicket.ui.components.actionlinks.entityactions.ActionLinksPanel;
 import org.apache.causeway.viewer.wicket.ui.util.Wkt;
 
-import org.jspecify.annotations.NonNull;
-
 public final class ActionColumn
 extends GenericColumnAbstract {
 
@@ -83,7 +83,7 @@ protected Component createCellComponent(
                     determineColumnActionModifier(act, elementType)))
             .collect(Can.toCan());
 
-        return ActionLinksPanel.actionLinks(componentId, actionModels, ActionLinksPanel.Style.DROPDOWN)
+        return ActionLinksPanel.actionLinks(componentId, actionModels, ActionLinksPanel.ActionPanelStyle.DROPDOWN, Where.ALL_TABLES)
                 .map(Component.class::cast)
                 .orElseGet(()->Wkt.label(componentId, ""));
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
index 0bb7102a94b..c2a5fa327ec 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/layout/bs/col/Col.java
@@ -26,6 +26,7 @@
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.model.Model;
 
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.applib.layout.component.ActionLayoutData;
 import org.apache.causeway.applib.layout.component.CollectionLayoutData;
 import org.apache.causeway.applib.layout.component.DomainObjectLayoutData;
@@ -132,7 +133,7 @@ private void buildGui() {
         .collect(Can.toCan());
 
         if (!visibleActions.isEmpty()) {
-            ActionLinksPanel.addActionLinks(actionOwner, actionIdToUse, visibleActions, ActionLinksPanel.Style.INLINE_LIST);
+            ActionLinksPanel.addActionLinks(actionOwner, actionIdToUse, visibleActions, ActionLinksPanel.ActionPanelStyle.INLINE_LIST, Where.OBJECT_FORMS);
             visible = true;
         } else {
             WktComponents.permanentlyHide(actionOwner, actionIdToUse);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/fieldset/PropertyGroup.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/fieldset/PropertyGroup.java
index 1e06422b578..197ac45cead 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/fieldset/PropertyGroup.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/fieldset/PropertyGroup.java
@@ -112,13 +112,15 @@ private List<Component> buildGui() {
                     panelHeading, ID_ASSOCIATED_ACTION_LINKS_PANEL,
                     memberGroupActions
                         .filter(ActionModel.isPositionedAt(ActionLayout.Position.PANEL)),
-                    ActionLinksPanel.Style.INLINE_LIST);
+                    ActionLinksPanel.ActionPanelStyle.INLINE_LIST,
+                    Where.OBJECT_FORMS);
 
             ActionLinksPanel.addActionLinks(
                     panelHeading, ID_ASSOCIATED_ACTION_LINKS_PANEL_DROPDOWN,
                     memberGroupActions
                         .filter(ActionModel.isPositionedAt(ActionLayout.Position.PANEL_DROPDOWN)),
-                    ActionLinksPanel.Style.DROPDOWN);
+                    ActionLinksPanel.ActionPanelStyle.DROPDOWN,
+                    Where.OBJECT_FORMS);
         }
 
         // either add the built content, or hide entire
@@ -219,15 +221,12 @@ public boolean isVisible() {
         // TODO: should remove this hack.  We need some sort of SPI for ScalarPanelAbstract2's and any other component,
         // (eg PdfJsViewer) that can implement.  It's "probably" just a matter of having PdfJsViewer do its work in the
         // correct Wicket callback (probably onConfigure).
-        if(childComponents.size() > childScalarPanels.size()) {
-            return true;
-        }
-        // HACK:END
+        if(childComponents.size() > childScalarPanels.size())
+         return true;
 
         for (final AttributePanel childComponent : childScalarPanels) {
-            if(childComponent.isVisibilityAllowed()) {
+            if(childComponent.isVisibilityAllowed())
                 return true;
-            }
         }
         return false;
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/header/ObjectHeaderPanel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/header/ObjectHeaderPanel.java
index 88b94dfc32d..113046c46a4 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/header/ObjectHeaderPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/header/ObjectHeaderPanel.java
@@ -20,6 +20,7 @@
 
 import org.apache.wicket.Component;
 
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
@@ -78,7 +79,7 @@ private void buildEntityActionsGui() {
 
             ActionLinksPanel
                     .addActionLinks(this, ID_OBJECT_ACTIONS, topLevelActions,
-                            ActionLinksPanel.Style.INLINE_LIST);
+                            ActionLinksPanel.ActionPanelStyle.INLINE_LIST, Where.OBJECT_FORMS);
         } else {
             WktComponents.permanentlyHide(this, ID_OBJECT_ACTIONS);
         }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
index 1d09eba3ade..f0b6c2ed2c6 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
@@ -27,6 +27,7 @@
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.jspecify.annotations.NonNull;
 
+import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.commons.internal.assertions._Assert;
 import org.apache.causeway.commons.internal.debug._Probe;
 import org.apache.causeway.commons.internal.debug._Probe.EntryPoint;
@@ -81,22 +82,36 @@ public final class ActionLink
     private static final long serialVersionUID = 1L;
     private static final String ID_ACTION_LINK = "actionLink";
 
+    public enum ActionRenderWhere {
+        ANYWHERE_BUT_NOT_TABLE,
+        TABLE_ACTION_COLUMN
+    }
+
     public static ActionLink create(
             final @NonNull ActionModel actionModel) {
+        return create(actionModel, Where.OBJECT_FORMS);
+    }
+
+    public static ActionLink create(
+            final @NonNull ActionModel actionModel,
+            final @NonNull Where where) {
 
-        var actionLink = new ActionLink(ID_ACTION_LINK, actionModel);
+        var actionLink = new ActionLink(ID_ACTION_LINK, actionModel, where);
         return Wkt.cssAppend(actionLink, "noVeil");
     }
 
     private final AjaxIndicatorAppender indicatorAppenderIfAny;
+    private final Where where; // where to render the action, used to check action's usability and visibility
 
     private ActionLink(
             final String id,
-            final ActionModel model) {
+            final ActionModel model,
+            final Where where) {
         super(id, model);
 
         _Assert.assertNotNull(model.getAction(), "ActionLink requires an Action");
 
+        this.where = where;
         this.indicatorAppenderIfAny = getSettings().useIndicatorForNoArgAction()
                 ? new AjaxIndicatorAppender()
                 : null;
@@ -134,18 +149,20 @@ protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
     public String getReasonDisabledIfAny() {
         // no point evaluating if not visible
         return isVisible()
-                ? getActionModel().getUsabilityConsent().getReasonAsString().orElse(null)
+                ? getActionModel().getUsabilityConsent(where).getReasonAsString().orElse(null)
                 : null;
     }
 
     @Override
     public boolean isVisible() {
-        return getActionModel().getVisibilityConsent().isAllowed();
+        //TODO honor where
+        return getActionModel().getVisibilityConsent(where).isAllowed();
     }
 
     @Override
     public boolean isEnabled() {
-        return getActionModel().getUsabilityConsent().isAllowed();
+        //TODO honor where
+        return getActionModel().getUsabilityConsent(where).isAllowed();
     }
 
     @SuppressWarnings("deprecation")