Skip to content

SAK-48981 msgcntr handle forum and topic permission levels when using Lesson's prerequisites - #14604

Merged
ern merged 9 commits into
sakaiproject:masterfrom
ern:SAK-48981
May 27, 2026
Merged

SAK-48981 msgcntr handle forum and topic permission levels when using Lesson's prerequisites#14604
ern merged 9 commits into
sakaiproject:masterfrom
ern:SAK-48981

Conversation

@ern

@ern ern commented May 16, 2026

Copy link
Copy Markdown
Contributor

https://sakaiproject.atlassian.net/browse/SAK-48981

Summary by CodeRabbit

  • New Features

    • Group-based access controls for forums and topics; new endpoints to load forums/topics with membership details.
  • Bug Fixes

    • Safer forum/site discovery and more robust URL handling when tools or sites are missing.
    • Permission cache and membership handling improved when editing settings; tolerates missing/null permission data.
  • Chores

    • More consistent default membership initialization and stronger, more typed forum/area collection APIs.
  • Documentation

    • Added sample configuration for message "send to email" behavior.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: daef4f5f-37b3-4376-8e0f-e97086c48e5f

📥 Commits

Reviewing files that changed from the base of the PR and between ae3f070 and 7b9174e.

📒 Files selected for processing (1)
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java

Walkthrough

Forum access restrictions were refactored to use injected Sakai services, new membership-aware data-access queries were added, and DiscussionForumManager gained APIs and implementations to apply group-based contributor restrictions. LessonBuilder ForumEntity now uses Optional-based site/tool lookups and delegates group updates to the new APIs.

Changes

Forum group access restriction refactoring

Layer / File(s) Summary
Membership data access layer
msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java, msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java, msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml, msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/Topic.hbm.xml
Added getForumByIdWithMemberships and documented getTopicByIdWithMemberships; added/updated HQL queries to eager-load membership associations.
Group restriction API and implementation
msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/ui/DiscussionForumManager.java, msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
New methods setTopicGroupRestrictions / setForumGroupRestrictions and implementation that loads entities with memberships, resolves site group IDs, mutates membershipItemSet permissions via applyGroupRestrictions, and persists changes (or logs/aborts on resolution failure).
ForumEntity service injection and group handling
lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java
Replaced static cover/service usage with injected ServerConfigurationService, SiteService, ToolManager, and SqlService; switched to getOptionalSite(...) patterns for discovery; removed legacy mask-based permission logic; delegates group updates to DiscussionForumManager.
Area domain and manager changes
msgcntr/messageforums-api/.../Area.java, msgcntr/messageforums-hbm/.../AreaImpl.java, msgcntr/messageforums-component-impl/.../AreaManagerImpl.java
Tightened Area API generics and concrete forum types; refactored AreaImpl to Lombok-backed typed collections and defensive copies; AreaManagerImpl uses Optional-based site lookups, default membership population helpers, Lombok wiring, and modernized logging/Hibernate callbacks.
UI permission handling and tool wiring
msgcntr/messageforums-app/.../DiscussionForumTool.java, msgcntr/messageforums-app/.../PermissionBean.java, msgcntr/messageforums-component-impl/.../UIPermissionsManagerImpl.java, msgcntr/messageforums-component-impl/.../webapp/WEB-INF/components.xml
Made permission checks null-safe, introduced displayLevel in PermissionBean, cleared membership/permissions caches when relevant, and added permissionLevelManager/authzGroupService DI for AreaManager.
SiteEntityController forum type update
webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java
Switched controller to use OpenForum types and avoid unsafe casts by using getOpenForumsSet().
Misc small edits and config
msgcntr/messageforums-component-impl/.../DiscussionForumServiceImpl.java, msgcntr/messageforums-component-impl/.../PrivateMessageManagerImpl.java, config/configuration/bundles/.../default.sakai.properties, msgcntr/messageforums-component-impl/.../webapp/WEB-INF/components.xml
Minor formatting/import cleanups, components.xml DI additions, and added commented default property documentation for msgcntr.defaultSendToEmailSetting.
  • Possibly related PRs:

    • sakaiproject/sakai#14331: Related changes touching legacy mask-based permission handling in lessonbuildertool/service/ForumEntity.java.
  • Suggested reviewers:

    • ottenhoff
    • jesusmmp
    • adrianfish
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.59% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly identifies the main change: handling forum and topic permission levels in the msgcntr module when using Lesson's prerequisites, referencing SAK-48981.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@ern

ern commented May 16, 2026

Copy link
Copy Markdown
Contributor Author

