Skip to content

Commit

Permalink
membership-requests [#855]: notify on membership-request changes
Browse files Browse the repository at this point in the history
  • Loading branch information
fenekku committed Jul 30, 2024
1 parent d8175f8 commit 3b877d2
Show file tree
Hide file tree
Showing 10 changed files with 686 additions and 16 deletions.
28 changes: 24 additions & 4 deletions invenio_communities/members/services/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
CommunityInvitationCancelNotificationBuilder,
CommunityInvitationDeclineNotificationBuilder,
CommunityInvitationExpireNotificationBuilder,
CommunityMembershipRequestAcceptNotificationBuilder,
CommunityMembershipRequestCancelNotificationBuilder,
CommunityMembershipRequestDeclineNotificationBuilder,
CommunityMembershipRequestExpireNotificationBuilder,
)

from ...proxies import current_communities
Expand Down Expand Up @@ -142,7 +146,11 @@ class CancelMembershipRequestAction(actions.CancelAction):
def execute(self, identity, uow):
"""Execute action."""
service().close_member_request(system_identity, self.request.id, uow=uow)
# TODO: Notification flow: Investigate notifications
uow.register(
NotificationOp(
CommunityMembershipRequestCancelNotificationBuilder.build(self.request)
)
)
super().execute(identity, uow)


Expand All @@ -152,7 +160,11 @@ class DeclineMembershipRequestAction(actions.DeclineAction):
def execute(self, identity, uow):
"""Execute action."""
service().close_member_request(system_identity, self.request.id, uow=uow)
# TODO: Notification flow: Investigate notifications
uow.register(
NotificationOp(
CommunityMembershipRequestDeclineNotificationBuilder.build(self.request)
)
)
super().execute(identity, uow)


Expand All @@ -165,7 +177,11 @@ class ExpireMembershipRequestAction(actions.ExpireAction):
def execute(self, identity, uow):
"""Execute action."""
service().close_member_request(system_identity, self.request.id, uow=uow)
# TODO: Notification flow: Investigate notifications
uow.register(
NotificationOp(
CommunityMembershipRequestExpireNotificationBuilder.build(self.request)
)
)
super().execute(identity, uow)


Expand All @@ -175,7 +191,11 @@ class AcceptMembershipRequestAction(actions.AcceptAction):
def execute(self, identity, uow):
"""Execute action."""
service().accept_member_request(system_identity, self.request.id, uow=uow)
# TODO: Notification flow: Investigate notifications
uow.register(
NotificationOp(
CommunityMembershipRequestAcceptNotificationBuilder.build(self.request)
)
)
super().execute(identity, uow)


Expand Down
28 changes: 16 additions & 12 deletions invenio_communities/members/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
from sqlalchemy.exc import IntegrityError
from werkzeug.local import LocalProxy

from ...notifications.builders import CommunityInvitationSubmittedNotificationBuilder
from ...notifications.builders import (
CommunityInvitationSubmittedNotificationBuilder,
CommunityMembershipRequestSubmittedNotificationBuilder,
)
from ...proxies import current_roles
from ..errors import AlreadyMemberError, InvalidMemberError
from ..records.api import ArchivedInvitation
Expand Down Expand Up @@ -839,22 +842,23 @@ def request_membership(self, identity, community_id, data, uow=None):
)

# TODO: Notification flow: Add notification mechanism
# uow.register(
# NotificationOp(
# MembershipRequestSubmittedNotificationBuilder.build(
# request=request_item._request,
# # explicit string conversion to get the value of LazyText
# role=str(role.title),
# message=message,
# )
# )
# )
role = current_roles["reader"]
uow.register(
NotificationOp(
CommunityMembershipRequestSubmittedNotificationBuilder.build(
request=request_item._record,
# explicit string conversion to get the value of LazyText
role=str(role.title),
message=message,
)
)
)

