Skip to content

Commit dbf1ce4

Browse files
committed
members service: [inveniosoftware#855] 5) display membership discussion link
1 parent 9f968cb commit dbf1ce4

File tree

5 files changed

+114
-11
lines changed

5 files changed

+114
-11
lines changed

Diff for: invenio_communities/members/records/api.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from invenio_records_resources.records.systemfields import IndexField
2020
from invenio_requests.records.api import Request
2121
from invenio_users_resources.records.api import GroupAggregate, UserAggregate
22-
from sqlalchemy import or_
22+
from sqlalchemy import or_, select
2323

2424
from ..errors import InvalidMemberError
2525
from .models import ArchivedInvitationModel, MemberModel
@@ -160,6 +160,21 @@ def has_members(cls, community_id, role=None):
160160
"""Get members of a community."""
161161
return cls.model_cls.count_members(community_id, role=role)
162162

163+
@classmethod
164+
def get_pending_request_id_if_any(cls, user_id, community_id):
165+
"""Return request id of membership request/invitation still pending.
166+
167+
Return type UUID.
168+
"""
169+
stmt = (
170+
select(cls.model_cls.request_id)
171+
.where(cls.model_cls.user_id == user_id)
172+
.where(cls.model_cls.community_id == community_id)
173+
.where(cls.model_cls.active == False)
174+
)
175+
request_id = db.session.scalars(stmt).one_or_none()
176+
return request_id
177+
163178

164179
class Member(Record, MemberMixin):
165180
"""A member/invitation record.

Diff for: invenio_communities/members/services/service.py

+7
Original file line numberDiff line numberDiff line change
@@ -857,3 +857,10 @@ def close_membership_request(self, identity, request_id, uow=None):
857857
member = self.record_cls.get_member_by_request(request_id)
858858
assert member.active is False
859859
uow.register(RecordDeleteOp(member, indexer=self.indexer, force=True))
860+
861+
def get_pending_request_id_if_any(self, user_id, community_id):
862+
"""Utility function to get associated active request id.
863+
864+
Only pending members fit this. In other cases return None.
865+
"""
866+
return self.record_cls.get_pending_request_id_if_any(user_id, community_id)

Diff for: invenio_communities/templates/semantic-ui/invenio_communities/details/header.html

+17-9
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,23 @@
1111
{%- from "invenio_theme/macros/truncate.html" import truncate_text %}
1212
{%- from "invenio_communities/details/macros/access-status-label.html" import access_status_label -%}
1313

14-
{% macro button_to_request_membership(community) %}
15-
{% if permissions.can_request_membership %}
16-
{# TODO: Add relation_to_community for other flows #}
17-
<div
18-
id="request-membership-app"
19-
data-community='{{ community | tojson }}'
20-
class="display-inline-block"
14+
{% macro button_for_membership(community) %}
15+
{# an associated request id is defined/not None only if user has membership discussion active #}
16+
{% if associated_request_id is defined and associated_request_id %}
17+
<a href="{{ url_for('invenio_app_rdm_requests.user_dashboard_request_view', request_pid_value=associated_request_id) }}"
18+
class="ui button labeled icon rel-mt-1 theme-secondary">
19+
<i class="sign-in icon" aria-hidden="true"></i>
20+
{{ _("Membership discussion") }}
21+
</a>
22+
{% elif permissions.can_request_membership %}
23+
<div
24+
id="request-membership-app"
25+
data-community='{{ community | tojson }}'
26+
class="display-inline-block"
2127
>
22-
</div>
28+
</div>
29+
{% else %}
30+
{# show nothing #}
2331
{% endif %}
2432
{% endmacro %}
2533

@@ -120,7 +128,7 @@ <h1 class="ui medium header mb-0">{{ community.metadata.title }}</h1>
120128
</div>
121129
<div
122130
class="sixteen wide mobile sixteen wide tablet five wide computer right aligned middle aligned column">
123-
{{ button_to_request_membership(community) }}
131+
{{ button_for_membership(community) }}
124132
{%- if not community_use_jinja_header %}
125133
<a href="/uploads/new?community={{ community.slug }}"
126134
class="ui positive button labeled icon rel-mt-1 theme-secondary">

Diff for: invenio_communities/views/communities.py

+16
Original file line numberDiff line numberDiff line change
@@ -391,13 +391,18 @@ def members(pid_value, community, community_ui):
391391
if not permissions["can_members_search_public"]:
392392
raise PermissionDeniedError()
393393

394+
members_service = current_communities.service.members
395+
394396
return render_community_theme_template(
395397
"invenio_communities/details/members/members.html",
396398
theme=community_ui.get("theme", {}),
397399
community=community_ui,
398400
permissions=permissions,
399401
roles_can_update=_get_roles_can_update(community.id),
400402
roles_can_invite=_get_roles_can_invite(community.id),
403+
associated_request_id=(
404+
members_service.get_pending_request_id_if_any(g.identity.id, community.id)
405+
),
401406
)
402407

403408

@@ -423,26 +428,37 @@ def communities_about(pid_value, community, community_ui):
423428
if not permissions["can_read"]:
424429
raise PermissionDeniedError()
425430

431+
members_service = current_communities.service.members
432+
426433
return render_community_theme_template(
427434
"invenio_communities/details/about/index.html",
428435
theme=community_ui.get("theme", {}),
429436
community=community_ui,
430437
permissions=permissions,
431438
custom_fields_ui=load_custom_fields(dump_only_required=False)["ui"],
439+
associated_request_id=(
440+
members_service.get_pending_request_id_if_any(
441+
g.identity.id, community.id)
442+
),
432443
)
433444

434445

435446
@pass_community(serialize=True)
436447
def communities_curation_policy(pid_value, community, community_ui):
437448
"""Community curation policy page."""
438449
permissions = community.has_permissions_to(HEADER_PERMISSIONS)
450+
members_service = current_communities.service.members
439451
if not permissions["can_read"]:
440452
raise PermissionDeniedError()
441453
return render_community_theme_template(
442454
"invenio_communities/details/curation_policy/index.html",
443455
theme=community_ui.get("theme", {}),
444456
community=community_ui,
445457
permissions=permissions,
458+
associated_request_id=(
459+
members_service.get_pending_request_id_if_any(
460+
g.identity.id, community.id)
461+
),
446462
)
447463

448464

Diff for: tests/members/test_members_services.py

+58-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from invenio_cache import current_cache
1818
from invenio_notifications.proxies import current_notifications_manager
1919
from invenio_records_resources.services.errors import PermissionDeniedError
20-
from invenio_requests.records.api import RequestEvent
20+
from invenio_requests.records.api import Request, RequestEvent
2121
from marshmallow import ValidationError
2222

2323
from invenio_communities.members.errors import AlreadyMemberError, InvalidMemberError
@@ -1208,6 +1208,63 @@ def test_request_cancel_request_flow(
12081208
)
12091209

12101210

1211+
def test_get_pending_request_id_if_any(
1212+
member_service,
1213+
community,
1214+
owner,
1215+
create_user,
1216+
requests_service,
1217+
db,
1218+
search_clear,
1219+
):
1220+
user = create_user()
1221+
1222+
# Case no membership (no associated request id)
1223+
request_id = member_service.get_pending_request_id_if_any(
1224+
user.id, community._record.id)
1225+
assert request_id is None
1226+
1227+
# Case pending membership (associated request id)
1228+
membership_request = member_service.request_membership(
1229+
user.identity,
1230+
community._record.id,
1231+
{"message": "Can I join the club?"},
1232+
)
1233+
request_id = member_service.get_pending_request_id_if_any(
1234+
user.id, community._record.id)
1235+
assert membership_request.id == str(request_id)
1236+
1237+
# Case (sanity check) pending membership from invitation (associated request id)
1238+
requests_service.execute_action(
1239+
user.identity, membership_request.id, "cancel"
1240+
)
1241+
data = {
1242+
"members": [{"type": "user", "id": str(user.id)}],
1243+
"role": "reader",
1244+
"message": "Welcome to the club!",
1245+
}
1246+
member_service.invite(owner.identity, community._record.id, data)
1247+
# get invitation_request_id
1248+
Request.index.refresh()
1249+
results = requests_service.search(
1250+
user.identity,
1251+
receiver={"user": user.id},
1252+
type="community-invitation",
1253+
).to_dict()
1254+
invitation_request_id = results["hits"]["hits"][0]["id"]
1255+
request_id = member_service.get_pending_request_id_if_any(
1256+
user.id, community._record.id)
1257+
assert invitation_request_id == str(request_id)
1258+
1259+
# Case membership established (associated request id but not pending)
1260+
requests_service.execute_action(
1261+
user.identity, invitation_request_id, "accept"
1262+
)
1263+
request_id = member_service.get_pending_request_id_if_any(
1264+
user.id, community._record.id)
1265+
assert request_id is None
1266+
1267+
12111268
#
12121269
# Change notifications
12131270
#

0 commit comments

Comments
 (0)