diff --git a/CHANGES.rst b/CHANGES.rst
index 055f8bc01b..fc6385cb1f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,5 +1,8 @@
-Changes in synapse 0.0.1
-=======================
+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.
* Improve presence reliability.
@@ -18,3 +21,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
diff --git a/README.rst b/README.rst
index 069f37ec06..cfdc2a1c75 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
diff --git a/docs/client-server/swagger_matrix/api-docs b/docs/client-server/swagger_matrix/api-docs
new file mode 100644
index 0000000000..d974dbb374
--- /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 0000000000..c9eb3f6ff7
--- /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 0000000000..4410d3c887
--- /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 0000000000..ee9deb12f0
--- /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 0000000000..1ebde62e20
--- /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 0000000000..ccd542d11e
--- /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 0000000000..47a8887240
--- /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: <target> 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"
+ }
+ }
+ }
+ }
+}
diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py
index 495149466c..40e3561ee5 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/handlers/presence.py b/synapse/handlers/presence.py
index 540e114b82..c88cc18788 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -142,6 +142,10 @@ class PresenceHandler(BaseHandler):
@defer.inlineCallbacks
def is_presence_visible(self, observer_user, observed_user):
+ defer.returnValue(True)
+ return
+ # FIXME (erikj): This code path absolutely kills the database.
+
assert(observed_user.is_mine)
if observer_user == observed_user:
@@ -187,6 +191,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/handlers/room.py b/synapse/handlers/room.py
index 899b653fb7..8ab0b8033e 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)
diff --git a/synapse/http/server.py b/synapse/http/server.py
index d1f99460c1..66f966fcaa 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}),
diff --git a/synapse/rest/register.py b/synapse/rest/register.py
index eb457562b9..f17ec11cf4 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,
diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py
index 89c87290cf..aca5cff737 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)
diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql
index 39a1ed703e..e92f21ef3b 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,
@@ -78,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
);
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 8b88c49a0b..6d3cd76dba 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 bba5dd4e53..c25c6889be 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 8ac246b4d5..970405d271 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"
diff --git a/webclient/app.css b/webclient/app.css
index 72b38cd950..dfa17fae62 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;
+ min-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 ed1d9bb031..938d70c86d 100644
--- a/webclient/index.html
+++ b/webclient/index.html
@@ -2,10 +2,12 @@
<html xmlns:ng="http://angularjs.org" ng-app="matrixWebClient" ng-controller="MatrixWebClientController">
<head>
<title>[matrix]</title>
-
+
<link rel="stylesheet" href="app.css">
<link rel="icon" href="favicon.ico">
+ <meta name="viewport" content="width=device-width">
+
<script type='text/javascript' src='js/jquery-1.8.3.min.js'></script>
<script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script>
@@ -37,8 +39,6 @@
<button ng-click='go("settings")'>Settings</button>
<button ng-click="logout()">Log out</button>
</div>
-
- <h1>[matrix]</h1>
</header>
<div ng-view></div>
diff --git a/webclient/login/login.html b/webclient/login/login.html
index b1488b37f0..4b2ea60928 100644
--- a/webclient/login/login.html
+++ b/webclient/login/login.html
@@ -1,4 +1,6 @@
-<div ng-controller="LoginController" class="login">
+<div ng-controller="LoginController" class="login">
+ <h1 id="logo">[matrix]</h1>
+
<div id="page">
<div id="wrapper">
diff --git a/webclient/room/room.html b/webclient/room/room.html
index 95da067714..06ca63d2ea 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -1,4 +1,5 @@
<div ng-controller="RoomController" data-ng-init="onInit()" class="room">
+ <h1 id="roomLogo">[matrix]</h1>
<div id="page">
<div id="wrapper">
@@ -32,7 +33,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>
<td class="leftBlock">
<div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id">{{ members[msg.user_id].displayname || msg.user_id }}</div>
- <div class="timestamp">{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm:ss' }}</div>
+ <div class="timestamp">{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }}</div>
</td>
<td class="avatar">
<img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.jpg' }}" width="32" height="32"
@@ -73,29 +74,28 @@
<div id="controls">
<table id="inputBarTable">
<tr>
- <td width="1">
+ <td id="userIdCell" width="1px">
{{ state.user_id }}
</td>
- <td width="*" style="min-width: 100px">
+ <td width="*">
<input id="mainInput" ng-model="textInput" ng-enter="send()" ng-focus="true" autocomplete="off" tab-complete/>
</td>
- <td width="150px">
+ <td id="buttonsCell">
<button ng-click="send()">Send</button>
- <button m-file-input="imageFileToSend">Send Image</button>
- </td>
- <td width="1">
-
+ <button m-file-input="imageFileToSend">Image</button>
</td>
</tr>
</table>
- <span>
- Invite a user:
- <input ng-model="userIDToInvite" size="32" type="text" placeholder="User ID (ex:@user:homeserver)"/>
- <button ng-click="inviteUser(userIDToInvite)">Invite</button>
- </span>
- <button ng-click="leaveRoom()">Leave</button>
- <button ng-click="loadMoreHistory()" ng-disabled="!state.can_paginate">Load more history</button>
+ <div id="extraControls">
+ <span>
+ Invite a user:
+ <input ng-model="userIDToInvite" size="32" type="text" placeholder="User ID (ex:@user:homeserver)"/>
+ <button ng-click="inviteUser(userIDToInvite)">Invite</button>
+ </span>
+ <button ng-click="leaveRoom()">Leave</button>
+ </div>
+
{{ feedback }}
<div ng-hide="!state.stream_failure">
{{ state.stream_failure.data.error || "Connection failure" }}
diff --git a/webclient/user/user.html b/webclient/user/user.html
index 47db09d1ee..4c91c8a48a 100644
--- a/webclient/user/user.html
+++ b/webclient/user/user.html
@@ -1,4 +1,5 @@
<div ng-controller="UserController" class="user">
+ <h1 id="logo">[matrix]</h1>
<div id="page">
<div id="wrapper">
|