From 2910a7f651a5073e7c75ed1f858b5663ff343fa8 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Mon, 18 Mar 2019 12:10:12 +0100 Subject: [PATCH] Fix StringIO on py3. Add documentation and missing changelog --- docs/source/users.rst | 17 ++++++++ news/701.feature | 2 + src/plone/restapi/services/users/update.py | 3 +- .../http-examples/users_update_portrait.req | 13 ++++++ .../http-examples/users_update_portrait.resp | 2 + .../users_update_portrait_get.req | 13 ++++++ .../users_update_portrait_get.resp | 17 ++++++++ .../users_update_portrait_scale.req | 14 ++++++ .../users_update_portrait_scale.resp | 2 + src/plone/restapi/tests/test_documentation.py | 43 +++++++++++++++++++ 10 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 news/701.feature create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait.req create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait.resp create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait_get.req create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait_get.resp create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait_scale.req create mode 100644 src/plone/restapi/tests/http-examples/users_update_portrait_scale.resp diff --git a/docs/source/users.rst b/docs/source/users.rst index c4a2a689e5..92211c209d 100644 --- a/docs/source/users.rst +++ b/docs/source/users.rst @@ -137,6 +137,23 @@ A successful response to a PATCH request will be indicated by a :term:`204 No Co Any user is able to update their own properties and password (if allowed) by using the same request. +The user portrait/avatar can also be updated using the same serialization as the file one: + +.. http:example:: curl httpie python-requests + :request: ../../src/plone/restapi/tests/http-examples/users_update_portrait.req + +A successful response to a PATCH request will be indicated by a :term:`204 No Content` response. +Then asking for the user the portrait URL should be on the response: + +.. literalinclude:: ../../src/plone/restapi/tests/http-examples/users_update_portrait_get.resp + :language: http + +Adding the portrait via the @user endpoint does not scale it since it's assumed that the frontend will take care of it (resizing/cropping). +If you still want that Plone to take care of the scaling using the default Plone behavior for portraits, you have to add the ``scale`` attribute to the request: + +.. http:example:: curl httpie python-requests + :request: ../../src/plone/restapi/tests/http-examples/users_update_portrait_scale.req + Delete User ----------- diff --git a/news/701.feature b/news/701.feature new file mode 100644 index 0000000000..9116be145b --- /dev/null +++ b/news/701.feature @@ -0,0 +1,2 @@ +- Add support for add/update user portraits (@user endpoint) + [sneridagh] diff --git a/src/plone/restapi/services/users/update.py b/src/plone/restapi/services/users/update.py index 13d8d0f36d..2a0b50bd1c 100644 --- a/src/plone/restapi/services/users/update.py +++ b/src/plone/restapi/services/users/update.py @@ -6,7 +6,6 @@ from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import set_own_login_name from Products.PlonePAS.utils import scale_image -from StringIO import StringIO from zope.component import getAdapter from zope.component.hooks import getSite from zope.interface import alsoProvides @@ -164,7 +163,7 @@ def set_member_portrait(self, user, portrait): if portrait.get('scale', False): # Only scale if the scale (default Plone behavior) boolean is set # This should be handled by the core in the future - scaled, mimetype = scale_image(StringIO(data)) + scaled, mimetype = scale_image(six.StringIO(data)) else: # Normally, the scale and cropping is going to be handled in the # frontend diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait.req b/src/plone/restapi/tests/http-examples/users_update_portrait.req new file mode 100644 index 0000000000..ffbf93b944 --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait.req @@ -0,0 +1,13 @@ +PATCH /plone/@users/noam HTTP/1.1 +Accept: application/json +Authorization: Basic YWRtaW46c2VjcmV0 +Content-Type: application/json + +{ + "portrait": { + "content-type": "image/png", + "data": "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=", + "encoding": "base64", + "filename": "image.png" + } +} \ No newline at end of file diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait.resp b/src/plone/restapi/tests/http-examples/users_update_portrait.resp new file mode 100644 index 0000000000..0074ded3bc --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait.resp @@ -0,0 +1,2 @@ +HTTP/1.1 204 No Content + diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait_get.req b/src/plone/restapi/tests/http-examples/users_update_portrait_get.req new file mode 100644 index 0000000000..244906eac2 --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait_get.req @@ -0,0 +1,13 @@ +GET /plone/@users/noam HTTP/1.1 +Accept: application/json +Authorization: Basic YWRtaW46c2VjcmV0 +Content-Type: application/json + +{ + "portrait": { + "content-type": "image/png", + "data": "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=", + "encoding": "base64", + "filename": "image.png" + } +} \ No newline at end of file diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait_get.resp b/src/plone/restapi/tests/http-examples/users_update_portrait_get.resp new file mode 100644 index 0000000000..e159773a1f --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait_get.resp @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "@id": "http://localhost:55001/plone/@users/noam", + "description": null, + "email": "noam.chomsky@example.com", + "fullname": null, + "home_page": null, + "id": "noam", + "location": null, + "portrait": "http://localhost:55001/plone/portal_memberdata/portraits/noam", + "roles": [ + "Member" + ], + "username": "noam" +} \ No newline at end of file diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait_scale.req b/src/plone/restapi/tests/http-examples/users_update_portrait_scale.req new file mode 100644 index 0000000000..9781e06091 --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait_scale.req @@ -0,0 +1,14 @@ +PATCH /plone/@users/noam HTTP/1.1 +Accept: application/json +Authorization: Basic YWRtaW46c2VjcmV0 +Content-Type: application/json + +{ + "portrait": { + "content-type": "image/png", + "data": "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=", + "encoding": "base64", + "filename": "image.png", + "scale": true + } +} \ No newline at end of file diff --git a/src/plone/restapi/tests/http-examples/users_update_portrait_scale.resp b/src/plone/restapi/tests/http-examples/users_update_portrait_scale.resp new file mode 100644 index 0000000000..0074ded3bc --- /dev/null +++ b/src/plone/restapi/tests/http-examples/users_update_portrait_scale.resp @@ -0,0 +1,2 @@ +HTTP/1.1 204 No Content + diff --git a/src/plone/restapi/tests/test_documentation.py b/src/plone/restapi/tests/test_documentation.py index a2ddbfc570..9b6d9bfb49 100644 --- a/src/plone/restapi/tests/test_documentation.py +++ b/src/plone/restapi/tests/test_documentation.py @@ -847,6 +847,49 @@ def test_documentation_users_update(self): ) save_request_and_response_for_docs('users_update', response) + def test_documentation_users_update_portrait(self): + payload = { + 'portrait': { + 'filename': 'image.png', + 'encoding': 'base64', + 'data': 'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', + 'content-type': 'image/png' + } + } + api.user.create( + email='noam.chomsky@example.com', + username='noam' + ) + transaction.commit() + response = self.api_session.patch('/@users/noam', json=payload) + transaction.commit() + + response_get = self.api_session.get('/@users/noam', json=payload) + + save_request_and_response_for_docs('users_update_portrait', response) + save_request_and_response_for_docs( + 'users_update_portrait_get', response_get) + + def test_documentation_users_update_portrait_with_scale(self): + payload = { + 'portrait': { + 'filename': 'image.png', + 'encoding': 'base64', + 'data': 'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', + 'content-type': 'image/png', + 'scale': True + } + } + api.user.create( + email='noam.chomsky@example.com', + username='noam' + ) + transaction.commit() + response = self.api_session.patch('/@users/noam', json=payload) + + save_request_and_response_for_docs( + 'users_update_portrait_scale', response) + def test_documentation_users_delete(self): properties = { 'email': 'noam.chomsky@example.com',