mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-10-01 11:49:51 -04:00
Merge pull request #276 from matrix-org/markjh/history_for_rooms_that_have_been_left
SPEC-216: Allow users to view the history of rooms that they have left.
This commit is contained in:
commit
ee2d722f0f
@ -119,6 +119,20 @@ class Auth(object):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_joined_room(self, room_id, user_id, current_state=None):
|
def check_joined_room(self, room_id, user_id, current_state=None):
|
||||||
|
"""Check if the user is currently joined in the room
|
||||||
|
Args:
|
||||||
|
room_id(str): The room to check.
|
||||||
|
user_id(str): The user to check.
|
||||||
|
current_state(dict): Optional map of the current state of the room.
|
||||||
|
If provided then that map is used to check whether they are a
|
||||||
|
member of the room. Otherwise the current membership is
|
||||||
|
loaded from the database.
|
||||||
|
Raises:
|
||||||
|
AuthError if the user is not in the room.
|
||||||
|
Returns:
|
||||||
|
A deferred membership event for the user if the user is in
|
||||||
|
the room.
|
||||||
|
"""
|
||||||
if current_state:
|
if current_state:
|
||||||
member = current_state.get(
|
member = current_state.get(
|
||||||
(EventTypes.Member, user_id),
|
(EventTypes.Member, user_id),
|
||||||
@ -134,6 +148,43 @@ class Auth(object):
|
|||||||
self._check_joined_room(member, user_id, room_id)
|
self._check_joined_room(member, user_id, room_id)
|
||||||
defer.returnValue(member)
|
defer.returnValue(member)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def check_user_was_in_room(self, room_id, user_id, current_state=None):
|
||||||
|
"""Check if the user was in the room at some point.
|
||||||
|
Args:
|
||||||
|
room_id(str): The room to check.
|
||||||
|
user_id(str): The user to check.
|
||||||
|
current_state(dict): Optional map of the current state of the room.
|
||||||
|
If provided then that map is used to check whether they are a
|
||||||
|
member of the room. Otherwise the current membership is
|
||||||
|
loaded from the database.
|
||||||
|
Raises:
|
||||||
|
AuthError if the user was never in the room.
|
||||||
|
Returns:
|
||||||
|
A deferred membership event for the user if the user was in the
|
||||||
|
room. This will be the join event if they are currently joined to
|
||||||
|
the room. This will be the leave event if they have left the room.
|
||||||
|
"""
|
||||||
|
if current_state:
|
||||||
|
member = current_state.get(
|
||||||
|
(EventTypes.Member, user_id),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
member = yield self.state.get_current_state(
|
||||||
|
room_id=room_id,
|
||||||
|
event_type=EventTypes.Member,
|
||||||
|
state_key=user_id
|
||||||
|
)
|
||||||
|
membership = member.membership if member else None
|
||||||
|
|
||||||
|
if membership not in (Membership.JOIN, Membership.LEAVE):
|
||||||
|
raise AuthError(403, "User %s not in room %s" % (
|
||||||
|
user_id, room_id
|
||||||
|
))
|
||||||
|
|
||||||
|
defer.returnValue(member)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_host_in_room(self, room_id, host):
|
def check_host_in_room(self, room_id, host):
|
||||||
curr_state = yield self.state.get_current_state(room_id)
|
curr_state = yield self.state.get_current_state(room_id)
|
||||||
|
@ -27,16 +27,6 @@ class Membership(object):
|
|||||||
LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN)
|
LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN)
|
||||||
|
|
||||||
|
|
||||||
class Feedback(object):
|
|
||||||
|
|
||||||
"""Represents the types of feedback a user can send in response to a
|
|
||||||
message."""
|
|
||||||
|
|
||||||
DELIVERED = u"delivered"
|
|
||||||
READ = u"read"
|
|
||||||
LIST = (DELIVERED, READ)
|
|
||||||
|
|
||||||
|
|
||||||
class PresenceState(object):
|
class PresenceState(object):
|
||||||
"""Represents the presence state of a user."""
|
"""Represents the presence state of a user."""
|
||||||
OFFLINE = u"offline"
|
OFFLINE = u"offline"
|
||||||
@ -73,7 +63,6 @@ class EventTypes(object):
|
|||||||
PowerLevels = "m.room.power_levels"
|
PowerLevels = "m.room.power_levels"
|
||||||
Aliases = "m.room.aliases"
|
Aliases = "m.room.aliases"
|
||||||
Redaction = "m.room.redaction"
|
Redaction = "m.room.redaction"
|
||||||
Feedback = "m.room.message.feedback"
|
|
||||||
|
|
||||||
RoomHistoryVisibility = "m.room.history_visibility"
|
RoomHistoryVisibility = "m.room.history_visibility"
|
||||||
CanonicalAlias = "m.room.canonical_alias"
|
CanonicalAlias = "m.room.canonical_alias"
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, Membership
|
from synapse.api.constants import EventTypes, Membership
|
||||||
from synapse.api.errors import RoomError, SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.streams.config import PaginationConfig
|
from synapse.streams.config import PaginationConfig
|
||||||
from synapse.events.utils import serialize_event
|
from synapse.events.utils import serialize_event
|
||||||
from synapse.events.validator import EventValidator
|
from synapse.events.validator import EventValidator
|
||||||
from synapse.util import unwrapFirstError
|
from synapse.util import unwrapFirstError
|
||||||
from synapse.util.logcontext import PreserveLoggingContext
|
from synapse.util.logcontext import PreserveLoggingContext
|
||||||
from synapse.types import UserID, RoomStreamToken
|
from synapse.types import UserID, RoomStreamToken, StreamToken
|
||||||
|
|
||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ class MessageHandler(BaseHandler):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_messages(self, user_id=None, room_id=None, pagin_config=None,
|
def get_messages(self, user_id=None, room_id=None, pagin_config=None,
|
||||||
feedback=False, as_client_event=True):
|
as_client_event=True):
|
||||||
"""Get messages in a room.
|
"""Get messages in a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -79,26 +79,52 @@ class MessageHandler(BaseHandler):
|
|||||||
room_id (str): The room they want messages from.
|
room_id (str): The room they want messages from.
|
||||||
pagin_config (synapse.api.streams.PaginationConfig): The pagination
|
pagin_config (synapse.api.streams.PaginationConfig): The pagination
|
||||||
config rules to apply, if any.
|
config rules to apply, if any.
|
||||||
feedback (bool): True to get compressed feedback with the messages
|
|
||||||
as_client_event (bool): True to get events in client-server format.
|
as_client_event (bool): True to get events in client-server format.
|
||||||
Returns:
|
Returns:
|
||||||
dict: Pagination API results
|
dict: Pagination API results
|
||||||
"""
|
"""
|
||||||
yield self.auth.check_joined_room(room_id, user_id)
|
member_event = yield self.auth.check_user_was_in_room(room_id, user_id)
|
||||||
|
|
||||||
data_source = self.hs.get_event_sources().sources["room"]
|
data_source = self.hs.get_event_sources().sources["room"]
|
||||||
|
|
||||||
if not pagin_config.from_token:
|
if pagin_config.from_token:
|
||||||
|
room_token = pagin_config.from_token.room_key
|
||||||
|
else:
|
||||||
pagin_config.from_token = (
|
pagin_config.from_token = (
|
||||||
yield self.hs.get_event_sources().get_current_token(
|
yield self.hs.get_event_sources().get_current_token(
|
||||||
direction='b'
|
direction='b'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
room_token = pagin_config.from_token.room_key
|
||||||
|
|
||||||
room_token = RoomStreamToken.parse(pagin_config.from_token.room_key)
|
room_token = RoomStreamToken.parse(room_token)
|
||||||
if room_token.topological is None:
|
if room_token.topological is None:
|
||||||
raise SynapseError(400, "Invalid token")
|
raise SynapseError(400, "Invalid token")
|
||||||
|
|
||||||
|
pagin_config.from_token = pagin_config.from_token.copy_and_replace(
|
||||||
|
"room_key", str(room_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
source_config = pagin_config.get_source_config("room")
|
||||||
|
|
||||||
|
if member_event.membership == Membership.LEAVE:
|
||||||
|
# If they have left the room then clamp the token to be before
|
||||||
|
# they left the room
|
||||||
|
leave_token = yield self.store.get_topological_token_for_event(
|
||||||
|
member_event.event_id
|
||||||
|
)
|
||||||
|
leave_token = RoomStreamToken.parse(leave_token)
|
||||||
|
if leave_token.topological < room_token.topological:
|
||||||
|
source_config.from_key = str(leave_token)
|
||||||
|
|
||||||
|
if source_config.direction == "f":
|
||||||
|
if source_config.to_key is None:
|
||||||
|
source_config.to_key = str(leave_token)
|
||||||
|
else:
|
||||||
|
to_token = RoomStreamToken.parse(source_config.to_key)
|
||||||
|
if leave_token.topological < to_token.topological:
|
||||||
|
source_config.to_key = str(leave_token)
|
||||||
|
|
||||||
yield self.hs.get_handlers().federation_handler.maybe_backfill(
|
yield self.hs.get_handlers().federation_handler.maybe_backfill(
|
||||||
room_id, room_token.topological
|
room_id, room_token.topological
|
||||||
)
|
)
|
||||||
@ -106,7 +132,7 @@ class MessageHandler(BaseHandler):
|
|||||||
user = UserID.from_string(user_id)
|
user = UserID.from_string(user_id)
|
||||||
|
|
||||||
events, next_key = yield data_source.get_pagination_rows(
|
events, next_key = yield data_source.get_pagination_rows(
|
||||||
user, pagin_config.get_source_config("room"), room_id
|
user, source_config, room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
next_token = pagin_config.from_token.copy_and_replace(
|
next_token = pagin_config.from_token.copy_and_replace(
|
||||||
@ -255,29 +281,26 @@ class MessageHandler(BaseHandler):
|
|||||||
Raises:
|
Raises:
|
||||||
SynapseError if something went wrong.
|
SynapseError if something went wrong.
|
||||||
"""
|
"""
|
||||||
have_joined = yield self.auth.check_joined_room(room_id, user_id)
|
member_event = yield self.auth.check_user_was_in_room(room_id, user_id)
|
||||||
if not have_joined:
|
|
||||||
raise RoomError(403, "User not in room.")
|
if member_event.membership == Membership.JOIN:
|
||||||
|
data = yield self.state_handler.get_current_state(
|
||||||
|
room_id, event_type, state_key
|
||||||
|
)
|
||||||
|
elif member_event.membership == Membership.LEAVE:
|
||||||
|
key = (event_type, state_key)
|
||||||
|
room_state = yield self.store.get_state_for_events(
|
||||||
|
room_id, [member_event.event_id], [key]
|
||||||
|
)
|
||||||
|
data = room_state[member_event.event_id].get(key)
|
||||||
|
|
||||||
data = yield self.state_handler.get_current_state(
|
|
||||||
room_id, event_type, state_key
|
|
||||||
)
|
|
||||||
defer.returnValue(data)
|
defer.returnValue(data)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def get_feedback(self, event_id):
|
|
||||||
# yield self.auth.check_joined_room(room_id, user_id)
|
|
||||||
|
|
||||||
# Pull out the feedback from the db
|
|
||||||
fb = yield self.store.get_feedback(event_id)
|
|
||||||
|
|
||||||
if fb:
|
|
||||||
defer.returnValue(fb)
|
|
||||||
defer.returnValue(None)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_state_events(self, user_id, room_id):
|
def get_state_events(self, user_id, room_id):
|
||||||
"""Retrieve all state events for a given room.
|
"""Retrieve all state events for a given room. If the user is
|
||||||
|
joined to the room then return the current state. If the user has
|
||||||
|
left the room return the state events from when they left.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id(str): The user requesting state events.
|
user_id(str): The user requesting state events.
|
||||||
@ -285,18 +308,23 @@ class MessageHandler(BaseHandler):
|
|||||||
Returns:
|
Returns:
|
||||||
A list of dicts representing state events. [{}, {}, {}]
|
A list of dicts representing state events. [{}, {}, {}]
|
||||||
"""
|
"""
|
||||||
yield self.auth.check_joined_room(room_id, user_id)
|
member_event = yield self.auth.check_user_was_in_room(room_id, user_id)
|
||||||
|
|
||||||
|
if member_event.membership == Membership.JOIN:
|
||||||
|
room_state = yield self.state_handler.get_current_state(room_id)
|
||||||
|
elif member_event.membership == Membership.LEAVE:
|
||||||
|
room_state = yield self.store.get_state_for_events(
|
||||||
|
room_id, [member_event.event_id], None
|
||||||
|
)
|
||||||
|
room_state = room_state[member_event.event_id]
|
||||||
|
|
||||||
# TODO: This is duplicating logic from snapshot_all_rooms
|
|
||||||
current_state = yield self.state_handler.get_current_state(room_id)
|
|
||||||
now = self.clock.time_msec()
|
now = self.clock.time_msec()
|
||||||
defer.returnValue(
|
defer.returnValue(
|
||||||
[serialize_event(c, now) for c in current_state.values()]
|
[serialize_event(c, now) for c in room_state.values()]
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def snapshot_all_rooms(self, user_id=None, pagin_config=None,
|
def snapshot_all_rooms(self, user_id=None, pagin_config=None, as_client_event=True):
|
||||||
feedback=False, as_client_event=True):
|
|
||||||
"""Retrieve a snapshot of all rooms the user is invited or has joined.
|
"""Retrieve a snapshot of all rooms the user is invited or has joined.
|
||||||
|
|
||||||
This snapshot may include messages for all rooms where the user is
|
This snapshot may include messages for all rooms where the user is
|
||||||
@ -306,7 +334,6 @@ class MessageHandler(BaseHandler):
|
|||||||
user_id (str): The ID of the user making the request.
|
user_id (str): The ID of the user making the request.
|
||||||
pagin_config (synapse.api.streams.PaginationConfig): The pagination
|
pagin_config (synapse.api.streams.PaginationConfig): The pagination
|
||||||
config used to determine how many messages *PER ROOM* to return.
|
config used to determine how many messages *PER ROOM* to return.
|
||||||
feedback (bool): True to get feedback along with these messages.
|
|
||||||
as_client_event (bool): True to get events in client-server format.
|
as_client_event (bool): True to get events in client-server format.
|
||||||
Returns:
|
Returns:
|
||||||
A list of dicts with "room_id" and "membership" keys for all rooms
|
A list of dicts with "room_id" and "membership" keys for all rooms
|
||||||
@ -316,7 +343,9 @@ class MessageHandler(BaseHandler):
|
|||||||
"""
|
"""
|
||||||
room_list = yield self.store.get_rooms_for_user_where_membership_is(
|
room_list = yield self.store.get_rooms_for_user_where_membership_is(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
membership_list=[Membership.INVITE, Membership.JOIN]
|
membership_list=[
|
||||||
|
Membership.INVITE, Membership.JOIN, Membership.LEAVE
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
user = UserID.from_string(user_id)
|
user = UserID.from_string(user_id)
|
||||||
@ -358,19 +387,32 @@ class MessageHandler(BaseHandler):
|
|||||||
|
|
||||||
rooms_ret.append(d)
|
rooms_ret.append(d)
|
||||||
|
|
||||||
if event.membership != Membership.JOIN:
|
if event.membership not in (Membership.JOIN, Membership.LEAVE):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if event.membership == Membership.JOIN:
|
||||||
|
room_end_token = now_token.room_key
|
||||||
|
deferred_room_state = self.state_handler.get_current_state(
|
||||||
|
event.room_id
|
||||||
|
)
|
||||||
|
elif event.membership == Membership.LEAVE:
|
||||||
|
room_end_token = "s%d" % (event.stream_ordering,)
|
||||||
|
deferred_room_state = self.store.get_state_for_events(
|
||||||
|
event.room_id, [event.event_id], None
|
||||||
|
)
|
||||||
|
deferred_room_state.addCallback(
|
||||||
|
lambda states: states[event.event_id]
|
||||||
|
)
|
||||||
|
|
||||||
(messages, token), current_state = yield defer.gatherResults(
|
(messages, token), current_state = yield defer.gatherResults(
|
||||||
[
|
[
|
||||||
self.store.get_recent_events_for_room(
|
self.store.get_recent_events_for_room(
|
||||||
event.room_id,
|
event.room_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
end_token=now_token.room_key,
|
end_token=room_end_token,
|
||||||
),
|
|
||||||
self.state_handler.get_current_state(
|
|
||||||
event.room_id
|
|
||||||
),
|
),
|
||||||
|
deferred_room_state,
|
||||||
]
|
]
|
||||||
).addErrback(unwrapFirstError)
|
).addErrback(unwrapFirstError)
|
||||||
|
|
||||||
@ -417,15 +459,85 @@ class MessageHandler(BaseHandler):
|
|||||||
defer.returnValue(ret)
|
defer.returnValue(ret)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def room_initial_sync(self, user_id, room_id, pagin_config=None,
|
def room_initial_sync(self, user_id, room_id, pagin_config=None):
|
||||||
feedback=False):
|
"""Capture the a snapshot of a room. If user is currently a member of
|
||||||
current_state = yield self.state.get_current_state(
|
the room this will be what is currently in the room. If the user left
|
||||||
room_id=room_id,
|
the room this will be what was in the room when they left.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id(str): The user to get a snapshot for.
|
||||||
|
room_id(str): The room to get a snapshot of.
|
||||||
|
pagin_config(synapse.streams.config.PaginationConfig):
|
||||||
|
The pagination config used to determine how many messages to
|
||||||
|
return.
|
||||||
|
Raises:
|
||||||
|
AuthError if the user wasn't in the room.
|
||||||
|
Returns:
|
||||||
|
A JSON serialisable dict with the snapshot of the room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
member_event = yield self.auth.check_user_was_in_room(room_id, user_id)
|
||||||
|
|
||||||
|
if member_event.membership == Membership.JOIN:
|
||||||
|
result = yield self._room_initial_sync_joined(
|
||||||
|
user_id, room_id, pagin_config, member_event
|
||||||
|
)
|
||||||
|
elif member_event.membership == Membership.LEAVE:
|
||||||
|
result = yield self._room_initial_sync_parted(
|
||||||
|
user_id, room_id, pagin_config, member_event
|
||||||
|
)
|
||||||
|
defer.returnValue(result)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _room_initial_sync_parted(self, user_id, room_id, pagin_config,
|
||||||
|
member_event):
|
||||||
|
room_state = yield self.store.get_state_for_events(
|
||||||
|
member_event.room_id, [member_event.event_id], None
|
||||||
)
|
)
|
||||||
|
|
||||||
yield self.auth.check_joined_room(
|
room_state = room_state[member_event.event_id]
|
||||||
room_id, user_id,
|
|
||||||
current_state=current_state
|
limit = pagin_config.limit if pagin_config else None
|
||||||
|
if limit is None:
|
||||||
|
limit = 10
|
||||||
|
|
||||||
|
stream_token = yield self.store.get_stream_token_for_event(
|
||||||
|
member_event.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
messages, token = yield self.store.get_recent_events_for_room(
|
||||||
|
room_id,
|
||||||
|
limit=limit,
|
||||||
|
end_token=stream_token
|
||||||
|
)
|
||||||
|
|
||||||
|
messages = yield self._filter_events_for_client(
|
||||||
|
user_id, room_id, messages
|
||||||
|
)
|
||||||
|
|
||||||
|
start_token = StreamToken(token[0], 0, 0, 0)
|
||||||
|
end_token = StreamToken(token[1], 0, 0, 0)
|
||||||
|
|
||||||
|
time_now = self.clock.time_msec()
|
||||||
|
|
||||||
|
defer.returnValue({
|
||||||
|
"membership": member_event.membership,
|
||||||
|
"room_id": room_id,
|
||||||
|
"messages": {
|
||||||
|
"chunk": [serialize_event(m, time_now) for m in messages],
|
||||||
|
"start": start_token.to_string(),
|
||||||
|
"end": end_token.to_string(),
|
||||||
|
},
|
||||||
|
"state": [serialize_event(s, time_now) for s in room_state.values()],
|
||||||
|
"presence": [],
|
||||||
|
"receipts": [],
|
||||||
|
})
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _room_initial_sync_joined(self, user_id, room_id, pagin_config,
|
||||||
|
member_event):
|
||||||
|
current_state = yield self.state.get_current_state(
|
||||||
|
room_id=room_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO(paul): I wish I was called with user objects not user_id
|
# TODO(paul): I wish I was called with user objects not user_id
|
||||||
@ -439,8 +551,6 @@ class MessageHandler(BaseHandler):
|
|||||||
for x in current_state.values()
|
for x in current_state.values()
|
||||||
]
|
]
|
||||||
|
|
||||||
member_event = current_state.get((EventTypes.Member, user_id,))
|
|
||||||
|
|
||||||
now_token = yield self.hs.get_event_sources().get_current_token()
|
now_token = yield self.hs.get_event_sources().get_current_token()
|
||||||
|
|
||||||
limit = pagin_config.limit if pagin_config else None
|
limit = pagin_config.limit if pagin_config else None
|
||||||
|
@ -25,7 +25,6 @@ from synapse.api.constants import (
|
|||||||
from synapse.api.errors import StoreError, SynapseError
|
from synapse.api.errors import StoreError, SynapseError
|
||||||
from synapse.util import stringutils, unwrapFirstError
|
from synapse.util import stringutils, unwrapFirstError
|
||||||
from synapse.util.async import run_on_reactor
|
from synapse.util.async import run_on_reactor
|
||||||
from synapse.events.utils import serialize_event
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import logging
|
import logging
|
||||||
@ -342,41 +341,6 @@ class RoomMemberHandler(BaseHandler):
|
|||||||
if remotedomains is not None:
|
if remotedomains is not None:
|
||||||
remotedomains.add(member.domain)
|
remotedomains.add(member.domain)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def get_room_members_as_pagination_chunk(self, room_id=None, user_id=None,
|
|
||||||
limit=0, start_tok=None,
|
|
||||||
end_tok=None):
|
|
||||||
"""Retrieve a list of room members in the room.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
room_id (str): The room to get the member list for.
|
|
||||||
user_id (str): The ID of the user making the request.
|
|
||||||
limit (int): The max number of members to return.
|
|
||||||
start_tok (str): Optional. The start token if known.
|
|
||||||
end_tok (str): Optional. The end token if known.
|
|
||||||
Returns:
|
|
||||||
dict: A Pagination streamable dict.
|
|
||||||
Raises:
|
|
||||||
SynapseError if something goes wrong.
|
|
||||||
"""
|
|
||||||
yield self.auth.check_joined_room(room_id, user_id)
|
|
||||||
|
|
||||||
member_list = yield self.store.get_room_members(room_id=room_id)
|
|
||||||
time_now = self.clock.time_msec()
|
|
||||||
event_list = [
|
|
||||||
serialize_event(entry, time_now)
|
|
||||||
for entry in member_list
|
|
||||||
]
|
|
||||||
chunk_data = {
|
|
||||||
"start": "START", # FIXME (erikj): START is no longer valid
|
|
||||||
"end": "END",
|
|
||||||
"chunk": event_list
|
|
||||||
}
|
|
||||||
# TODO honor Pagination stream params
|
|
||||||
# TODO snapshot this list to return on subsequent requests when
|
|
||||||
# paginating
|
|
||||||
defer.returnValue(chunk_data)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def change_membership(self, event, context, do_auth=True):
|
def change_membership(self, event, context, do_auth=True):
|
||||||
""" Change the membership status of a user in a room.
|
""" Change the membership status of a user in a room.
|
||||||
@ -646,7 +610,6 @@ class RoomEventSource(object):
|
|||||||
to_key=config.to_key,
|
to_key=config.to_key,
|
||||||
direction=config.direction,
|
direction=config.direction,
|
||||||
limit=config.limit,
|
limit=config.limit,
|
||||||
with_feedback=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
defer.returnValue((events, next_key))
|
defer.returnValue((events, next_key))
|
||||||
|
@ -26,14 +26,12 @@ class InitialSyncRestServlet(ClientV1RestServlet):
|
|||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_GET(self, request):
|
def on_GET(self, request):
|
||||||
user, _ = yield self.auth.get_user_by_req(request)
|
user, _ = yield self.auth.get_user_by_req(request)
|
||||||
with_feedback = "feedback" in request.args
|
|
||||||
as_client_event = "raw" not in request.args
|
as_client_event = "raw" not in request.args
|
||||||
pagination_config = PaginationConfig.from_request(request)
|
pagination_config = PaginationConfig.from_request(request)
|
||||||
handler = self.handlers.message_handler
|
handler = self.handlers.message_handler
|
||||||
content = yield handler.snapshot_all_rooms(
|
content = yield handler.snapshot_all_rooms(
|
||||||
user_id=user.to_string(),
|
user_id=user.to_string(),
|
||||||
pagin_config=pagination_config,
|
pagin_config=pagination_config,
|
||||||
feedback=with_feedback,
|
|
||||||
as_client_event=as_client_event
|
as_client_event=as_client_event
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -290,12 +290,18 @@ class RoomMemberListRestServlet(ClientV1RestServlet):
|
|||||||
def on_GET(self, request, room_id):
|
def on_GET(self, request, room_id):
|
||||||
# TODO support Pagination stream API (limit/tokens)
|
# TODO support Pagination stream API (limit/tokens)
|
||||||
user, _ = yield self.auth.get_user_by_req(request)
|
user, _ = yield self.auth.get_user_by_req(request)
|
||||||
handler = self.handlers.room_member_handler
|
handler = self.handlers.message_handler
|
||||||
members = yield handler.get_room_members_as_pagination_chunk(
|
events = yield handler.get_state_events(
|
||||||
room_id=room_id,
|
room_id=room_id,
|
||||||
user_id=user.to_string())
|
user_id=user.to_string(),
|
||||||
|
)
|
||||||
|
|
||||||
for event in members["chunk"]:
|
chunk = []
|
||||||
|
|
||||||
|
for event in events:
|
||||||
|
if event["type"] != EventTypes.Member:
|
||||||
|
continue
|
||||||
|
chunk.append(event)
|
||||||
# FIXME: should probably be state_key here, not user_id
|
# FIXME: should probably be state_key here, not user_id
|
||||||
target_user = UserID.from_string(event["user_id"])
|
target_user = UserID.from_string(event["user_id"])
|
||||||
# Presence is an optional cache; don't fail if we can't fetch it
|
# Presence is an optional cache; don't fail if we can't fetch it
|
||||||
@ -308,7 +314,9 @@ class RoomMemberListRestServlet(ClientV1RestServlet):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
defer.returnValue((200, members))
|
defer.returnValue((200, {
|
||||||
|
"chunk": chunk
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
# TODO: Needs unit testing
|
# TODO: Needs unit testing
|
||||||
@ -321,14 +329,12 @@ class RoomMessageListRestServlet(ClientV1RestServlet):
|
|||||||
pagination_config = PaginationConfig.from_request(
|
pagination_config = PaginationConfig.from_request(
|
||||||
request, default_limit=10,
|
request, default_limit=10,
|
||||||
)
|
)
|
||||||
with_feedback = "feedback" in request.args
|
|
||||||
as_client_event = "raw" not in request.args
|
as_client_event = "raw" not in request.args
|
||||||
handler = self.handlers.message_handler
|
handler = self.handlers.message_handler
|
||||||
msgs = yield handler.get_messages(
|
msgs = yield handler.get_messages(
|
||||||
room_id=room_id,
|
room_id=room_id,
|
||||||
user_id=user.to_string(),
|
user_id=user.to_string(),
|
||||||
pagin_config=pagination_config,
|
pagin_config=pagination_config,
|
||||||
feedback=with_feedback,
|
|
||||||
as_client_event=as_client_event
|
as_client_event=as_client_event
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
RoomsForUser = namedtuple(
|
RoomsForUser = namedtuple(
|
||||||
"RoomsForUser",
|
"RoomsForUser",
|
||||||
("room_id", "sender", "membership")
|
("room_id", "sender", "membership", "event_id", "stream_ordering")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -141,9 +141,11 @@ class RoomMemberStore(SQLBaseStore):
|
|||||||
args.extend(membership_list)
|
args.extend(membership_list)
|
||||||
|
|
||||||
sql = (
|
sql = (
|
||||||
"SELECT m.room_id, m.sender, m.membership"
|
"SELECT m.room_id, m.sender, m.membership, m.event_id, e.stream_ordering"
|
||||||
" FROM room_memberships as m"
|
" FROM room_memberships as m"
|
||||||
" INNER JOIN current_state_events as c"
|
" INNER JOIN current_state_events as c"
|
||||||
|
" ON e.event_id = c.event_id "
|
||||||
|
" INNER JOIN events as e "
|
||||||
" ON m.event_id = c.event_id "
|
" ON m.event_id = c.event_id "
|
||||||
" AND m.room_id = c.room_id "
|
" AND m.room_id = c.room_id "
|
||||||
" AND m.user_id = c.state_key"
|
" AND m.user_id = c.state_key"
|
||||||
|
@ -159,9 +159,7 @@ class StreamStore(SQLBaseStore):
|
|||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
def get_room_events_stream(self, user_id, from_key, to_key, room_id,
|
def get_room_events_stream(self, user_id, from_key, to_key, room_id,
|
||||||
limit=0, with_feedback=False):
|
limit=0):
|
||||||
# TODO (erikj): Handle compressed feedback
|
|
||||||
|
|
||||||
current_room_membership_sql = (
|
current_room_membership_sql = (
|
||||||
"SELECT m.room_id FROM room_memberships as m "
|
"SELECT m.room_id FROM room_memberships as m "
|
||||||
" INNER JOIN current_state_events as c"
|
" INNER JOIN current_state_events as c"
|
||||||
@ -227,10 +225,7 @@ class StreamStore(SQLBaseStore):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def paginate_room_events(self, room_id, from_key, to_key=None,
|
def paginate_room_events(self, room_id, from_key, to_key=None,
|
||||||
direction='b', limit=-1,
|
direction='b', limit=-1):
|
||||||
with_feedback=False):
|
|
||||||
# TODO (erikj): Handle compressed feedback
|
|
||||||
|
|
||||||
# Tokens really represent positions between elements, but we use
|
# Tokens really represent positions between elements, but we use
|
||||||
# the convention of pointing to the event before the gap. Hence
|
# the convention of pointing to the event before the gap. Hence
|
||||||
# we have a bit of asymmetry when it comes to equalities.
|
# we have a bit of asymmetry when it comes to equalities.
|
||||||
@ -302,7 +297,6 @@ class StreamStore(SQLBaseStore):
|
|||||||
|
|
||||||
@cachedInlineCallbacks(num_args=4)
|
@cachedInlineCallbacks(num_args=4)
|
||||||
def get_recent_events_for_room(self, room_id, limit, end_token, from_token=None):
|
def get_recent_events_for_room(self, room_id, limit, end_token, from_token=None):
|
||||||
# TODO (erikj): Handle compressed feedback
|
|
||||||
|
|
||||||
end_token = RoomStreamToken.parse_stream_token(end_token)
|
end_token = RoomStreamToken.parse_stream_token(end_token)
|
||||||
|
|
||||||
@ -379,6 +373,38 @@ class StreamStore(SQLBaseStore):
|
|||||||
)
|
)
|
||||||
defer.returnValue("t%d-%d" % (topo, token))
|
defer.returnValue("t%d-%d" % (topo, token))
|
||||||
|
|
||||||
|
def get_stream_token_for_event(self, event_id):
|
||||||
|
"""The stream token for an event
|
||||||
|
Args:
|
||||||
|
event_id(str): The id of the event to look up a stream token for.
|
||||||
|
Raises:
|
||||||
|
StoreError if the event wasn't in the database.
|
||||||
|
Returns:
|
||||||
|
A deferred "s%d" stream token.
|
||||||
|
"""
|
||||||
|
return self._simple_select_one_onecol(
|
||||||
|
table="events",
|
||||||
|
keyvalues={"event_id": event_id},
|
||||||
|
retcol="stream_ordering",
|
||||||
|
).addCallback(lambda row: "s%d" % (row,))
|
||||||
|
|
||||||
|
def get_topological_token_for_event(self, event_id):
|
||||||
|
"""The stream token for an event
|
||||||
|
Args:
|
||||||
|
event_id(str): The id of the event to look up a stream token for.
|
||||||
|
Raises:
|
||||||
|
StoreError if the event wasn't in the database.
|
||||||
|
Returns:
|
||||||
|
A deferred "t%d-%d" topological token.
|
||||||
|
"""
|
||||||
|
return self._simple_select_one(
|
||||||
|
table="events",
|
||||||
|
keyvalues={"event_id": event_id},
|
||||||
|
retcols=("stream_ordering", "topological_ordering"),
|
||||||
|
).addCallback(lambda row: "t%d-%d" % (
|
||||||
|
row["topological_ordering"], row["stream_ordering"],)
|
||||||
|
)
|
||||||
|
|
||||||
def _get_max_topological_txn(self, txn):
|
def _get_max_topological_txn(self, txn):
|
||||||
txn.execute(
|
txn.execute(
|
||||||
"SELECT MAX(topological_ordering) FROM events"
|
"SELECT MAX(topological_ordering) FROM events"
|
||||||
|
@ -239,7 +239,7 @@ class RoomPermissionsTestCase(RestTestCase):
|
|||||||
"PUT", topic_path, topic_content)
|
"PUT", topic_path, topic_content)
|
||||||
self.assertEquals(403, code, msg=str(response))
|
self.assertEquals(403, code, msg=str(response))
|
||||||
(code, response) = yield self.mock_resource.trigger_get(topic_path)
|
(code, response) = yield self.mock_resource.trigger_get(topic_path)
|
||||||
self.assertEquals(403, code, msg=str(response))
|
self.assertEquals(200, code, msg=str(response))
|
||||||
|
|
||||||
# get topic in PUBLIC room, not joined, expect 403
|
# get topic in PUBLIC room, not joined, expect 403
|
||||||
(code, response) = yield self.mock_resource.trigger_get(
|
(code, response) = yield self.mock_resource.trigger_get(
|
||||||
@ -301,11 +301,11 @@ class RoomPermissionsTestCase(RestTestCase):
|
|||||||
room=room, expect_code=200)
|
room=room, expect_code=200)
|
||||||
|
|
||||||
# get membership of self, get membership of other, private room + left
|
# get membership of self, get membership of other, private room + left
|
||||||
# expect all 403s
|
# expect all 200s
|
||||||
yield self.leave(room=room, user=self.user_id)
|
yield self.leave(room=room, user=self.user_id)
|
||||||
yield self._test_get_membership(
|
yield self._test_get_membership(
|
||||||
members=[self.user_id, self.rmcreator_id],
|
members=[self.user_id, self.rmcreator_id],
|
||||||
room=room, expect_code=403)
|
room=room, expect_code=200)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_membership_public_room_perms(self):
|
def test_membership_public_room_perms(self):
|
||||||
@ -326,11 +326,11 @@ class RoomPermissionsTestCase(RestTestCase):
|
|||||||
room=room, expect_code=200)
|
room=room, expect_code=200)
|
||||||
|
|
||||||
# get membership of self, get membership of other, public room + left
|
# get membership of self, get membership of other, public room + left
|
||||||
# expect 403.
|
# expect 200.
|
||||||
yield self.leave(room=room, user=self.user_id)
|
yield self.leave(room=room, user=self.user_id)
|
||||||
yield self._test_get_membership(
|
yield self._test_get_membership(
|
||||||
members=[self.user_id, self.rmcreator_id],
|
members=[self.user_id, self.rmcreator_id],
|
||||||
room=room, expect_code=403)
|
room=room, expect_code=200)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_invited_permissions(self):
|
def test_invited_permissions(self):
|
||||||
@ -492,9 +492,9 @@ class RoomsMemberListTestCase(RestTestCase):
|
|||||||
self.assertEquals(200, code, msg=str(response))
|
self.assertEquals(200, code, msg=str(response))
|
||||||
|
|
||||||
yield self.leave(room=room_id, user=self.user_id)
|
yield self.leave(room=room_id, user=self.user_id)
|
||||||
# can no longer see list, you've left.
|
# can see old list once left
|
||||||
(code, response) = yield self.mock_resource.trigger_get(room_path)
|
(code, response) = yield self.mock_resource.trigger_get(room_path)
|
||||||
self.assertEquals(403, code, msg=str(response))
|
self.assertEquals(200, code, msg=str(response))
|
||||||
|
|
||||||
|
|
||||||
class RoomsCreateTestCase(RestTestCase):
|
class RoomsCreateTestCase(RestTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user