From 1587ea26fef65157f2a35b150f01bd8035e5e785 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 14:38:22 +0100 Subject: [PATCH 01/65] Wait for getting a Join in response to an invite/join dance. --- synapse/handlers/_base.py | 1 + synapse/handlers/federation.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index c2f4685c9..3f07b5aa4 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -24,4 +24,5 @@ class BaseHandler(object): self.notifier = hs.get_notifier() self.room_lock = hs.get_room_lock_manager() self.state_handler = hs.get_state_handler() + self.distributor = hs.get_distributor() self.hs = hs diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index aa3bf273f..9cff44477 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -32,6 +32,15 @@ logger = logging.getLogger(__name__) class FederationHandler(BaseHandler): """Handles events that originated from federation.""" + def __init__(self, hs): + super(FederationHandler, self).__init__(hs) + + self.distributor.observe( + "user_joined_room", + self._on_user_joined + ) + + self.waiting_for_join_list = {} @log_function @defer.inlineCallbacks @@ -103,6 +112,13 @@ class FederationHandler(BaseHandler): if not backfilled: yield self.notifier.on_new_room_event(event, store_id) + if event.type == RoomMemberEvent.TYPE: + if event.membership == Membership.JOIN: + user = self.hs.parse_userid(event.target_user_id) + self.distributor.fire( + "user_joined_room", user=user, room_id=event.room_id + ) + @log_function @defer.inlineCallbacks @@ -152,8 +168,10 @@ class FederationHandler(BaseHandler): yield federation.handle_new_event(new_event) - store_id = yield self.store.persist_event(new_event) - self.notifier.on_new_room_event(new_event, store_id) + # TODO (erikj): Time out here. + d = defer.Deferred() + self.waiting_for_join_list.setdefault((joinee, room_id), []).append(d) + yield d try: yield self.store.store_room( @@ -166,3 +184,10 @@ class FederationHandler(BaseHandler): defer.returnValue(True) + + + @log_function + def _on_user_joined(self, user, room_id): + waiters = self.waiting_for_join_list.get((user.to_string(), room_id), []) + while waiters: + waiters.pop().callback(None) From 063e1b22e62915ec77bfd3cb9477c29600acb568 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 15:06:00 +0100 Subject: [PATCH 02/65] Stop internal keys from getting into SynapseEvents --- synapse/api/events/__init__.py | 1 + synapse/storage/_base.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py index 921fd0883..aa04dbece 100644 --- a/synapse/api/events/__init__.py +++ b/synapse/api/events/__init__.py @@ -51,6 +51,7 @@ class SynapseEvent(JsonEncodedObject): "depth", "destinations", "origin", + "outlier", ] required_keys = [ diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py index 36cc57c1b..75aab2d3b 100644 --- a/synapse/storage/_base.py +++ b/synapse/storage/_base.py @@ -294,6 +294,11 @@ class SQLBaseStore(object): def _parse_event_from_row(self, row_dict): d = copy.deepcopy({k: v for k, v in row_dict.items() if v}) + + d.pop("stream_ordering", None) + d.pop("topological_ordering", None) + d.pop("processed", None) + d.update(json.loads(row_dict["unrecognized_keys"])) d["content"] = json.loads(d["content"]) del d["unrecognized_keys"] From c6950b18cca665f6afe8ac00fcfa2322d8b35544 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 15:06:22 +0100 Subject: [PATCH 03/65] Return the current state in the initial sync api. --- synapse/handlers/room.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 6229ee9bf..91415afbb 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -279,6 +279,9 @@ class MessageHandler(BaseHandler): "start": token[0], "end": token[1], } + + current_state = yield self.store.get_current_state(event.room_id) + d["state"] = [c.get_dict() for c in current_state] except: logger.exception("Failed to get snapshot") From 3d1cae0e7954085bdc1dd1fca6a4ea4986e3d6f5 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 15:07:08 +0100 Subject: [PATCH 04/65] In the initial sync api, return the inviter for rooms in the 'invited' state --- synapse/handlers/room.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 91415afbb..d9809bd6d 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -264,6 +264,10 @@ class MessageHandler(BaseHandler): "room_id": event.room_id, "membership": event.membership, } + + if event.membership == Membership.INVITE: + d["inviter"] = event.user_id + ret.append(d) if event.membership != Membership.JOIN: From 01a129cb9a3dea54faf65bea4cf10dee22b55fde Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 21 Aug 2014 15:26:19 +0100 Subject: [PATCH 05/65] cheer up erik and remove the double-horizontal-border between adjacent text plinths --- webclient/app.css | 1 + 1 file changed, 1 insertion(+) diff --git a/webclient/app.css b/webclient/app.css index 869db69cd..1717b1b35 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -192,6 +192,7 @@ h1 { border: 1px solid #d8d8d8; height: 31px; display: inline-table; + margin-top: -1px; max-width: 90%; font-size: 16px; /* word-wrap: break-word; */ From 14b99896604c8860ebb2a6ed607fae40fe04494b Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Thu, 21 Aug 2014 16:27:15 +0200 Subject: [PATCH 06/65] Fixed first pagination detection --- webclient/room/room-controller.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 35abeeca0..6d714151f 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -28,6 +28,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) user_id: matrixService.config().user_id, events_from: "END", // when to start the event stream from. earliest_token: "END", // stores how far back we've paginated. + first_pagination: true, // this is toggled off when the first pagination is done can_paginate: true, // this is toggled off when we run out of items paginating: false, // used to avoid concurrent pagination requests pulling in dup contents stream_failure: undefined, // the response when the stream fails @@ -99,7 +100,6 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) var originalTopRow = $("#messageTable>tbody>tr:first")[0]; matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then( function(response) { - var firstPagination = !$scope.events.rooms[$scope.room_id]; eventHandlerService.handleEvents(response.data.chunk, false); $scope.state.earliest_token = response.data.end; if (response.data.chunk.length < MESSAGES_PER_PAGINATION) { @@ -125,8 +125,9 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) }, 0); } - if (firstPagination) { + if ($scope.state.first_pagination) { scrollToBottom(); + $scope.state.first_pagination = false; } else { // lock the scroll position From 4c228df167ca1708964c93c3c20e46d631899ce1 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 15:30:57 +0100 Subject: [PATCH 07/65] Use the new 'inviter' key from im sync for room display names. --- webclient/rooms/rooms-controller.js | 9 +++++++-- webclient/rooms/rooms.html | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index c25e24c8b..f2ff4a25b 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -59,7 +59,7 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', // FIXME push membership to top level key to match /im/sync event.membership = event.content.membership; // FIXME bodge a nicer name than the room ID for this invite. - event.room_alias = event.user_id + "'s room"; + event.room_display_name = event.user_id + "'s room"; $scope.rooms[event.room_id] = event; } }); @@ -70,15 +70,20 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', if (alias) { // use the existing alias from storage data[i].room_alias = alias; + data[i].room_display_name = alias; } else if (data[i].aliases && data[i].aliases[0]) { // save the mapping // TODO: select the smarter alias from the array matrixService.createRoomIdToAliasMapping(data[i].room_id, data[i].aliases[0]); + data[i].room_display_name = data[i].aliases[0]; + } + else if (data[i].membership == "invite" && "inviter" in data[i]) { + data[i].room_display_name = data[i].inviter + "'s room" } else { // last resort use the room id - data[i].room_alias = data[i].room_id; + data[i].room_display_name = data[i].room_id; } } return data; diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html index 2602209bd..ba3b7d8ba 100644 --- a/webclient/rooms/rooms.html +++ b/webclient/rooms/rooms.html @@ -65,7 +65,7 @@
- {{ room.room_alias }} {{room.membership === 'invite' ? ' (invited)' : ''}} + {{ room.room_display_name }} {{room.membership === 'invite' ? ' (invited)' : ''}}

