From 1bf9f991e0345a93c029b1607e4c9a6ac84d8cbd Mon Sep 17 00:00:00 2001
From: Thomas NOEL <tnoel@entrouvert.com>
Date: Fri, 7 Feb 2020 15:23:38 +0100
Subject: [PATCH] upload_storage: add get_json_value and use it in json exports
 (#39517)

---
 tests/test_upload_storage.py | 16 +++++++++++++++-
 wcs/fields.py                | 10 ++--------
 wcs/qommon/misc.py           |  8 ++++----
 wcs/qommon/upload_storage.py | 20 ++++++++++++++++++++
 4 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/tests/test_upload_storage.py b/tests/test_upload_storage.py
index 61d00dd5..e7fb74d7 100644
--- a/tests/test_upload_storage.py
+++ b/tests/test_upload_storage.py
@@ -185,5 +185,19 @@ def test_form_file_field_upload_storage(wscall, pub):
     # api access (json export)
     resp = admin_app.get('/api/forms/test/1/', status=200)
     assert resp.json['fields']['file']['content'].startswith('/9j/4AAQSkZJRg')
+    assert 'storage' not in resp.json['fields']['file']
     assert resp.json['fields']['remote_file']['content'] == ''
-    assert resp.json['fields']['remote_file']['storage_attrs']['redirect_url'] == 'https://crypto.example.net/'
+    assert resp.json['fields']['remote_file']['storage'] == 'remote'
+    assert resp.json['fields']['remote_file']['storage_attrs'] == {'redirect_url': 'https://crypto.example.net/'}
+
+    resp = admin_app.get('/api/forms/test/2/', status=200)
+    assert resp.json['fields']['remote_file']['content'] == ''
+    assert resp.json['fields']['remote_file']['storage'] == 'remote'
+    assert resp.json['fields']['remote_file']['storage_attrs'] == {
+            'redirect_url': 'https://crypto.example.net/',
+            'backoffice_redirect_url': None, 'frontoffice_redirect_url': None}
+
+    resp = admin_app.get('/api/forms/test/3/', status=200)
+    assert resp.json['fields']['remote_file']['content'] == ''
+    assert resp.json['fields']['remote_file']['storage'] == 'remote-bo'
+    assert resp.json['fields']['remote_file']['storage_attrs'] == {'redirect_url': 'https://crypto.example.net/'}
diff --git a/wcs/fields.py b/wcs/fields.py
index aea70893..02835b78 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -1058,14 +1058,8 @@ class FileField(WidgetField):
         return [str(value) if value else '']
 
     def get_json_value(self, value):
-        out = {
-            'field_id': self.id,
-            'filename': value.base_filename,
-            'content_type': value.content_type or 'application/octet-stream',
-            'content': force_text(base64.b64encode(value.get_content())),
-        }
-        if hasattr(value, 'storage_attrs'):
-            out['storage_attrs'] = value.storage_attrs
+        out = value.get_json_value()
+        out['field_id'] = self.id
         return out
 
     def from_json_value(self, value):
diff --git a/wcs/qommon/misc.py b/wcs/qommon/misc.py
index 1f50d166..2f866673 100644
--- a/wcs/qommon/misc.py
+++ b/wcs/qommon/misc.py
@@ -539,15 +539,15 @@ class JSONEncoder(json.JSONEncoder):
         if six.PY3 and isinstance(obj, bytes):
             return obj.decode('ascii')
 
+        if hasattr(obj, 'get_json_value'):
+            return obj.get_json_value()
+
         if hasattr(obj, 'base_filename'):
-            out = {
+            return {
                 'filename': obj.base_filename,
                 'content_type': obj.content_type or 'application/octet-stream',
                 'content': base64.b64encode(obj.get_content()),
             }
-            if hasattr(obj, 'storage_attrs'):  # remote storage
-                out['storage_attrs'] = obj.storage_attrs
-            return out
 
         # Let the base class default method raise the TypeError
         return json.JSONEncoder.default(self, obj)
diff --git a/wcs/qommon/upload_storage.py b/wcs/qommon/upload_storage.py
index 54236c82..67a255fc 100644
--- a/wcs/qommon/upload_storage.py
+++ b/wcs/qommon/upload_storage.py
@@ -20,6 +20,7 @@ import os
 from quixote import get_publisher
 from quixote.http_request import Upload
 
+from django.utils.encoding import force_text
 from django.utils.module_loading import import_string
 from django.utils.six import StringIO
 
@@ -77,6 +78,9 @@ class PicklableUpload(Upload):
             return base64.encodestring(content)
         return b''
 
+    def get_json_value(self):
+        return get_storage_object(getattr(self, 'storage', None)).get_json_value(self)
+
     def can_thumbnail(self):
         return get_storage_object(getattr(self, 'storage', None)).can_thumbnail(self)
 
@@ -129,6 +133,13 @@ class UploadStorage(object):
             upload.fp.seek(0)
             atomic_write(filepath, upload.fp, async_op=False)
 
+    def get_json_value(self, upload):
+        return {
+            'filename': upload.base_filename,
+            'content_type': upload.content_type or 'application/octet-stream',
+            'content': force_text(base64.b64encode(upload.get_content())),
+        }
+
     def can_thumbnail(self, upload):
         return can_thumbnail(upload.content_type)
 
@@ -202,6 +213,15 @@ class RemoteOpaqueUploadStorage(object):
     def save(self, upload):
         pass
 
+    def get_json_value(self, upload):
+        return {
+            'filename': upload.base_filename,
+            'content_type': upload.content_type or 'application/octet-stream',
+            'content': '',
+            'storage': upload.storage,
+            'storage_attrs': upload.storage_attrs,
+        }
+
     def can_thumbnail(self, upload):
         return False
 
-- 
2.24.1