This was a nasty issue to solve but I am pretty happy with how this fix evolved, here are the highlights

  • remove forum and topic permission level manipulation logic from lessons ForumEntity
  • create methods in DiscussionForumManager service for handling permission level changes in forums and topics
  • the service methods properly manipulate and update the database (the Hibernate way)
  • some additional related bugs were solved in this PR, like dangling permission levels left behind by the old manipulation

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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
`@lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java`:
- Around line 672-690: The current setGroups method maps incoming group IDs to
site group titles and then blindly passes the resulting collection to
discussionForumManager, but if the caller supplied a non-empty groups collection
and none of the IDs resolve, the code will pass an empty collection which
downstream treats as "clear restrictions"; instead, after resolving IDs (using
siteService and toolManager as you already do in setGroups), detect the three
cases: 1) groups == null -> do nothing and return, 2) groups.isEmpty() -> caller
explicitly wants to clear restrictions so proceed to call
discussionForumManager.setTopicGroupRestrictions / setForumGroupRestrictions
with an empty collection, and 3) groups non-empty but groupNames.isEmpty() ->
treat as unresolved IDs and abort (return) without calling
discussionForumManager (optionally log a warning); make this change in setGroups
(handling TYPE_FORUM_TOPIC / TYPE_FORUM_FORUM) before invoking
discussionForumManager.setTopicGroupRestrictions or setForumGroupRestrictions.

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java`:
- Around line 2429-2446: Validate groupNames against the site membership before
creating DBMembershipItems: in setTopicGroupRestrictions (use topic to get
site/area via topic.getBaseForum().getArea()) and in setForumGroupRestrictions
(use forum.getArea()) resolve each entry in groupNames to an existing site group
id/title and filter out or reject any names that do not map to a current group;
if any unknown names are found, fail fast (return or throw) or omit them per
policy, and only pass the validated collection to
applyGroupRestrictions(topic.getMembershipItemSet(), validatedGroupNames) or
applyGroupRestrictions(forum.getMembershipItemSet(), validatedGroupNames) so no
unmatched titles become persisted DBMembershipItems.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2b1b4baa-d411-43f2-a12e-934a1abd1599

📥 Commits

Reviewing files that changed from the base of the PR and between acd606f and 6658427.

📒 Files selected for processing (7)
  • lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/ui/DiscussionForumManager.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/Topic.hbm.xml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java (1)

671-677: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep null group updates as a no-op.

Line 672 collapses null into an empty set, and the new manager treats an empty collection as “clear all restrictions”. That means a caller that omits groups can accidentally reopen a previously restricted forum/topic to the whole site.

Suggested fix
 public void setGroups(Collection<String> groups) {
-	Set<String> groupIds = (groups == null) ? Collections.emptySet() : new HashSet<>(groups);
+	if (groups == null) {
+		return;
+	}
+	Set<String> groupIds = new HashSet<>(groups);
 	if (type == TYPE_FORUM_TOPIC) {
 		discussionForumManager.setTopicGroupRestrictions(id, groupIds);
 	} else if (type == TYPE_FORUM_FORUM) {
 		discussionForumManager.setForumGroupRestrictions(id, groupIds);
 	}
 }
🤖 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
`@lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java`
around lines 671 - 677, The current setGroups method collapses a null groups
argument into an empty set which the discussionForumManager interprets as "clear
restrictions"; change setGroups to treat null as a no-op by returning early when
groups == null, and only compute groupIds and call
discussionForumManager.setTopicGroupRestrictions(id, groupIds) or
setForumGroupRestrictions(id, groupIds) when groups is non-null; keep references
to the existing symbols (setGroups, groups, groupIds, type, TYPE_FORUM_TOPIC,
TYPE_FORUM_FORUM, id, discussionForumManager) so the behavior change is limited
to adding the null-check and early return.
🤖 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.

Duplicate comments:
In
`@lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java`:
- Around line 671-677: The current setGroups method collapses a null groups
argument into an empty set which the discussionForumManager interprets as "clear
restrictions"; change setGroups to treat null as a no-op by returning early when
groups == null, and only compute groupIds and call
discussionForumManager.setTopicGroupRestrictions(id, groupIds) or
setForumGroupRestrictions(id, groupIds) when groups is non-null; keep references
to the existing symbols (setGroups, groups, groupIds, type, TYPE_FORUM_TOPIC,
TYPE_FORUM_FORUM, id, discussionForumManager) so the behavior change is limited
to adding the null-check and early return.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 38320702-0b6b-4371-80cf-6850056468eb

📥 Commits

Reviewing files that changed from the base of the PR and between 6658427 and f68dfdb.

📒 Files selected for processing (3)
  • lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/ui/DiscussionForumManager.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/ui/DiscussionForumManager.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java`:
- Around line 1700-1705: The comparison misses changes when
oldItem.getPermissionLevel() is null for a custom level because
getPermissionLevelByName(...) returns the shared PERMISSION_LEVEL_NAME_CUSTOM
entry; update the logic around PermissionLevel oldLevel (and the derived
oldModerate) to detect the case where oldItem.getPermissionLevel() == null &&
PERMISSION_LEVEL_NAME_CUSTOM.equals(oldItem.getPermissionLevelName()) and treat
that as "changed" (e.g., force oldModerate to a value that will not equal
permBean.getModeratePostings() so the condition in the if
(permBean.getModeratePostings() != Boolean.TRUE.equals(oldModerate)) branch
evaluates true); adjust the same pattern found at the other locations (around
lines 7288-7308) using the same checks and symbols
(oldItem.getPermissionLevel(), oldItem.getPermissionLevelName(),
permissionLevelManager.getPermissionLevelByName(...),
permBean.getModeratePostings()) to ensure custom-permission nulls trigger a
refresh.

In
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`:
- Around line 75-89: setPermissionsForLevel may leave displayLevel null when
permissionLevelManager.getPermissionLevelByName(selectedLevel) returns null and
also calls displayLevel.getTypeUuid() which can NPE; fix by making the
non-"Custom" branch assign a safe fallback when lookup returns null (e.g.,
create or load a default level) and make the custom-type check null-safe by
testing displayLevel != null before calling getTypeUuid(), or by comparing
typeUuid from displayLevel via Objects.equals(displayLevel == null ? null :
displayLevel.getTypeUuid(), typeManager.getCustomLevelType()); update
references: setPermissionsForLevel, displayLevel,
permissionLevelManager.getPermissionLevelByName,
MessageForumsTypeManager.getCustomLevelType, displayLevel.getTypeUuid, and
permissionLevelManager.createPermissionLevel so displayLevel is never left null
and the custom-type check cannot throw an NPE.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55a70d80-4784-42fc-85c5-de011ef45d06

📥 Commits

Reviewing files that changed from the base of the PR and between f68dfdb and 8cb42fe.

📒 Files selected for processing (11)
  • config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/Area.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/PrivateMessageManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java
  • msgcntr/messageforums-component-impl/src/webapp/WEB-INF/components.xml
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/AreaImpl.java
💤 Files with no reviewable changes (1)
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/PrivateMessageManagerImpl.java
✅ Files skipped from review due to trivial changes (1)
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java

@ern
ern marked this pull request as ready for review May 21, 2026 20:51
@ern ern changed the title SAK-48981 Lessons properly configure forum and topic permission levels when using prerequisites SAK-48981 msgcntr handle forum and topic permission levels when using Lesson's prerequisites May 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java (1)

1652-1659: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Forum moderation changes still skip synoptic refreshes.

In the DiscussionForum branch, isModerated/isModeratedOld are never populated, and the later permission-diff block is guarded by target instanceof Topic. That makes the inner forum branch unreachable, so forum-level moderation or moderatePostings permission changes can still save without refreshing synoptic counts.

Suggested fix
   if (target instanceof DiscussionForum){
 	  DiscussionForum forum = ((DiscussionForum) target);
+	  isModerated = forum.getModerated();
 	  
 	  DiscussionForum oldForum = forumManager.getForumById(forum.getId());
+	  isModeratedOld = oldForum.getModerated();
 	  isDraftOld = oldForum.getDraft();		  
 
 	  availabilityChanged = availabilityChanged(forum, oldForum);
   }
@@
-  if(!update && isModerated && permissions != null && target instanceof Topic){
+  if(!update && isModerated && permissions != null){
 	  //only need to look up permission changes for moderate postings if it is moderated
 
 	  if (target instanceof DiscussionForum){
 		  oldMembershipItemSet = uiPermissionsManager.getForumItemsSet((DiscussionForum) target);
 	  }else  if (target instanceof DiscussionTopic){

Also applies to: 1677-1684

🤖 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
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java`
around lines 1652 - 1659, The DiscussionForum branch never sets moderation flags
and the permission-diff logic is only executed for Topic, so forum-level
moderation or moderatePostings permission changes skip synoptic refresh; in the
DiscussionForum branch (where you call DiscussionForum forum = (DiscussionForum)
target and fetch oldForum via forumManager.getForumById(forum.getId())), set
isModerated = forum.getModerated() and isModeratedOld = oldForum.getModerated()
(in addition to the existing isDraftOld and availabilityChanged assignment) and
ensure the later permission-diff / synoptic-refresh code that currently runs
only under target instanceof Topic is also executed for DiscussionForum (or
factor the permission-diff logic into a shared method and call it for both
DiscussionForum and Topic) so changes to moderatePostings or forum-level
moderation trigger the synoptic refresh.
♻️ Duplicate comments (1)
msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java (1)

81-84: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't preserve a stale displayLevel when the level lookup fails.

