forked from MirrorHub/synapse
Merge pull request #2363 from matrix-org/erikj/group_server_summary
Add group summary APIs
This commit is contained in:
commit
b3de67234e
4 changed files with 1131 additions and 22 deletions
|
@ -615,8 +615,8 @@ class FederationGroupsProfileServlet(BaseFederationServlet):
|
||||||
PATH = "/groups/(?P<group_id>[^/]*)/profile$"
|
PATH = "/groups/(?P<group_id>[^/]*)/profile$"
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id):
|
def on_GET(self, origin, content, query, group_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -627,14 +627,30 @@ class FederationGroupsProfileServlet(BaseFederationServlet):
|
||||||
defer.returnValue((200, new_content))
|
defer.returnValue((200, new_content))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsSummaryServlet(BaseFederationServlet):
|
||||||
|
PATH = "/groups/(?P<group_id>[^/]*)/summary$"
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, origin, content, query, group_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
new_content = yield self.handler.get_group_summary(
|
||||||
|
group_id, requester_user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, new_content))
|
||||||
|
|
||||||
|
|
||||||
class FederationGroupsRoomsServlet(BaseFederationServlet):
|
class FederationGroupsRoomsServlet(BaseFederationServlet):
|
||||||
"""Get the rooms in a group on behalf of a user
|
"""Get the rooms in a group on behalf of a user
|
||||||
"""
|
"""
|
||||||
PATH = "/groups/(?P<group_id>[^/]*)/rooms$"
|
PATH = "/groups/(?P<group_id>[^/]*)/rooms$"
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id):
|
def on_GET(self, origin, content, query, group_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -652,7 +668,7 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id, room_id):
|
def on_POST(self, origin, content, query, group_id, room_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -669,8 +685,8 @@ class FederationGroupsUsersServlet(BaseFederationServlet):
|
||||||
PATH = "/groups/(?P<group_id>[^/]*)/users$"
|
PATH = "/groups/(?P<group_id>[^/]*)/users$"
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id):
|
def on_GET(self, origin, content, query, group_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -688,7 +704,7 @@ class FederationGroupsInviteServlet(BaseFederationServlet):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id, user_id):
|
def on_POST(self, origin, content, query, group_id, user_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -723,7 +739,7 @@ class FederationGroupsRemoveUserServlet(BaseFederationServlet):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_POST(self, origin, content, query, group_id, user_id):
|
def on_POST(self, origin, content, query, group_id, user_id):
|
||||||
requester_user_id = content["requester_user_id"]
|
requester_user_id = query["requester_user_id"]
|
||||||
if get_domain_from_id(requester_user_id) != origin:
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
raise SynapseError(403, "requester_user_id doesn't match origin")
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
@ -750,6 +766,244 @@ class FederationGroupsRenewAttestaionServlet(BaseFederationServlet):
|
||||||
defer.returnValue((200, new_content))
|
defer.returnValue((200, new_content))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsSummaryRoomsServlet(BaseFederationServlet):
|
||||||
|
"""Add/remove a room from the group summary, with optional category.
|
||||||
|
|
||||||
|
Matches both:
|
||||||
|
- /groups/:group/summary/rooms/:room_id
|
||||||
|
- /groups/:group/summary/categories/:category/rooms/:room_id
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/summary"
|
||||||
|
"(/categories/(?P<category_id>[^/]+))?"
|
||||||
|
"/rooms/(?P<room_id>[^/]*)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, origin, content, query, group_id, category_id, room_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if category_id == "":
|
||||||
|
raise SynapseError(400, "category_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.update_group_summary_room(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
room_id=room_id,
|
||||||
|
category_id=category_id,
|
||||||
|
content=content,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_DELETE(self, origin, content, query, group_id, category_id, room_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if category_id == "":
|
||||||
|
raise SynapseError(400, "category_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.delete_group_summary_room(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
room_id=room_id,
|
||||||
|
category_id=category_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsCategoriesServlet(BaseFederationServlet):
|
||||||
|
"""Get all categories for a group
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/categories/$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, origin, content, query, group_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
resp = yield self.handler.get_group_categories(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsCategoryServlet(BaseFederationServlet):
|
||||||
|
"""Add/remove/get a category in a group
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/categories/(?P<category_id>[^/]+)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, origin, content, query, group_id, category_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
resp = yield self.handler.get_group_category(
|
||||||
|
group_id, requester_user_id, category_id
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, origin, content, query, group_id, category_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if category_id == "":
|
||||||
|
raise SynapseError(400, "category_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.upsert_group_category(
|
||||||
|
group_id, requester_user_id, category_id, content,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_DELETE(self, origin, content, query, group_id, category_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if category_id == "":
|
||||||
|
raise SynapseError(400, "category_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.delete_group_category(
|
||||||
|
group_id, requester_user_id, category_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsRolesServlet(BaseFederationServlet):
|
||||||
|
"""Get roles in a group
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/roles/$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, origin, content, query, group_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
resp = yield self.handler.get_group_roles(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsRoleServlet(BaseFederationServlet):
|
||||||
|
"""Add/remove/get a role in a group
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/roles/(?P<role_id>[^/]+)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, origin, content, query, group_id, role_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
resp = yield self.handler.get_group_role(
|
||||||
|
group_id, requester_user_id, role_id
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, origin, content, query, group_id, role_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if role_id == "":
|
||||||
|
raise SynapseError(400, "role_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.update_group_role(
|
||||||
|
group_id, requester_user_id, role_id, content,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_DELETE(self, origin, content, query, group_id, role_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if role_id == "":
|
||||||
|
raise SynapseError(400, "role_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.delete_group_role(
|
||||||
|
group_id, requester_user_id, role_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
|
class FederationGroupsSummaryUsersServlet(BaseFederationServlet):
|
||||||
|
"""Add/remove a user from the group summary, with optional role.
|
||||||
|
|
||||||
|
Matches both:
|
||||||
|
- /groups/:group/summary/users/:user_id
|
||||||
|
- /groups/:group/summary/roles/:role/users/:user_id
|
||||||
|
"""
|
||||||
|
PATH = (
|
||||||
|
"/groups/(?P<group_id>[^/]*)/summary"
|
||||||
|
"(/roles/(?P<role_id>[^/]+))?"
|
||||||
|
"/users/(?P<user_id>[^/]*)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, origin, content, query, group_id, role_id, user_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if role_id == "":
|
||||||
|
raise SynapseError(400, "role_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.update_group_summary_user(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
user_id=user_id,
|
||||||
|
role_id=role_id,
|
||||||
|
content=content,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_DELETE(self, origin, content, query, group_id, role_id, user_id):
|
||||||
|
requester_user_id = query["requester_user_id"]
|
||||||
|
if get_domain_from_id(requester_user_id) != origin:
|
||||||
|
raise SynapseError(403, "requester_user_id doesn't match origin")
|
||||||
|
|
||||||
|
if role_id == "":
|
||||||
|
raise SynapseError(400, "role_id cannot be empty string")
|
||||||
|
|
||||||
|
resp = yield self.handler.delete_group_summary_user(
|
||||||
|
group_id, requester_user_id,
|
||||||
|
user_id=user_id,
|
||||||
|
role_id=role_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((200, resp))
|
||||||
|
|
||||||
|
|
||||||
FEDERATION_SERVLET_CLASSES = (
|
FEDERATION_SERVLET_CLASSES = (
|
||||||
FederationSendServlet,
|
FederationSendServlet,
|
||||||
FederationPullServlet,
|
FederationPullServlet,
|
||||||
|
@ -784,11 +1038,18 @@ ROOM_LIST_CLASSES = (
|
||||||
|
|
||||||
GROUP_SERVER_SERVLET_CLASSES = (
|
GROUP_SERVER_SERVLET_CLASSES = (
|
||||||
FederationGroupsProfileServlet,
|
FederationGroupsProfileServlet,
|
||||||
|
FederationGroupsSummaryServlet,
|
||||||
FederationGroupsRoomsServlet,
|
FederationGroupsRoomsServlet,
|
||||||
FederationGroupsUsersServlet,
|
FederationGroupsUsersServlet,
|
||||||
FederationGroupsInviteServlet,
|
FederationGroupsInviteServlet,
|
||||||
FederationGroupsAcceptInviteServlet,
|
FederationGroupsAcceptInviteServlet,
|
||||||
FederationGroupsRemoveUserServlet,
|
FederationGroupsRemoveUserServlet,
|
||||||
|
FederationGroupsSummaryRoomsServlet,
|
||||||
|
FederationGroupsCategoriesServlet,
|
||||||
|
FederationGroupsCategoryServlet,
|
||||||
|
FederationGroupsRolesServlet,
|
||||||
|
FederationGroupsRoleServlet,
|
||||||
|
FederationGroupsSummaryUsersServlet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,16 @@ class GroupsServerHandler(object):
|
||||||
hs.get_groups_attestation_renewer()
|
hs.get_groups_attestation_renewer()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_group_is_ours(self, group_id, and_exists=False):
|
def check_group_is_ours(self, group_id, and_exists=False, and_is_admin=None):
|
||||||
"""Check that the group is ours, and optionally if it exists.
|
"""Check that the group is ours, and optionally if it exists.
|
||||||
|
|
||||||
If group does exist then return group.
|
If group does exist then return group.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
group_id (str)
|
||||||
|
and_exists (bool): whether to also check if group exists
|
||||||
|
and_is_admin (str): whether to also check if given str is a user_id
|
||||||
|
that is an admin
|
||||||
"""
|
"""
|
||||||
if not self.is_mine_id(group_id):
|
if not self.is_mine_id(group_id):
|
||||||
raise SynapseError(400, "Group not on this server")
|
raise SynapseError(400, "Group not on this server")
|
||||||
|
@ -62,8 +68,261 @@ class GroupsServerHandler(object):
|
||||||
if and_exists and not group:
|
if and_exists and not group:
|
||||||
raise SynapseError(404, "Unknown group")
|
raise SynapseError(404, "Unknown group")
|
||||||
|
|
||||||
|
if and_is_admin:
|
||||||
|
is_admin = yield self.store.is_user_admin_in_group(group_id, and_is_admin)
|
||||||
|
if not is_admin:
|
||||||
|
raise SynapseError(403, "User is not admin in group")
|
||||||
|
|
||||||
defer.returnValue(group)
|
defer.returnValue(group)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_summary(self, group_id, requester_user_id):
|
||||||
|
"""Get the summary for a group as seen by requester_user_id.
|
||||||
|
|
||||||
|
The group summary consists of the profile of the room, and a curated
|
||||||
|
list of users and rooms. These list *may* be organised by role/category.
|
||||||
|
The roles/categories are ordered, and so are the users/rooms within them.
|
||||||
|
|
||||||
|
A user/room may appear in multiple roles/categories.
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True)
|
||||||
|
|
||||||
|
is_user_in_group = yield self.store.is_user_in_group(requester_user_id, group_id)
|
||||||
|
|
||||||
|
profile = yield self.get_group_profile(group_id, requester_user_id)
|
||||||
|
|
||||||
|
users, roles = yield self.store.get_users_for_summary_by_role(
|
||||||
|
group_id, include_private=is_user_in_group,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: Add profiles to users
|
||||||
|
|
||||||
|
rooms, categories = yield self.store.get_rooms_for_summary_by_category(
|
||||||
|
group_id, include_private=is_user_in_group,
|
||||||
|
)
|
||||||
|
|
||||||
|
for room_entry in rooms:
|
||||||
|
room_id = room_entry["room_id"]
|
||||||
|
joined_users = yield self.store.get_users_in_room(room_id)
|
||||||
|
entry = yield self.room_list_handler.generate_room_entry(
|
||||||
|
room_id, len(joined_users),
|
||||||
|
with_alias=False, allow_private=True,
|
||||||
|
)
|
||||||
|
entry.pop("room_id", None)
|
||||||
|
|
||||||
|
room_entry["profile"] = entry
|
||||||
|
|
||||||
|
rooms.sort(key=lambda e: e.get("order", 0))
|
||||||
|
|
||||||
|
for entry in users:
|
||||||
|
user_id = entry["user_id"]
|
||||||
|
|
||||||
|
if not self.is_mine_id(requester_user_id):
|
||||||
|
attestation = yield self.store.get_remote_attestation(group_id, user_id)
|
||||||
|
if not attestation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry["attestation"] = attestation
|
||||||
|
else:
|
||||||
|
entry["attestation"] = self.attestations.create_attestation(
|
||||||
|
group_id, user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
users.sort(key=lambda e: e.get("order", 0))
|
||||||
|
|
||||||
|
defer.returnValue({
|
||||||
|
"profile": profile,
|
||||||
|
"users_section": {
|
||||||
|
"users": users,
|
||||||
|
"roles": roles,
|
||||||
|
"total_user_count_estimate": 0, # TODO
|
||||||
|
},
|
||||||
|
"rooms_section": {
|
||||||
|
"rooms": rooms,
|
||||||
|
"categories": categories,
|
||||||
|
"total_room_count_estimate": 0, # TODO
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def update_group_summary_room(self, group_id, user_id, room_id, category_id, content):
|
||||||
|
"""Add/update a room to the group summary
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
order = content.get("order", None)
|
||||||
|
|
||||||
|
is_public = _parse_visibility_from_contents(content)
|
||||||
|
|
||||||
|
yield self.store.add_room_to_summary(
|
||||||
|
group_id=group_id,
|
||||||
|
room_id=room_id,
|
||||||
|
category_id=category_id,
|
||||||
|
order=order,
|
||||||
|
is_public=is_public,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def delete_group_summary_room(self, group_id, user_id, room_id, category_id):
|
||||||
|
"""Remove a room from the summary
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
yield self.store.remove_room_from_summary(
|
||||||
|
group_id=group_id,
|
||||||
|
room_id=room_id,
|
||||||
|
category_id=category_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_categories(self, group_id, user_id):
|
||||||
|
"""Get all categories in a group (as seen by user)
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True)
|
||||||
|
|
||||||
|
categories = yield self.store.get_group_categories(
|
||||||
|
group_id=group_id,
|
||||||
|
)
|
||||||
|
defer.returnValue({"categories": categories})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_category(self, group_id, user_id, category_id):
|
||||||
|
"""Get a specific category in a group (as seen by user)
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True)
|
||||||
|
|
||||||
|
res = yield self.store.get_group_category(
|
||||||
|
group_id=group_id,
|
||||||
|
category_id=category_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(res)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def update_group_category(self, group_id, user_id, category_id, content):
|
||||||
|
"""Add/Update a group category
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
is_public = _parse_visibility_from_contents(content)
|
||||||
|
profile = content.get("profile")
|
||||||
|
|
||||||
|
yield self.store.upsert_group_category(
|
||||||
|
group_id=group_id,
|
||||||
|
category_id=category_id,
|
||||||
|
is_public=is_public,
|
||||||
|
profile=profile,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def delete_group_category(self, group_id, user_id, category_id):
|
||||||
|
"""Delete a group category
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
yield self.store.remove_group_category(
|
||||||
|
group_id=group_id,
|
||||||
|
category_id=category_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_roles(self, group_id, user_id):
|
||||||
|
"""Get all roles in a group (as seen by user)
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True)
|
||||||
|
|
||||||
|
roles = yield self.store.get_group_roles(
|
||||||
|
group_id=group_id,
|
||||||
|
)
|
||||||
|
defer.returnValue({"roles": roles})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_role(self, group_id, user_id, role_id):
|
||||||
|
"""Get a specific role in a group (as seen by user)
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True)
|
||||||
|
|
||||||
|
res = yield self.store.get_group_role(
|
||||||
|
group_id=group_id,
|
||||||
|
role_id=role_id,
|
||||||
|
)
|
||||||
|
defer.returnValue(res)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def update_group_role(self, group_id, user_id, role_id, content):
|
||||||
|
"""Add/update a role in a group
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
is_public = _parse_visibility_from_contents(content)
|
||||||
|
|
||||||
|
profile = content.get("profile")
|
||||||
|
|
||||||
|
yield self.store.upsert_group_role(
|
||||||
|
group_id=group_id,
|
||||||
|
role_id=role_id,
|
||||||
|
is_public=is_public,
|
||||||
|
profile=profile,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def delete_group_role(self, group_id, user_id, role_id):
|
||||||
|
"""Remove role from group
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
yield self.store.remove_group_role(
|
||||||
|
group_id=group_id,
|
||||||
|
role_id=role_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def update_group_summary_user(self, group_id, requester_user_id, user_id, role_id,
|
||||||
|
content):
|
||||||
|
"""Add/update a users entry in the group summary
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
order = content.get("order", None)
|
||||||
|
|
||||||
|
is_public = _parse_visibility_from_contents(content)
|
||||||
|
|
||||||
|
yield self.store.add_user_to_summary(
|
||||||
|
group_id=group_id,
|
||||||
|
user_id=user_id,
|
||||||
|
role_id=role_id,
|
||||||
|
order=order,
|
||||||
|
is_public=is_public,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def delete_group_summary_user(self, group_id, requester_user_id, user_id, role_id):
|
||||||
|
"""Remove a user from the group summary
|
||||||
|
"""
|
||||||
|
yield self.check_group_is_ours(group_id, and_exists=True, and_is_admin=user_id)
|
||||||
|
|
||||||
|
yield self.store.remove_user_from_summary(
|
||||||
|
group_id=group_id,
|
||||||
|
user_id=user_id,
|
||||||
|
role_id=role_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({})
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_group_profile(self, group_id, requester_user_id):
|
def get_group_profile(self, group_id, requester_user_id):
|
||||||
"""Get the group profile as seen by requester_user_id
|
"""Get the group profile as seen by requester_user_id
|
||||||
|
@ -170,12 +429,9 @@ class GroupsServerHandler(object):
|
||||||
def add_room(self, group_id, requester_user_id, room_id, content):
|
def add_room(self, group_id, requester_user_id, room_id, content):
|
||||||
"""Add room to group
|
"""Add room to group
|
||||||
"""
|
"""
|
||||||
|
yield self.check_group_is_ours(
|
||||||
yield self.check_group_is_ours(group_id, and_exists=True)
|
group_id, and_exists=True, and_is_admin=requester_user_id
|
||||||
|
)
|
||||||
is_admin = yield self.store.is_user_admin_in_group(group_id, requester_user_id)
|
|
||||||
if not is_admin:
|
|
||||||
raise SynapseError(403, "User is not admin in group")
|
|
||||||
|
|
||||||
# TODO: Check if room has already been added
|
# TODO: Check if room has already been added
|
||||||
|
|
||||||
|
@ -190,13 +446,9 @@ class GroupsServerHandler(object):
|
||||||
"""Invite user to group
|
"""Invite user to group
|
||||||
"""
|
"""
|
||||||
|
|
||||||
group = yield self.check_group_is_ours(group_id, and_exists=True)
|
group = yield self.check_group_is_ours(
|
||||||
|
group_id, and_exists=True, and_is_admin=requester_user_id
|
||||||
is_admin = yield self.store.is_user_admin_in_group(
|
|
||||||
group_id, requester_user_id
|
|
||||||
)
|
)
|
||||||
if not is_admin:
|
|
||||||
raise SynapseError(403, "User is not admin in group")
|
|
||||||
|
|
||||||
# TODO: Check if user knocked
|
# TODO: Check if user knocked
|
||||||
# TODO: Check if user is already invited
|
# TODO: Check if user is already invited
|
||||||
|
|
|
@ -15,11 +15,19 @@
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
from synapse.api.errors import SynapseError
|
||||||
|
|
||||||
from ._base import SQLBaseStore
|
from ._base import SQLBaseStore
|
||||||
|
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
|
||||||
|
|
||||||
|
# The category ID for the "default" category. We don't store as null in the
|
||||||
|
# database to avoid the fun of null != null
|
||||||
|
_DEFAULT_CATEGORY_ID = ""
|
||||||
|
_DEFAULT_ROLE_ID = ""
|
||||||
|
|
||||||
|
|
||||||
class GroupServerStore(SQLBaseStore):
|
class GroupServerStore(SQLBaseStore):
|
||||||
def get_group(self, group_id):
|
def get_group(self, group_id):
|
||||||
return self._simple_select_one(
|
return self._simple_select_one(
|
||||||
|
@ -64,6 +72,531 @@ class GroupServerStore(SQLBaseStore):
|
||||||
desc="get_rooms_in_group",
|
desc="get_rooms_in_group",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_rooms_for_summary_by_category(self, group_id, include_private=False):
|
||||||
|
"""Get the rooms and categories that should be included in a summary request
|
||||||
|
|
||||||
|
Returns ([rooms], [categories])
|
||||||
|
"""
|
||||||
|
def _get_rooms_for_summary_txn(txn):
|
||||||
|
keyvalues = {
|
||||||
|
"group_id": group_id,
|
||||||
|
}
|
||||||
|
if not include_private:
|
||||||
|
keyvalues["is_public"] = True
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT room_id, is_public, category_id, room_order
|
||||||
|
FROM group_summary_rooms
|
||||||
|
WHERE group_id = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not include_private:
|
||||||
|
sql += " AND is_public = ?"
|
||||||
|
txn.execute(sql, (group_id, True))
|
||||||
|
else:
|
||||||
|
txn.execute(sql, (group_id,))
|
||||||
|
|
||||||
|
rooms = [
|
||||||
|
{
|
||||||
|
"room_id": row[0],
|
||||||
|
"is_public": row[1],
|
||||||
|
"category_id": row[2] if row[2] != _DEFAULT_CATEGORY_ID else None,
|
||||||
|
"order": row[3],
|
||||||
|
}
|
||||||
|
for row in txn
|
||||||
|
]
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT category_id, is_public, profile, cat_order
|
||||||
|
FROM group_summary_room_categories
|
||||||
|
INNER JOIN group_room_categories USING (group_id, category_id)
|
||||||
|
WHERE group_id = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not include_private:
|
||||||
|
sql += " AND is_public = ?"
|
||||||
|
txn.execute(sql, (group_id, True))
|
||||||
|
else:
|
||||||
|
txn.execute(sql, (group_id,))
|
||||||
|
|
||||||
|
categories = {
|
||||||
|
row[0]: {
|
||||||
|
"is_public": row[1],
|
||||||
|
"profile": json.loads(row[2]),
|
||||||
|
"order": row[3],
|
||||||
|
}
|
||||||
|
for row in txn
|
||||||
|
}
|
||||||
|
|
||||||
|
return rooms, categories
|
||||||
|
return self.runInteraction(
|
||||||
|
"get_rooms_for_summary", _get_rooms_for_summary_txn
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_room_to_summary(self, group_id, room_id, category_id, order, is_public):
|
||||||
|
return self.runInteraction(
|
||||||
|
"add_room_to_summary", self._add_room_to_summary_txn,
|
||||||
|
group_id, room_id, category_id, order, is_public,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _add_room_to_summary_txn(self, txn, group_id, room_id, category_id, order,
|
||||||
|
is_public):
|
||||||
|
"""Add (or update) room's entry in summary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
group_id (str)
|
||||||
|
room_id (str)
|
||||||
|
category_id (str): If not None then adds the category to the end of
|
||||||
|
the summary if its not already there. [Optional]
|
||||||
|
order (int): If not None inserts the room at that position, e.g.
|
||||||
|
an order of 1 will put the room first. Otherwise, the room gets
|
||||||
|
added to the end.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if category_id is None:
|
||||||
|
category_id = _DEFAULT_CATEGORY_ID
|
||||||
|
else:
|
||||||
|
cat_exists = self._simple_select_one_onecol_txn(
|
||||||
|
txn,
|
||||||
|
table="group_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
retcol="group_id",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
if not cat_exists:
|
||||||
|
raise SynapseError(400, "Category doesn't exist")
|
||||||
|
|
||||||
|
# TODO: Check category is part of summary already
|
||||||
|
cat_exists = self._simple_select_one_onecol_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
retcol="group_id",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
if not cat_exists:
|
||||||
|
# If not, add it with an order larger than all others
|
||||||
|
txn.execute("""
|
||||||
|
INSERT INTO group_summary_room_categories
|
||||||
|
(group_id, category_id, cat_order)
|
||||||
|
SELECT ?, ?, COALESCE(MAX(cat_order), 0) + 1
|
||||||
|
FROM group_summary_room_categories
|
||||||
|
WHERE group_id = ? AND category_id = ?
|
||||||
|
""", (group_id, category_id, group_id, category_id))
|
||||||
|
|
||||||
|
existing = self._simple_select_one_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_rooms",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"room_id": room_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
retcols=("room_order", "is_public",),
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if order is not None:
|
||||||
|
# Shuffle other room orders that come after the given order
|
||||||
|
sql = """
|
||||||
|
UPDATE group_summary_rooms SET room_order = room_order + 1
|
||||||
|
WHERE group_id = ? AND category_id = ? AND room_order >= ?
|
||||||
|
"""
|
||||||
|
txn.execute(sql, (group_id, category_id, order,))
|
||||||
|
elif not existing:
|
||||||
|
sql = """
|
||||||
|
SELECT COALESCE(MAX(room_order), 0) + 1 FROM group_summary_rooms
|
||||||
|
WHERE group_id = ? AND category_id = ?
|
||||||
|
"""
|
||||||
|
txn.execute(sql, (group_id, category_id,))
|
||||||
|
order, = txn.fetchone()
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
to_update = {}
|
||||||
|
if order is not None:
|
||||||
|
to_update["room_order"] = order
|
||||||
|
if is_public is not None:
|
||||||
|
to_update["is_public"] = is_public
|
||||||
|
self._simple_update_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_rooms",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
"room_id": room_id,
|
||||||
|
},
|
||||||
|
values=to_update,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if is_public is None:
|
||||||
|
is_public = True
|
||||||
|
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_rooms",
|
||||||
|
values={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
"room_id": room_id,
|
||||||
|
"room_order": order,
|
||||||
|
"is_public": is_public,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_room_from_summary(self, group_id, room_id, category_id):
|
||||||
|
if category_id is None:
|
||||||
|
category_id = _DEFAULT_CATEGORY_ID
|
||||||
|
|
||||||
|
return self._simple_delete(
|
||||||
|
table="group_summary_rooms",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
"room_id": room_id,
|
||||||
|
},
|
||||||
|
desc="remove_room_from_summary",
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_categories(self, group_id):
|
||||||
|
rows = yield self._simple_select_list(
|
||||||
|
table="group_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
},
|
||||||
|
retcols=("category_id", "is_public", "profile"),
|
||||||
|
desc="get_group_categories",
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({
|
||||||
|
row["category_id"]: {
|
||||||
|
"is_public": row["is_public"],
|
||||||
|
"profile": json.loads(row["profile"]),
|
||||||
|
}
|
||||||
|
for row in rows
|
||||||
|
})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_category(self, group_id, category_id):
|
||||||
|
category = yield self._simple_select_one(
|
||||||
|
table="group_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
retcols=("is_public", "profile"),
|
||||||
|
desc="get_group_category",
|
||||||
|
)
|
||||||
|
|
||||||
|
category["profile"] = json.loads(category["profile"])
|
||||||
|
|
||||||
|
defer.returnValue(category)
|
||||||
|
|
||||||
|
def upsert_group_category(self, group_id, category_id, profile, is_public):
|
||||||
|
"""Add/update room category for group
|
||||||
|
"""
|
||||||
|
insertion_values = {}
|
||||||
|
update_values = {"category_id": category_id} # This cannot be empty
|
||||||
|
|
||||||
|
if profile is None:
|
||||||
|
insertion_values["profile"] = "{}"
|
||||||
|
else:
|
||||||
|
update_values["profile"] = json.dumps(profile)
|
||||||
|
|
||||||
|
if is_public is None:
|
||||||
|
insertion_values["is_public"] = True
|
||||||
|
else:
|
||||||
|
update_values["is_public"] = is_public
|
||||||
|
|
||||||
|
return self._simple_upsert(
|
||||||
|
table="group_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
values=update_values,
|
||||||
|
insertion_values=insertion_values,
|
||||||
|
desc="upsert_group_category",
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_group_category(self, group_id, category_id):
|
||||||
|
return self._simple_delete(
|
||||||
|
table="group_room_categories",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"category_id": category_id,
|
||||||
|
},
|
||||||
|
desc="remove_group_category",
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_roles(self, group_id):
|
||||||
|
rows = yield self._simple_select_list(
|
||||||
|
table="group_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
},
|
||||||
|
retcols=("role_id", "is_public", "profile"),
|
||||||
|
desc="get_group_roles",
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue({
|
||||||
|
row["role_id"]: {
|
||||||
|
"is_public": row["is_public"],
|
||||||
|
"profile": json.loads(row["profile"]),
|
||||||
|
}
|
||||||
|
for row in rows
|
||||||
|
})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_group_role(self, group_id, role_id):
|
||||||
|
role = yield self._simple_select_one(
|
||||||
|
table="group_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
retcols=("is_public", "profile"),
|
||||||
|
desc="get_group_role",
|
||||||
|
)
|
||||||
|
|
||||||
|
role["profile"] = json.loads(role["profile"])
|
||||||
|
|
||||||
|
defer.returnValue(role)
|
||||||
|
|
||||||
|
def upsert_group_role(self, group_id, role_id, profile, is_public):
|
||||||
|
"""Add/remove user role
|
||||||
|
"""
|
||||||
|
insertion_values = {}
|
||||||
|
update_values = {"role_id": role_id} # This cannot be empty
|
||||||
|
|
||||||
|
if profile is None:
|
||||||
|
insertion_values["profile"] = "{}"
|
||||||
|
else:
|
||||||
|
update_values["profile"] = json.dumps(profile)
|
||||||
|
|
||||||
|
if is_public is None:
|
||||||
|
insertion_values["is_public"] = True
|
||||||
|
else:
|
||||||
|
update_values["is_public"] = is_public
|
||||||
|
|
||||||
|
return self._simple_upsert(
|
||||||
|
table="group_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
values=update_values,
|
||||||
|
insertion_values=insertion_values,
|
||||||
|
desc="upsert_group_role",
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_group_role(self, group_id, role_id):
|
||||||
|
return self._simple_delete(
|
||||||
|
table="group_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
desc="remove_group_role",
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_user_to_summary(self, group_id, user_id, role_id, order, is_public):
|
||||||
|
return self.runInteraction(
|
||||||
|
"add_user_to_summary", self._add_user_to_summary_txn,
|
||||||
|
group_id, user_id, role_id, order, is_public,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _add_user_to_summary_txn(self, txn, group_id, user_id, role_id, order,
|
||||||
|
is_public):
|
||||||
|
"""Add (or update) user's entry in summary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
group_id (str)
|
||||||
|
user_id (str)
|
||||||
|
role_id (str): If not None then adds the role to the end of
|
||||||
|
the summary if its not already there. [Optional]
|
||||||
|
order (int): If not None inserts the user at that position, e.g.
|
||||||
|
an order of 1 will put the user first. Otherwise, the user gets
|
||||||
|
added to the end.
|
||||||
|
"""
|
||||||
|
if role_id is None:
|
||||||
|
role_id = _DEFAULT_ROLE_ID
|
||||||
|
else:
|
||||||
|
role_exists = self._simple_select_one_onecol_txn(
|
||||||
|
txn,
|
||||||
|
table="group_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
retcol="group_id",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
if not role_exists:
|
||||||
|
raise SynapseError(400, "Role doesn't exist")
|
||||||
|
|
||||||
|
# TODO: Check role is part of the summary already
|
||||||
|
role_exists = self._simple_select_one_onecol_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_roles",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
retcol="group_id",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
if not role_exists:
|
||||||
|
# If not, add it with an order larger than all others
|
||||||
|
txn.execute("""
|
||||||
|
INSERT INTO group_summary_roles
|
||||||
|
(group_id, role_id, role_order)
|
||||||
|
SELECT ?, ?, COALESCE(MAX(role_order), 0) + 1
|
||||||
|
FROM group_summary_roles
|
||||||
|
WHERE group_id = ? AND role_id = ?
|
||||||
|
""", (group_id, role_id, group_id, role_id))
|
||||||
|
|
||||||
|
existing = self._simple_select_one_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_users",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
},
|
||||||
|
retcols=("user_order", "is_public",),
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if order is not None:
|
||||||
|
# Shuffle other users orders that come after the given order
|
||||||
|
sql = """
|
||||||
|
UPDATE group_summary_users SET user_order = user_order + 1
|
||||||
|
WHERE group_id = ? AND role_id = ? AND user_order >= ?
|
||||||
|
"""
|
||||||
|
txn.execute(sql, (group_id, role_id, order,))
|
||||||
|
elif not existing:
|
||||||
|
sql = """
|
||||||
|
SELECT COALESCE(MAX(user_order), 0) + 1 FROM group_summary_users
|
||||||
|
WHERE group_id = ? AND role_id = ?
|
||||||
|
"""
|
||||||
|
txn.execute(sql, (group_id, role_id,))
|
||||||
|
order, = txn.fetchone()
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
to_update = {}
|
||||||
|
if order is not None:
|
||||||
|
to_update["user_order"] = order
|
||||||
|
if is_public is not None:
|
||||||
|
to_update["is_public"] = is_public
|
||||||
|
self._simple_update_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_users",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
},
|
||||||
|
values=to_update,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if is_public is None:
|
||||||
|
is_public = True
|
||||||
|
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
table="group_summary_users",
|
||||||
|
values={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"user_order": order,
|
||||||
|
"is_public": is_public,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_user_from_summary(self, group_id, user_id, role_id):
|
||||||
|
if role_id is None:
|
||||||
|
role_id = _DEFAULT_ROLE_ID
|
||||||
|
|
||||||
|
return self._simple_delete(
|
||||||
|
table="group_summary_users",
|
||||||
|
keyvalues={
|
||||||
|
"group_id": group_id,
|
||||||
|
"role_id": role_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
},
|
||||||
|
desc="remove_user_from_summary",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_users_for_summary_by_role(self, group_id, include_private=False):
|
||||||
|
"""Get the users and roles that should be included in a summary request
|
||||||
|
|
||||||
|
Returns ([users], [roles])
|
||||||
|
"""
|
||||||
|
def _get_users_for_summary_txn(txn):
|
||||||
|
keyvalues = {
|
||||||
|
"group_id": group_id,
|
||||||
|
}
|
||||||
|
if not include_private:
|
||||||
|
keyvalues["is_public"] = True
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT user_id, is_public, role_id, user_order
|
||||||
|
FROM group_summary_users
|
||||||
|
WHERE group_id = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not include_private:
|
||||||
|
sql += " AND is_public = ?"
|
||||||
|
txn.execute(sql, (group_id, True))
|
||||||
|
else:
|
||||||
|
txn.execute(sql, (group_id,))
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{
|
||||||
|
"user_id": row[0],
|
||||||
|
"is_public": row[1],
|
||||||
|
"role_id": row[2] if row[2] != _DEFAULT_ROLE_ID else None,
|
||||||
|
"order": row[3],
|
||||||
|
}
|
||||||
|
for row in txn
|
||||||
|
]
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT role_id, is_public, profile, role_order
|
||||||
|
FROM group_summary_roles
|
||||||
|
INNER JOIN group_roles USING (group_id, role_id)
|
||||||
|
WHERE group_id = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not include_private:
|
||||||
|
sql += " AND is_public = ?"
|
||||||
|
txn.execute(sql, (group_id, True))
|
||||||
|
else:
|
||||||
|
txn.execute(sql, (group_id,))
|
||||||
|
|
||||||
|
roles = {
|
||||||
|
row[0]: {
|
||||||
|
"is_public": row[1],
|
||||||
|
"profile": json.loads(row[2]),
|
||||||
|
"order": row[3],
|
||||||
|
}
|
||||||
|
for row in txn
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, roles
|
||||||
|
return self.runInteraction(
|
||||||
|
"get_users_for_summary_by_role", _get_users_for_summary_txn
|
||||||
|
)
|
||||||
|
|
||||||
def is_user_in_group(self, user_id, group_id):
|
def is_user_in_group(self, user_id, group_id):
|
||||||
return self._simple_select_one_onecol(
|
return self._simple_select_one_onecol(
|
||||||
table="group_users",
|
table="group_users",
|
||||||
|
|
|
@ -56,6 +56,69 @@ CREATE INDEX groups_rooms_g_idx ON group_rooms(group_id, room_id);
|
||||||
CREATE INDEX groups_rooms_r_idx ON group_rooms(room_id);
|
CREATE INDEX groups_rooms_r_idx ON group_rooms(room_id);
|
||||||
|
|
||||||
|
|
||||||
|
-- Rooms to include in the summary
|
||||||
|
CREATE TABLE group_summary_rooms (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
category_id TEXT NOT NULL,
|
||||||
|
room_order BIGINT NOT NULL,
|
||||||
|
is_public BOOLEAN NOT NULL, -- whether the room should be show to everyone
|
||||||
|
UNIQUE (group_id, category_id, room_id, room_order),
|
||||||
|
CHECK (room_order > 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX group_summary_rooms_g_idx ON group_summary_rooms(group_id, room_id, category_id);
|
||||||
|
|
||||||
|
|
||||||
|
-- Categories to include in the summary
|
||||||
|
CREATE TABLE group_summary_room_categories (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
category_id TEXT NOT NULL,
|
||||||
|
cat_order BIGINT NOT NULL,
|
||||||
|
UNIQUE (group_id, category_id, cat_order),
|
||||||
|
CHECK (cat_order > 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- The categories in the group
|
||||||
|
CREATE TABLE group_room_categories (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
category_id TEXT NOT NULL,
|
||||||
|
profile TEXT NOT NULL,
|
||||||
|
is_public BOOLEAN NOT NULL, -- whether the category should be show to everyone
|
||||||
|
UNIQUE (group_id, category_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- The users to include in the group summary
|
||||||
|
CREATE TABLE group_summary_users (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
role_id TEXT NOT NULL,
|
||||||
|
user_order BIGINT NOT NULL,
|
||||||
|
is_public BOOLEAN NOT NULL -- whether the user should be show to everyone
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX group_summary_users_g_idx ON group_summary_users(group_id);
|
||||||
|
|
||||||
|
-- The roles to include in the group summary
|
||||||
|
CREATE TABLE group_summary_roles (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
role_id TEXT NOT NULL,
|
||||||
|
role_order BIGINT NOT NULL,
|
||||||
|
UNIQUE (group_id, role_id, role_order),
|
||||||
|
CHECK (role_order > 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- The roles in a groups
|
||||||
|
CREATE TABLE group_roles (
|
||||||
|
group_id TEXT NOT NULL,
|
||||||
|
role_id TEXT NOT NULL,
|
||||||
|
profile TEXT NOT NULL,
|
||||||
|
is_public BOOLEAN NOT NULL, -- whether the role should be show to everyone
|
||||||
|
UNIQUE (group_id, role_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
-- List of attestations we've given out and need to renew
|
-- List of attestations we've given out and need to renew
|
||||||
CREATE TABLE group_attestations_renewals (
|
CREATE TABLE group_attestations_renewals (
|
||||||
group_id TEXT NOT NULL,
|
group_id TEXT NOT NULL,
|
||||||
|
|
Loading…
Reference in a new issue