From ad869fa4b30d660bb3307e8bdab26c36c52a7221 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 21 Aug 2014 15:43:47 +0100 Subject: [PATCH 08/65] stop hammering the HS for displayname and avatar URLs --- webclient/room/room-controller.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 8dea64a80..eee805daf 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -160,8 +160,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) if ("mtime_age" in chunk.content) { chunk.mtime_age = chunk.content.mtime_age; } -/* - // FIXME: once the HS reliably returns the displaynames & avatar_urls for both + // Once the HS reliably returns the displaynames & avatar_urls for both // local and remote users, we should use this rather than the evalAsync block // below if ("displayname" in chunk.content) { @@ -170,9 +169,11 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) if ("avatar_url" in chunk.content) { chunk.avatar_url = chunk.content.avatar_url; } - */ $scope.members[chunk.target_user_id] = chunk; +/* + // Stale code for explicitly hammering the homeserver for every displayname & avatar_url + // get their display name and profile picture and set it to their // member entry in $scope.members. We HAVE to use $timeout with 0 delay // to make this function run AFTER the current digest cycle, else the @@ -196,6 +197,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) } ); }); +*/ } else { // selectively update membership else it will nuke the picture and displayname too :/ From e7ee0b9fc113b1fd29b8cb96eea7a00641e56887 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 16:40:21 +0100 Subject: [PATCH 09/65] Change IM sync api to also return the current presence list. --- synapse/handlers/room.py | 24 +++++++++++++++++++++--- synapse/storage/stream.py | 5 ++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index d9809bd6d..899b653fb 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -24,6 +24,7 @@ from synapse.api.events.room import ( RoomConfigEvent ) from synapse.api.streams.event import EventStream, EventsStreamData +from synapse.handlers.presence import PresenceStreamData from synapse.util import stringutils from ._base import BaseHandler @@ -257,7 +258,19 @@ class MessageHandler(BaseHandler): membership_list=[Membership.INVITE, Membership.JOIN] ) - ret = [] + rooms_ret = [] + + now_rooms_token = yield self.store.get_room_events_max_id() + + # FIXME (erikj): Fix this. + presence_stream = PresenceStreamData(self.hs) + now_presence_token = yield presence_stream.max_token() + presence = yield presence_stream.get_rows( + user_id, 0, now_presence_token, None, None + ) + + # FIXME (erikj): We need to not generate this token, + now_token = "%s_%s" % (now_rooms_token, now_presence_token) for event in room_list: d = { @@ -268,14 +281,15 @@ class MessageHandler(BaseHandler): if event.membership == Membership.INVITE: d["inviter"] = event.user_id - ret.append(d) + rooms_ret.append(d) if event.membership != Membership.JOIN: continue try: messages, token = yield self.store.get_recent_events_for_room( event.room_id, - limit=50, + limit=10, + end_token=now_rooms_token, ) d["messages"] = { @@ -289,6 +303,10 @@ class MessageHandler(BaseHandler): except: logger.exception("Failed to get snapshot") + user = self.hs.parse_userid(user_id) + + ret = {"rooms": rooms_ret, "presence": presence[0], "end": now_token} + logger.debug("snapshot_all_rooms returning: %s", ret) defer.returnValue(ret) diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py index e994017bf..8bc502483 100644 --- a/synapse/storage/stream.py +++ b/synapse/storage/stream.py @@ -249,11 +249,10 @@ class StreamStore(SQLBaseStore): ) @defer.inlineCallbacks - def get_recent_events_for_room(self, room_id, limit, with_feedback=False): + def get_recent_events_for_room(self, room_id, limit, end_token, + with_feedback=False): # TODO (erikj): Handle compressed feedback - end_token = yield self.get_room_events_max_id() - sql = ( "SELECT * FROM events " "WHERE room_id = ? AND stream_ordering <= ? " From 7dac1bfc9148e4e23d388d8281aacee2bb41d5db Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 17:17:41 +0100 Subject: [PATCH 10/65] Change webclient to always hit the im sync api before streaming so we get current presence state --- .../components/matrix/event-stream-service.js | 41 ++++++++++++++++--- webclient/rooms/rooms-controller.js | 7 +++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index a446fad5d..9a8f6eac4 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -48,11 +48,12 @@ angular.module('eventStreamService', []) var saveStreamSettings = function() { localStorage.setItem("streamSettings", JSON.stringify(settings)); }; - - var startEventStream = function() { + + var doEventStream = function(deferred) { settings.shouldPoll = true; settings.isActive = true; - var deferred = $q.defer(); + deferred = deferred || $q.defer(); + // run the stream from the latest token matrixService.getEventStream(settings.from, TIMEOUT_MS).then( function(response) { @@ -63,13 +64,16 @@ angular.module('eventStreamService', []) settings.from = response.data.end; - console.log("[EventStream] Got response from "+settings.from+" to "+response.data.end); + console.log( + "[EventStream] Got response from "+settings.from+ + " to "+response.data.end + ); eventHandlerService.handleEvents(response.data.chunk, true); deferred.resolve(response); if (settings.shouldPoll) { - $timeout(startEventStream, 0); + $timeout(doEventStream, 0); } else { console.log("[EventStream] Stopping poll."); @@ -83,13 +87,38 @@ angular.module('eventStreamService', []) deferred.reject(error); if (settings.shouldPoll) { - $timeout(startEventStream, ERR_TIMEOUT_MS); + $timeout(doEventStream, ERR_TIMEOUT_MS); } else { console.log("[EventStream] Stopping polling."); } } ); + + return deferred.promise; + } + + var startEventStream = function() { + settings.shouldPoll = true; + settings.isActive = true; + var deferred = $q.defer(); + + // FIXME: We are discarding all the messages. + matrixService.rooms().then( + function(response) { + var presence = response.data.presence; + for (var i = 0; i < presence.length; ++i) { + eventHandlerService.handleEvent(presence[i], false); + } + + settings.from = response.data.end + doEventStream(deferred); + }, + function(error) { + $scope.feedback = "Failure: " + error.data; + } + ); + return deferred.promise; }; diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index f2ff4a25b..6bbb2b2ba 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -93,11 +93,16 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', // List all rooms joined or been invited to matrixService.rooms().then( function(response) { - var data = assignRoomAliases(response.data); + var data = assignRoomAliases(response.data.rooms); $scope.feedback = "Success"; for (var i=0; i Date: Thu, 21 Aug 2014 17:46:52 +0100 Subject: [PATCH 11/65] Add ts field to all events. --- synapse/api/events/factory.py | 7 ++++++- synapse/server.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py index b61dac7ac..c2cdcddf4 100644 --- a/synapse/api/events/factory.py +++ b/synapse/api/events/factory.py @@ -33,16 +33,21 @@ class EventFactory(object): RoomConfigEvent ] - def __init__(self): + def __init__(self, hs): self._event_list = {} # dict of TYPE to event class for event_class in EventFactory._event_classes: self._event_list[event_class.TYPE] = event_class + self.clock = hs.get_clock() + def create_event(self, etype=None, **kwargs): kwargs["type"] = etype if "event_id" not in kwargs: kwargs["event_id"] = random_string(10) + if "ts" not in kwargs: + kwargs["ts"] = int(self.clock.time_msec()) + if etype in self._event_list: handler = self._event_list[etype] else: diff --git a/synapse/server.py b/synapse/server.py index d4c248148..c5b0a3275 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -159,7 +159,7 @@ class HomeServer(BaseHomeServer): return DataStore(self) def build_event_factory(self): - return EventFactory() + return EventFactory(self) def build_handlers(self): return Handlers(self) From 2e1ab9db08e3fe41822a65fdf38feafbd22173b6 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 17:55:41 +0100 Subject: [PATCH 12/65] Only start event streaming after having set up the controllers. --- demo/start.sh | 3 ++- webclient/app-controller.js | 2 +- webclient/app.js | 2 +- webclient/components/matrix/event-stream-service.js | 10 ++++++++++ webclient/room/room-controller.js | 1 + webclient/rooms/rooms-controller.js | 6 ++++-- 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/demo/start.sh b/demo/start.sh index 470187292..fa2998a5e 100755 --- a/demo/start.sh +++ b/demo/start.sh @@ -15,7 +15,8 @@ for port in "8080" "8081" "8082"; do -f "$DIR/$port.log" \ -d "$DIR/$port.db" \ -vv \ - -D --pid-file "$DIR/$port.pid" + -D --pid-file "$DIR/$port.pid"\ + -w done echo "Starting webclient on port 8000..." diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 96656e12c..c53f29aa7 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -53,7 +53,7 @@ angular.module('MatrixWebClientController', ['matrixService']) }; if (matrixService.isUserLoggedIn()) { - eventStreamService.resume(); + // eventStreamService.resume(); } // Logs the user out diff --git a/webclient/app.js b/webclient/app.js index f27ebedc6..944b8ec27 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -80,6 +80,6 @@ matrixWebClient.run(['$location', 'matrixService', 'eventStreamService', functio $location.path("login"); } else { - eventStreamService.resume(); + // eventStreamService.resume(); } }]); diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 9a8f6eac4..a1a98b2a3 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -106,6 +106,16 @@ angular.module('eventStreamService', []) // FIXME: We are discarding all the messages. matrixService.rooms().then( function(response) { + var rooms = response.data.rooms; + for (var i = 0; i < rooms.length; ++i) { + var room = rooms[i]; + if ("state" in room) { + for (var j = 0; j < room.state.length; ++j) { + eventHandlerService.handleEvents(room.state[j], false); + } + } + } + var presence = response.data.presence; for (var i = 0; i < presence.length; ++i) { eventHandlerService.handleEvent(presence[i], false); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index eee805daf..214166a43 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -328,6 +328,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) var chunk = response.data.chunk[i]; updateMemberList(chunk); } + eventStreamService.resume(); }, function(error) { $scope.feedback = "Failed get member list: " + error.data.error; diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index 6bbb2b2ba..c2d7bcb6f 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -17,8 +17,8 @@ limitations under the License. 'use strict'; angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) -.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', - function($scope, $location, matrixService, mFileUpload, eventHandlerService) { +.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', + function($scope, $location, matrixService, mFileUpload, eventHandlerService, eventStreamService) { $scope.rooms = {}; $scope.public_rooms = []; @@ -113,6 +113,8 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', $scope.public_rooms = assignRoomAliases(response.data.chunk); } ); + + eventStreamService.resume(); }; $scope.createNewRoom = function(room_id, isPrivate) { From 0045a2647ad3e0e088dacbee8497dbbb7d118269 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 17:59:07 +0100 Subject: [PATCH 13/65] Add a var. --- webclient/app-filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/app-filter.js b/webclient/app-filter.js index 64c3bb04d..f007b4c89 100644 --- a/webclient/app-filter.js +++ b/webclient/app-filter.js @@ -58,7 +58,7 @@ angular.module('matrixWebClient') angular.forEach(displayNames, function(value, key) { if (value.length > 1) { // console.log(key + ": " + value); - for (i=0; i < value.length; i++) { + for (var i=0; i < value.length; i++) { var v = value[i]; members[v].displayname += " (" + v + ")"; // console.log(v + " " + members[v]); From 3277a650529d4ecaf816987e6cbcb87fdf3371da Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 21 Aug 2014 19:02:00 +0100 Subject: [PATCH 14/65] actually display room metadata based on m.room.membe events --- webclient/app.css | 4 ++++ webclient/components/matrix/event-handler-service.js | 11 +++++++++++ webclient/room/room.html | 10 ++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/webclient/app.css b/webclient/app.css index 83b0c9c65..a63b5db4d 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -245,6 +245,10 @@ h1 { background-color: #fff ! important; } +.mine .membership { + background-color: #fff ! important; +} + .mine .text .bubble { text-align: left ! important; } diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index b8529895f..6a01b3fb5 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -69,6 +69,17 @@ angular.module('eventHandlerService', []) var handleRoomMember = function(event, isLiveEvent) { initRoom(event.room_id); + + // add membership changes as if they were a room message if something interesting changed + if (event.content.prev !== event.content.membership) { + if (isLiveEvent) { + $rootScope.events.rooms[event.room_id].messages.push(event); + } + else { + $rootScope.events.rooms[event.room_id].messages.unshift(event); + } + } + $rootScope.events.rooms[event.room_id].members[event.user_id] = event; $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); }; diff --git a/webclient/room/room.html b/webclient/room/room.html index cb9cf1d1f..4a07dfdaa 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -26,19 +26,25 @@
+ -
{{ members[msg.user_id].displayname || msg.user_id }}
-
{{ msg.content.hsob_ts | date:'MMM d HH:mm:ss' }}
+
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm:ss' }}
+
+ + {{ members[msg.user_id].displayname || msg.user_id }} + {{ {"join": "joined", "leave": "left", "invite": "invited"}[msg.content.membership] }} + {{ msg.content.target_id || '' }} +
From 1b0d4272853ee2187014536de253e47bd318e198 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 21 Aug 2014 23:35:45 +0100 Subject: [PATCH 15/65] host a webclient by default --- synapse/app/homeserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index ca102236c..6b39da4a7 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -235,8 +235,8 @@ def setup(): parser.add_argument('--pid-file', dest="pid", help="When running as a " "daemon, the file to store the pid in", default="hs.pid") - parser.add_argument("-w", "--webclient", dest="webclient", - action="store_true", help="Host the web client.") + parser.add_argument("-W", "--webclient", dest="webclient", default=True, + action="store_false", help="Don't host a web client.") args = parser.parse_args() verbosity = int(args.verbose) if args.verbose else None From 019f3a66f605222576d4a061df1ecfbaebebf0c0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 01:32:17 +0100 Subject: [PATCH 16/65] add fixme pointing out name disambiguation is a bit flakey --- webclient/app-filter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webclient/app-filter.js b/webclient/app-filter.js index f007b4c89..b8f4ed25b 100644 --- a/webclient/app-filter.js +++ b/webclient/app-filter.js @@ -54,12 +54,15 @@ angular.module('matrixWebClient') }); // FIXME: we shouldn't disambiguate displayNames on every orderMembersList - // invocation but keep track of duplicates incrementally somewhere + // invocation but keep track of duplicates incrementally somewhere angular.forEach(displayNames, function(value, key) { if (value.length > 1) { // console.log(key + ": " + value); for (var i=0; i < value.length; i++) { var v = value[i]; + // FIXME: this permenantly rewrites the displayname for a given + // room member. which means we can't reset their name if it is + // no longer ambiguous! members[v].displayname += " (" + v + ")"; // console.log(v + " " + members[v]); }; From ab27b49deddbd6f74bad126b9a275b015a7fb6cd Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 01:33:05 +0100 Subject: [PATCH 17/65] rename autoComplete directive as tabComplete to avoid confusion with the autocomplete html attribute --- webclient/room/room-directive.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/webclient/room/room-directive.js b/webclient/room/room-directive.js index 94655336d..1a99a37ab 100644 --- a/webclient/room/room-directive.js +++ b/webclient/room/room-directive.js @@ -17,30 +17,30 @@ 'use strict'; angular.module('RoomController') -.directive('autoComplete', ['$timeout', function ($timeout) { +.directive('tabComplete', ['$timeout', function ($timeout) { return function (scope, element, attrs) { element.bind("keydown keypress", function (event) { // console.log("event: " + event.which); if (event.which === 9) { - if (!scope.autoCompleting) { // cache our starting text + if (!scope.tabCompleting) { // cache our starting text // console.log("caching " + element[0].value); - scope.autoCompleteOriginal = element[0].value; - scope.autoCompleting = true; + scope.tabCompleteOriginal = element[0].value; + scope.tabCompleting = true; } if (event.shiftKey) { - scope.autoCompleteIndex--; - if (scope.autoCompleteIndex < 0) { - scope.autoCompleteIndex = 0; + scope.tabCompleteIndex--; + if (scope.tabCompleteIndex < 0) { + scope.tabCompleteIndex = 0; } } else { - scope.autoCompleteIndex++; + scope.tabCompleteIndex++; } var searchIndex = 0; - var targetIndex = scope.autoCompleteIndex; - var text = scope.autoCompleteOriginal; + var targetIndex = scope.tabCompleteIndex; + var text = scope.tabCompleteOriginal; // console.log("targetIndex: " + targetIndex + ", text=" + text); @@ -90,17 +90,17 @@ angular.module('RoomController') element[0].className = ""; }, 150); element[0].value = text; - scope.autoCompleteIndex = 0; + scope.tabCompleteIndex = 0; } } else { - scope.autoCompleteIndex = 0; + scope.tabCompleteIndex = 0; } event.preventDefault(); } - else if (event.which !== 16 && scope.autoCompleting) { - scope.autoCompleting = false; - scope.autoCompleteIndex = 0; + else if (event.which !== 16 && scope.tabCompleting) { + scope.tabCompleting = false; + scope.tabCompleteIndex = 0; } }); }; From fd47f55e943dc6950a1a84414e0ed8a08fbc504c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 01:33:34 +0100 Subject: [PATCH 18/65] sacrifice a goat or two to make wordwrap actually work properly --- webclient/app.css | 45 +++++++++++++++++----------------------- webclient/room/room.html | 4 ++-- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/webclient/app.css b/webclient/app.css index a63b5db4d..dfc919e4c 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -145,6 +145,7 @@ h1 { max-width: 1280px; width: 100%; border-collapse: collapse; + table-layout: fixed; } #messageTable td { @@ -190,25 +191,13 @@ h1 { object-fit: cover; } -.text { - background-color: #eee; - border: 1px solid #d8d8d8; - height: 31px; - display: inline-table; - margin-top: -1px; - max-width: 90%; - font-size: 16px; - /* word-wrap: break-word; */ - word-break: break-all; -} - .emote { - background-color: #fff ! important; + background-color: transparent ! important; border: 0px ! important; } .membership { - background-color: #fff ! important; + background-color: transparent ! important; border: 0px ! important; } @@ -221,6 +210,13 @@ h1 { } .bubble { + background-color: #eee; + border: 1px solid #d8d8d8; + display: inline-block; + margin-bottom: -1px; + max-width: 90%; + font-size: 16px; + word-wrap: break-word; padding-top: 7px; padding-bottom: 5px; padding-left: 1em; @@ -229,27 +225,24 @@ h1 { } .differentUser td { - padding-top: 5px ! important; - margin-top: 5px ! important; + padding-bottom: 5px ! important; } .mine { text-align: right; } -.mine .text { - background-color: #f8f8ff ! important; -} - -.mine .emote { - background-color: #fff ! important; -} - -.mine .membership { - background-color: #fff ! important; +.text.emote .bubble, +.text.membership .bubble, +.mine .text.emote .bubble, +.mine .text.membership .bubble + { + background-color: transparent ! important; + border: 0px ! important; } .mine .text .bubble { + background-color: #f8f8ff ! important; text-align: left ! important; } diff --git a/webclient/room/room.html b/webclient/room/room.html index 4a07dfdaa..e7560a5dc 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -29,7 +29,7 @@ + ng-class="(events.rooms[room_id].messages[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item> From aaf623fa534d2486eeadb79de0905c374e019098 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 17:11:39 +0200 Subject: [PATCH 44/65] Move profile parts of the rooms page and the config content into a new page: settings --- webclient/app-controller.js | 24 +--- webclient/app.css | 12 -- webclient/app.js | 5 + webclient/index.html | 12 +- webclient/rooms/rooms-controller.js | 118 +---------------- webclient/rooms/rooms.html | 48 +------ webclient/settings/settings-controller.js | 146 ++++++++++++++++++++++ webclient/settings/settings.html | 73 +++++++++++ 8 files changed, 237 insertions(+), 201 deletions(-) create mode 100644 webclient/settings/settings-controller.js create mode 100644 webclient/settings/settings.html diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 92ad01e4f..84cb94dc7 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -31,31 +31,15 @@ angular.module('MatrixWebClientController', ['matrixService']) $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { $scope.location = $location.path(); }); - - - // Manage the display of the current config - $scope.config; - - // Toggles the config display - $scope.showConfig = function() { - if ($scope.config) { - $scope.config = undefined; - } - else { - $scope.config = matrixService.config(); - } - }; - - $scope.closeConfig = function() { - if ($scope.config) { - $scope.config = undefined; - } - }; if (matrixService.isUserLoggedIn()) { // eventStreamService.resume(); } + $scope.go = function(url) { + $location.url(url); + }; + // Logs the user out $scope.logout = function() { // kill the event stream diff --git a/webclient/app.css b/webclient/app.css index 207f35f5f..72b38cd95 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -308,18 +308,6 @@ h1 { float: right; } -#config { - position: absolute; - z-index: 100; - top: 100px; - left: 50%; - width: 500px; - margin-left: -250px; - text-align: center; - padding: 20px; - background-color: #aaa; -} - .text_entry_section { position: fixed; bottom: 0; diff --git a/webclient/app.js b/webclient/app.js index 944b8ec27..f666a63bf 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -20,6 +20,7 @@ var matrixWebClient = angular.module('matrixWebClient', [ 'LoginController', 'RoomController', 'RoomsController', + 'SettingsController', 'UserController', 'matrixService', 'eventStreamService', @@ -48,6 +49,10 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'rooms/rooms.html', controller: 'RoomsController' }). + when('/settings', { + templateUrl: 'settings/settings.html', + controller: 'SettingsController' + }). when('/user/:user_matrix_id', { templateUrl: 'user/user.html', controller: 'UserController' diff --git a/webclient/index.html b/webclient/index.html index 27d920819..95f682580 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -19,6 +19,7 @@ + @@ -33,22 +34,13 @@ -
-
Home server: {{ config.homeserver }}
-
User ID: {{ config.user_id }}
-
Access token: {{ config.access_token }}
-
-
-
- -
diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index 557fbe237..d891558be 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -19,7 +19,8 @@ limitations under the License. angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) .controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', function($scope, $location, matrixService, mFileUpload, eventHandlerService, eventStreamService) { - + + $scope.config = matrixService.config(); $scope.rooms = {}; $scope.public_rooms = []; $scope.newRoomId = ""; @@ -37,20 +38,6 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', $scope.joinAlias = { room_alias: "", }; - - $scope.newProfileInfo = { - name: matrixService.config().displayName, - avatar: matrixService.config().avatarUrl, - avatarFile: undefined - }; - - $scope.linkedEmails = { - linkNewEmail: "", // the email entry box - emailBeingAuthed: undefined, // to populate verification text - authTokenId: undefined, // the token id from the IS - emailCode: "", // the code entry box - linkedEmailList: matrixService.config().emailList // linked email list - }; $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { var config = matrixService.config(); @@ -170,107 +157,6 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', } ); }; - - $scope.setDisplayName = function(newName) { - matrixService.setDisplayName(newName).then( - function(response) { - $scope.feedback = "Updated display name."; - var config = matrixService.config(); - config.displayName = newName; - matrixService.setConfig(config); - matrixService.saveConfig(); - }, - function(error) { - $scope.feedback = "Can't update display name: " + error.data; - } - ); - }; - - - $scope.$watch("newProfileInfo.avatarFile", function(newValue, oldValue) { - if ($scope.newProfileInfo.avatarFile) { - console.log("Uploading new avatar file..."); - mFileUpload.uploadFile($scope.newProfileInfo.avatarFile).then( - function(url) { - $scope.newProfileInfo.avatar = url; - $scope.setAvatar($scope.newProfileInfo.avatar); - }, - function(error) { - $scope.feedback = "Can't upload image"; - } - ); - } - }); - - $scope.setAvatar = function(newUrl) { - console.log("Updating avatar to "+newUrl); - matrixService.setProfilePictureUrl(newUrl).then( - function(response) { - console.log("Updated avatar"); - $scope.feedback = "Updated avatar."; - var config = matrixService.config(); - config.avatarUrl = newUrl; - matrixService.setConfig(config); - matrixService.saveConfig(); - }, - function(error) { - $scope.feedback = "Can't update avatar: " + error.data; - } - ); - }; - - $scope.linkEmail = function(email) { - matrixService.linkEmail(email).then( - function(response) { - if (response.data.success === true) { - $scope.linkedEmails.authTokenId = response.data.tokenId; - $scope.emailFeedback = "You have been sent an email."; - $scope.linkedEmails.emailBeingAuthed = email; - } - else { - $scope.emailFeedback = "Failed to send email."; - } - }, - function(error) { - $scope.emailFeedback = "Can't send email: " + error.data; - } - ); - }; - - $scope.submitEmailCode = function(code) { - var tokenId = $scope.linkedEmails.authTokenId; - if (tokenId === undefined) { - $scope.emailFeedback = "You have not requested a code with this email."; - return; - } - matrixService.authEmail(matrixService.config().user_id, tokenId, code).then( - function(response) { - if ("success" in response.data && response.data.success === false) { - $scope.emailFeedback = "Failed to authenticate email."; - return; - } - var config = matrixService.config(); - var emailList = {}; - if ("emailList" in config) { - emailList = config.emailList; - } - emailList[response.address] = response; - // save the new email list - config.emailList = emailList; - matrixService.setConfig(config); - matrixService.saveConfig(); - // invalidate the email being authed and update UI. - $scope.linkedEmails.emailBeingAuthed = undefined; - $scope.emailFeedback = ""; - $scope.linkedEmails.linkedEmailList = emailList; - $scope.linkedEmails.linkNewEmail = ""; - $scope.linkedEmails.emailCode = ""; - }, - function(reason) { - $scope.emailFeedback = "Failed to auth email: " + reason; - } - ); - }; $scope.refresh(); }]); diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html index ba3b7d8ba..2e25c0f08 100644 --- a/webclient/rooms/rooms.html +++ b/webclient/rooms/rooms.html @@ -2,64 +2,26 @@
- +
{{ members[msg.user_id].displayname || msg.user_id }}
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm:ss' }}
@@ -77,7 +77,7 @@ {{ state.user_id }}
- + From 868fa1a1e349d365a41b9594a6fad64fbac36777 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 01:41:38 +0100 Subject: [PATCH 19/65] fix weird fontsizes on iOS --- webclient/app.css | 1 + 1 file changed, 1 insertion(+) diff --git a/webclient/app.css b/webclient/app.css index dfc919e4c..da1a840f4 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -222,6 +222,7 @@ h1 { padding-left: 1em; padding-right: 1em; vertical-align: middle; + -webkit-text-size-adjust:100% } .differentUser td { From 3248aed03b03e0eba3a4b43776ef2f7685b27701 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 01:54:37 +0100 Subject: [PATCH 20/65] fix mainInput retaining focus between sending consecutive messages by disabling commit 955662d6 --- webclient/room/room-controller.js | 4 ++-- webclient/room/room.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 214166a43..451c6242f 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -33,6 +33,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) can_paginate: true, // this is toggled off when we run out of items paginating: false, // used to avoid concurrent pagination requests pulling in dup contents stream_failure: undefined, // the response when the stream fails + // FIXME: sending has been disabled, as surely messages should be sent in the background rather than locking the UI synchronously --Matthew sending: false // true when a message is being sent. It helps to disable the UI when a process is running }; $scope.members = {}; @@ -239,7 +240,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) } $scope.state.sending = true; - + // Send the text message var promise; // FIXME: handle other commands too @@ -263,7 +264,6 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) }; $scope.onInit = function() { - // $timeout(function() { document.getElementById('textInput').focus() }, 0); console.log("onInit"); // Does the room ID provided in the URL? diff --git a/webclient/room/room.html b/webclient/room/room.html index e7560a5dc..95da06771 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -77,10 +77,10 @@ {{ state.user_id }} - + - + From 8d5ceccfc797c60723e84d0b0d3ad6045f694a61 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 02:04:13 +0100 Subject: [PATCH 21/65] -w is no more --- demo/start.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/start.sh b/demo/start.sh index fa2998a5e..470187292 100755 --- a/demo/start.sh +++ b/demo/start.sh @@ -15,8 +15,7 @@ for port in "8080" "8081" "8082"; do -f "$DIR/$port.log" \ -d "$DIR/$port.db" \ -vv \ - -D --pid-file "$DIR/$port.pid"\ - -w + -D --pid-file "$DIR/$port.pid" done echo "Starting webclient on port 8000..." From 8f7fbc1bb0d9ace628cdf4fa824e96b5d4d30c13 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 02:11:33 +0100 Subject: [PATCH 22/65] improve leftBlock css --- webclient/app.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/webclient/app.css b/webclient/app.css index da1a840f4..207f35f5f 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -153,7 +153,8 @@ h1 { } .leftBlock { - width: 10em; + width: 14em; + word-wrap: break-word; vertical-align: top; background-color: #fff; color: #888; @@ -209,6 +210,10 @@ h1 { height: auto; } +.text { + vertical-align: top; +} + .bubble { background-color: #eee; border: 1px solid #d8d8d8; From be2f948da512a2f62c49635f24033892e39d359e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 02:23:59 +0100 Subject: [PATCH 23/65] homeserver runs webclient by default now --- README.rst | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 378b460d0..50440f57f 100644 --- a/README.rst +++ b/README.rst @@ -24,11 +24,8 @@ To get up and running: - To run your own **private** homeserver on localhost:8080, install synapse with ``python setup.py develop --user`` and then run one with - ``python synapse/app/homeserver.py`` - - - To run your own webclient, add ``-w``: - ``python synapse/app/homeserver.py -w`` and hit http://localhost:8080/matrix/client - in your web browser (a recent Chrome, Safari or Firefox for now, + ``python synapse/app/homeserver.py`` - you will find a webclient running + at http://localhost:8080 (use a recent Chrome, Safari or Firefox for now, please...) - To make the homeserver **public** and let it exchange messages with @@ -201,9 +198,7 @@ http://localhost:8080. Simply run:: Running The Demo Web Client =========================== -You can run the web client when you run the homeserver by adding ``-w`` to the -command to run ``homeserver.py``. The web client can be accessed via -http://localhost:8080/matrix/client +The homeserver runs a web client by default at http://localhost:8080. If this is the first time you have used the client from that browser (it uses HTML5 local storage to remember its config), you will need to log in to your From c8d0c4762da432aafa4372928aa70ef55646134b Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 10:15:15 +0200 Subject: [PATCH 24/65] Safari needs the img.onload event before actually working on the img --- .../fileUpload/file-upload-service.js | 1 + .../components/utilities/utilities-service.js | 65 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 6606f31e2..398124fcc 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -82,6 +82,7 @@ angular.module('mFileUpload', ['matrixService', 'mUtilities']) // First, get the image size mUtilities.getImageSize(imageFile).then( function(size) { + console.log("image size: " + JSON.stringify(size)); // The final operation: send imageFile var uploadImage = function() { diff --git a/webclient/components/utilities/utilities-service.js b/webclient/components/utilities/utilities-service.js index 9cf858ef3..5e9f70722 100644 --- a/webclient/components/utilities/utilities-service.js +++ b/webclient/components/utilities/utilities-service.js @@ -38,10 +38,15 @@ angular.module('mUtilities', []) img.src = e.target.result; // Once ready, returns its size - deferred.resolve({ - width: img.width, - height: img.height - }); + img.onload = function() { + deferred.resolve({ + width: img.width, + height: img.height + }); + }; + img.onerror = function(e) { + deferred.reject(e); + }; }; reader.onerror = function(e) { deferred.reject(e); @@ -71,33 +76,39 @@ angular.module('mUtilities', []) reader.onload = function(e) { img.src = e.target.result; + + // Once ready, returns its size + img.onload = function() { + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); + var MAX_WIDTH = maxSize; + var MAX_HEIGHT = maxSize; + var width = img.width; + var height = img.height; - var MAX_WIDTH = maxSize; - var MAX_HEIGHT = maxSize; - var width = img.width; - var height = img.height; - - if (width > height) { - if (width > MAX_WIDTH) { - height *= MAX_WIDTH / width; - width = MAX_WIDTH; + if (width > height) { + if (width > MAX_WIDTH) { + height *= MAX_WIDTH / width; + width = MAX_WIDTH; + } + } else { + if (height > MAX_HEIGHT) { + width *= MAX_HEIGHT / height; + height = MAX_HEIGHT; + } } - } else { - if (height > MAX_HEIGHT) { - width *= MAX_HEIGHT / height; - height = MAX_HEIGHT; - } - } - canvas.width = width; - canvas.height = height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0, width, height); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, width, height); - var dataUrl = canvas.toDataURL("image/jpeg", 0.7); - deferred.resolve(self.dataURItoBlob(dataUrl)); + var dataUrl = canvas.toDataURL("image/jpeg", 0.7); + deferred.resolve(self.dataURItoBlob(dataUrl)); + }; + img.onerror = function(e) { + deferred.reject(e); + }; }; reader.onerror = function(e) { deferred.reject(e); From 53f4fbd99a223a4321377ea670d1d19b669b6f4a Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 10:48:00 +0200 Subject: [PATCH 25/65] resizeImage: generate an image in the format of the original image. (Tested with tranparent PNG, transparent GIF, BMP, JPEG) --- webclient/components/utilities/utilities-service.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webclient/components/utilities/utilities-service.js b/webclient/components/utilities/utilities-service.js index 5e9f70722..3df2f0445 100644 --- a/webclient/components/utilities/utilities-service.js +++ b/webclient/components/utilities/utilities-service.js @@ -103,7 +103,9 @@ angular.module('mUtilities', []) var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); - var dataUrl = canvas.toDataURL("image/jpeg", 0.7); + // Extract image data in the same format as the original one. + // The 0.7 compression value will work with formats that supports it like JPEG. + var dataUrl = canvas.toDataURL(imageFile.type, 0.7); deferred.resolve(self.dataURItoBlob(dataUrl)); }; img.onerror = function(e) { From acf51276042cf438cbb02bb5ef31c42206d7685d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 10:25:27 +0100 Subject: [PATCH 26/65] Make the content repo work with in daemon mode. Return the full url on upload. Update the webclient to use new content repo api. --- synapse/app/homeserver.py | 5 ++-- synapse/http/server.py | 26 ++++++++++++++----- .../fileUpload/file-upload-service.js | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 6b39da4a7..495149466 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -56,7 +56,7 @@ class SynapseHomeServer(HomeServer): return File("webclient") # TODO configurable? def build_resource_for_content_repo(self): - return ContentRepoResource("uploads", self.auth) + return ContentRepoResource(self, self.upload_dir, self.auth) def build_db_pool(self): """ Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we @@ -257,7 +257,8 @@ def setup(): hs = SynapseHomeServer( args.host, - db_name=db_name + upload_dir=os.path.abspath("uploads"), + db_name=db_name, ) # This object doesn't need to be saved because it's set as the handler for diff --git a/synapse/http/server.py b/synapse/http/server.py index c28d9a33f..d1f99460c 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -212,8 +212,9 @@ class ContentRepoResource(resource.Resource): """ isLeaf = True - def __init__(self, directory, auth): + def __init__(self, hs, directory, auth): resource.Resource.__init__(self) + self.hs = hs self.directory = directory self.auth = auth @@ -250,7 +251,8 @@ class ContentRepoResource(resource.Resource): file_ext = re.sub("[^a-z]", "", file_ext) suffix += "." + file_ext - file_path = os.path.join(self.directory, prefix + main_part + suffix) + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) logger.info("User %s is uploading a file to path %s", auth_user.to_string(), file_path) @@ -259,8 +261,8 @@ class ContentRepoResource(resource.Resource): attempts = 0 while os.path.exists(file_path): main_part = random_string(24) - file_path = os.path.join(self.directory, - prefix + main_part + suffix) + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) attempts += 1 if attempts > 25: # really? Really? raise SynapseError(500, "Unable to create file.") @@ -272,11 +274,14 @@ class ContentRepoResource(resource.Resource): # servers. # TODO: A little crude here, we could do this better. - filename = request.path.split(self.directory + "/")[1] + filename = request.path.split('/')[-1] # be paranoid filename = re.sub("[^0-9A-z.-_]", "", filename) file_path = self.directory + "/" + filename + + logger.debug("Searching for %s", file_path) + if os.path.isfile(file_path): # filename has the content type base64_contentype = filename.split(".")[1] @@ -304,6 +309,10 @@ class ContentRepoResource(resource.Resource): self._async_render(request) return server.NOT_DONE_YET + def render_OPTIONS(self, request): + respond_with_json_bytes(request, 200, {}, send_cors=True) + return server.NOT_DONE_YET + @defer.inlineCallbacks def _async_render(self, request): try: @@ -313,8 +322,13 @@ class ContentRepoResource(resource.Resource): with open(fname, "wb") as f: f.write(request.content.read()) + + # FIXME (erikj): These should use constants. + file_name = os.path.basename(fname) + url = "http://%s/matrix/content/%s" % (self.hs.hostname, file_name) + respond_with_json_bytes(request, 200, - json.dumps({"content_token": fname}), + json.dumps({"content_token": url}), send_cors=True) except CodeMessageException as e: diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 398124fcc..5f01478fd 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -33,7 +33,7 @@ angular.module('mFileUpload', ['matrixService', 'mUtilities']) console.log("Uploading " + file.name + "... to /matrix/content"); matrixService.uploadContent(file).then( function(response) { - var content_url = location.origin + "/matrix/content/" + response.data.content_token; + var content_url = response.data.content_token; console.log(" -> Successfully uploaded! Available at " + content_url); deferred.resolve(content_url); }, From dde50d4245136cdbd11ac3b4af42102945cd14f9 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 11:43:54 +0200 Subject: [PATCH 27/65] Use $location.url instead of $location.path to get clean page URL without hash arguments of the previous page. This happpens with room URL like http://127.0.0.1:8080/matrix/client/#/room/#public:localhost. The second hash part is transferred to the next page when using $location.path. --- webclient/app-controller.js | 2 +- webclient/login/login-controller.js | 4 ++-- webclient/room/room-controller.js | 6 +++--- webclient/rooms/rooms-controller.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/webclient/app-controller.js b/webclient/app-controller.js index c53f29aa7..92ad01e4f 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -66,7 +66,7 @@ angular.module('MatrixWebClientController', ['matrixService']) matrixService.saveConfig(); // And go to the login page - $location.path("login"); + $location.url("login"); }; // Listen to the event indicating that the access token is no longer valid. diff --git a/webclient/login/login-controller.js b/webclient/login/login-controller.js index 67d0b7b90..2f1f224a9 100644 --- a/webclient/login/login-controller.js +++ b/webclient/login/login-controller.js @@ -53,7 +53,7 @@ angular.module('LoginController', ['matrixService']) matrixService.saveConfig(); eventStreamService.resume(); // Go to the user's rooms list page - $location.path("rooms"); + $location.url("rooms"); }, function(error) { if (error.data) { @@ -84,7 +84,7 @@ angular.module('LoginController', ['matrixService']) }); matrixService.saveConfig(); eventStreamService.resume(); - $location.path("rooms"); + $location.url("rooms"); } else { $scope.feedback = "Failed to login: " + JSON.stringify(response.data); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 451c6242f..26d1836fc 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -293,7 +293,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) else { // In case of issue, go to the default page console.log("Error: cannot extract room alias"); - $location.path("/"); + $location.url("/"); return; } } @@ -310,7 +310,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) function () { // In case of issue, go to the default page console.log("Error: cannot resolve room alias"); - $location.path("/"); + $location.url("/"); }); } }; @@ -364,7 +364,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) matrixService.leave($scope.room_id).then( function(response) { console.log("Left room "); - $location.path("rooms"); + $location.url("rooms"); }, function(error) { $scope.feedback = "Failed to leave room: " + error.data.error; diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index c2d7bcb6f..557fbe237 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -141,17 +141,17 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', // Go to a room $scope.goToRoom = function(room_id) { // Simply open the room page on this room id - //$location.path("room/" + room_id); + //$location.url("room/" + room_id); matrixService.join(room_id).then( function(response) { if (response.data.hasOwnProperty("room_id")) { if (response.data.room_id != room_id) { - $location.path("room/" + response.data.room_id); + $location.url("room/" + response.data.room_id); return; } } - $location.path("room/" + room_id); + $location.url("room/" + room_id); }, function(error) { $scope.feedback = "Can't join room: " + error.data; @@ -163,7 +163,7 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', matrixService.joinAlias(room_alias).then( function(response) { // Go to this room - $location.path("room/" + room_alias); + $location.url("room/" + room_alias); }, function(error) { $scope.feedback = "Can't join room: " + error.data; From 74c90f78159e8067b25bfa8a009d2e68419947c8 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 10:50:10 +0100 Subject: [PATCH 28/65] Reinitialize room when creating a RoomController so that we start off with a clean slate, as it expects/ --- webclient/components/matrix/event-handler-service.js | 12 +++++++++++- webclient/room/room-controller.js | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 6a01b3fb5..aa8867425 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -44,6 +44,12 @@ angular.module('eventHandlerService', []) $rootScope.events.rooms[room_id].members = {}; } } + + var reInitRoom = function(room_id) { + $rootScope.events.rooms[room_id] = {}; + $rootScope.events.rooms[room_id].messages = []; + $rootScope.events.rooms[room_id].members = {}; + } var handleMessage = function(event, isLiveEvent) { if ("membership_target" in event.content) { @@ -118,6 +124,10 @@ angular.module('eventHandlerService', []) for (var i=0; i Date: Fri, 22 Aug 2014 10:50:38 +0100 Subject: [PATCH 29/65] Keep track of people's presence and query that when we update the members list. --- webclient/components/matrix/event-handler-service.js | 3 +++ webclient/room/room-controller.js | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index aa8867425..b5eb73d92 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -35,6 +35,8 @@ angular.module('eventHandlerService', []) $rootScope.events = { rooms: {}, // will contain roomId: { messages:[], members:{userid1: event} } }; + + $rootScope.presence = {}; var initRoom = function(room_id) { if (!(room_id in $rootScope.events.rooms)) { @@ -91,6 +93,7 @@ angular.module('eventHandlerService', []) }; var handlePresence = function(event, isLiveEvent) { + $rootScope.presence[event.content.user_id] = event; $rootScope.$broadcast(PRESENCE_EVENT, event, isLiveEvent); }; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index a0485e84e..e204a27e0 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -15,8 +15,8 @@ limitations under the License. */ angular.module('RoomController', ['ngSanitize', 'mUtilities']) -.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService', 'mFileUpload', 'mUtilities', - function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService, mFileUpload, mUtilities) { +.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService', 'mFileUpload', 'mUtilities', '$rootScope', + function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService, mFileUpload, mUtilities, $rootScope) { 'use strict'; var MESSAGES_PER_PAGINATION = 30; var THUMBNAIL_SIZE = 320; @@ -199,6 +199,10 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) ); }); */ + + if (chunk.target_user_id in $rootScope.presence) { + updatePresence($rootScope.presence[chunk.target_user_id]); + } } else { // selectively update membership else it will nuke the picture and displayname too :/ @@ -265,7 +269,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) $scope.onInit = function() { console.log("onInit"); - + // Does the room ID provided in the URL? var room_id_or_alias; if ($routeParams.room_id_or_alias) { From f3cea238b9c51861965d31cd9352153338d6705b Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 10:56:09 +0100 Subject: [PATCH 30/65] Check if the membership message was for the room we were in before updating the membership list --- webclient/room/room-controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index e204a27e0..58ba432ce 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -152,6 +152,8 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) }; var updateMemberList = function(chunk) { + if (chunk.room_id != $scope.room_id) return; + var isNewMember = !(chunk.target_user_id in $scope.members); if (isNewMember) { // FIXME: why are we copying these fields around inside chunk? From c7d7bc02543eb0d2b794d8d70b23e27616384222 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 12:06:27 +0100 Subject: [PATCH 31/65] Allow people to specify database location in database-save.sh --- database-save.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database-save.sh b/database-save.sh index c80f676f7..040c8a494 100755 --- a/database-save.sh +++ b/database-save.sh @@ -8,7 +8,7 @@ # # $ sqlite3 homeserver.db < table-save.sql -sqlite3 homeserver.db <<'EOF' >table-save.sql +sqlite3 "$1" <<'EOF' >table-save.sql .dump users .dump access_tokens .dump presence From c2e983b8db466a8f456c9a22d4438dec5060490d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 12:06:50 +0100 Subject: [PATCH 32/65] Bump versions to 0.0.1 --- VERSION | 1 + setup.py | 2 +- synapse/__init__.py | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..8acdd82b7 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/setup.py b/setup.py index fca3c7770..f01eec436 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(fname): setup( name="SynapseHomeServer", - version="0.1", + version="0.0.1", packages=find_packages(exclude=["tests"]), description="Reference Synapse Home Server", install_requires=[ diff --git a/synapse/__init__.py b/synapse/__init__.py index 1e7b2ab27..47fc1b2ea 100644 --- a/synapse/__init__.py +++ b/synapse/__init__.py @@ -15,3 +15,5 @@ """ This is a reference implementation of a synapse home server. """ + +__version__ = "0.0.1" From 5494815c701f806d012a60d5117519e4d2ef1b39 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 12:18:05 +0100 Subject: [PATCH 33/65] Add database-prepare-for-0.0.1.sh that should be run before starting a v0.0.1 homeserver. --- database-prepare-for-0.0.1.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 database-prepare-for-0.0.1.sh diff --git a/database-prepare-for-0.0.1.sh b/database-prepare-for-0.0.1.sh new file mode 100755 index 000000000..17c0c5f34 --- /dev/null +++ b/database-prepare-for-0.0.1.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# This is will prepare a synapse database for running with v0.0.1 of synapse. +# It will store all the user information, but will *delete* all messages and +# room data. + +cp "$1" "$1.bak" + +DUMP=$(sqlite3 "$1" << 'EOF' +.dump users +.dump access_tokens +.dump presence +.dump profiles +EOF +) + +rm "$1" + +sqlite3 "$1" <<< "$DUMP" From 1317afcb9a457a9b60dde95dc5d9aea9b9d80789 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 12:22:38 +0100 Subject: [PATCH 34/65] Add a database-prepare-for-0.0.1.sh --- database-prepare-for-0.0.1.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/database-prepare-for-0.0.1.sh b/database-prepare-for-0.0.1.sh index 17c0c5f34..43d759a5c 100755 --- a/database-prepare-for-0.0.1.sh +++ b/database-prepare-for-0.0.1.sh @@ -4,6 +4,8 @@ # It will store all the user information, but will *delete* all messages and # room data. +set -e + cp "$1" "$1.bak" DUMP=$(sqlite3 "$1" << 'EOF' From 808f663ed179dcb16a315810f7f0f8a7eec77e01 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 13:06:07 +0100 Subject: [PATCH 35/65] Don't return state event outlier's when paginating. --- synapse/storage/__init__.py | 7 ++++++- synapse/storage/schema/im.sql | 1 + synapse/storage/stream.py | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py index 773290692..d06033b98 100644 --- a/synapse/storage/__init__.py +++ b/synapse/storage/__init__.py @@ -105,6 +105,11 @@ class DataStore(RoomMemberStore, RoomStore, "processed": True, } + if hasattr(event, "outlier"): + vals["outlier"] = event.outlier + else: + vals["outlier"] = False + if backfilled: if not self.min_token_deferred.called: yield self.min_token_deferred @@ -123,7 +128,7 @@ class DataStore(RoomMemberStore, RoomStore, except: logger.exception( "Failed to persist, probably duplicate: %s", - event_id + event.event_id ) return diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql index ea04261ff..39a1ed703 100644 --- a/synapse/storage/schema/im.sql +++ b/synapse/storage/schema/im.sql @@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS events( content TEXT NOT NULL, unrecognized_keys TEXT, processed BOOL NOT NULL, + outlier BOOL NOT NULL, CONSTRAINT ev_uniq UNIQUE (event_id) ); diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py index 8bc502483..87ae961cc 100644 --- a/synapse/storage/stream.py +++ b/synapse/storage/stream.py @@ -177,6 +177,7 @@ class StreamStore(SQLBaseStore): "((room_id IN (%(current)s)) OR " "(event_id IN (%(invites)s))) " "AND e.stream_ordering > ? AND e.stream_ordering < ? " + "AND e.outlier = 0 " "ORDER BY stream_ordering ASC LIMIT %(limit)d " ) % { "current": current_room_membership_sql, @@ -224,7 +225,7 @@ class StreamStore(SQLBaseStore): sql = ( "SELECT * FROM events " - "WHERE room_id = ? AND %(bounds)s " + "WHERE outlier = 0 AND room_id = ? AND %(bounds)s " "ORDER BY topological_ordering %(order)s, stream_ordering %(order)s %(limit)s " ) % {"bounds": bounds, "order": order, "limit": limit_str} From e3c6c9057bd62b2a3c4ef2fd894b9c68c8e06bba Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 22 Aug 2014 13:40:37 +0100 Subject: [PATCH 36/65] Added initial swagger REST API spec. --- docs/client-server/swagger_matrix/api-docs | 38 + docs/client-server/swagger_matrix/events | 299 +++++++ docs/client-server/swagger_matrix/login | 102 +++ docs/client-server/swagger_matrix/presence | 164 ++++ docs/client-server/swagger_matrix/profile | 122 +++ .../client-server/swagger_matrix/registration | 75 ++ docs/client-server/swagger_matrix/rooms | 807 ++++++++++++++++++ 7 files changed, 1607 insertions(+) create mode 100644 docs/client-server/swagger_matrix/api-docs create mode 100644 docs/client-server/swagger_matrix/events create mode 100644 docs/client-server/swagger_matrix/login create mode 100644 docs/client-server/swagger_matrix/presence create mode 100644 docs/client-server/swagger_matrix/profile create mode 100644 docs/client-server/swagger_matrix/registration create mode 100644 docs/client-server/swagger_matrix/rooms diff --git a/docs/client-server/swagger_matrix/api-docs b/docs/client-server/swagger_matrix/api-docs new file mode 100644 index 000000000..d974dbb37 --- /dev/null +++ b/docs/client-server/swagger_matrix/api-docs @@ -0,0 +1,38 @@ +{ + "apiVersion": "1.0.0", + "swaggerVersion": "1.2", + "apis": [ + { + "path": "/login", + "description": "Login operations" + }, + { + "path": "/registration", + "description": "Registration operations" + }, + { + "path": "/rooms", + "description": "Room operations" + }, + { + "path": "/profile", + "description": "Profile operations" + }, + { + "path": "/presence", + "description": "Presence operations" + } + ], + "authorizations": { + "token": { + "scopes": [] + } + }, + "info": { + "title": "Matrix Client-Server API Reference", + "description": "This contains the client-server API for the reference implementation of the home server", + "termsOfServiceUrl": "http://matrix.org", + "license": "Apache 2.0", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" + } +} diff --git a/docs/client-server/swagger_matrix/events b/docs/client-server/swagger_matrix/events new file mode 100644 index 000000000..c9eb3f6ff --- /dev/null +++ b/docs/client-server/swagger_matrix/events @@ -0,0 +1,299 @@ +{ + "apiVersion": "1.0.0", + "swaggerVersion": "1.2", + "basePath": "http://petstore.swagger.wordnik.com/api", + "resourcePath": "/user", + "produces": [ + "application/json" + ], + "apis": [ + { + "path": "/user", + "operations": [ + { + "method": "POST", + "summary": "Create user", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "createUser", + "authorizations": { + "oauth2": [ + { + "scope": "test:anything", + "description": "anything" + } + ] + }, + "parameters": [ + { + "name": "body", + "description": "Created user object", + "required": true, + "type": "User", + "paramType": "body" + } + ] + } + ] + }, + { + "path": "/user/logout", + "operations": [ + { + "method": "GET", + "summary": "Logs out current logged in user session", + "notes": "", + "type": "void", + "nickname": "logoutUser", + "authorizations": {}, + "parameters": [] + } + ] + }, + { + "path": "/user/createWithArray", + "operations": [ + { + "method": "POST", + "summary": "Creates list of users with given input array", + "notes": "", + "type": "void", + "nickname": "createUsersWithArrayInput", + "authorizations": { + "oauth2": [ + { + "scope": "test:anything", + "description": "anything" + } + ] + }, + "parameters": [ + { + "name": "body", + "description": "List of user object", + "required": true, + "type": "array", + "items": { + "$ref": "User" + }, + "paramType": "body" + } + ] + } + ] + }, + { + "path": "/user/createWithList", + "operations": [ + { + "method": "POST", + "summary": "Creates list of users with given list input", + "notes": "", + "type": "void", + "nickname": "createUsersWithListInput", + "authorizations": { + "oauth2": [ + { + "scope": "test:anything", + "description": "anything" + } + ] + }, + "parameters": [ + { + "name": "body", + "description": "List of user object", + "required": true, + "type": "array", + "items": { + "$ref": "User" + }, + "paramType": "body" + } + ] + } + ] + }, + { + "path": "/user/{username}", + "operations": [ + { + "method": "PUT", + "summary": "Updated user", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "updateUser", + "authorizations": { + "oauth2": [ + { + "scope": "test:anything", + "description": "anything" + } + ] + }, + "parameters": [ + { + "name": "username", + "description": "name that need to be deleted", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "body", + "description": "Updated user object", + "required": true, + "type": "User", + "paramType": "body" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Invalid username supplied" + }, + { + "code": 404, + "message": "User not found" + } + ] + }, + { + "method": "DELETE", + "summary": "Delete user", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "deleteUser", + "authorizations": { + "oauth2": [ + { + "scope": "test:anything", + "description": "anything" + } + ] + }, + "parameters": [ + { + "name": "username", + "description": "The name that needs to be deleted", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Invalid username supplied" + }, + { + "code": 404, + "message": "User not found" + } + ] + }, + { + "method": "GET", + "summary": "Get user by user name", + "notes": "", + "type": "User", + "nickname": "getUserByName", + "authorizations": {}, + "parameters": [ + { + "name": "username", + "description": "The name that needs to be fetched. Use user1 for testing.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Invalid username supplied" + }, + { + "code": 404, + "message": "User not found" + } + ] + } + ] + }, + { + "path": "/user/login", + "operations": [ + { + "method": "GET", + "summary": "Logs user into the system", + "notes": "", + "type": "string", + "nickname": "loginUser", + "authorizations": {}, + "parameters": [ + { + "name": "username", + "description": "The user name for login", + "required": true, + "type": "string", + "paramType": "query" + }, + { + "name": "password", + "description": "The password for login in clear text", + "required": true, + "type": "string", + "paramType": "query" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Invalid username and password combination" + } + ] + } + ] + } + ], + "models": { + "User": { + "id": "User", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "firstName": { + "type": "string" + }, + "username": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status", + "enum": [ + "1-registered", + "2-active", + "3-closed" + ] + } + } + } + } +} \ No newline at end of file diff --git a/docs/client-server/swagger_matrix/login b/docs/client-server/swagger_matrix/login new file mode 100644 index 000000000..4410d3c88 --- /dev/null +++ b/docs/client-server/swagger_matrix/login @@ -0,0 +1,102 @@ +{ + "apiVersion": "1.0.0", + "apis": [ + { + "operations": [ + { + "method": "GET", + "nickname": "get_login_info", + "notes": "All login stages MUST be mentioned if there is >1 login type.", + "summary": "Get the login mechanism to use when logging in.", + "type": "LoginInfo" + }, + { + "method": "POST", + "nickname": "submit_login", + "notes": "If this is part of a multi-stage login, there MUST be a 'session' key.", + "parameters": [ + { + "description": "A login submission", + "name": "body", + "paramType": "body", + "required": true, + "type": "LoginSubmission" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Bad login type" + }, + { + "code": 400, + "message": "Missing JSON keys" + } + ], + "summary": "Submit a login action.", + "type": "LoginResult" + } + ], + "path": "/login" + } + ], + "basePath": "http://localhost:8080/matrix/client/api/v1", + "consumes": [ + "application/json" + ], + "models": { + "LoginInfo": { + "id": "LoginInfo", + "properties": { + "stages": { + "description": "Multi-stage login only: An array of all the login types required to login.", + "format": "string", + "type": "array" + }, + "type": { + "description": "The login type that must be used when logging in.", + "type": "string" + } + } + }, + "LoginResult": { + "id": "LoginResult", + "properties": { + "access_token": { + "description": "The access token for this user's login if this is the final stage of the login process.", + "type": "string" + }, + "next": { + "description": "Multi-stage login only: The next login type to submit.", + "type": "string" + }, + "session": { + "description": "Multi-stage login only: The session token to send when submitting the next login type.", + "type": "string" + } + } + }, + "LoginSubmission": { + "id": "LoginSubmission", + "properties": { + "type": { + "description": "The type of login being submitted.", + "type": "string" + }, + "session": { + "description": "Multi-stage login only: The session token from an earlier login stage.", + "type": "string" + }, + "_login_type_defined_keys_": { + "description": "Keys as defined by the specified login type, e.g. \"user\", \"password\"" + } + } + } + }, + "produces": [ + "application/json" + ], + "resourcePath": "/login", + "swaggerVersion": "1.2" +} + diff --git a/docs/client-server/swagger_matrix/presence b/docs/client-server/swagger_matrix/presence new file mode 100644 index 000000000..ee9deb12f --- /dev/null +++ b/docs/client-server/swagger_matrix/presence @@ -0,0 +1,164 @@ +{ + "apiVersion": "1.0.0", + "swaggerVersion": "1.2", + "basePath": "http://localhost:8080/matrix/client/api/v1", + "resourcePath": "/presence", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "apis": [ + { + "path": "/presence/{userId}/status", + "operations": [ + { + "method": "PUT", + "summary": "Update this user's presence state.", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "update_presence", + "parameters": [ + { + "name": "body", + "description": "The new presence state", + "required": true, + "type": "PresenceUpdate", + "paramType": "body" + }, + { + "name": "userId", + "description": "The user whose presence to set.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + }, + { + "method": "GET", + "summary": "Get this user's presence state.", + "notes": "Get this user's presence state.", + "type": "PresenceUpdate", + "nickname": "get_presence", + "parameters": [ + { + "name": "userId", + "description": "The user whose presence to get.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + } + ] + }, + { + "path": "/presence_list/{userId}", + "operations": [ + { + "method": "GET", + "summary": "Retrieve a list of presences for all of this user's friends.", + "notes": "", + "type": "array", + "items": { + "$ref": "Presence" + }, + "nickname": "get_presence_list", + "parameters": [ + { + "name": "userId", + "description": "The user whose presence list to get.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + }, + { + "method": "POST", + "summary": "Add or remove users from this presence list.", + "notes": "Add or remove users from this presence list.", + "type": "void", + "nickname": "modify_presence_list", + "parameters": [ + { + "name": "userId", + "description": "The user whose presence list is being modified.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "body", + "description": "The modifications to make to this presence list.", + "required": true, + "type": "PresenceListModifications", + "paramType": "body" + } + ] + } + ] + } + ], + "models": { + "PresenceUpdate": { + "id": "PresenceUpdate", + "properties": { + "state": { + "type": "string", + "description": "Enum: The presence state.", + "enum": [ + "offline", + "unavailable", + "online", + "free_for_chat" + ] + }, + "status_msg": { + "type": "string", + "description": "The user-defined message associated with this presence state." + } + }, + "subTypes": [ + "Presence" + ] + }, + "Presence": { + "id": "Presence", + "properties": { + "mtime_age": { + "type": "integer", + "format": "int64", + "description": "The last time this user's presence state changed, in milliseconds." + }, + "user_id": { + "type": "string", + "description": "The fully qualified user ID" + } + } + }, + "PresenceListModifications": { + "id": "PresenceListModifications", + "properties": { + "invite": { + "type": "array", + "description": "A list of user IDs to add to the list.", + "items": { + "type": "string", + "description": "A fully qualified user ID." + } + }, + "drop": { + "type": "array", + "description": "A list of user IDs to remove from the list.", + "items": { + "type": "string", + "description": "A fully qualified user ID." + } + } + } + } + } +} diff --git a/docs/client-server/swagger_matrix/profile b/docs/client-server/swagger_matrix/profile new file mode 100644 index 000000000..1ebde62e2 --- /dev/null +++ b/docs/client-server/swagger_matrix/profile @@ -0,0 +1,122 @@ +{ + "apiVersion": "1.0.0", + "swaggerVersion": "1.2", + "basePath": "http://localhost:8080/matrix/client/api/v1", + "resourcePath": "/profile", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "apis": [ + { + "path": "/profile/{userId}/displayname", + "operations": [ + { + "method": "PUT", + "summary": "Set a display name.", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "set_display_name", + "parameters": [ + { + "name": "body", + "description": "The new display name for this user.", + "required": true, + "type": "DisplayName", + "paramType": "body" + }, + { + "name": "userId", + "description": "The user whose display name to set.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + }, + { + "method": "GET", + "summary": "Get a display name.", + "notes": "This can be done by anyone.", + "type": "DisplayName", + "nickname": "get_display_name", + "parameters": [ + { + "name": "userId", + "description": "The user whose display name to get.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + } + ] + }, + { + "path": "/profile/{userId}/avatar_url", + "operations": [ + { + "method": "PUT", + "summary": "Set an avatar URL.", + "notes": "This can only be done by the logged in user.", + "type": "void", + "nickname": "set_avatar_url", + "parameters": [ + { + "name": "body", + "description": "The new avatar url for this user.", + "required": true, + "type": "AvatarUrl", + "paramType": "body" + }, + { + "name": "userId", + "description": "The user whose avatar url to set.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + }, + { + "method": "GET", + "summary": "Get an avatar url.", + "notes": "This can be done by anyone.", + "type": "AvatarUrl", + "nickname": "get_avatar_url", + "parameters": [ + { + "name": "userId", + "description": "The user whose avatar url to get.", + "required": true, + "type": "string", + "paramType": "path" + } + ] + } + ] + } + ], + "models": { + "DisplayName": { + "id": "DisplayName", + "properties": { + "displayname": { + "type": "string", + "description": "The textual display name" + } + } + }, + "AvatarUrl": { + "id": "AvatarUrl", + "properties": { + "avatar_url": { + "type": "string", + "description": "A url to an image representing an avatar." + } + } + } + } +} diff --git a/docs/client-server/swagger_matrix/registration b/docs/client-server/swagger_matrix/registration new file mode 100644 index 000000000..ccd542d11 --- /dev/null +++ b/docs/client-server/swagger_matrix/registration @@ -0,0 +1,75 @@ +{ + "apiVersion": "1.0.0", + "apis": [ + { + "operations": [ + { + "method": "POST", + "nickname": "register", + "notes": "Volatile: This API is likely to change.", + "parameters": [ + { + "description": "A registration request", + "name": "body", + "paramType": "body", + "required": true, + "type": "RegistrationRequest" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "No JSON object." + }, + { + "code": 400, + "message": "User ID must only contain characters which do not require url encoding." + }, + { + "code": 400, + "message": "User ID already taken." + } + ], + "summary": "Register with the home server.", + "type": "RegistrationResponse" + } + ], + "path": "/register" + } + ], + "basePath": "http://localhost:8080/matrix/client/api/v1", + "consumes": [ + "application/json" + ], + "models": { + "RegistrationResponse": { + "id": "RegistrationResponse", + "properties": { + "access_token": { + "description": "The access token for this user.", + "type": "string" + }, + "user_id": { + "description": "The fully-qualified user ID.", + "type": "string" + } + } + }, + "RegistrationRequest": { + "id": "RegistrationRequest", + "properties": { + "user_id": { + "description": "The desired user ID. If not specified, a random user ID will be allocated.", + "type": "string", + "required": false + } + } + } + }, + "produces": [ + "application/json" + ], + "resourcePath": "/register", + "swaggerVersion": "1.2" +} + diff --git a/docs/client-server/swagger_matrix/rooms b/docs/client-server/swagger_matrix/rooms new file mode 100644 index 000000000..47a888724 --- /dev/null +++ b/docs/client-server/swagger_matrix/rooms @@ -0,0 +1,807 @@ +{ + "apiVersion": "1.0.0", + "swaggerVersion": "1.2", + "basePath": "http://localhost:8080/matrix/client/api/v1", + "resourcePath": "/rooms", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "authorizations": { + "token": [] + }, + "apis": [ + { + "path": "/rooms/{roomId}/messages/{userId}/{messageId}", + "operations": [ + { + "method": "PUT", + "summary": "Send a message in this room.", + "notes": "Send a message in this room.", + "type": "void", + "nickname": "send_message", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "description": "The message contents", + "required": true, + "type": "Message", + "paramType": "body" + }, + { + "name": "roomId", + "description": "The room to send the message in.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "userId", + "description": "The fully qualified message sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "messageId", + "description": "A message ID which is unique for each room and user.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 403, + "message": "Must send messages as yourself." + } + ] + }, + { + "method": "GET", + "summary": "Get a message from this room.", + "notes": "Get a message from this room.", + "type": "Message", + "nickname": "get_message", + "parameters": [ + { + "name": "roomId", + "description": "The room to send the message in.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "userId", + "description": "The fully qualified message sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "messageId", + "description": "A message ID which is unique for each room and user.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 404, + "message": "Message not found." + } + ] + } + ] + }, + { + "path": "/rooms/{roomId}/topic", + "operations": [ + { + "method": "PUT", + "summary": "Set the topic for this room.", + "notes": "Set the topic for this room.", + "type": "void", + "nickname": "set_topic", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "description": "The topic contents", + "required": true, + "type": "Topic", + "paramType": "body" + }, + { + "name": "roomId", + "description": "The room to set the topic in.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 403, + "message": "Must send messages as yourself." + } + ] + }, + { + "method": "GET", + "summary": "Get the topic for this room.", + "notes": "Get the topic for this room.", + "type": "Topic", + "nickname": "get_topic", + "parameters": [ + { + "name": "roomId", + "description": "The room to get topic in.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 404, + "message": "Topic not found." + } + ] + } + ] + }, + { + "path": "/rooms/{roomId}/messages/{msgSenderId}/{messageId}/feedback/{senderId}/{feedbackType}", + "operations": [ + { + "method": "PUT", + "summary": "Send feedback to a message.", + "notes": "Send feedback to a message.", + "type": "void", + "nickname": "send_feedback", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "description": "The feedback contents", + "required": true, + "type": "Feedback", + "paramType": "body" + }, + { + "name": "roomId", + "description": "The room to send the feedback in.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "msgSenderId", + "description": "The fully qualified message sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "messageId", + "description": "A message ID which is unique for each room and user.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "senderId", + "description": "The fully qualified feedback sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "feedbackType", + "description": "The type of feedback being sent.", + "required": true, + "type": "string", + "paramType": "path", + "enum": [ + "d", + "r" + ] + } + ], + "responseMessages": [ + { + "code": 403, + "message": "Must send feedback as yourself." + }, + { + "code": 400, + "message": "Bad feedback type." + } + ] + }, + { + "method": "GET", + "summary": "Get feedback for a message.", + "notes": "Get feedback for a message.", + "type": "Feedback", + "nickname": "get_feedback", + "parameters": [ + { + "name": "roomId", + "description": "The room to send the message in.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "msgSenderId", + "description": "The fully qualified message sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "messageId", + "description": "A message ID which is unique for each room and user.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "senderId", + "description": "The fully qualified feedback sender's user ID.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "feedbackType", + "description": "Enum: The type of feedback being sent.", + "required": true, + "type": "string", + "paramType": "path", + "enum": [ + "d", + "r" + ] + } + ], + "responseMessages": [ + { + "code": 404, + "message": "Feedback not found." + } + ] + } + ] + }, + { + "path": "/rooms/{roomId}/members/{userId}/state", + "operations": [ + { + "method": "PUT", + "summary": "Change the membership state for a user in a room.", + "notes": "Change the membership state for a user in a room.", + "type": "void", + "nickname": "set_membership", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "description": "The new membership state", + "required": true, + "type": "Member", + "paramType": "body" + }, + { + "name": "userId", + "description": "The user whose membership is being changed.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "roomId", + "description": "The room which has this user.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "No membership key." + }, + { + "code": 400, + "message": "Bad membership value." + }, + { + "code": 403, + "message": "When inviting: You are not in the room." + }, + { + "code": 403, + "message": "When inviting: is already in the room." + }, + { + "code": 403, + "message": "When joining: Cannot force another user to join." + }, + { + "code": 403, + "message": "When joining: You are not invited to this room." + } + ] + }, + { + "method": "GET", + "summary": "Get the membership state of a user in a room.", + "notes": "Get the membership state of a user in a room.", + "type": "Member", + "nickname": "get_membership", + "parameters": [ + { + "name": "userId", + "description": "The user whose membership state you want to get.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "roomId", + "description": "The room which has this user.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 404, + "message": "Member not found." + } + ] + }, + { + "method": "DELETE", + "summary": "Leave a room.", + "notes": "Leave a room.", + "type": "void", + "nickname": "remove_membership", + "parameters": [ + { + "name": "userId", + "description": "The user who is leaving.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "roomId", + "description": "The room which has this user.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 403, + "message": "You are not in the room." + }, + { + "code": 403, + "message": "Cannot force another user to leave." + } + ] + } + ] + }, + { + "path": "/join/{roomAlias}", + "operations": [ + { + "method": "PUT", + "summary": "Join a room via a room alias.", + "notes": "Join a room via a room alias.", + "type": "RoomInfo", + "nickname": "join_room_via_alias", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "roomAlias", + "description": "The room alias to join.", + "required": true, + "type": "string", + "paramType": "path" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Bad room alias." + } + ] + } + ] + }, + { + "path": "/rooms", + "operations": [ + { + "method": "POST", + "summary": "Create a room.", + "notes": "Create a room.", + "type": "RoomInfo", + "nickname": "create_room", + "consumes": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "description": "The desired configuration for the room.", + "required": true, + "type": "RoomConfig", + "paramType": "body" + } + ], + "responseMessages": [ + { + "code": 400, + "message": "Body must be JSON." + }, + { + "code": 400, + "message": "Room alias already taken." + } + ] + } + ] + }, + { + "path": "/rooms/{roomId}/messages/list", + "operations": [ + { + "method": "GET", + "summary": "Get a list of messages for this room.", + "notes": "Get a list of messages for this room.", + "type": "MessagePaginationChunk", + "nickname": "get_messages", + "parameters": [ + { + "name": "roomId", + "description": "The room to get messages in.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "from", + "description": "The token to start getting results from.", + "required": false, + "type": "string", + "paramType": "query" + }, + { + "name": "to", + "description": "The token to stop getting results at.", + "required": false, + "type": "string", + "paramType": "query" + }, + { + "name": "limit", + "description": "The maximum number of messages to return.", + "required": false, + "type": "integer", + "paramType": "query" + } + ] + } + ] + }, + { + "path": "/rooms/{roomId}/members/list", + "operations": [ + { + "method": "GET", + "summary": "Get a list of members for this room.", + "notes": "Get a list of members for this room.", + "type": "MemberPaginationChunk", + "nickname": "get_members", + "parameters": [ + { + "name": "roomId", + "description": "The room to get a list of members from.", + "required": true, + "type": "string", + "paramType": "path" + }, + { + "name": "from", + "description": "The token to start getting results from.", + "required": false, + "type": "string", + "paramType": "query" + }, + { + "name": "to", + "description": "The token to stop getting results at.", + "required": false, + "type": "string", + "paramType": "query" + }, + { + "name": "limit", + "description": "The maximum number of members to return.", + "required": false, + "type": "integer", + "paramType": "query" + } + ] + } + ] + } + ], + "models": { + "Topic": { + "id": "Topic", + "properties": { + "topic": { + "type": "string", + "description": "The topic text" + } + } + }, + "Message": { + "id": "Message", + "properties": { + "msgtype": { + "type": "string", + "description": "The type of message being sent, e.g. \"m.text\"", + "required": true + }, + "_msgtype_defined_keys_": { + "description": "Additional keys as defined by the msgtype, e.g. \"body\"" + } + } + }, + "Feedback": { + "id": "Feedback", + "properties": { + } + }, + "Member": { + "id": "Member", + "properties": { + "membership": { + "type": "string", + "description": "Enum: The membership state of this member.", + "enum": [ + "invite", + "join", + "leave", + "knock" + ] + } + } + }, + "RoomInfo": { + "id": "RoomInfo", + "properties": { + "room_id": { + "type": "string", + "description": "The allocated room ID.", + "required": true + }, + "room_alias": { + "type": "string", + "description": "The alias for the room.", + "required": false + } + } + }, + "RoomConfig": { + "id": "RoomConfig", + "properties": { + "visibility": { + "type": "string", + "description": "Enum: The room visibility.", + "required": false, + "enum": [ + "public", + "private" + ] + }, + "room_alias_name": { + "type": "string", + "description": "The alias to give the new room.", + "required": false + } + } + }, + "PaginationRequest": { + "id": "PaginationRequest", + "properties": { + "from": { + "type": "string", + "description": "The token to start getting results from." + }, + "to": { + "type": "string", + "description": "The token to stop getting results at." + }, + "limit": { + "type": "integer", + "description": "The maximum number of entries to return." + } + } + }, + "PaginationChunk": { + "id": "PaginationChunk", + "properties": { + "start": { + "type": "string", + "description": "A token which correlates to the first value in \"chunk\" for paginating.", + "required": true + }, + "end": { + "type": "string", + "description": "A token which correlates to the last value in \"chunk\" for paginating.", + "required": true + } + }, + "subTypes": [ + "MessagePaginationChunk" + ] + }, + "MessagePaginationChunk": { + "id": "MessagePaginationChunk", + "properties": { + "chunk": { + "type": "array", + "description": "A list of message events.", + "items": { + "$ref": "MessageEvent" + }, + "required": true + } + } + }, + "MemberPaginationChunk": { + "id": "MemberPaginationChunk", + "properties": { + "chunk": { + "type": "array", + "description": "A list of member events.", + "items": { + "$ref": "MemberEvent" + }, + "required": true + } + } + }, + "Event": { + "id": "Event", + "properties": { + "event_id": { + "type": "string", + "description": "An ID which uniquely identifies this event.", + "required": true + }, + "room_id": { + "type": "string", + "description": "The room in which this event occurred.", + "required": true + } + }, + "subTypes": [ + "MessageEvent" + ] + }, + "MessageEvent": { + "id": "MessageEvent", + "properties": { + "content": { + "type": "Message" + } + } + }, + "MemberEvent": { + "id": "MemberEvent", + "properties": { + "content": { + "type": "Member" + } + } + }, + "Tag": { + "id": "Tag", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "Pet": { + "id": "Pet", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "description": "unique identifier for the pet", + "minimum": "0.0", + "maximum": "100.0" + }, + "category": { + "$ref": "Category" + }, + "name": { + "type": "string" + }, + "photoUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + } + }, + "Category": { + "id": "Category", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "pet": { + "$ref": "Pet" + } + } + } + } +} From 87b315ce21deca1db67917acd8b40a20ccdd8c2c Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 13:52:38 +0100 Subject: [PATCH 37/65] Add CHANGES and UPGRADE files. --- CHANGES | 20 ++++++++++++++++++++ UPGRADE | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 CHANGES create mode 100644 UPGRADE diff --git a/CHANGES b/CHANGES new file mode 100644 index 000000000..055f8bc01 --- /dev/null +++ b/CHANGES @@ -0,0 +1,20 @@ +Changes in synapse 0.0.1 +======================= +Homeserver: + * Completely change the database schema to support generic event types. + * Improve presence reliability. + * Improve reliability of joining remote rooms. + * Fix bug where room join events were duplicated. + * Improve initial sync API to return more information to the client. + * Stop generating fake messages for room membership events. + +Webclient: + * Add tab completion of names. + * Add ability to upload and send images. + * Add profile pages. + * Improve CSS layout of room. + * Disambiguate identical display names. + * Don't get remote users display names and avatars individually. + * Use the new initial sync API to reduce number of round trips to the homeserver. + * Change url scheme to use room aliases instead of room ids where known. + * Increase longpoll timeout. diff --git a/UPGRADE b/UPGRADE new file mode 100644 index 000000000..95f933597 --- /dev/null +++ b/UPGRADE @@ -0,0 +1,23 @@ +Upgrading to v0.0.1 +================== +This release completely changes the database schema and so requires upgrading +it before starting the new version of the homeserver. + +The script "database-prepare-for-0.0.1.sh" should be used to upgrade the +database. This will save all user information, such as logins and profiles, +but will otherwise purge the database. This includes messages, which +rooms the home server was a member of and room alias mappings. + +Before running the command the homeserver should be first completely +shutdown. To run it, simply specify the location of the database, e.g.: + + ./database-prepare-for-0.0.1.sh "homeserver.db" + +Once this has successfully completed it will be safe to restart the +homeserver. You may notice that the homeserver takes a few seconds longer to +restart than usual as it reinitializes the database. + +On startup of the new version, users can either rejoin remote rooms using room +aliases or by being reinvited. Alternatively, if any other homeserver sends a +message to a room that the homeserver was previously in the local HS will +automatically rejoin the room. From 7d3a841a83d7e6b84e2972a4a5d4e8e41cfa738d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 15:09:03 +0100 Subject: [PATCH 38/65] Add a missing '=' --- UPGRADE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UPGRADE b/UPGRADE index 95f933597..2e75d77bc 100644 --- a/UPGRADE +++ b/UPGRADE @@ -1,5 +1,6 @@ Upgrading to v0.0.1 -================== +=================== + This release completely changes the database schema and so requires upgrading it before starting the new version of the homeserver. From a0e114fe648f2c74b56bdb82687b3c86c86d34d4 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 15:20:33 +0100 Subject: [PATCH 39/65] Rename files to .rst for consistency. --- CHANGES => CHANGES.rst | 0 UPGRADE => UPGRADE.rst | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename CHANGES => CHANGES.rst (100%) rename UPGRADE => UPGRADE.rst (100%) diff --git a/CHANGES b/CHANGES.rst similarity index 100% rename from CHANGES rename to CHANGES.rst diff --git a/UPGRADE b/UPGRADE.rst similarity index 100% rename from UPGRADE rename to UPGRADE.rst From f81692dab4695afb00e1197b69b87974d22ecefc Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 15:20:53 +0100 Subject: [PATCH 40/65] Update the README.rst to refer people to UPGRADE.rst --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 50440f57f..0e85c91af 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,12 @@ To get up and running: up port 8080 and run ``python synapse/app/homeserver.py --host machine.my.domain.name``. Then come join ``#matrix:matrix.org`` and say hi! :) + +Upgrading an existing homeserver +================================ + +Before upgrading an existing homeserver to a new version, please refer to +UPGRADE.rst for any additional instructions. About Matrix ============ From 9521e6758f6ca870f377f4b757b50c6a6826f328 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 15:23:02 +0100 Subject: [PATCH 41/65] Move the 'Upgrade' section to just below the 'Installation' section --- README.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 0e85c91af..069f37ec0 100644 --- a/README.rst +++ b/README.rst @@ -34,12 +34,7 @@ To get up and running: machine.my.domain.name``. Then come join ``#matrix:matrix.org`` and say hi! :) -Upgrading an existing homeserver -================================ - -Before upgrading an existing homeserver to a new version, please refer to -UPGRADE.rst for any additional instructions. - + About Matrix ============ @@ -149,6 +144,13 @@ This should end with a 'PASSED' result:: PASSED (successes=143) +Upgrading an existing homeserver +================================ + +Before upgrading an existing homeserver to a new version, please refer to +UPGRADE.rst for any additional instructions. + + Setting up Federation ===================== From a96076f335679fc67790bf9bf0f37577d7aa1abc Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 22 Aug 2014 16:13:09 +0100 Subject: [PATCH 42/65] add 0.0.0 into the changelog, and add dates --- CHANGES.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 055f8bc01..86e78c288 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,6 @@ -Changes in synapse 0.0.1 -======================= +Changes in synapse 0.0.1 (2014-08-22) +===================================== + Homeserver: * Completely change the database schema to support generic event types. * Improve presence reliability. @@ -18,3 +19,8 @@ Webclient: * Use the new initial sync API to reduce number of round trips to the homeserver. * Change url scheme to use room aliases instead of room ids where known. * Increase longpoll timeout. + +Changes in synapse 0.0.0 (2014-08-13) +===================================== + + * Initial alpha release From f40844def237f514e2a7398e8c7ed65b45277b0d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 Aug 2014 16:20:53 +0100 Subject: [PATCH 43/65] avatar url --- webclient/rooms/rooms.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html index ba3b7d8ba..5e422bf83 100644 --- a/webclient/rooms/rooms.html +++ b/webclient/rooms/rooms.html @@ -19,7 +19,7 @@ -->
- +
- +
- -
- - +
+
{{ config.displayName }}
+
{{ config.user_id }}
- -
-
- - -
-
- -
- -
-
- - - {{ emailFeedback }} -
-
- Enter validation token for {{ linkedEmails.emailBeingAuthed }}: -
- - -
- Linked emails: - - - - -
{{address}}
-
-

My rooms

diff --git a/webclient/settings/settings-controller.js b/webclient/settings/settings-controller.js new file mode 100644 index 000000000..5d3f7cb2b --- /dev/null +++ b/webclient/settings/settings-controller.js @@ -0,0 +1,146 @@ +/* +Copyright 2014 matrix.org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +angular.module('SettingsController', ['matrixService', 'mFileUpload']) +.controller('SettingsController', ['$scope', 'matrixService', 'mFileUpload', + function($scope, matrixService, mFileUpload) { + $scope.config = matrixService.config(); + + $scope.profile = { + displayName: $scope.config.displayName, + avatarUrl: $scope.config.avatarUrl + }; + + $scope.$watch("profile.avatarFile", function(newValue, oldValue) { + if ($scope.profile.avatarFile) { + console.log("Uploading new avatar file..."); + mFileUpload.uploadFile($scope.profile.avatarFile).then( + function(url) { + $scope.profile.avatarUrl = url; + }, + function(error) { + $scope.feedback = "Can't upload image"; + } + ); + } + }); + + $scope.saveProfile = function() { + if ($scope.profile.displayName !== $scope.config.displayName) { + setDisplayName($scope.profile.displayName); + } + if ($scope.profile.avatarUrl !== $scope.config.avatarUrl) { + setAvatar($scope.profile.avatarUrl); + } + }; + + var setDisplayName = function(displayName) { + matrixService.setDisplayName(displayName).then( + function(response) { + $scope.feedback = "Updated display name."; + + var config = matrixService.config(); + config.displayName = displayName; + matrixService.setConfig(config); + matrixService.saveConfig(); + }, + function(error) { + $scope.feedback = "Can't update display name: " + error.data; + } + ); + }; + + var setAvatar = function(avatarURL) { + console.log("Updating avatar to " + avatarURL); + matrixService.setProfilePictureUrl(avatarURL).then( + function(response) { + console.log("Updated avatar"); + $scope.feedback = "Updated avatar."; + + var config = matrixService.config(); + config.avatarUrl = avatarURL; + matrixService.setConfig(config); + matrixService.saveConfig(); + }, + function(error) { + $scope.feedback = "Can't update avatar: " + error.data; + } + ); + }; + + $scope.linkedEmails = { + linkNewEmail: "", // the email entry box + emailBeingAuthed: undefined, // to populate verification text + authTokenId: undefined, // the token id from the IS + emailCode: "", // the code entry box + linkedEmailList: matrixService.config().emailList // linked email list + }; + + $scope.linkEmail = function(email) { + matrixService.linkEmail(email).then( + function(response) { + if (response.data.success === true) { + $scope.linkedEmails.authTokenId = response.data.tokenId; + $scope.emailFeedback = "You have been sent an email."; + $scope.linkedEmails.emailBeingAuthed = email; + } + else { + $scope.emailFeedback = "Failed to send email."; + } + }, + function(error) { + $scope.emailFeedback = "Can't send email: " + error.data; + } + ); + }; + + $scope.submitEmailCode = function(code) { + var tokenId = $scope.linkedEmails.authTokenId; + if (tokenId === undefined) { + $scope.emailFeedback = "You have not requested a code with this email."; + return; + } + matrixService.authEmail(matrixService.config().user_id, tokenId, code).then( + function(response) { + if ("success" in response.data && response.data.success === false) { + $scope.emailFeedback = "Failed to authenticate email."; + return; + } + var config = matrixService.config(); + var emailList = {}; + if ("emailList" in config) { + emailList = config.emailList; + } + emailList[response.address] = response; + // save the new email list + config.emailList = emailList; + matrixService.setConfig(config); + matrixService.saveConfig(); + // invalidate the email being authed and update UI. + $scope.linkedEmails.emailBeingAuthed = undefined; + $scope.emailFeedback = ""; + $scope.linkedEmails.linkedEmailList = emailList; + $scope.linkedEmails.linkNewEmail = ""; + $scope.linkedEmails.emailCode = ""; + }, + function(reason) { + $scope.emailFeedback = "Failed to auth email: " + reason; + } + ); + }; +}]); \ No newline at end of file diff --git a/webclient/settings/settings.html b/webclient/settings/settings.html new file mode 100644 index 000000000..63b12c8f5 --- /dev/null +++ b/webclient/settings/settings.html @@ -0,0 +1,73 @@ +
+ +
+
+ +

Me

+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+ +
+
+
+
+ +

Linked emails

+
+
+ + + {{ emailFeedback }} +
+
+ Enter validation token for {{ linkedEmails.emailBeingAuthed }}: +
+ + +
+ + + + +
{{address}}
+
+
+ +

Configuration

+
+
Home server: {{ config.homeserver }}
+
User ID: {{ config.user_id }}
+
Access token: {{ config.access_token }}
+
+
+ +
+
+
+
+ + {{ feedback }} + +
+
+
From 61cac4df6e40eb19a874e35e8ed79efc7ec1fb38 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 17:59:48 +0200 Subject: [PATCH 45/65] renamed rooms to home --- webclient/{rooms => home}/rooms-controller.js | 0 webclient/{rooms => home}/rooms.html | 0 webclient/nbproject/licenseheader.txt | 16 ++++++++++++++++ 3 files changed, 16 insertions(+) rename webclient/{rooms => home}/rooms-controller.js (100%) rename webclient/{rooms => home}/rooms.html (100%) create mode 100644 webclient/nbproject/licenseheader.txt diff --git a/webclient/rooms/rooms-controller.js b/webclient/home/rooms-controller.js similarity index 100% rename from webclient/rooms/rooms-controller.js rename to webclient/home/rooms-controller.js diff --git a/webclient/rooms/rooms.html b/webclient/home/rooms.html similarity index 100% rename from webclient/rooms/rooms.html rename to webclient/home/rooms.html diff --git a/webclient/nbproject/licenseheader.txt b/webclient/nbproject/licenseheader.txt new file mode 100644 index 000000000..36e6dcf18 --- /dev/null +++ b/webclient/nbproject/licenseheader.txt @@ -0,0 +1,16 @@ +/* +Copyright 2014 matrix.org + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + From 4c7df5236098006b62a71e2e06a918a812d5dd3f Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 18:01:08 +0200 Subject: [PATCH 46/65] renamed rooms to home - renamed files --- ...rooms-controller.js => home-controller.js} | 0 webclient/home/{rooms.html => home.html} | 0 .../nbproject/private/private.properties | 1 + webclient/nbproject/private/private.xml | 22 +++++++++++++++++++ 4 files changed, 23 insertions(+) rename webclient/home/{rooms-controller.js => home-controller.js} (100%) rename webclient/home/{rooms.html => home.html} (100%) create mode 100644 webclient/nbproject/private/private.properties create mode 100644 webclient/nbproject/private/private.xml diff --git a/webclient/home/rooms-controller.js b/webclient/home/home-controller.js similarity index 100% rename from webclient/home/rooms-controller.js rename to webclient/home/home-controller.js diff --git a/webclient/home/rooms.html b/webclient/home/home.html similarity index 100% rename from webclient/home/rooms.html rename to webclient/home/home.html diff --git a/webclient/nbproject/private/private.properties b/webclient/nbproject/private/private.properties new file mode 100644 index 000000000..3b1a22e6c --- /dev/null +++ b/webclient/nbproject/private/private.properties @@ -0,0 +1 @@ +browser=Chrome.INTEGRATED diff --git a/webclient/nbproject/private/private.xml b/webclient/nbproject/private/private.xml new file mode 100644 index 000000000..75bc70106 --- /dev/null +++ b/webclient/nbproject/private/private.xml @@ -0,0 +1,22 @@ + + + + + + file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/matrix-service.js + file:/Users/manu/UC/matrix/github/synapse/webclient/app.css + file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/event-handler-service.js + file:/Users/manu/UC/matrix/github/synapse/webclient/app-controller.js + file:/Users/manu/UC/matrix/github/synapse/webclient/room/room.html + file:/Users/manu/UC/matrix/github/synapse/webclient/login/login.html + file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/event-stream-service.js + file:/Users/manu/UC/matrix/github/synapse/webclient/components/fileUpload/file-upload-service.js + file:/Users/manu/UC/matrix/github/synapse/webclient/index.html + file:/Users/manu/UC/matrix/github/synapse/webclient/room/room-controller.js + file:/Users/manu/UC/matrix/github/synapse/webclient/rooms/rooms.html + file:/Users/manu/UC/matrix/github/synapse/webclient/app.js + file:/Users/manu/UC/matrix/github/synapse/webclient/rooms/rooms-controller.js + file:/Users/manu/UC/matrix/github/synapse/webclient/components/fileInput/file-input-directive.js + + + From de0706493affc7d8aa6d294fbd531fa0b75ef49c Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 18:08:03 +0200 Subject: [PATCH 47/65] Use /home everywhere --- webclient/app.js | 10 +++++----- webclient/home/home-controller.js | 4 ++-- webclient/home/home.html | 2 +- webclient/index.html | 2 +- webclient/login/login-controller.js | 4 ++-- webclient/room/room-controller.js | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/webclient/app.js b/webclient/app.js index f666a63bf..e5d851394 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -19,7 +19,7 @@ var matrixWebClient = angular.module('matrixWebClient', [ 'MatrixWebClientController', 'LoginController', 'RoomController', - 'RoomsController', + 'HomeController', 'SettingsController', 'UserController', 'matrixService', @@ -45,9 +45,9 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'room/room.html', controller: 'RoomController' }). - when('/rooms', { - templateUrl: 'rooms/rooms.html', - controller: 'RoomsController' + when('/home', { + templateUrl: 'home/home.html', + controller: 'HomeController' }). when('/settings', { templateUrl: 'settings/settings.html', @@ -58,7 +58,7 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', controller: 'UserController' }). otherwise({ - redirectTo: '/rooms' + redirectTo: '/home' }); $provide.factory('AccessTokenInterceptor', ['$q', '$rootScope', diff --git a/webclient/home/home-controller.js b/webclient/home/home-controller.js index d891558be..a3d730831 100644 --- a/webclient/home/home-controller.js +++ b/webclient/home/home-controller.js @@ -16,8 +16,8 @@ limitations under the License. 'use strict'; -angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) -.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', +angular.module('HomeController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) +.controller('HomeController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', function($scope, $location, matrixService, mFileUpload, eventHandlerService, eventStreamService) { $scope.config = matrixService.config(); diff --git a/webclient/home/home.html b/webclient/home/home.html index 2e25c0f08..4818d414b 100644 --- a/webclient/home/home.html +++ b/webclient/home/home.html @@ -1,4 +1,4 @@ -
+
diff --git a/webclient/index.html b/webclient/index.html index 95f682580..ed1d9bb03 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -15,10 +15,10 @@ + - diff --git a/webclient/login/login-controller.js b/webclient/login/login-controller.js index 2f1f224a9..cd13dcea8 100644 --- a/webclient/login/login-controller.js +++ b/webclient/login/login-controller.js @@ -53,7 +53,7 @@ angular.module('LoginController', ['matrixService']) matrixService.saveConfig(); eventStreamService.resume(); // Go to the user's rooms list page - $location.url("rooms"); + $location.url("home"); }, function(error) { if (error.data) { @@ -84,7 +84,7 @@ angular.module('LoginController', ['matrixService']) }); matrixService.saveConfig(); eventStreamService.resume(); - $location.url("rooms"); + $location.url("home"); } else { $scope.feedback = "Failed to login: " + JSON.stringify(response.data); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 26d1836fc..65a33dd60 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -364,7 +364,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) matrixService.leave($scope.room_id).then( function(response) { console.log("Left room "); - $location.url("rooms"); + $location.url("home"); }, function(error) { $scope.feedback = "Failed to leave room: " + error.data.error; From 31e7cec4868bbe98e2bfec04db47ec1d0bf3b8ed Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 18:23:38 +0200 Subject: [PATCH 48/65] Added "Your name" as placeholder to help user understand what is this alone input box --- webclient/settings/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/settings/settings.html b/webclient/settings/settings.html index 63b12c8f5..453a4fc35 100644 --- a/webclient/settings/settings.html +++ b/webclient/settings/settings.html @@ -15,7 +15,7 @@
- +
From 9f514915afe813cfb531748e252479b6332e5d76 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 18:02:45 +0100 Subject: [PATCH 49/65] Add indices to schema --- synapse/handlers/presence.py | 4 ++++ synapse/storage/schema/im.sql | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 540e114b8..4ce77cbff 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -187,6 +187,10 @@ class PresenceHandler(BaseHandler): @defer.inlineCallbacks def set_state(self, target_user, auth_user, state): + return + # TODO (erikj): Turn this back on. Why did we end up sending EDUs + # everywhere? + if not target_user.is_mine: raise SynapseError(400, "User is not hosted on this Home Server") diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql index 39a1ed703..7f4e75889 100644 --- a/synapse/storage/schema/im.sql +++ b/synapse/storage/schema/im.sql @@ -26,6 +26,11 @@ CREATE TABLE IF NOT EXISTS events( CONSTRAINT ev_uniq UNIQUE (event_id) ); +CREATE INDEX IF NOT EXISTS events_event_id ON events (event_id); +CREATE INDEX IF NOT EXISTS events_stream_ordering ON events (stream_ordering); +CREATE INDEX IF NOT EXISTS events_topological_ordering ON events (topological_ordering); +CREATE INDEX IF NOT EXISTS events_room_id ON events (room_id); + CREATE TABLE IF NOT EXISTS state_events( event_id TEXT NOT NULL, room_id TEXT NOT NULL, @@ -34,6 +39,12 @@ CREATE TABLE IF NOT EXISTS state_events( prev_state TEXT ); +CREATE UNIQUE INDEX IF NOT EXISTS state_events_event_id ON state_events (event_id); +CREATE INDEX IF NOT EXISTS state_events_room_id ON state_events (room_id); +CREATE INDEX IF NOT EXISTS state_events_type ON state_events (type); +CREATE INDEX IF NOT EXISTS state_events_state_key ON state_events (state_key); + + CREATE TABLE IF NOT EXISTS current_state_events( event_id TEXT NOT NULL, room_id TEXT NOT NULL, @@ -42,6 +53,11 @@ CREATE TABLE IF NOT EXISTS current_state_events( CONSTRAINT curr_uniq UNIQUE (room_id, type, state_key) ON CONFLICT REPLACE ); +CREATE INDEX IF NOT EXISTS curr_events_event_id ON current_state_events (event_id); +CREATE INDEX IF NOT EXISTS current_state_events_room_id ON current_state_events (room_id); +CREATE INDEX IF NOT EXISTS current_state_events_type ON current_state_events (type); +CREATE INDEX IF NOT EXISTS current_state_events_state_key ON current_state_events (state_key); + CREATE TABLE IF NOT EXISTS room_memberships( event_id TEXT NOT NULL, user_id TEXT NOT NULL, @@ -50,6 +66,10 @@ CREATE TABLE IF NOT EXISTS room_memberships( membership TEXT NOT NULL ); +CREATE INDEX IF NOT EXISTS room_memberships_event_id ON room_memberships (event_id); +CREATE INDEX IF NOT EXISTS room_memberships_room_id ON room_memberships (room_id); +CREATE INDEX IF NOT EXISTS room_memberships_user_id ON room_memberships (user_id); + CREATE TABLE IF NOT EXISTS feedback( event_id TEXT NOT NULL, feedback_type TEXT, From 537ecd4e9929cc66eec3747aa37ce8c19ad6aa0a Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 18:12:38 +0100 Subject: [PATCH 50/65] Turn off spammy logging --- synapse/storage/roommember.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py index 89c87290c..aca5cff73 100644 --- a/synapse/storage/roommember.py +++ b/synapse/storage/roommember.py @@ -145,7 +145,7 @@ class RoomMemberStore(SQLBaseStore): rows = yield self._execute_and_decode(sql, *where_values) - logger.debug("_get_members_query Got rows %s", rows) + # logger.debug("_get_members_query Got rows %s", rows) results = [self._parse_event_from_row(r) for r in rows] defer.returnValue(results) From b18db63c066682677f82bdba43db3754eb9bbb2e Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 18:13:50 +0100 Subject: [PATCH 51/65] Turn off more spammy logging. --- synapse/handlers/room.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 899b653fb..8ab0b8033 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -307,7 +307,7 @@ class MessageHandler(BaseHandler): ret = {"rooms": rooms_ret, "presence": presence[0], "end": now_token} - logger.debug("snapshot_all_rooms returning: %s", ret) + # logger.debug("snapshot_all_rooms returning: %s", ret) defer.returnValue(ret) From 5b058a79cbe4ba0e8d35a52aa79693fe5a074537 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 18:21:21 +0100 Subject: [PATCH 52/65] Make is_presence_visible always return true as it was thrashing the database. --- synapse/handlers/presence.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 4ce77cbff..bccb24a10 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -142,6 +142,9 @@ class PresenceHandler(BaseHandler): @defer.inlineCallbacks def is_presence_visible(self, observer_user, observed_user): + return defer.succeed(True) + # FIXME (erikj): This code path absolutely kills the database. + assert(observed_user.is_mine) if observer_user == observed_user: From cda4ff85198fd68ba10905e90f3e7172fe24559b Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 18:23:02 +0100 Subject: [PATCH 53/65] Oops, we need to use defer.returnValue. --- synapse/handlers/presence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index bccb24a10..c88cc1878 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -142,7 +142,8 @@ class PresenceHandler(BaseHandler): @defer.inlineCallbacks def is_presence_visible(self, observer_user, observed_user): - return defer.succeed(True) + defer.returnValue(True) + return # FIXME (erikj): This code path absolutely kills the database. assert(observed_user.is_mine) From 104808107ac6857e3d7bafefaa21f9ea90d9bad3 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Fri, 22 Aug 2014 18:40:31 +0100 Subject: [PATCH 54/65] skip presence tests which broke when presence polling was disabled --- tests/handlers/test_presence.py | 7 +++++++ tests/handlers/test_presencelike.py | 4 ++++ tests/rest/test_presence.py | 2 ++ 3 files changed, 13 insertions(+) diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 8b88c49a0..6d3cd76db 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -190,6 +190,7 @@ class PresenceStateTestCase(unittest.TestCase): ), SynapseError ) + test_get_disallowed_state.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_set_my_state(self): @@ -214,6 +215,7 @@ class PresenceStateTestCase(unittest.TestCase): state={"state": OFFLINE}) self.mock_stop.assert_called_with(self.u_apple) + test_set_my_state.skip = "Presence polling is disabled" class PresenceInvitesTestCase(unittest.TestCase): @@ -653,6 +655,7 @@ class PresencePushTestCase(unittest.TestCase): observed_user=self.u_banana, statuscache=ANY), # self-reflection ]) # and no others... + test_push_local.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_push_remote(self): @@ -704,6 +707,7 @@ class PresencePushTestCase(unittest.TestCase): ) yield put_json.await_calls() + test_push_remote.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_recv_remote(self): @@ -996,6 +1000,8 @@ class PresencePollingTestCase(unittest.TestCase): self.assertFalse("banana" in self.handler._local_pushmap) self.assertFalse("clementine" in self.handler._local_pushmap) + test_push_local.skip = "Presence polling is disabled" + @defer.inlineCallbacks def test_remote_poll_send(self): @@ -1044,6 +1050,7 @@ class PresencePollingTestCase(unittest.TestCase): put_json.await_calls() self.assertFalse(self.u_potato in self.handler._remote_recvmap) + test_remote_poll_send.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_remote_poll_receive(self): diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index bba5dd4e5..c25c6889b 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -135,6 +135,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): mocked_set.assert_called_with("apple", {"state": UNAVAILABLE, "status_msg": "Away"}) + test_set_my_state.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_push_local(self): @@ -209,6 +210,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): "displayname": "I am an Apple", "avatar_url": "http://foo", }, statuscache.state) + test_push_local.skip = "Presence polling is disabled" + @defer.inlineCallbacks def test_push_remote(self): @@ -239,6 +242,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): ], }, ) + test_push_remote.skip = "Presence polling is disabled" @defer.inlineCallbacks def test_recv_remote(self): diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index 8ac246b4d..970405d27 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -114,6 +114,7 @@ class PresenceStateTestCase(unittest.TestCase): self.assertEquals(200, code) mocked_set.assert_called_with("apple", {"state": UNAVAILABLE, "status_msg": "Away"}) + test_set_my_status.skip = "Presence polling is disabled" class PresenceListTestCase(unittest.TestCase): @@ -309,3 +310,4 @@ class PresenceEventStreamTestCase(unittest.TestCase): "mtime_age": 0, }}, ]}, response) + test_shortpoll.skip = "Presence polling is disabled" From 68f4d737175c44f05b750f4bd1f0a3d9943c4b7f Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 19:03:28 +0100 Subject: [PATCH 55/65] Mention in changelog that we disabled presence. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 86e78c288..fc6385cb1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,7 @@ Changes in synapse 0.0.1 (2014-08-22) ===================================== +Presence has been disabled in this release due to a bug that caused the +homeserver to spam other remote homeservers. Homeserver: * Completely change the database schema to support generic event types. From 45e70a6b70745b9e5e1d2e37900a1dc43737a47b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 23 Aug 2014 00:50:49 +0100 Subject: [PATCH 56/65] point out the non-quick-start guide --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 069f37ec0..cfdc2a1c7 100644 --- a/README.rst +++ b/README.rst @@ -34,6 +34,10 @@ To get up and running: machine.my.domain.name``. Then come join ``#matrix:matrix.org`` and say hi! :) +For more detailed setup instructions, please see further down this document. + +[1] VoIP currently in development + About Matrix ============ @@ -85,8 +89,6 @@ https://github.com/matrix-org/synapse/issues or at matrix@matrix.org. Thanks for trying Matrix! -[1] VoIP currently in development - [2] Cryptographic signing of messages isn't turned on yet [3] End-to-end encryption is currently in development From d2bb28d2df6ffe58db1a56e7aec85c34d9d1425f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 23 Aug 2014 20:45:00 +0100 Subject: [PATCH 57/65] very quick and dirty responsive design for iPhones --- webclient/app.css | 98 +++++++++++++++++++++++++++++++++----- webclient/index.html | 6 +-- webclient/login/login.html | 4 +- webclient/room/room.html | 30 ++++++------ webclient/rooms/rooms.html | 1 + webclient/user/user.html | 1 + 6 files changed, 110 insertions(+), 30 deletions(-) diff --git a/webclient/app.css b/webclient/app.css index 207f35f5f..6f320414b 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -1,3 +1,71 @@ +/*** Mobile voodoo ***/ +@media all and (max-device-width: 640px) { + + #messageTableWrapper { + margin-right: 0px ! important; + } + + .leftBlock { + width: 8em ! important; + } + + #header, + #messageTable, + #wrapper, + #roomName, + #controls { + max-width: 640px ! important; + } + + #userIdCell, + #usersTableWrapper, + #extraControls { + display: none; + } + + #buttonsCell { + width: 60px ! important; + padding-left: 20px ! important; + } + + #roomLogo { + display: none; + } + + #roomName { + text-align: left ! important; + top: -35px ! important; + } + + .bubble { + font-size: 12px ! important; + height: 20px ! important; + } + + #page { + top: 35px ! important; + bottom: 70px ! important; + } + + #header, + #page { + margin: 5px ! important; + } + + #header { + padding: 5px ! important; + } + + /* stop zoom on select */ + select:focus, + textarea, + input + { + font-size: 16px ! important; + } + +} + body { font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif; font-size: 12pt; @@ -17,7 +85,6 @@ h1 { left: 0px; right: 0px; margin: 20px; - margin: 20px; } #wrapper { @@ -32,8 +99,7 @@ h1 { text-align: right; top: -40px; position: absolute; - font-size: 16pt; - margin-bottom: 10px; + font-size: 16px; } #controlPanel { @@ -50,6 +116,10 @@ h1 { margin: auto; } +#buttonsCell { + width: 150px; +} + #inputBarTable { width: 100%; } @@ -111,13 +181,13 @@ h1 { color: #fff; margin: 2px; bottom: 0px; - font-size: 8pt; + font-size: 12px; word-break: break-all; } .userPresence { text-align: center; - font-size: 8pt; + font-size: 12px; color: #fff; background-color: #aaa; border-bottom: 1px #ddd solid; @@ -159,7 +229,7 @@ h1 { background-color: #fff; color: #888; font-weight: medium; - font-size: 8pt; + font-size: 12px; text-align: right; border-top: 1px #ddd solid; } @@ -277,7 +347,7 @@ h1 { .profile-avatar { width: 160px; height: 160px; - display:table-cell; + display: table-cell; vertical-align: middle; text-align: center; } @@ -293,13 +363,19 @@ h1 { } #user-displayname { - font-size: 16pt; + font-size: 24px; } /******************************/ -#header { - padding-left: 20px; - padding-right: 20px; +#header +{ + padding: 20px; + max-width: 1280px; + margin: auto; +} + +#logo, +#roomLogo { max-width: 1280px; margin: auto; } diff --git a/webclient/index.html b/webclient/index.html index 27d920819..5b8e27fa6 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -2,10 +2,12 @@ [matrix] - + + + @@ -36,8 +38,6 @@ - -

[matrix]

diff --git a/webclient/login/login.html b/webclient/login/login.html index b1488b37f..4b2ea6092 100644 --- a/webclient/login/login.html +++ b/webclient/login/login.html @@ -1,4 +1,6 @@ -
{{ members[msg.user_id].displayname || msg.user_id }}
-
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm:ss' }}
+
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }}
- - - -
+ {{ state.user_id }} + + - - - +
- - Invite a user: - - - - - +
+ + Invite a user: + + + + +
+ {{ feedback }}
{{ state.stream_failure.data.error || "Connection failure" }} diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html index 5e422bf83..2a12cbc8a 100644 --- a/webclient/rooms/rooms.html +++ b/webclient/rooms/rooms.html @@ -1,4 +1,5 @@
+