If getPermissionLevelByName(selectedLevel) returns null, selectedLevel has already changed but displayLevel stays on the previous object. That can write permission changes to the wrong level, and if the previous value was null the setters below still throw. Fail fast or clear/reject the selection here instead of keeping stale state.

🤖 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
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`
around lines 81 - 84, The code in PermissionBean uses
permissionLevelManager.getPermissionLevelByName(selectedLevel) and if it returns
null leaves this.displayLevel unchanged, which can lead to stale state; update
the logic so that when level == null you either clear this.displayLevel (set to
null) or reject the selection (throw/return after logging) instead of preserving
the old value—locate the block referencing selectedLevel and this.displayLevel
and replace the current if (level != null) { this.displayLevel = level; } with
explicit handling for the null case (e.g., this.displayLevel = null;
processLogger.warn(...) or throw an IllegalArgumentException) so subsequent
setters operate on the correct state.
🤖 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.

Outside diff comments:
In
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java`:
- Around line 1652-1659: The DiscussionForum branch never sets moderation flags
and the permission-diff logic is only executed for Topic, so forum-level
moderation or moderatePostings permission changes skip synoptic refresh; in the
DiscussionForum branch (where you call DiscussionForum forum = (DiscussionForum)
target and fetch oldForum via forumManager.getForumById(forum.getId())), set
isModerated = forum.getModerated() and isModeratedOld = oldForum.getModerated()
(in addition to the existing isDraftOld and availabilityChanged assignment) and
ensure the later permission-diff / synoptic-refresh code that currently runs
only under target instanceof Topic is also executed for DiscussionForum (or
factor the permission-diff logic into a shared method and call it for both
DiscussionForum and Topic) so changes to moderatePostings or forum-level
moderation trigger the synoptic refresh.

---

Duplicate comments:
In
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`:
- Around line 81-84: The code in PermissionBean uses
permissionLevelManager.getPermissionLevelByName(selectedLevel) and if it returns
null leaves this.displayLevel unchanged, which can lead to stale state; update
the logic so that when level == null you either clear this.displayLevel (set to
null) or reject the selection (throw/return after logging) instead of preserving
the old value—locate the block referencing selectedLevel and this.displayLevel
and replace the current if (level != null) { this.displayLevel = level; } with
explicit handling for the null case (e.g., this.displayLevel = null;
processLogger.warn(...) or throw an IllegalArgumentException) so subsequent
setters operate on the correct state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0cb48be6-9e0c-464c-bc41-707f6be9cd41

📥 Commits

Reviewing files that changed from the base of the PR and between 8cb42fe and 5ae2a86.

📒 Files selected for processing (2)
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

♻️ Duplicate comments (1)
lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java (1)

671-677: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve null as “no change” instead of “open to site”.

Line 672 turns null into an empty set, and the new DiscussionForumManager contract treats empty as “clear restrictions”. If Lessons omits groups in an update path, this silently broadens access.

Suggested fix
 	public void setGroups(Collection<String> groups) {
-		Set<String> groupIds = (groups == null) ? Collections.emptySet() : new HashSet<>(groups);
+		if (groups == null) {
+			return;
+		}
+		Set<String> groupIds = new HashSet<>(groups);
 		if (type == TYPE_FORUM_TOPIC) {
 			discussionForumManager.setTopicGroupRestrictions(id, groupIds);
 		} else if (type == TYPE_FORUM_FORUM) {
 			discussionForumManager.setForumGroupRestrictions(id, groupIds);
 		}
 	}
🤖 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
`@lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java`
around lines 671 - 677, The setGroups method currently converts a null groups
parameter into Collections.emptySet(), which the DiscussionForumManager now
interprets as "clear restrictions"; instead preserve null as "no change": in
setGroups, compute groupIds only when groups != null (e.g., new
HashSet<>(groups)) and pass null through to
discussionForumManager.setTopicGroupRestrictions(id, groupIds) and
setForumGroupRestrictions(id, groupIds) when groups was null so the manager
receives null for no-op and a non-null Set when an explicit change is intended;
key symbols: setGroups, discussionForumManager.setTopicGroupRestrictions,
discussionForumManager.setForumGroupRestrictions, TYPE_FORUM_TOPIC,
TYPE_FORUM_FORUM.
🧹 Nitpick comments (2)
msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java (1)

662-664: ⚡ Quick win

Reuse the area fetched before the loop to avoid redundant calls.

The toArea is already fetched at line 561 and saved at line 579 before this loop begins. Re-fetching the area on every iteration (line 663) is redundant and causes N additional calls where N is the number of forums being imported.

♻️ Proposed fix to reuse the existing area reference
-		// Save the forum in the target discussion area after it has been initialized for this import mode.
-		Area area = areaManager.getDiscussionArea(toContext, createDefaultForum);
-		newForum.setArea(area);
+		// Save the forum in the target discussion area after it has been initialized for this import mode.
+		newForum.setArea(toArea);
🤖 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
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java`
around lines 662 - 664, The code currently calls
areaManager.getDiscussionArea(toContext, createDefaultForum) inside the loop and
assigns it to a local Area variable before calling newForum.setArea(area);
instead reuse the already-fetched toArea reference (the Area saved earlier)
instead of re-fetching it each iteration — update the loop to call
newForum.setArea(toArea) (or use the existing toArea variable) so you avoid
redundant areaManager.getDiscussionArea calls.
msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java (1)

174-186: ⚖️ Poor tradeoff

Casting to implementation class breaks encapsulation.

The cast (DBMembershipItemImpl) item to access setForum/setTopic couples this code to the implementation. Consider adding setForum/setTopic to the DBMembershipItem interface to avoid this cast.

🤖 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
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java`
around lines 174 - 186, The code in createDefaultMembershipItems casts
DBMembershipItem to DBMembershipItemImpl to call setForum/setTopic, breaking
encapsulation; modify the DBMembershipItem interface to declare
setForum(DiscussionForum) and setTopic(DiscussionTopic), implement these methods
in DBMembershipItemImpl, and then remove the casts in
createDefaultMembershipItems so you call item.setForum(forum) /
item.setTopic(topic) directly (ensure
permissionLevelManager.createDBMembershipItem still returns DBMembershipItem).
🤖 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
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java`:
- Around line 1204-1205: Reset the session attachment state before populating
when opening a different forum/topic settings screen: clear the session fields
attachments and prepareRemoveAttach at the start of the settings-population code
path so previously selected attachments are not carried over and appended to the
wrong object; locate the methods that assemble attachments for the settings
pages (where attachments and prepareRemoveAttach are referenced/modified) and
add explicit resets before appending new forum/topic attachments — apply the
same reset in the equivalent code paths noted in the review (also applies to the
other occurrence around the second referenced block).

In
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`:
- Around line 106-110: The setter methods (e.g., setChangeSettings) call
this.displayLevel.setXxx(...) without guarding for displayLevel being null;
update the setters (setChangeSettings, setDeleteAny, etc.) to check for
displayLevel != null before calling its setters, or ensure displayLevel is
always initialized in the constructor when item.getPermissionLevel() and
permissionLevelManager.getPermissionLevelByName(...) both return null
(initialize displayLevel to a default PermissionLevelDisplay object); reference
the displayLevel field, the setter methods like setChangeSettings, and the
constructor initialization code that uses item.getPermissionLevel() and
permissionLevelManager.getPermissionLevelByName(...) when making the change.

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java`:
- Around line 197-199: The isPrivateAreaEnabled() method risks NPE because
getPrivateArea().getEnabled() returns a nullable Boolean; change
isPrivateAreaEnabled() to defensively handle null (e.g., assign Boolean b =
getPrivateArea().getEnabled() and return Boolean.TRUE.equals(b) or return b !=
null && b.booleanValue()) so it never auto-unboxes a null; update any related
tests if needed.

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java`:
- Around line 2540-2578: applyGroupRestrictions currently demotes any
contributor-level DBMembershipItem (including TYPE_USER) when toggling group
restrictions; change the logic so only group and role membership items are
modified: in the "restricting" branch inside applyGroupRestrictions, only
promote/demote items whose getType() equals MembershipItem.TYPE_GROUP (do not
run the contributor->none demotion for other types like TYPE_USER), and in the
"clearing" branch only promote/demote items whose getType() equals
MembershipItem.TYPE_ROLE or TYPE_GROUP as appropriate; leave TYPE_USER entries
untouched (ensure the existing loops that call
permissionLevelManager.createDBMembershipItem and membershipItemSet.add(...)
remain the same for new group entries).
- Around line 2475-2505: Both setTopicGroupRestrictions and
setForumGroupRestrictions may call applyGroupRestrictions on an empty
membershipItemSet, which only adds group rows and leaves default role membership
rows missing so the entity remains open; before calling applyGroupRestrictions
ensure the entity has bootstrapped role membership items
(owner/contributor/reader/etc.) so role entries exist and can be switched to
"None". Locate setTopicGroupRestrictions and setForumGroupRestrictions and add
logic to initialize/insert default role membership rows into the Topic/Forum
membershipItemSet when it is empty (use the same membership model the codebase
uses for stored role rows), then proceed to resolveSiteGroupNames and call
applyGroupRestrictions; keep the bootstrap step idempotent and tied to the same
membership item structure applyGroupRestrictions expects.

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java`:
- Around line 114-117: resolvePermissionLevel may return null when both
item.getPermissionLevel() and
permissionLevelManager.getPermissionLevelByName(...) are null, leading to NPEs
where callers do resolvePermissionLevel(item).getChangeSettings() etc.; change
resolvePermissionLevel(DBMembershipItem) to return Optional<PermissionLevel>
(wrap existing lookup with Optional.ofNullable(...)) and then update all
predicate lambdas that currently call resolvePermissionLevel(item).getXxx() to
use optional mapping and a safe default, e.g.
resolvePermissionLevel(item).map(pl -> pl.getChangeSettings()).orElse(false)
(apply for getChangeSettings/getCreateForums/getModerateMessages/etc.), or
otherwise handle the empty Optional appropriately so no direct get* is invoked
on null.

