From e5257b21b3cb6a1823159273b07a0ada25cdf8a8 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 17:11:08 +0200 Subject: Support room alias in rooms URL (ex: http://127.0.0.1:8000/#/room/#public:localhost:8080) --- webclient/app.js | 6 ++++ webclient/components/matrix/matrix-service.js | 10 +++++++ webclient/room/room-controller.js | 43 ++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) (limited to 'webclient') diff --git a/webclient/app.js b/webclient/app.js index 8d64db92d3..193c11d461 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -37,6 +37,12 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'room/room.html', controller: 'RoomController' }). + when('/room/', { // room URL with room alias in it (ex: http://127.0.0.1:8000/#/room/#public:localhost:8080) will come here. + // The reason is that 2nd hash key breaks routeProvider parameters cutting so that the URL will not match with + // the previous '/room/:room_id' URL rule + templateUrl: 'room/room.html', + controller: 'RoomController' + }). when('/rooms', { templateUrl: 'rooms/rooms.html', controller: 'RoomsController' diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index c52c94c310..47828993a1 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -165,6 +165,16 @@ angular.module('matrixService', []) return doRequest("DELETE", path, undefined, undefined); }, + // Retrieves the room ID corresponding to a room alias + resolveRoomAlias:function(room_alias) { + var path = "/matrix/client/api/v1/ds/room/$room_alias"; + room_alias = encodeURIComponent(room_alias); + + path = path.replace("$room_alias", room_alias); + + return doRequest("GET", path, undefined, {}); + }, + sendMessage: function(room_id, msg_id, content) { // The REST path spec var path = "/rooms/$room_id/messages/$from/$msg_id"; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index b859f3d7e8..999c48f02d 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -108,8 +108,11 @@ angular.module('RoomController', ['ngSanitize']) function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService) { 'use strict'; var MESSAGES_PER_PAGINATION = 30; + + // Room ids. Checked, computed and resolved in onInit $scope.room_id = $routeParams.room_id; - $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id); + $scope.room_alias = undefined; + $scope.state = { user_id: matrixService.config().user_id, events_from: "END", // when to start the event stream from. @@ -342,7 +345,45 @@ angular.module('RoomController', ['ngSanitize']) $scope.onInit = function() { // $timeout(function() { document.getElementById('textInput').focus() }, 0); console.log("onInit"); + + // Does the room ID provided in the URL? + if ($scope.room_id) { + // Yes, we can start right now + $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id); + onInit2(); + } + else { + // No, the URL contains the room alias. Get this alias. + // ie: extract #public:localhost:8080 from http://127.0.0.1:8000/#/room/#public:localhost:8080 + if (3 === location.hash.split("#").length) { + $scope.room_alias = "#" + location.hash.split("#")[2]; + } + else { + // In case of issue, go to the default page + console.log("Error: cannot extract room alias"); + $location.path("/"); + return; + } + + console.log("Resolving alias: " + $scope.room_alias); + + // Need a room ID required in Matrix API requests + matrixService.resolveRoomAlias($scope.room_alias).then(function(response) { + $scope.room_id = response.data.room_id; + console.log(" -> Room ID: " + $scope.room_id); + + // Now, we can start + onInit2(); + }, + function () { + // In case of issue, go to the default page + console.log("Error: cannot resolve room alias"); + $location.path("/"); + }); + } + }; + var onInit2 = function() { // Join the room matrixService.join($scope.room_id).then( function() { -- cgit 1.4.1 From cebceb7b9d8050c8f50d2d1277caf03f0acf40ea Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 17:14:57 +0200 Subject: If possible, use href with room alias in rooms list --- webclient/rooms/rooms.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'webclient') diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html index 007ad29999..2602209bd3 100644 --- a/webclient/rooms/rooms.html +++ b/webclient/rooms/rooms.html @@ -65,7 +65,7 @@
- {{ room.room_alias }} {{room.membership === 'invite' ? ' (invited)' : ''}} + {{ room.room_alias }} {{room.membership === 'invite' ? ' (invited)' : ''}}

@@ -74,7 +74,7 @@
-- cgit 1.4.1 From 43772d0b15f749e2e4356097560cca3c86e1e4e6 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 17:40:05 +0200 Subject: Support urlencoded room aliases in room URL --- webclient/app.js | 4 ++-- webclient/room/room-controller.js | 42 +++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) (limited to 'webclient') diff --git a/webclient/app.js b/webclient/app.js index 193c11d461..a3d1505f81 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -33,13 +33,13 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'login/login.html', controller: 'LoginController' }). - when('/room/:room_id', { + when('/room/:room_id_or_alias', { templateUrl: 'room/room.html', controller: 'RoomController' }). when('/room/', { // room URL with room alias in it (ex: http://127.0.0.1:8000/#/room/#public:localhost:8080) will come here. // The reason is that 2nd hash key breaks routeProvider parameters cutting so that the URL will not match with - // the previous '/room/:room_id' URL rule + // the previous '/room/:room_id_or_alias' URL rule templateUrl: 'room/room.html', controller: 'RoomController' }). diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 999c48f02d..d130a28fd7 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -109,8 +109,8 @@ angular.module('RoomController', ['ngSanitize']) 'use strict'; var MESSAGES_PER_PAGINATION = 30; - // Room ids. Checked, computed and resolved in onInit - $scope.room_id = $routeParams.room_id; + // Room ids. Computed and resolved in onInit + $scope.room_id = undefined; $scope.room_alias = undefined; $scope.state = { @@ -347,27 +347,39 @@ angular.module('RoomController', ['ngSanitize']) console.log("onInit"); // Does the room ID provided in the URL? - if ($scope.room_id) { - // Yes, we can start right now + var room_id_or_alias; + if ($routeParams.room_id_or_alias) { + room_id_or_alias = decodeURIComponent($routeParams.room_id_or_alias); + } + + if (room_id_or_alias && '!' === room_id_or_alias[0]) { + // Yes. We can start right now + $scope.room_id = room_id_or_alias; $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id); onInit2(); } else { - // No, the URL contains the room alias. Get this alias. - // ie: extract #public:localhost:8080 from http://127.0.0.1:8000/#/room/#public:localhost:8080 - if (3 === location.hash.split("#").length) { - $scope.room_alias = "#" + location.hash.split("#")[2]; + // No. The URL contains the room alias. Get this alias. + if (room_id_or_alias) { + // The room alias was passed urlencoded, use it as is + $scope.room_alias = room_id_or_alias; } - else { - // In case of issue, go to the default page - console.log("Error: cannot extract room alias"); - $location.path("/"); - return; + else { + // Else get the room alias by hand from the URL + // ie: extract #public:localhost:8080 from http://127.0.0.1:8000/#/room/#public:localhost:8080 + if (3 === location.hash.split("#").length) { + $scope.room_alias = "#" + location.hash.split("#")[2]; + } + else { + // In case of issue, go to the default page + console.log("Error: cannot extract room alias"); + $location.path("/"); + return; + } } - console.log("Resolving alias: " + $scope.room_alias); - // Need a room ID required in Matrix API requests + console.log("Resolving alias: " + $scope.room_alias); matrixService.resolveRoomAlias($scope.room_alias).then(function(response) { $scope.room_id = response.data.room_id; console.log(" -> Room ID: " + $scope.room_id); -- cgit 1.4.1 From f8693c6b481b7ee34b55e8ca68e0b4aebf3f9b03 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 17:41:23 +0200 Subject: Join room: open the room URL with the room alias in it --- webclient/rooms/rooms-controller.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'webclient') diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index da79c23d55..a237b59b4e 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -149,12 +149,8 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', $scope.joinAlias = function(room_alias) { matrixService.joinAlias(room_alias).then( function(response) { - if (response.data.hasOwnProperty("room_id")) { - $location.path("room/" + response.data.room_id); - return; - } else { - // TODO (erikj): Do something here? - } + // Go to this room + $location.path("room/" + room_alias); }, function(error) { $scope.feedback = "Can't join room: " + error.data; -- cgit 1.4.1 From 301e55d11da31a505bba58abb1dbb2caf766a5c3 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 17:49:50 +0200 Subject: In members list, on avatar mouseover, show a tooltip with the user matrix id --- webclient/room/room.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'webclient') diff --git a/webclient/room/room.html b/webclient/room/room.html index 106a9dfd15..1c6a44f6fc 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -11,8 +11,12 @@
- - + {{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }} +
{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}
{{ member.displayname ? "" : member.id.substr(member.id.indexOf(':')) }}
-- cgit 1.4.1 From ecfdf232504599d93524c48d034345560fa231f1 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 18 Aug 2014 18:05:42 +0200 Subject: Created boilerplate for user profile page --- webclient/app.js | 5 +++++ webclient/index.html | 1 + webclient/user/user-controller.js | 25 +++++++++++++++++++++++++ webclient/user/user.html | 12 ++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 webclient/user/user-controller.js create mode 100644 webclient/user/user.html (limited to 'webclient') diff --git a/webclient/app.js b/webclient/app.js index a3d1505f81..576912be46 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -20,6 +20,7 @@ var matrixWebClient = angular.module('matrixWebClient', [ 'LoginController', 'RoomController', 'RoomsController', + 'UserController', 'matrixService', 'eventStreamService', 'eventHandlerService', @@ -47,6 +48,10 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', templateUrl: 'rooms/rooms.html', controller: 'RoomsController' }). + when('/user/:user_matrix_id', { + templateUrl: 'user/user.html', + controller: 'UserController' + }). otherwise({ redirectTo: '/rooms' }); diff --git a/webclient/index.html b/webclient/index.html index 455eff4a13..51f6ff1f4d 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -16,6 +16,7 @@ + diff --git a/webclient/user/user-controller.js b/webclient/user/user-controller.js new file mode 100644 index 0000000000..4b7aa62d42 --- /dev/null +++ b/webclient/user/user-controller.js @@ -0,0 +1,25 @@ +/* +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('UserController', ['matrixService']) +.controller('UserController', ['$scope', '$routeParams', + function($scope, $routeParams) { + $scope.user = { + id: $routeParams.user_matrix_id + }; +}]); \ No newline at end of file diff --git a/webclient/user/user.html b/webclient/user/user.html new file mode 100644 index 0000000000..f548e05a71 --- /dev/null +++ b/webclient/user/user.html @@ -0,0 +1,12 @@ +
+ +
+
+ +
{{ user.id }}
+ + {{ feedback }} + +
+
+
-- cgit 1.4.1 From 35da1bf4a3ee6eeafec5965af05cefbb4bd3c0b5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Aug 2014 15:50:55 +0100 Subject: Auth content uploads. Added a mapping function from request > filename. Added exception handling for content uploads. webclient: Only prefix the client API path on doRequest, not doBaseRequest (this would've broken the identity server auth too). Added matrixService.uploadContent. May not require mFileUpload anymore. --- synapse/app/homeserver.py | 2 +- synapse/http/server.py | 44 ++++++++++++++++------ .../components/fileUpload/file-upload-service.js | 29 +++++++------- webclient/components/matrix/matrix-service.js | 18 +++++++-- 4 files changed, 62 insertions(+), 31 deletions(-) (limited to 'webclient') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index e5bd13a6eb..f7c1da9201 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 FileUploadResource("uploads") + return FileUploadResource("uploads", self.auth) def build_db_pool(self): """ Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we diff --git a/synapse/http/server.py b/synapse/http/server.py index f86151e51c..7d6e225e74 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -180,28 +180,48 @@ class RootRedirect(resource.Resource): class FileUploadResource(resource.Resource): isLeaf = True - def __init__(self, directory): + def __init__(self, directory, auth, file_map_func=None): resource.Resource.__init__(self) self.directory = directory + self.auth = auth + if not file_map_func: + file_map_func = self.map_request_to_name + self.get_name_for_request = file_map_func + + @defer.inlineCallbacks + def map_request_to_name(self, request): + # auth the user + auth_user = yield self.auth.get_user_by_req(request) + logger.info("User %s is uploading a file.", auth_user) + defer.returnValue("boo2.png") def render(self, request): self._async_render(request) return server.NOT_DONE_YET - # @defer.inlineCallbacks + @defer.inlineCallbacks def _async_render(self, request): - request.setResponseCode(200) - request.setHeader(b"Content-Type", b"application/json") + try: + fname = yield self.get_name_for_request(request) - request.setHeader("Access-Control-Allow-Origin", "*") - request.setHeader("Access-Control-Allow-Methods", - "GET, POST, PUT, DELETE, OPTIONS") - request.setHeader("Access-Control-Allow-Headers", - "Origin, X-Requested-With, Content-Type, Accept") + with open(fname, "wb") as f: + f.write(request.content.read()) + + respond_with_json_bytes(request, 200, + json.dumps({"url": "not_implemented2"}), + send_cors=True) - request.write(json.dumps({"url": "not_implemented"})) - request.finish() - defer.succeed("not implemented") + except CodeMessageException as e: + logger.exception(e) + respond_with_json_bytes(request, e.code, + json.dumps(cs_exception(e))) + except Exception as e: + logger.error("Failed to store file: %s" % e) + respond_with_json_bytes( + request, + 500, + json.dumps({"error": "Internal server error"}), + send_cors=True) def respond_with_json_bytes(request, code, json_bytes, send_cors=False): diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 5729d5da48..0826666fe4 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -16,11 +16,12 @@ 'use strict'; +// TODO determine if this is really required as a separate service to matrixService. /* * Upload an HTML5 file to a server */ angular.module('mFileUpload', []) -.service('mFileUpload', ['$http', '$q', function ($http, $q) { +.service('mFileUpload', ['matrixService', '$q', function (matrixService, $q) { /* * Upload an HTML5 file to a server and returned a promise @@ -28,20 +29,18 @@ angular.module('mFileUpload', []) */ this.uploadFile = function(file) { var deferred = $q.defer(); - - // @TODO: This service runs with the do_POST hacky implementation of /synapse/demos/webserver.py. - // This is temporary until we have a true file upload service - console.log("Uploading " + file.name + "..."); - $http.post(file.name, file) - .success(function(data, status, headers, config) { - deferred.resolve(location.origin + data.url); - console.log(" -> Successfully uploaded! Available at " + location.origin + data.url); - }). - error(function(data, status, headers, config) { - console.log(" -> Failed to upload" + file.name); - deferred.reject(); - }); + console.log("Uploading " + file.name + "... to /matrix/content"); + matrixService.uploadContent(file).then( + function(response) { + console.log(" -> Successfully uploaded! Available at " + location.origin + response.data.url); + deferred.resolve(location.origin + response.data.url); + }, + function(error) { + console.log(" -> Failed to upload " + file.name); + deferred.reject(error); + } + ); return deferred.promise; }; -}]); \ No newline at end of file +}]); diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 47828993a1..b67beb007a 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -54,13 +54,14 @@ angular.module('matrixService', []) params.access_token = config.access_token; + if (path.indexOf(prefixPath) !== 0) { + path = prefixPath + path; + } + return doBaseRequest(config.homeserver, method, path, params, data, undefined); }; var doBaseRequest = function(baseUrl, method, path, params, data, headers) { - if (path.indexOf(prefixPath) !== 0) { - path = prefixPath + path; - } return $http({ method: method, url: baseUrl + path, @@ -319,6 +320,17 @@ angular.module('matrixService', []) return doBaseRequest(config.identityServer, "POST", path, {}, data, headers); }, + uploadContent: function(file) { + var path = "/matrix/content"; + var headers = { + "Content-Type": undefined // undefined means angular will figure it out + }; + var params = { + access_token: config.access_token + }; + return doBaseRequest(config.homeserver, "POST", path, params, file, headers); + }, + // start listening on /events getEventStream: function(from, timeout) { var path = "/events"; -- cgit 1.4.1 From 58548ab557bbbdd95644997e00777b0aec8532bc Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Aug 2014 17:18:39 +0100 Subject: Implemented GETs for the ContentRepoResource. It all actually appears to be working. --- synapse/app/homeserver.py | 4 +- synapse/http/server.py | 71 ++++++++++++++++++---- .../components/fileUpload/file-upload-service.js | 5 +- 3 files changed, 65 insertions(+), 15 deletions(-) (limited to 'webclient') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index f7c1da9201..ca102236cf 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -24,7 +24,7 @@ from twisted.python.log import PythonLoggingObserver from twisted.web.resource import Resource from twisted.web.static import File from twisted.web.server import Site -from synapse.http.server import JsonResource, RootRedirect, FileUploadResource +from synapse.http.server import JsonResource, RootRedirect, ContentRepoResource from synapse.http.client import TwistedHttpClient from synapse.api.urls import ( CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX @@ -56,7 +56,7 @@ class SynapseHomeServer(HomeServer): return File("webclient") # TODO configurable? def build_resource_for_content_repo(self): - return FileUploadResource("uploads", self.auth) + return ContentRepoResource("uploads", self.auth) def build_db_pool(self): """ Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we diff --git a/synapse/http/server.py b/synapse/http/server.py index 8502416fca..9b6ae993ab 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -17,10 +17,13 @@ from syutil.jsonutil import ( encode_canonical_json, encode_pretty_printed_json ) -from synapse.api.errors import cs_exception, SynapseError, CodeMessageException +from synapse.api.errors import ( + cs_exception, SynapseError, CodeMessageException, Codes, cs_error +) from synapse.util.stringutils import random_string from twisted.internet import defer, reactor +from twisted.protocols.basic import FileSender from twisted.web import server, resource from twisted.web.server import NOT_DONE_YET from twisted.web.util import redirectTo @@ -30,7 +33,7 @@ import collections import json import logging import os - +import re logger = logging.getLogger(__name__) @@ -180,16 +183,25 @@ class RootRedirect(resource.Resource): return resource.Resource.getChild(self, name, request) -class FileUploadResource(resource.Resource): +class ContentRepoResource(resource.Resource): + """Provides file uploading and downloading. + + Uploads are POSTed to wherever this Resource is linked to. This resource + returns a "content token" which can be used to GET this content again. The + token is typically a path, but it may not be. + + In this case, the token contains 3 sections: + - User ID base64d (for namespacing content to each user) + - random string + - Content type base64d (so we can return it when clients GET it) + + """ isLeaf = True - def __init__(self, directory, auth, file_map_func=None): + def __init__(self, directory, auth): resource.Resource.__init__(self) self.directory = directory self.auth = auth - if not file_map_func: - file_map_func = self.map_request_to_name - self.get_name_for_request = file_map_func if not os.path.isdir(self.directory): os.mkdir(self.directory) @@ -210,14 +222,19 @@ class FileUploadResource(resource.Resource): main_part = random_string(24) # suffix with a file extension if we can make one. This is nice to - # provide a hint to clients on the file information. + # provide a hint to clients on the file information. We will also reuse + # this info to spit back the content type to the client. suffix = "" if request.requestHeaders.hasHeader("Content-Type"): content_type = request.requestHeaders.getRawHeaders( "Content-Type")[0] + suffix = "." + base64.urlsafe_b64encode(content_type) if (content_type.split("/")[0].lower() in ["image", "video", "audio"]): - suffix = "." + content_type.split("/")[-1] + file_ext = content_type.split("/")[-1] + # be a little paranoid and only allow a-z + file_ext = re.sub("[^a-z]", "", file_ext) + suffix += "." + file_ext file_path = os.path.join(self.directory, prefix + main_part + suffix) logger.info("User %s is uploading a file to path %s", @@ -236,6 +253,37 @@ class FileUploadResource(resource.Resource): defer.returnValue(file_path) + def render_GET(self, request): + # no auth here on purpose, to allow anyone to view, even across home + # servers. + + # TODO: A little crude here, we could do this better. + filename = request.path.split(self.directory + "/")[1] + # be paranoid + filename = re.sub("[^0-9A-z.-_]", "", filename) + + file_path = self.directory + "/" + filename + if os.path.isfile(file_path): + # filename has the content type + base64_contentype = filename.split(".")[1] + content_type = base64.urlsafe_b64decode(base64_contentype) + logger.info("Sending file %s", file_path) + f = open(file_path, 'rb') + request.setHeader('Content-Type', content_type) + d = FileSender().beginFileTransfer(f, request) + def cbFinished(ignored): + f.close() + request.finish() + d.addCallback(cbFinished) + else: + respond_with_json_bytes( + request, + 404, + json.dumps(cs_error("Not found", code=Codes.NOT_FOUND)), + send_cors=True) + + return server.NOT_DONE_YET + def render_POST(self, request): self._async_render(request) return server.NOT_DONE_YET @@ -243,13 +291,14 @@ class FileUploadResource(resource.Resource): @defer.inlineCallbacks def _async_render(self, request): try: - fname = yield self.get_name_for_request(request) + fname = yield self.map_request_to_name(request) + # TODO I have a suspcious feeling this is just going to block with open(fname, "wb") as f: f.write(request.content.read()) respond_with_json_bytes(request, 200, - json.dumps({"path": fname}), + json.dumps({"content_token": fname}), 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 0826666fe4..d620e6a4d0 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -32,8 +32,9 @@ angular.module('mFileUpload', []) console.log("Uploading " + file.name + "... to /matrix/content"); matrixService.uploadContent(file).then( function(response) { - console.log(" -> Successfully uploaded! Available at " + location.origin + response.data.url); - deferred.resolve(location.origin + response.data.url); + var content_url = location.origin + "/matrix/content/" + response.data.content_token; + console.log(" -> Successfully uploaded! Available at " + content_url); + deferred.resolve(content_url); }, function(error) { console.log(" -> Failed to upload " + file.name); -- cgit 1.4.1 From cdc5ffe2a2457e50790ab73cf4ceffa0c552a14d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Aug 2014 17:14:43 +0100 Subject: show private room_ids rather than nulls in notifs if there is no room_alias --- webclient/room/room-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webclient') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index d130a28fd7..1414fbbe6a 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -147,7 +147,7 @@ angular.module('RoomController', ['ngSanitize']) if (document.hidden) { var notification = new window.Notification( ($scope.members[event.user_id].displayname || event.user_id) + - " (" + $scope.room_alias + ")", + " (" + ($scope.room_alias || $scope.room_id) + ")", // FIXME: don't leak room_ids here { "body": event.content.body, "icon": $scope.members[event.user_id].avatar_url, -- cgit 1.4.1 From c3f1548bb4b485aef00ac67079cea7e65216d4d4 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Tue, 19 Aug 2014 08:58:53 +0200 Subject: Added link to user profile pages --- webclient/room/room-controller.js | 5 +++++ webclient/room/room.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'webclient') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index d130a28fd7..bb9167d9df 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -433,6 +433,11 @@ angular.module('RoomController', ['ngSanitize']) }); }; + // Open the user profile page + $scope.goToUserPage = function(user_id) { + $location.path("user/" + user_id); + }; + $scope.leaveRoom = function() { matrixService.leave($scope.room_id).then( diff --git a/webclient/room/room.html b/webclient/room/room.html index 1c6a44f6fc..36bd95c1bb 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -10,7 +10,7 @@
-
+ {{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }} Date: Tue, 19 Aug 2014 09:06:21 +0200 Subject: Use $location.url to open the user profile page. The user page URL is then the one expected: http://127.0.0.1:8000/#/user/@Manu:localhost:8080 insteaf of http://127.0.0.1:8000/#/user/@Manu:localhost:8080#public:localhost:8080 --- webclient/room/room-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webclient') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index bb9167d9df..8878c3d209 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -435,7 +435,7 @@ angular.module('RoomController', ['ngSanitize']) // Open the user profile page $scope.goToUserPage = function(user_id) { - $location.path("user/" + user_id); + $location.url("/user/" + user_id); }; $scope.leaveRoom = function() { -- cgit 1.4.1 From 38f5c1c378d2a8c25dbc70b2e238732730852a61 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Tue, 19 Aug 2014 09:24:35 +0200 Subject: Made small avatar image center --- webclient/app.css | 1 + 1 file changed, 1 insertion(+) (limited to 'webclient') diff --git a/webclient/app.css b/webclient/app.css index b9e7771ca8..471c52ea79 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -251,6 +251,7 @@ h1 { height: 160px; display:table-cell; vertical-align: middle; + text-align: center; } .profile-avatar img { -- cgit 1.4.1 From d7a4f2ed7fec3640aea68c5280548470d6c53108 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Tue, 19 Aug 2014 09:37:10 +0200 Subject: Fill user page with avatar, display name and matrix id --- webclient/app.css | 8 ++++++++ webclient/user/user-controller.js | 19 ++++++++++++++++--- webclient/user/user.html | 22 ++++++++++++++++++++-- 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'webclient') diff --git a/webclient/app.css b/webclient/app.css index 471c52ea79..e2b6538aa0 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -259,6 +259,14 @@ h1 { max-height: 100%; } +/*** User profile page ***/ +#user-ids { + padding-left: 1em; +} + +#user-displayname { + font-size: 16pt; +} /******************************/ #header { diff --git a/webclient/user/user-controller.js b/webclient/user/user-controller.js index 4b7aa62d42..620230561c 100644 --- a/webclient/user/user-controller.js +++ b/webclient/user/user-controller.js @@ -17,9 +17,22 @@ limitations under the License. 'use strict'; angular.module('UserController', ['matrixService']) -.controller('UserController', ['$scope', '$routeParams', - function($scope, $routeParams) { +.controller('UserController', ['$scope', '$routeParams', 'matrixService', + function($scope, $routeParams, matrixService) { $scope.user = { - id: $routeParams.user_matrix_id + id: $routeParams.user_matrix_id, + displayname: "", + avatar_url: undefined }; + + matrixService.getDisplayName($scope.user.id).then( + function(response) { + $scope.user.displayname = response.data.displayname; + } + ); + matrixService.getProfilePictureUrl($scope.user.id).then( + function(response) { + $scope.user.avatar_url = response.data.avatar_url; + } + ); }]); \ No newline at end of file diff --git a/webclient/user/user.html b/webclient/user/user.html index f548e05a71..47db09d1ee 100644 --- a/webclient/user/user.html +++ b/webclient/user/user.html @@ -3,8 +3,26 @@
-
{{ user.id }}
- +
+
+ + + + + +
+
+ +
+
+
+
{{ user.displayname }}
+
{{ user.id }}
+
+
+
+
+ {{ feedback }}
-- cgit 1.4.1 From 509ce6c137da80470c3967e411f896223d80e859 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Tue, 19 Aug 2014 11:37:07 +0200 Subject: Change mouse cursor to pointer on elements user can click on --- webclient/app.css | 1 + webclient/components/fileInput/file-input-directive.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'webclient') diff --git a/webclient/app.css b/webclient/app.css index e2b6538aa0..869db69cd6 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -89,6 +89,7 @@ h1 { height: 100px; position: relative; background-color: #000; + cursor: pointer; } .userAvatar .userAvatarImage { diff --git a/webclient/components/fileInput/file-input-directive.js b/webclient/components/fileInput/file-input-directive.js index 9b73f877e9..c5e4ae07a8 100644 --- a/webclient/components/fileInput/file-input-directive.js +++ b/webclient/components/fileInput/file-input-directive.js @@ -29,7 +29,7 @@ angular.module('mFileInput', []) scope: { selectedFile: '=mFileInput' }, - + link: function(scope, element, attrs, ctrl) { element.bind("click", function() { element.find("input")[0].click(); @@ -38,6 +38,9 @@ angular.module('mFileInput', []) scope.$apply(); }); }); + + // Change the mouse icon on mouseover on this element + element.css("cursor", "pointer"); } }; }); \ No newline at end of file -- cgit 1.4.1