# Create an inactive member entry linked to the request.
self._add_factory(
identity,
community=community,
role=current_roles["reader"],
role=role,
visible=False,
member={"type": "user", "id": str(identity.user.id)},
message=message,
Expand Down
86 changes: 86 additions & 0 deletions invenio_communities/notifications/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,89 @@ class SubCommunityDecline(SubCommunityBuilderBase):
"""Notification builder for subcommunity request decline."""

type = f"{SubCommunityBuilderBase.type}.decline"


class MembershipRequestBaseNotificationBuilder(BaseNotificationBuilder):
"""Base membership request notification builder."""
type = "community-membership-request"

@classmethod
def build(cls, request, message=None):
"""Build notification with request context."""
return Notification(
type=cls.type,
context={
"request": EntityResolverRegistry.reference_entity(request),
},
)


class CommunityMembershipRequestSubmittedNotificationBuilder(MembershipRequestBaseNotificationBuilder):
"""Notification builder for community membership request submission."""

# identifier
type = f"{MembershipRequestBaseNotificationBuilder.type}.submit"
recipients = [
CommunityMembersRecipient(key="request.receiver", roles=["owner", "manager"]),
]

@classmethod
def build(cls, request, role, message=None):
"""Build notification with request context."""
return Notification(
type=cls.type,
context={
"request": EntityResolverRegistry.reference_entity(request),
"role": role,
"message": message,
},
)


class CommunityMembershipRequestCancelNotificationBuilder(
MembershipRequestBaseNotificationBuilder
):
"""Notification builder for community membership request cancel action."""

# identifier
type = f"{MembershipRequestBaseNotificationBuilder.type}.cancel"
recipients = [
CommunityMembersRecipient(key="request.receiver", roles=["owner", "manager"]),
]


class CommunityMembershipRequestDeclineNotificationBuilder(
MembershipRequestBaseNotificationBuilder
):
"""Notification builder for community membership request decline action."""

# identifier
type = f"{MembershipRequestBaseNotificationBuilder.type}.decline"
recipients = [
UserRecipient(key="request.created_by"),
]


class CommunityMembershipRequestExpireNotificationBuilder(
MembershipRequestBaseNotificationBuilder
):
"""Notification builder for community membership request expire action."""

# identifier
type = f"{MembershipRequestBaseNotificationBuilder.type}.expire"
recipients = [
CommunityMembersRecipient(key="request.receiver", roles=["owner", "manager"]),
UserRecipient(key="request.created_by"),
]


class CommunityMembershipRequestAcceptNotificationBuilder(
MembershipRequestBaseNotificationBuilder
):
"""Notification builder for community membership request accept action."""

# identifier
type = f"{MembershipRequestBaseNotificationBuilder.type}.accept"
recipients = [
UserRecipient(key="request.created_by"),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{% set request = notification.context.request %}
{% set community = request.receiver %}
{% set created_by = request.created_by %}
{% set request_id = request.id %}
{# TODO: Action-based notifications don't pass `message` so this will always be empty #}
{% set message = notification.context.message | safe if notification.context.message else '' %}
{% set community_title = community.metadata.title %}
{# This email is sent to the requester only so omitted requester's name #}

{# TODO: use request.links.self_html when this issue is resolved: https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 #}
{% set request_link = "{ui}/me/requests/{id}".format(ui=config.SITE_UI_URL, id=request_id) %}
{# "/account/settings/notifications" is hardcoded in invenio-notifications
and not publicly exposed so ok to refer to it directly for now #}
{% set account_settings_link = "{ui}/account/settings/notifications".format(ui=config.SITE_UI_URL) %}

{%- block subject -%}
{{ _("✅ Request to join the community '{community_title}' was accepted").format(community_title=community_title) }}
{%- endblock subject -%}

{%- block html_body -%}
<table style="font-family:'Lato',Helvetica,Arial,sans-serif;border-spacing:15px">
<tr>
<td>{{ _("The membership request to join the community '{community_title}' was accepted").format(community_title=community_title) }}
{% if message %}{{ _(" with the following message:")}}{% endif %}
</td>
</tr>
<tr>
{% if message %}
<td><em>"{{message}}"</em></td>
{% endif %}
</tr>
<tr>
<td><a href="{{ request_link }}" class="button">{{ _("Check out the membership request")}}</a></td>
</tr>
<tr>
<td><strong>_</strong></td>
</tr>
<tr>
<td style="font-size:smaller">{{ _("This is an auto-generated message. To manage notifications, visit your")}} <a href="{{account_settings_link}}">{{ _("account settings")}}</a>.</td>
</tr>
</table>
{%- endblock html_body %}

{%- block plain_body -%}
{{ _("The membership request to join the community '{community_title}' was accepted").format(community_title=community_title) }}
{% if message %}{{ _("with the following message:")}} {{message}}{% endif %}

{{ _("Check out the membership request:") }} {{ request_link }}
{%- endblock plain_body %}

{# Markdown for Slack/Mattermost/chat #}
{%- block md_body -%}
{{ _("The membership request to join the community *{community_title}* was accepted").format(community_title=community_title) }}
{% if message %}{{ _("with the following message:")}} {{ message }}{% endif %}

[{{ _("Check out the membership request") }}]({{ request_link }})
{%- endblock md_body %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{% set request = notification.context.request %}
{% set community = request.receiver %}
{% set created_by = request.created_by %}
{% set requester_name = created_by.username or created_by.profile.full_name %}
{% set request_id = request.id %}
{% set message = notification.context.message | safe if notification.context.message else '' %}
{% set community_title = community.metadata.title %}

{# WARNING: this uses a config set by invenio-app-rdm (not a dependency) to at least not hardcode the URL #}
{# invenio-app-rdm should have been using REQUESTS_ROUTES a config of a dependency however. #}
{% set request_link_path = config.RDM_REQUESTS_ROUTES["community-dashboard-request-details"].lstrip("/") %}
{% set request_link_path = request_link_path.replace("<pid_value>", community.id).replace("<request_pid_value>", request_id) %}
{% set request_link = "{ui}/{request_link_path}".format(ui=config.SITE_UI_URL, request_link_path=request_link_path) %}

{%- block subject -%}
{{ _("❌ Request for '@{requester_name}' to join the community '{community_title}' was cancelled").format(requester_name=requester_name, community_title=community_title) }}
{%- endblock subject -%}

{%- block html_body -%}
<table style="font-family:'Lato',Helvetica,Arial,sans-serif;border-spacing:15px">
<tr>
<td>{{ _("The membership request for '@{requester_name}' to join the community '{community_title}' was cancelled").format(requester_name=requester_name, community_title=community_title) }}
{% if message %}{{ _(" with the following message:")}}{% endif %}
</td>
</tr>
<tr>
{% if message %}
<td><em>"{{message}}"</em></td>
{% endif %}
</tr>
<tr>
<td><a href="{{ request_link }}" class="button">{{ _("Check out the membership request")}}</a></td>
</tr>
<tr>
<td><strong>_</strong></td>
</tr>
<tr>
<td style="font-size:smaller">{{ _("This is an auto-generated message. To manage notifications, visit your")}} <a href="{{account_settings_link}}">{{ _("account settings")}}</a>.</td>
</tr>
</table>
{%- endblock html_body %}

{%- block plain_body -%}
{{ _("The membership request for '@{requester_name}' to join the community '{community_title}' was cancelled").format(requester_name=requester_name, community_title=community_title) }}
{% if message %}{{ _(" with the following message:")}} {{ message }}{% endif %}

{{ _("Check out the membership request:") }} {{ request_link }}
{%- endblock plain_body %}

{# Markdown for Slack/Mattermost/chat #}
{%- block md_body -%}
{{ _("The membership_request for *@{requester_name}* to join the community *{community_title}* was cancelled").format(requester_name=requester_name, community_title=community_title) }}
{% if message %}{{ _("with the following message:")}} {{message}}{% endif %}

[{{ _("Check out the membership request") }}]({{ request_link }})
{%- endblock md_body %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{% set request = notification.context.request %}
{% set community = request.receiver %}
{% set created_by = request.created_by %}
{% set request_id = request.id %}
{# TODO: Action-based notifications don't pass `message` so this will always be empty #}
{% set message = notification.context.message | safe if notification.context.message else '' %}
{% set community_title = community.metadata.title %}
{# This email is sent to the requester only so omitted requester's name #}

{# TODO: use request.links.self_html when this issue is resolved: https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 #}
{% set request_link = "{ui}/me/requests/{id}".format(ui=config.SITE_UI_URL, id=request_id) %}
{# "/account/settings/notifications" is hardcoded in invenio-notifications
and not publicly exposed so ok to refer to it directly for now #}
{% set account_settings_link = "{ui}/account/settings/notifications".format(ui=config.SITE_UI_URL) %}

{%- block subject -%}
{{ _("⛔️ Request to join the community '{community_title}' was declined").format(community_title=community_title) }}
{%- endblock subject -%}

{%- block html_body -%}
<table style="font-family:'Lato',Helvetica,Arial,sans-serif;border-spacing:15px">
<tr>
<td>{{ _("The membership request to join the community '{community_title}' was declined").format(community_title=community_title) }}
{% if message %}{{ _(" with the following message:")}}{% endif %}
</td>
</tr>
<tr>
{% if message %}
<td><em>"{{message}}"</em></td>
{% endif %}
</tr>
<tr>
<td><a href="{{ request_link }}" class="button">{{ _("Check out the membership request")}}</a></td>
</tr>
<tr>
<td><strong>_</strong></td>
</tr>
<tr>
<td style="font-size:smaller">{{ _("This is an auto-generated message. To manage notifications, visit your")}} <a href="{{account_settings_link}}">{{ _("account settings")}}</a>.</td>
</tr>
</table>
{%- endblock html_body %}

{%- block plain_body -%}
{{ _("The membership request to join the community '{community_title}' was declined").format(community_title=community_title) }}
{% if message %}{{ _("with the following message:")}} {{message}}{% endif %}

{{ _("Check out the membership request:") }} {{ request_link }}
{%- endblock plain_body %}

{# Markdown for Slack/Mattermost/chat #}
{%- block md_body -%}
{{ _("The membership request to join the community *{community_title}* was declined").format(community_title=community_title) }}
{% if message %}{{ _("with the following message:")}} {{ message }}{% endif %}

[{{ _("Check out the membership request") }}]({{ request_link }})
{%- endblock md_body %}
Loading

0 comments on commit 3b877d2

Please sign in to comment.