In
`@msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/AreaImpl.java`:
- Around line 105-112: The remove methods (removePrivateForum,
removeDiscussionForum, removeOpenForum, removeMembershipItem) can NPE when their
backing sets (privateForumsSet, discussionForumsSet, openForumsSet,
membershipItemsSet) are null; update each method to guard against a null backing
set before calling remove()—for example, check if the set is non-null (and still
perform any needed inverse updates like forum.setArea(null) or
item.setArea(null)) and only call remove() when the set exists, mirroring the
initialization/null-safety pattern used in the add* methods.
- Around line 65-87: The getters getOpenForums, getPrivateForums and
getDiscussionForums currently call new
ArrayList<>(openForumsSet/privateForumsSet/discussionForumsSet) which will NPE
if the backing set is null; change each getter to defensively handle null by
returning an empty list (e.g., Collections.emptyList() or new ArrayList<>())
when the corresponding set is null, and update the setters
setOpenForums/setPrivateForums/setDiscussionForums to defensively handle a null
input list by assigning an empty HashSet instead of new HashSet<>(null) so the
backing fields are never left null.

---

Duplicate comments:
In
`@lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java`:
- Around line 671-677: The setGroups method currently converts a null groups
parameter into Collections.emptySet(), which the DiscussionForumManager now
interprets as "clear restrictions"; instead preserve null as "no change": in
setGroups, compute groupIds only when groups != null (e.g., new
HashSet<>(groups)) and pass null through to
discussionForumManager.setTopicGroupRestrictions(id, groupIds) and
setForumGroupRestrictions(id, groupIds) when groups was null so the manager
receives null for no-op and a non-null Set when an explicit change is intended;
key symbols: setGroups, discussionForumManager.setTopicGroupRestrictions,
discussionForumManager.setForumGroupRestrictions, TYPE_FORUM_TOPIC,
TYPE_FORUM_FORUM.

---

Nitpick comments:
In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java`:
- Around line 174-186: The code in createDefaultMembershipItems casts
DBMembershipItem to DBMembershipItemImpl to call setForum/setTopic, breaking
encapsulation; modify the DBMembershipItem interface to declare
setForum(DiscussionForum) and setTopic(DiscussionTopic), implement these methods
in DBMembershipItemImpl, and then remove the casts in
createDefaultMembershipItems so you call item.setForum(forum) /
item.setTopic(topic) directly (ensure
permissionLevelManager.createDBMembershipItem still returns DBMembershipItem).

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java`:
- Around line 662-664: The code currently calls
areaManager.getDiscussionArea(toContext, createDefaultForum) inside the loop and
assigns it to a local Area variable before calling newForum.setArea(area);
instead reuse the already-fetched toArea reference (the Area saved earlier)
instead of re-fetching it each iteration — update the loop to call
newForum.setArea(toArea) (or use the existing toArea variable) so you avoid
redundant areaManager.getDiscussionArea calls.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9433fd46-35b3-4c00-8f31-106cb78bfaa9

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae2a86 and 0cbe49b.

📒 Files selected for processing (17)
  • config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
  • lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/Area.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java
  • msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/ui/DiscussionForumManager.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/DiscussionForumServiceImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/PrivateMessageManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java
  • msgcntr/messageforums-component-impl/src/webapp/WEB-INF/components.xml
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/AreaImpl.java
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/Topic.hbm.xml
💤 Files with no reviewable changes (1)
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/PrivateMessageManagerImpl.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java (1)

517-526: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard forum ID parsing to avoid 500 on malformed input.

Line 525 parses forumId with Long.valueOf(...) directly. A non-numeric ID will throw NumberFormatException and skip the intended Optional.empty() path, resulting in a server error instead of a clean bad request response.

💡 Proposed fix
 private Optional<OpenForum> findForumInArea(Area forumArea, String forumId) {
     if (forumArea == null) {
         return Optional.empty();
     }

     Set<OpenForum> forums = forumArea.getOpenForumsSet();
+    Long forumIdLong;
+    try {
+        forumIdLong = Long.valueOf(forumId);
+    } catch (NumberFormatException e) {
+        return Optional.empty();
+    }

     return forums.stream()
-            .filter(forum -> Long.valueOf(forumId).equals(forum.getId()))
+            .filter(forum -> forumIdLong.equals(forum.getId()))
             .findAny();
 }
