Skip to content

[8793] Do not allow a user to select a soft publishing dependency for publish that they are not allowed to publish - #8893

Open
jvega190 wants to merge 5 commits into
craftersoftware:developfrom
jvega190:enhancement/8793
Open

[8793] Do not allow a user to select a soft publishing dependency for publish that they are not allowed to publish #8893
jvega190 wants to merge 5 commits into
craftersoftware:developfrom
jvega190:enhancement/8793

Conversation

@jvega190

@jvega190 jvega190 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

#8793

This needs to be merged with #8888

Summary by CodeRabbit

  • Bug Fixes
    • Disabled publishing options for items that cannot be requested or published.
    • Added localized tooltips explaining why unavailable soft dependencies cannot be selected.
    • Prevented submission when required dependencies lack publishing permission, dependency data is unavailable, or validation fails.
    • Displayed separate error alerts in the publish dialog for permission and validation issues.
    • Improved dependency validation status handling when package details are recalculated.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The publish workflow now checks item publish permissions. Soft dependency checkboxes show localized explanations when disabled. Hard dependency permission failures and dependency fetch failures disable submission and display separate alerts. LightItem exposes the related capability flags.

Changes

Publish permission controls

Layer / File(s) Summary
Item capability and dependency selection
studio-ui/ui/app/src/models/Item.ts, studio-ui/ui/app/src/components/PackageItems/utils.tsx, studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx
LightItem includes optional capability flags. Soft dependency checkboxes are disabled when canRequestPublish is false and show a localized permission message.
Hard dependency submission validation
studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx
Dependency fetch failures and unavailable hard dependencies disable submission. The dialog displays separate error alerts for both conditions and resets validation failure state after successful dependency calculation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main change: preventing unauthorized selection of soft publishing dependencies.
Description check ✅ Passed The description provides the ticket reference and identifies the required backend pull request for merging.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx`:
- Around line 141-145: Track dependency validation failure separately from
dependencyData in PublishDialogContainer, preserving that state when the
calculation error handler clears dependencyData and isFetchingItems. Update
hardDependenciesWithoutPublishPermission and the submission guard around the
existing dependency checks so submission remains disabled unless dependency
validation completed successfully. Update the alert logic to show an explicit
error that dependency validation could not complete instead of hiding it when
dependencyData is unavailable.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c37877da-e452-48f5-8121-fc71baffa9e9

📥 Commits

Reviewing files that changed from the base of the PR and between cdfede1 and 266ff11.

📒 Files selected for processing (4)
  • studio-ui/ui/app/src/components/PackageItems/utils.tsx
  • studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx
  • studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx
  • studio-ui/ui/app/src/models/Item.ts

Comment thread studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx (1)

519-532: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Show validation errors in first-publish mode.

These alerts are rendered only when published is truthy. When published is false or null, the component renders only the first-publish message, while submitDisabled remains true for dependency validation failures and permission failures.

Move these alerts outside the published conditional, or render them in the first-publish branch. Otherwise, users can be blocked without seeing the reason.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx`
around lines 519 - 532, Update the conditional rendering around
packageDependenciesFetchFailed and hardDependenciesWithoutPublishPermission in
PublishDialogContainer so both validation alerts render when published is false
or null as well as when it is truthy. Move them outside the published-only
branch or include them in the first-publish branch, preserving submitDisabled
behavior and preventing users from being blocked without an explanation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx`:
- Around line 142-152: Update the dependency validation flow around
dependencyData and dependencyValidationIncomplete so results from a previous
publishing target or dependency input are not treated as current during
revalidation. Associate dependencyData with the inputs used to calculate it, or
clear it and account for the in-flight request, and keep submission disabled
until the current dependency calculation succeeds.
- Around line 235-239: Update handleSubmit in PublishDialogContainer so it
returns immediately after preventDefault() when submitDisabled is true,
preventing form submission via Enter when dependency validation is incomplete or
hard dependencies lack publish permission. Keep the existing submission flow
unchanged when submission is enabled.

---

Outside diff comments:
In `@studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx`:
- Around line 519-532: Update the conditional rendering around
packageDependenciesFetchFailed and hardDependenciesWithoutPublishPermission in
PublishDialogContainer so both validation alerts render when published is false
or null as well as when it is truthy. Move them outside the published-only
branch or include them in the first-publish branch, preserving submitDisabled
behavior and preventing users from being blocked without an explanation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3976e622-2b15-4159-9f3a-6387c586e656

📥 Commits

Reviewing files that changed from the base of the PR and between 266ff11 and a6ad13f.

📒 Files selected for processing (1)
  • studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx

@jvega190

Copy link
Copy Markdown
Contributor Author

@coderabbitai how does this work look to be merged? Any issues you can point out?

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== PR diff ==='
git diff --find-renames origin/master...HEAD -- \
  studio-ui/ui/app/src/components/PackageItems/utils.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx \
  studio-ui/ui/app/src/models/Item.ts

echo '=== Changed file outlines ==='
for f in \
  studio-ui/ui/app/src/components/PackageItems/utils.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx \
  studio-ui/ui/app/src/models/Item.ts
do
  echo "--- $f ---"
  ast-grep outline "$f" --items all || true
done

echo '=== Publish-related tests ==='
fd -t f . studio-ui/ui/app | rg -i '(publish.*(test|spec)|package.*(test|spec)|(test|spec).*publish)'

echo '=== Permission properties: declarations, producers, and consumers ==='
rg -n -C 3 --glob '*.{ts,tsx}' \
  '\b(canRequestPublish|canApprove)\b' \
  studio-ui/ui/app/src

Length of output: 1412


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Current branch and refs ==='
git status --short --branch
git branch -a
git remote -v
git show-ref --heads --remotes | sed -n '1,160p'

base=""
for candidate in \
  origin/main origin/develop origin/next origin/master \
  main develop next master
do
  if git rev-parse --verify -q "$candidate" >/dev/null; then
    base="$candidate"
    break
  fi
done

if [ -z "$base" ]; then
  echo 'ERROR: No conventional base ref is available.'
  echo '=== Recent commits ==='
  git log --oneline -n 20
  exit 0
fi

merge_base="$(git merge-base "$base" HEAD)"
echo "=== Selected base: $base ==="
echo "=== Merge base: $merge_base ==="

echo '=== Changed paths ==='
git diff --name-status "$merge_base"...HEAD

echo '=== Targeted diff ==='
git diff --find-renames "$merge_base"...HEAD -- \
  studio-ui/ui/app/src/components/PackageItems/utils.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx \
  studio-ui/ui/app/src/models/Item.ts

echo '=== Changed file outlines ==='
for f in \
  studio-ui/ui/app/src/components/PackageItems/utils.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx \
  studio-ui/ui/app/src/models/Item.ts
do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    ast-grep outline "$f" --items all || true
  fi
done

echo '=== Publish and package test files ==='
fd -t f . studio-ui/ui/app | rg -i '(publish|package).*\.(test|spec)\.[cm]?[jt]sx?$|(\.test|\.spec)\.[cm]?[jt]sx?$'

echo '=== Permission properties: declarations, producers, and consumers ==='
rg -n -C 3 --glob '*.{ts,tsx}' \
  '\b(canRequestPublish|canApprove)\b' \
  studio-ui/ui/app/src

Length of output: 3661


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="origin/develop"
merge_base="$(git merge-base "$base" HEAD)"

echo "=== Merge base: $merge_base ==="
echo '=== Changed paths ==='
git diff --name-status "$merge_base"...HEAD

echo '=== Targeted diff ==='
git diff --find-renames "$merge_base"...HEAD -- \
  studio-ui/ui/app/src/components/PackageItems/utils.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx \
  studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx \
  studio-ui/ui/app/src/models/Item.ts

echo '=== Source: PackageItems utils ==='
sed -n '1,280p' studio-ui/ui/app/src/components/PackageItems/utils.tsx

echo '=== Source: PublishDialog container ==='
sed -n '1,420p' studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx

echo '=== Source: Publish package-items view ==='
sed -n '1,300p' studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx

echo '=== LightItem interface ==='
rg -n -A70 -B10 --glob 'Item.ts' '\b(interface|type)\s+LightItem\b' studio-ui/ui/app/src/models

echo '=== Permission properties: declarations, producers, and consumers ==='
rg -n -C 4 --glob '*.{ts,tsx}' \
  '\b(canRequestPublish|canApprove)\b' \
  studio-ui/ui/app/src

echo '=== Candidate test files ==='
fd -t f . studio-ui/ui/app | rg -i '(publish|package).*\.(test|spec)\.[cm]?[jt]sx?$|(\.test|\.spec)\.[cm]?[jt]sx?$'

Length of output: 49724


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== calculatePackage definition and response types ==='
rg -n -C 12 --glob '*.{ts,tsx}' '\bcalculatePackage\b' studio-ui/ui/app/src

echo '=== Publishing service source ==='
service_file="$(fd -t f '^publishing\.(ts|tsx)$' studio-ui/ui/app/src/services | head -n 1 || true)"
if [ -n "$service_file" ]; then
  echo "--- $service_file ---"
  sed -n '1,360p' "$service_file"
fi

echo '=== Server-side calculate-package endpoints and permission fields ==='
rg -n -i -C 10 \
  '(calculate.?package|calculatePackage|includeSoftDeps|canRequestPublish|canApprove)' \
  --glob '*.{java,kt,groovy,xml,json,yml,yaml}' \
  .

echo '=== Test directories and files near the changed components ==='
fd -t d -i '(test|tests|__tests__)' studio-ui/ui/app | sed -n '1,160p'
fd -t f -e ts -e tsx -e js -e jsx studio-ui/ui/app | rg -i \
  '(/|^)(publishdialog|publishpackageitemsview|packageitems).*(test|spec)?|((test|spec).*(publishdialog|publishpackageitemsview|packageitems))'

echo '=== Test framework configuration ==='
fd -t f -i 'package.json' studio-ui/ui/app -x sh -c 'echo "--- $1 ---"; sed -n "1,240p" "$1"' sh {}

Length of output: 30200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== CalculatedPublishPackageResult declaration ==='
result_file="$(fd -t f -e java 'CalculatedPublishPackageResult' studio | head -n 1 || true)"
if [ -n "$result_file" ]; then
  echo "--- $result_file ---"
  cat -n "$result_file"
fi

echo '=== Server LightItem declarations ==='
fd -t f -e java '^LightItem\.java$' studio | while IFS= read -r f; do
  echo "--- $f ---"
  cat -n "$f"
done

echo '=== Server package-calculation implementation ==='
rg -n -C 15 --glob '*.java' \
  '\b(calculatePublishPackage|recalculatePublishPackage)\s*\(' \
  studio/src

echo '=== Server permission checks used during publishing ==='
rg -n -i -C 8 --glob '*.java' \
  '(request.?publish|publish.?permission|canRequest|Permission.*publish|publish.*Permission)' \
  studio/src/main/java/org/craftercms/studio

echo '=== Frontend test files ==='
fd -t f . studio-ui/ui/app | rg '\.(test|spec)\.[cm]?[jt]sx?$' || true

echo '=== Test configuration ==='
if [ -f studio-ui/ui/app/package.json ]; then
  cat -n studio-ui/ui/app/package.json | sed -n '1,260p'
fi

Length of output: 140371


@jvega190: I found one blocking issue. I do not recommend merge yet.

canRequestPublish is added only to the TypeScript LightItem interface. The calculate-package API does not provide this field.

The server response uses studio/src/main/java/org/craftercms/studio/api/v2/dal/item/LightItem.java. That model exposes path and metadata only. A repository search found no server-side producer for canRequestPublish or canApprove.

As a result, canRequestPublish is undefined for calculated dependencies. The new checks use !item.canRequestPublish, and !undefined is true.

This causes two regressions:

  • In studio-ui/ui/app/src/components/PackageItems/utils.tsx, Line 110, and studio-ui/ui/app/src/components/PublishDialog/PublishPackageItemsView.tsx, Line 230, all soft dependencies become disabled.
  • In studio-ui/ui/app/src/components/PublishDialog/PublishDialogContainer.tsx, Line 147, every hard dependency is treated as unauthorized. A package with any hard dependency cannot be submitted.

The fix needs an authoritative per-item publish-request permission field in the calculate-package response. The UI should consume that field only after the backend provides it. Alternatively, use the existing item available-actions data if it is already available for each calculated dependency.

I also found no frontend test files or test script under studio-ui/ui/app. Add coverage for these cases:

  1. A permitted soft dependency stays selectable.
  2. A denied soft dependency is disabled.
  3. A permitted hard dependency does not block submission.
  4. A denied hard dependency blocks submission.
  5. A missing permission field fails safely according to the defined API contract.

You are interacting with an AI system.

@jvega190

Copy link
Copy Markdown
Contributor Author

@coderabbitai, the backend work has not been merged yet. It'll include the canRequestPublish field. Considering that, how does it look to be merged?

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@jvega190 With that backend change, the frontend logic looks correct for the intended authorization behavior.

I recommend merging this PR only with the backend change, or after the backend change is already available. The calculate-package response must include canRequestPublish: boolean for every dependency, including hard and soft dependencies.

The current frontend intentionally treats a missing field as unauthorized. This is safe, but it means a frontend-only deployment will disable soft dependencies and block packages with hard dependencies.

Before merge, verify these conditions:

  • The backend serializes canRequestPublish in the calculate-package response.
  • The field is present for all dependency item types.
  • The API contract defines the field as a boolean, not an optional value.
  • Deployment order prevents this UI from reaching users before the backend supports the field.

Subject to that dependency and integration verification, I do not see another merge blocker from the changes described here.


✏️ Learnings added
Learnt from: jvega190
URL: https://github.com/craftersoftware/craftercms/pull/8893

Timestamp: 2026-08-11T18:41:57.527Z
Learning: For GitHub PR `#8893` in craftersoftware/craftercms, the backend work that adds `canRequestPublish` to the calculate-package response is pending merge. The frontend permission checks depend on this field being present for every calculated hard and soft dependency. A missing field intentionally fails closed and blocks publication.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@jvega190
jvega190 marked this pull request as ready for review August 11, 2026 20:12
@jvega190
jvega190 requested a review from rart August 12, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant