Skip to content

button for membership discussion part reviving membership#1353

Merged
fenekku merged 4 commits intoinveniosoftware:masterfrom
fenekku:button_for_membership_discussion_part_reviving_membership
Mar 13, 2026
Merged

button for membership discussion part reviving membership#1353
fenekku merged 4 commits intoinveniosoftware:masterfrom
fenekku:button_for_membership_discussion_part_reviving_membership

Conversation

@fenekku
Copy link
Copy Markdown
Contributor

@fenekku fenekku commented Feb 17, 2026

I am reviving the feature for a user to be able to request to become part of a community.

The (draft) RFC describing this feature is here: inveniosoftware/rfcs#111

The rotting corpse of the former PR is here: #1175 .

This PR is just the first in a series — I will submit smaller PRs than a 49 files one, but more of them.

This PR covers the part of the feature that shows the "Request membership" button or "Membership discussion" button on the community header ribbon when feature is enabled.

  • feat(mshp-req): default communities to be closed to membership requests
  • feat(mshp-req): update can_request_membership permission
  • feat(mshp-req): show button of membership discussion on header if applicable

Comment thread invenio_communities/communities/records/systemfields/access.py
@property
def member_policy_is_open(self):
"""Return true when member policy is open."""
return self.member_policy == MemberPolicyEnum.OPEN.value
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

so that clients of this code don't need to know about MemberPolicyEnum.Open.value

if self._errors:
res["errors"] = self._errors
return res
Kept for legacy reasons and availability in the future. Would be happy
Copy link
Copy Markdown
Contributor Author

@fenekku fenekku Feb 17, 2026

Choose a reason for hiding this comment

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

Would honestly be happy to remove CommunityItem in its entirety (we are already in a breaking change and non-adopted version).

To be clear the removed code was removed because completely redundant. The parent class already implements the functionality.


class ArchivedInvitation(Record, MemberMixin):
"""An archived invitation record.
"""An archived invitation or membership request record.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

may look into ramifications of changing that name in future PRs.

Comment on lines +20 to +25
{% if request_id_of_pending_member %}
<a href="{{ url_for('invenio_app_rdm_requests.user_dashboard_request_view', request_pid_value=request_id_of_pending_member) }}"
class="ui button labeled icon rel-mt-1 theme-secondary">
<i class="sign-in icon" aria-hidden="true"></i>
{{ _("Membership discussion") }}
</a>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To note: this is creating a button for invitation discussion even if membership feature itself is not enabled.

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.

The button should appear only if the membership feature is enabled.
I wonder if we should have a feature flag too, which disables entirely this feature, based on my comment above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed it to only display if the feature was enabled at site + community levels. If those are enabled, that button would show up if there is a membership request or an invitation pending. It's a better UX in my opinion to include invitation because someone can't request to join a community if they have already been invited to it.

Comment on lines +262 to +263
@blueprint.app_context_processor
def processor_for_member_button():
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is so that the community records page also gets the request id without changing its view (and having breakage in between change).

Consequences of having the community's records page (aka detail page) defined in invenio-app-rdm. (case of "polluting" the global processor context for something that could have been scoped)

else_value="open",
then_=then_,
else_=else_,
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced by below. This was unreadable when I returned to it.

needs = [CommunityRoleNeed(community_id, role) for role in roles]
return needs
return []
return [CommunityRoleNeed(community_id, r) for r in self.roles(**kwargs) or []]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

just making more readable in passing.

else_=[SystemProcess()],
),
IfCommunityAllowsMembershipRequests(
then_=[AuthenticatedUserButNotCommunityMember()], else_=[Disable()]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@ntarocco This used to be Disable() but was set to SystemProcess() in d3daea2 . What was the context for that change? I am setting it to Disable() again because of the explanation above. To add to that explanation: it's both surprising for a superuser to encounter the membership button and other related functionality when the feature is off and surprising for a non-superuser manager of a community to then receive a membership request when their community doesn't allow it (a superuser or systemprocess sending one).

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.

It was done because otherwise no CLI/programmatic script can use it, which they normally act with a system user/process.
I agree with you that superusers will see it and it will be confusing. At the same time, we need to allow a system user/process to perform changes. This is useful when running scripts to update things, for example. It might not be needed in this context.

My change was a bulk update Disable -> SystemProcess.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, this is a case of not needed in this context (programmatic request to join only makes sense if the feature is enabled and scripts can always workaround anyway). I've kept the Disable().

r = client.get(url_of_request, headers=headers)
assert 200 == r.status_code
assert 'Request to join "My Community"' in r.json["title"]
assert community_id == r.json["receiver"]["community"]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

just more accurate test (don't really care about the title)

@fenekku fenekku added this to v14 Feb 17, 2026
@fenekku fenekku moved this to 👀 In review in v14 Feb 17, 2026
…ture enabled [+]

- also optimizes for reduced chance of hitting the DB
@fenekku
Copy link
Copy Markdown
Contributor Author

fenekku commented Mar 13, 2026

Merging as mentioned on Discord. This PR introduces pretty thorough gating of the feature and its impact (no additional DB hit until feature enabled at site and community level and not enabled at community level by default even if feature enabled at site-level: https://github.com/inveniosoftware/invenio-communities/pull/1353/changes#diff-a4058314680fdd8690322e8b12c653c6b24e629311cc22f6caebc6506cbdea5aR19), so it should be good.

@fenekku fenekku merged commit a3f67a0 into inveniosoftware:master Mar 13, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from 👀 In review to To release 🤖 in v14 Mar 13, 2026
@fenekku fenekku deleted the button_for_membership_discussion_part_reviving_membership branch March 13, 2026 18:15
Copy link
Copy Markdown
Contributor

@ntarocco ntarocco left a comment

Choose a reason for hiding this comment

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

Sorry for the extremely late review, added a couple of comments. Please feel free to ignore them if they don't make sense to you.

:param community_id: Description
:return: UUID: id of active request associated with user and community
"""
return self.record_cls.get_pending_request_id(identity.id, community_id)
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.

Is it OK not having a permission check here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep no permission here is normal as it's more a service utility function (maybe like: https://github.com/inveniosoftware/invenio-communities/blob/master/invenio_communities/members/services/service.py#L526 ). Mostly didn't want the presentation UI layer to be using something from the data layer directly by passing service layer. Gives us a little buffer and keeps it relatively consistent with what we try to do.

:param record: api.Community
"""
config_enabled = bool(
current_app.config.get("COMMUNITIES_ALLOW_MEMBERSHIP_REQUESTS")
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.

Suggested change
current_app.config.get("COMMUNITIES_ALLOW_MEMBERSHIP_REQUESTS")
current_app.config["COMMUNITIES_ALLOW_MEMBERSHIP_REQUESTS"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍 it's true that it's in the config.py anyway so it will be present. I can switch that in next PRs (if I don't forget!)

@fenekku
Copy link
Copy Markdown
Contributor Author

fenekku commented Mar 20, 2026

Thanks for taking a look @ntarocco ! There are 2 other PRs out covering the requester side (#1358 + inveniosoftware/invenio-app-rdm#3364) and I will be submitting another to cover the backend of the reviewer side soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To release 🤖

Development

Successfully merging this pull request may close these issues.

2 participants