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 <[email protected]>
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")

Reply via email to