🤖 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
`@webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java`
around lines 517 - 526, The findForumInArea method currently calls
Long.valueOf(forumId) inside the stream filter which will throw
NumberFormatException for non-numeric forumId and cause a 500; change the logic
to validate/parse forumId before streaming (e.g. try to parseLong forumId in a
try/catch or use a numeric check) and if parsing fails return Optional.empty();
update references to Long.valueOf(...) in findForumInArea to use the pre-parsed
long value (or skip filtering) so malformed IDs yield Optional.empty() instead
of an exception.
🤖 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.

Outside diff comments:
In
`@webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java`:
- Around line 517-526: The findForumInArea method currently calls
Long.valueOf(forumId) inside the stream filter which will throw
NumberFormatException for non-numeric forumId and cause a 500; change the logic
to validate/parse forumId before streaming (e.g. try to parseLong forumId in a
try/catch or use a numeric check) and if parsing fails return Optional.empty();
update references to Long.valueOf(...) in findForumInArea to use the pre-parsed
long value (or skip filtering) so malformed IDs yield Optional.empty() instead
of an exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc6a3bb0-1eac-4bc3-840e-6eff3bdd047d

📥 Commits

Reviewing files that changed from the base of the PR and between 0cbe49b and 4625d9c.

📒 Files selected for processing (1)
  • webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java (2)

2485-2491: ⚠️ Potential issue | 🟠 Major

Handle the clear-restrictions path when memberships are still null.

resolveSiteGroupNames(...) returns an empty list for null/empty input, so these methods still call applyGroupRestrictions(...) when clearing restrictions. Because the bootstrap now runs only for non-empty groupNames, an older forum/topic with membershipItemSet == null will still hit a NullPointerException on the first iteration inside applyGroupRestrictions(...).

Also applies to: 2507-2514

🤖 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
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java`
around lines 2485 - 2491, When clearing group restrictions the code may pass a
null membership set into applyGroupRestrictions and cause an NPE; modify the
block around topic.getMembershipItemSet() so you fetch it into a local variable
(e.g., topicItems), and if topicItems is null then only
createDefaultMembershipItemsForTopic when groupNames is non-empty, otherwise
skip calling applyGroupRestrictions; finally call
applyGroupRestrictions(topicItems, groupNames) with the non-null topicItems (or
return/continue when it remains null). Apply the same change to the similar
block for the forum-level code that mirrors lines 2507-2514.

2554-2567: ⚠️ Potential issue | 🔴 Critical

Demote role-level Contributor rows when enabling group restrictions.

The restricting branch now only demotes TYPE_GROUP Contributor rows. The default TYPE_ROLE Contributor memberships stay active, so the forum/topic remains open to the whole site even after selecting prerequisite groups. This branch needs to demote Contributor rows for both TYPE_ROLE and TYPE_GROUP, while still leaving TYPE_USER entries untouched.

Suggested fix
-				} else if (Objects.equals(item.getType(), MembershipItem.TYPE_GROUP)
+				} else if ((Objects.equals(item.getType(), MembershipItem.TYPE_ROLE)
+						|| Objects.equals(item.getType(), MembershipItem.TYPE_GROUP))
 						&& PermissionLevelManager.PERMISSION_LEVEL_NAME_CONTRIBUTOR.equals(item.getPermissionLevelName())) {
 					item.setPermissionLevel(null);
 					item.setPermissionLevelName(PermissionLevelManager.PERMISSION_LEVEL_NAME_NONE);
 				}
🤖 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
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java`
around lines 2554 - 2567, In DiscussionForumManagerImpl inside the loop over
membershipItemSet (DBMembershipItem entries), update the restricting branch
logic so Contributor rows are demoted for both MembershipItem.TYPE_ROLE and
MembershipItem.TYPE_GROUP (but not TYPE_USER): keep the first branch that
promotes groupNames (remove from toAdd only when item.getType() == TYPE_GROUP
and groupNames.contains(item.getName())), and change the subsequent demotion
condition to check if item.getType() is TYPE_GROUP OR TYPE_ROLE and the current
permission equals PermissionLevelManager.PERMISSION_LEVEL_NAME_CONTRIBUTOR, then
setPermissionLevel(null) and
setPermissionLevelName(PermissionLevelManager.PERMISSION_LEVEL_NAME_NONE).
msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java (1)

55-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fallback still leaves displayLevel nullable.

If item.getPermissionLevelName() is null or points at a non-"Custom" level that no longer resolves, this new constructor path still returns with displayLevel == null because setPermissionsForLevel(...) is a no-op in that case. The setters below still dereference displayLevel unconditionally, so the first permission toggle will NPE. Please guarantee a non-null fallback here, or fail fast before the bean is used.