[matrix]

diff --git a/webclient/user/user.html b/webclient/user/user.html index 47db09d1e..4c91c8a48 100644 --- a/webclient/user/user.html +++ b/webclient/user/user.html @@ -1,4 +1,5 @@
+

[matrix]

From 3f08a7ad21cfa0529ed074a37617103e49c8c7e2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 23 Aug 2014 20:48:14 +0100 Subject: [PATCH 58/65] oops --- webclient/app.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/app.css b/webclient/app.css index 6f320414b..7fe3cb367 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -39,7 +39,7 @@ .bubble { font-size: 12px ! important; - height: 20px ! important; + min-height: 20px ! important; } #page { From a9a5329a1166e13ec490ef239d94b2dcc51f5d72 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Sun, 24 Aug 2014 11:28:00 +0100 Subject: [PATCH 59/65] Encode unicode from json as utf-8. This was required to allow people to register on my laptop --- synapse/rest/register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/rest/register.py b/synapse/rest/register.py index eb457562b..f17ec11cf 100644 --- a/synapse/rest/register.py +++ b/synapse/rest/register.py @@ -33,10 +33,10 @@ class RegisterRestServlet(RestServlet): try: register_json = json.loads(request.content.read()) if "password" in register_json: - password = register_json["password"] + password = register_json["password"].encode("utf-8") if type(register_json["user_id"]) == unicode: - desired_user_id = register_json["user_id"] + desired_user_id = register_json["user_id"].encode("utf-8") if urllib.quote(desired_user_id) != desired_user_id: raise SynapseError( 400, From 9d86c8c7a68fdbfa49df8bb027a112158df40bb8 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Sun, 24 Aug 2014 11:28:03 +0100 Subject: [PATCH 60/65] Add a unique constraint on the room hosts table --- synapse/storage/schema/im.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql index 7f4e75889..e92f21ef3 100644 --- a/synapse/storage/schema/im.sql +++ b/synapse/storage/schema/im.sql @@ -98,5 +98,6 @@ CREATE TABLE IF NOT EXISTS rooms( CREATE TABLE IF NOT EXISTS room_hosts( room_id TEXT NOT NULL, - host TEXT NOT NULL + host TEXT NOT NULL, + CONSTRAINT room_hosts_uniq UNIQUE (room_id, host) ON CONFLICT IGNORE ); From 0c3b4a1f63413c4d3195a14ee4346a6d9cfdf618 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Sun, 24 Aug 2014 11:56:55 +0100 Subject: [PATCH 61/65] For the content repo, don't just use homeserver.hostname as that might not include the port due to SRV. --- synapse/app/homeserver.py | 7 +++++++ synapse/http/server.py | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 495149466..40e3561ee 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -37,6 +37,7 @@ import logging import logging.config import sqlite3 import os +import re logger = logging.getLogger(__name__) @@ -255,8 +256,14 @@ def setup(): logger.info("Server hostname: %s", args.host) + if re.search(":[0-9]+$", args.host): + domain_with_port = args.host + else: + domain_with_port = "%s:%s" % (args.host, args.port) + hs = SynapseHomeServer( args.host, + domain_with_port=domain_with_port, upload_dir=os.path.abspath("uploads"), db_name=db_name, ) diff --git a/synapse/http/server.py b/synapse/http/server.py index d1f99460c..66f966fca 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -325,7 +325,9 @@ class ContentRepoResource(resource.Resource): # FIXME (erikj): These should use constants. file_name = os.path.basename(fname) - url = "http://%s/matrix/content/%s" % (self.hs.hostname, file_name) + url = "http://%s/matrix/content/%s" % ( + self.hs.domain_with_port, file_name + ) respond_with_json_bytes(request, 200, json.dumps({"content_token": url}), From 2c4908ed268c7ae07af2b829c34d18ed5d743b81 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Sun, 24 Aug 2014 14:35:13 +0100 Subject: [PATCH 62/65] Ensure that we don't have duplicate hosts in the pdu destinations list --- synapse/federation/replication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py index 8030d0963..cf634a64b 100644 --- a/synapse/federation/replication.py +++ b/synapse/federation/replication.py @@ -509,10 +509,10 @@ class _TransactionQueue(object): # a transaction in progress. If we do, stick it in the pending_pdus # table and we'll get back to it later. - destinations = [ + destinations = set([ d for d in pdu.destinations if d != self.server_name - ] + ]) logger.debug("Sending to: %s", str(destinations)) From 8b0473d5b9cb66ce69b152af3f2d61f9d8e78bb6 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 25 Aug 2014 10:25:43 +0200 Subject: [PATCH 63/65] Oops. Removed my NetBeans private folders --- webclient/nbproject/licenseheader.txt | 16 -------------- .../nbproject/private/private.properties | 1 - webclient/nbproject/private/private.xml | 22 ------------------- 3 files changed, 39 deletions(-) delete mode 100644 webclient/nbproject/licenseheader.txt delete mode 100644 webclient/nbproject/private/private.properties delete mode 100644 webclient/nbproject/private/private.xml diff --git a/webclient/nbproject/licenseheader.txt b/webclient/nbproject/licenseheader.txt deleted file mode 100644 index 36e6dcf18..000000000 --- a/webclient/nbproject/licenseheader.txt +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2014 matrix.org - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - diff --git a/webclient/nbproject/private/private.properties b/webclient/nbproject/private/private.properties deleted file mode 100644 index 3b1a22e6c..000000000 --- a/webclient/nbproject/private/private.properties +++ /dev/null @@ -1 +0,0 @@ -browser=Chrome.INTEGRATED diff --git a/webclient/nbproject/private/private.xml b/webclient/nbproject/private/private.xml deleted file mode 100644 index 75bc70106..000000000 --- a/webclient/nbproject/private/private.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/matrix-service.js - file:/Users/manu/UC/matrix/github/synapse/webclient/app.css - file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/event-handler-service.js - file:/Users/manu/UC/matrix/github/synapse/webclient/app-controller.js - file:/Users/manu/UC/matrix/github/synapse/webclient/room/room.html - file:/Users/manu/UC/matrix/github/synapse/webclient/login/login.html - file:/Users/manu/UC/matrix/github/synapse/webclient/components/matrix/event-stream-service.js - file:/Users/manu/UC/matrix/github/synapse/webclient/components/fileUpload/file-upload-service.js - file:/Users/manu/UC/matrix/github/synapse/webclient/index.html - file:/Users/manu/UC/matrix/github/synapse/webclient/room/room-controller.js - file:/Users/manu/UC/matrix/github/synapse/webclient/rooms/rooms.html - file:/Users/manu/UC/matrix/github/synapse/webclient/app.js - file:/Users/manu/UC/matrix/github/synapse/webclient/rooms/rooms-controller.js - file:/Users/manu/UC/matrix/github/synapse/webclient/components/fileInput/file-input-directive.js - - - From 95839212a707f87d5927cde1187087afb7410b34 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 25 Aug 2014 11:35:33 +0200 Subject: [PATCH 64/65] The landing URL is now '#/' which actually points to homeController --- webclient/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webclient/app.js b/webclient/app.js index e5d851394..6cd50c5e5 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -45,7 +45,7 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'room/room.html', controller: 'RoomController' }). - when('/home', { + when('/', { templateUrl: 'home/home.html', controller: 'HomeController' }). @@ -58,7 +58,7 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', controller: 'UserController' }). otherwise({ - redirectTo: '/home' + redirectTo: '/' }); $provide.factory('AccessTokenInterceptor', ['$q', '$rootScope', From be6abdff19ce8dcc5b5d727aba0142f9c1aae2ec Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Tue, 26 Aug 2014 09:22:58 +0100 Subject: [PATCH 65/65] Order 'get_recent_events_for_room' correctly. --- synapse/storage/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py index 87ae961cc..3a17a723f 100644 --- a/synapse/storage/stream.py +++ b/synapse/storage/stream.py @@ -257,7 +257,7 @@ class StreamStore(SQLBaseStore): sql = ( "SELECT * FROM events " "WHERE room_id = ? AND stream_ordering <= ? " - "ORDER BY topological_ordering, stream_ordering DESC LIMIT ? " + "ORDER BY topological_ordering DESC, stream_ordering DESC LIMIT ? " ) rows = yield self._execute_and_decode(