0001-json-export-content-of-uploads-7254.patch
help/fr/api-get.page | ||
---|---|---|
71 | 71 |
} |
72 | 72 |
</code> |
73 | 73 | |
74 |
<p> |
|
75 |
Seuls les champs ayant un <em>nom de variable</em> sont exportés dans <code>fields</code>. |
|
76 |
</p> |
|
77 | ||
74 | 78 |
<note> |
75 | 79 |
<p> |
76 | 80 |
Il est bien sûr nécessaire de disposer des autorisations nécessaires pour |
... | ... | |
102 | 106 |
<section id="datatypes"> |
103 | 107 |
<title>Types de données</title> |
104 | 108 | |
109 |
<p> |
|
110 |
Les données d'un formulaire sont placées dans le champs <code>fields</code> de |
|
111 |
la réponse. Les champs de type simple tels que « Texte », « Texte long » ou |
|
112 |
« Courriel » sont vus en tant que chaîne de caractères : |
|
113 |
</p> |
|
114 | ||
115 |
<code mime="application/json"> |
|
116 |
(...) |
|
117 |
"fields": { |
|
118 |
"email": "marc@example.net", |
|
119 |
"nom": "L.", |
|
120 |
"prenom": "Marc" |
|
121 |
}, |
|
122 |
(...) |
|
123 |
</code> |
|
124 | ||
125 |
<section> |
|
126 |
<title>Représentation d'un champ « Fichier »</title> |
|
127 | ||
128 |
<p> |
|
129 |
Les champs de type « Fichier » sont exportés selon le schéma suivant : |
|
130 |
</p> |
|
131 | ||
132 |
<code mime="application/json"> |
|
133 |
(...) |
|
134 |
"fields": { |
|
135 |
"photo": { |
|
136 |
"filename": "exemple.txt", |
|
137 |
"content_type": "text/plain", |
|
138 |
"content": "Q2VjaSBuJ2VzdCBwYXMgdW4gZXhlbXBsZS4=" |
|
139 |
} |
|
140 |
}, |
|
141 |
(...) |
|
142 |
</code> |
|
143 | ||
144 |
<p> |
|
145 |
La valeur de <code>content</code> est le contenu du fichier, encodé en base64. |
|
146 |
</p> |
|
147 | ||
148 |
</section> |
|
149 | ||
105 | 150 |
</section> |
106 | 151 | |
107 | 152 |
<section id="listing"> |
... | ... | |
184 | 229 |
https://www.example.net/api/forms/inscriptions/list?full=on</input> |
185 | 230 |
</screen> |
186 | 231 | |
232 |
<p> |
|
233 |
À noter que pour ne pas alourdir l'export en mode <code>full=on</code>, les |
|
234 |
champs de type « Fichier » ne sont pas exportés. |
|
235 |
</p> |
|
236 | ||
187 | 237 |
</section> |
188 | 238 | |
189 | 239 |
tests/test_api.py | ||
---|---|---|
10 | 10 |
import time |
11 | 11 | |
12 | 12 |
from quixote import cleanup, get_publisher |
13 |
from wcs.qommon.form import PicklableUpload |
|
13 | 14 |
from wcs.users import User |
14 | 15 |
from wcs.roles import Role |
15 | 16 |
from wcs.formdef import FormDef |
... | ... | |
242 | 243 |
formdef.fields = [ |
243 | 244 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
244 | 245 |
fields.StringField(id='1', label='foobar2'), |
245 |
fields.DateField(id='2', label='foobar3', varname='date'),] |
|
246 |
fields.DateField(id='2', label='foobar3', varname='date'), |
|
247 |
fields.FileField(id='3', label='foobar4', varname='file'),] |
|
246 | 248 |
formdef.store() |
247 | 249 | |
248 | 250 |
formdata = formdef.data_class()() |
249 | 251 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
250 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': date} |
|
252 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
253 |
upload.receive(['base64me']) |
|
254 |
formdata.data = {'0': 'foo@localhost', '1': 'xxx', '2': date, '3': upload} |
|
251 | 255 |
formdata.user_id = local_user.id |
252 | 256 |
formdata.just_created() |
253 | 257 |
formdata.store() |
254 | 258 | |
255 | 259 |
resp = get_app(pub).get(sign_uri('/test/%s/' % formdata.id, user=local_user)) |
256 | 260 |
assert 'last_update_time' in resp.json |
261 |
assert len(resp.json['fields']) == 3 # foobar2 has no varname, not in json |
|
257 | 262 |
assert resp.json['user']['name'] == local_user.name |
258 | 263 |
assert resp.json['fields']['foobar'] == 'foo@localhost' |
259 | 264 |
assert resp.json['fields']['date'] == '2014-01-20' |
260 |
assert len(resp.json['fields']) == 2 # foobar2 has no varname, not in json |
|
265 |
assert resp.json['fields']['file']['content'] == 'YmFzZTY0bWU=' # base64('base64me') |
|
266 |
assert resp.json['fields']['file']['filename'] == 'test.txt' |
|
267 |
assert resp.json['fields']['file']['content_type'] == 'text/plain' |
|
261 | 268 | |
262 | 269 |
def test_myspace_forms(local_user): |
263 | 270 |
FormDef.wipe() |
... | ... | |
318 | 325 |
fields.StringField(id='0', label='foobar', varname='foobar'), |
319 | 326 |
fields.ItemField(id='1', label='foobar3', varname='foobar3', type='item', |
320 | 327 |
items=['foo', 'bar', 'baz']), |
328 |
fields.FileField(id='2', label='foobar4', varname='file'), |
|
321 | 329 |
] |
322 | 330 |
formdef.store() |
323 | 331 | |
... | ... | |
327 | 335 |
for i in range(30): |
328 | 336 |
formdata = data_class() |
329 | 337 |
date = time.strptime('2014-01-20', '%Y-%m-%d') |
330 |
formdata.data = {'0': 'FOO BAR %d' % i} |
|
338 |
upload = PicklableUpload('test.txt', 'text/plain', 'ascii') |
|
339 |
upload.receive(['base64me']) |
|
340 |
formdata.data = {'0': 'FOO BAR %d' % i, '2': upload} |
|
331 | 341 |
if i%4 == 0: |
332 | 342 |
formdata.data['1'] = 'foo' |
333 | 343 |
formdata.data['1_display'] = 'foo' |
... | ... | |
363 | 373 |
assert len(resp.json) == 30 |
364 | 374 |
assert 'receipt_time' in resp.json[0] |
365 | 375 |
assert 'fields' in resp.json[0] |
376 |
assert 'file' not in resp.json[0]['fields'] # no file export in full lists |
|
366 | 377 | |
367 | 378 |
# check filtered results |
368 | 379 |
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-foobar3=foo', user=local_user)) |
wcs/backoffice/management.py | ||
---|---|---|
861 | 861 |
selected_filter, user=user, query=query, criterias=criterias, |
862 | 862 |
order_by=order_by) |
863 | 863 |
if get_request().form.get('full') == 'on': |
864 |
output = [json.loads(filled.export_to_json()) for filled in items] |
|
864 |
output = [json.loads(filled.export_to_json(include_files=False)) for filled in items]
|
|
865 | 865 |
else: |
866 | 866 |
output = [{'id': filled.id, |
867 | 867 |
'url': filled.get_url(), |
wcs/fields.py | ||
---|---|---|
17 | 17 |
import time |
18 | 18 |
import random |
19 | 19 |
import re |
20 |
import base64 |
|
20 | 21 |
import xml.etree.ElementTree as ET |
21 | 22 | |
22 | 23 |
from quixote import get_request, get_publisher |
... | ... | |
709 | 710 |
return ['%s' % value] |
710 | 711 | |
711 | 712 |
def get_json_value(self, value): |
712 |
return {'field_id': self.id, 'filename': value.base_filename} |
|
713 |
return { |
|
714 |
'field_id': self.id, |
|
715 |
'filename': value.base_filename, |
|
716 |
'content_type': value.content_type or 'application/octet-stream', |
|
717 |
'content': base64.b64encode(value.get_content()) |
|
718 |
} |
|
713 | 719 | |
714 | 720 |
def perform_more_widget_changes(self, form, kwargs, edit = True): |
715 | 721 |
if not edit: |
wcs/formdata.py | ||
---|---|---|
28 | 28 |
from qommon.substitution import Substitutions |
29 | 29 | |
30 | 30 |
from roles import Role |
31 |
from fields import FileField |
|
31 | 32 | |
32 | 33 | |
33 | 34 |
def get_dict_with_varnames(fields, data, formdata=None, varnames_only=False): |
... | ... | |
87 | 88 |
del d[k] |
88 | 89 | |
89 | 90 | |
90 |
def get_json_dict(fields, data): |
|
91 |
def get_json_dict(fields, data, include_files=True):
|
|
91 | 92 |
new_data = {} |
92 | 93 |
for field in fields: |
93 | 94 |
if not field.varname: # exports only named fields |
94 | 95 |
continue |
96 |
if not include_files and isinstance(field, FileField): |
|
97 |
continue |
|
95 | 98 |
if data is not None: |
96 | 99 |
value = data.get(field.id) |
97 | 100 |
if value and hasattr(field, 'get_json_value'): |
... | ... | |
527 | 530 |
evo.parts = None |
528 | 531 |
self.store() |
529 | 532 | |
530 |
def export_to_json(self): |
|
533 |
def export_to_json(self, include_files=True):
|
|
531 | 534 |
data = {} |
532 | 535 |
data['id'] = '%s/%s' % (self.formdef.url_name, self.id) |
533 | 536 |
data['display_id'] = self.get_display_id() |
... | ... | |
553 | 556 |
if user: |
554 | 557 |
data['user'] = {'id': user.id, 'name': user.display_name} |
555 | 558 | |
556 |
data['fields'] = get_json_dict(self.formdef.fields, self.data) |
|
559 |
data['fields'] = get_json_dict(self.formdef.fields, self.data, |
|
560 |
include_files=include_files) |
|
557 | 561 | |
558 | 562 |
data['workflow'] = {} |
559 | 563 |
wf_status = self.get_visible_status() |
wcs/qommon/form.py | ||
---|---|---|
687 | 687 |
# quack like UploadedFile |
688 | 688 |
return self.get_file_pointer() |
689 | 689 | |
690 |
def get_content(self): |
|
691 |
if hasattr(self, 'qfilename'): |
|
692 |
filename = os.path.join(get_publisher().app_dir, 'uploads', self.qfilename) |
|
693 |
return file(filename).read() |
|
694 |
return None |
|
695 | ||
690 | 696 | |
691 | 697 |
class EmailWidget(StringWidget): |
692 | 698 |
HTML_TYPE = 'email' |
693 |
- |