🤖 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
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`
around lines 55 - 57, The constructor leaves displayLevel null when
item.getPermissionLevelName() is null or resolves to an unknown non-"Custom"
level because setPermissionsForLevel(...) is a no-op for unresolved names;
ensure displayLevel is set to a safe non-null default (e.g., the "Default" or
"Inherited" PermissionLevel instance your app expects) or throw an
IllegalStateException immediately from the PermissionBean constructor if no
valid level can be resolved so subsequent setters that dereference displayLevel
cannot NPE; update the constructor logic around the call to
setPermissionsForLevel(selectedLevel) to assign a concrete fallback to
displayLevel (or fail fast) before returning.
🤖 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
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java`:
- Around line 114-117: resolvePermissionLevel currently assumes the
DBMembershipItem parameter is non-null and will NPE when callers (e.g.,
getAreaItemsByCurrentUser or forumManager.getDBMember) pass null; update
resolvePermissionLevel(DBMembershipItem item) to first check for a null item and
return Optional.empty() if so, then proceed to resolve using
item.getPermissionLevel() or
permissionLevelManager.getPermissionLevelByName(item.getPermissionLevelName());
also defensively handle a null permissionLevelName by returning Optional.empty()
instead of calling the manager with null.

---

Duplicate comments:
In
`@msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java`:
- Around line 55-57: The constructor leaves displayLevel null when
item.getPermissionLevelName() is null or resolves to an unknown non-"Custom"
level because setPermissionsForLevel(...) is a no-op for unresolved names;
ensure displayLevel is set to a safe non-null default (e.g., the "Default" or
"Inherited" PermissionLevel instance your app expects) or throw an
IllegalStateException immediately from the PermissionBean constructor if no
valid level can be resolved so subsequent setters that dereference displayLevel
cannot NPE; update the constructor logic around the call to
setPermissionsForLevel(selectedLevel) to assign a concrete fallback to
displayLevel (or fail fast) before returning.

In
`@msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java`:
- Around line 2485-2491: When clearing group restrictions the code may pass a
null membership set into applyGroupRestrictions and cause an NPE; modify the
block around topic.getMembershipItemSet() so you fetch it into a local variable
(e.g., topicItems), and if topicItems is null then only
createDefaultMembershipItemsForTopic when groupNames is non-empty, otherwise
skip calling applyGroupRestrictions; finally call
applyGroupRestrictions(topicItems, groupNames) with the non-null topicItems (or
return/continue when it remains null). Apply the same change to the similar
block for the forum-level code that mirrors lines 2507-2514.
- Around line 2554-2567: In DiscussionForumManagerImpl inside the loop over
membershipItemSet (DBMembershipItem entries), update the restricting branch
logic so Contributor rows are demoted for both MembershipItem.TYPE_ROLE and
MembershipItem.TYPE_GROUP (but not TYPE_USER): keep the first branch that
promotes groupNames (remove from toAdd only when item.getType() == TYPE_GROUP
and groupNames.contains(item.getName())), and change the subsequent demotion
condition to check if item.getType() is TYPE_GROUP OR TYPE_ROLE and the current
permission equals PermissionLevelManager.PERMISSION_LEVEL_NAME_CONTRIBUTOR, then
setPermissionLevel(null) and
setPermissionLevelName(PermissionLevelManager.PERMISSION_LEVEL_NAME_NONE).
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e096c093-6e78-4dba-aef1-b28acc3ddb4e

📥 Commits

Reviewing files that changed from the base of the PR and between 4625d9c and ae3f070.

📒 Files selected for processing (6)
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
  • msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/DiscussionForumManagerImpl.java
  • msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java
  • msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/AreaImpl.java

@adrianfish adrianfish left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good.

@ern
ern merged commit f74a497 into sakaiproject:master May 27, 2026
4 checks passed
ern added a commit that referenced this pull request May 28, 2026
… Lesson's prerequisites (#14604)

(cherry picked from commit f74a497)

 Conflicts:
	msgcntr/messageforums-component-impl/src/webapp/WEB-INF/components.xml
ern added a commit that referenced this pull request Jun 25, 2026
… Lesson's prerequisites (#14604)

(cherry picked from commit f74a497)

 Conflicts:
	lessonbuilder/tool/src/java/org/sakaiproject/lessonbuildertool/service/ForumEntity.java
	msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java
	msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/ui/PermissionBean.java
	msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/AreaManagerImpl.java
	msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/PrivateMessageManagerImpl.java
	msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/ui/UIPermissionsManagerImpl.java
	msgcntr/messageforums-component-impl/src/webapp/WEB-INF/components.xml
	webapi/src/main/java/org/sakaiproject/webapi/controllers/SiteEntityController.java
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.

2 participants