general: add live prefill of files (#70077) #251

Merged
fpeters merged 1 commits from wip/70077-file-live-prefill into main 2023-04-20 17:12:02 +02:00
4 changed files with 112 additions and 18 deletions

View File

@ -1,5 +1,6 @@
import datetime
import itertools
import os
from unittest import mock
import pytest
@ -12,6 +13,7 @@ from wcs.carddef import CardDef
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
from wcs.qommon import misc
from wcs.qommon.upload_storage import PicklableUpload
from wcs.wf.form import WorkflowFormFieldsFormDef
from wcs.workflows import Workflow
@ -1974,6 +1976,76 @@ def test_comment_from_card_field(pub):
assert live_resp.json['result']['3']['content'] == '<p>Xbar {{ form_var_foo }}Ybar plopZ</p>'
def test_live_file_prefill_from_card_field(pub):
create_user(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'Template'
carddef.digest_templates = {'default': '{{ form_var_file }}'}
carddef.fields = [
fields.FileField(id='0', label='file', varname='file'),
]
carddef.store()
carddef.data_class().wipe()
for value in ('foo', 'bar'):
carddata = carddef.data_class()()
upload = PicklableUpload(f'{value}.txt', 'text/plain', 'ascii')
upload.receive([value.encode()])
carddata.data = {'0': upload}
carddata.just_created()
carddata.store()
ds = {'type': 'carddef:%s' % carddef.url_name}
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ItemField(id='1', label='card', type='item', varname='card', data_source=ds),
fields.FileField(
id='2',
label='file',
type='file',
prefill={'type': 'string', 'value': '{{ form_var_card_live_var_file }}'},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = '1'
live_resp = app.post('/test/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content']['name'] == 'foo.txt'
assert app.get('/test/' + live_resp.json['result']['2']['content']['url']).text == 'foo'
resp.form['f2$token'] = live_resp.json['result']['2']['content']['token']
resp = resp.form.submit('submit') # -> validation
resp = resp.form.submit('submit') # -> done
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['2'].base_filename == 'foo.txt'
assert formdata.data['2'].get_content() == b'foo'
# do not get file if storage is opaque remote.
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'a+') as fd:
fd.write(
'''
[storage-remote]
label = remote storage
class = wcs.qommon.upload_storage.RemoteOpaqueUploadStorage
ws = https://crypto.example.net/ws1/
'''
)
formdef.fields[1].storage = 'remote'
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
resp.form['f1'] = '1'
live_resp = app.post('/test/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] is None
def test_field_live_validation(pub):
FormDef.wipe()
formdef = FormDef()

View File

@ -31,6 +31,7 @@ from wcs import data_sources
from wcs.api_utils import get_query_flag, get_user_from_api_query_string, is_url_signed, sign_url_auto_orig
from wcs.blocks import BlockSubWidget, BlockWidget
from wcs.qommon.admin.texts import TextsDirectory
from wcs.qommon.upload_storage import get_storage_object
from wcs.wf.editable import EditableWorkflowStatusItem
from wcs.workflows import RedisplayFormException
@ -949,6 +950,20 @@ class FormStatusPage(Directory, FormTemplateMixin):
id_value = field.get_id_by_option_text(value)
if id_value:
value = id_value
elif field.key == 'file' and value:
file_storage = field.storage
if get_storage_object(file_storage).has_redirect_url(None):

chipottage, utiliser file_storage ici ?

chipottage, utiliser `file_storage` ici ?

Pris en compte.

Pris en compte.
# do not return anything if the file is not locally stored.
value = None

On ne sait jamais.

On ne sait jamais.
else:
tempfile = get_session().add_tempfile(value, file_storage)
value = {
'name': tempfile.get('base_filename'),
'type': tempfile.get('content_type'),
'size': tempfile.get('size'),
'token': tempfile.get('token'),
'url': 'tempfile?t=%s' % tempfile.get('token'),
}

C'est les mêmes infos que celles retournées par le tmp-upload, ça va permettre au javascript d'être identique.

C'est les mêmes infos que celles retournées par le tmp-upload, ça va permettre au javascript d'être identique.
entry['content'] = value
elif field.get_prefill_configuration().get('type') == 'user':
update_prefill = bool(get_request().form.get('modified_field_id') == 'user')

View File

@ -376,23 +376,10 @@ $.WcsFileUpload = {
return;
}
$(base_widget).find('.fileprogress').hide();
var base_file_name = data.result[0].name;
if (data.result[0].url) {
$(base_widget).find('.filename').empty().append(
$('<a>', {href: data.result[0].url, download: base_file_name, text: base_file_name}));
} else {
$(base_widget).find('.filename').text(base_file_name);
}
var $remove_button = $(base_widget).find('a.remove');
$remove_button.attr('title', $remove_button[0].dataset.titlePrefix + ' ' + base_file_name);
$(base_widget).find('.fileinfo').show();
$(base_widget).find('input[type=text]').val(data.result[0].token);
$(base_widget).parents('form').find('div.buttons button').prop('disabled', false);
$(this).hide();
$(base_widget).find('.use-file-from-fargo').hide();
$(base_widget).addClass('has-file').removeClass('has-no-file');
$(this).trigger('change');
$.WcsFileUpload.set_file(base_widget, data.result[0]);
$(base_widget).find('[type=file]').trigger('change');

Le code est déplacé dans ce nouveau $.WcsFileUpload.set_file, pour pouvoir être appelé lors de la mise à jour live. (c'est vraiment juste du code déplacé, pas de travail d'actualisation nettoyage ou mise à de nouvelles normes).

Le code est déplacé dans ce nouveau $.WcsFileUpload.set_file, pour pouvoir être appelé lors de la mise à jour live. (c'est vraiment juste du code déplacé, pas de travail d'actualisation nettoyage ou mise à de nouvelles normes).
$(base_widget).find('input[type=text]').trigger('change');
var $remove_button = $(base_widget).find('a.remove');
$remove_button.find('a.remove').focus();
},
fail: function(e, data) {
@ -448,5 +435,24 @@ $.WcsFileUpload = {
$(base_widget).find('.fileinfo').hide();
$(base_widget).parents('form').find('div.buttons button').prop('disabled', true);
var jqXHR = data.submit();
},
set_file: function(base_widget, data) {
if (! data) return;
var base_file_name = data.name;
if (data.url) {
$(base_widget).find('.filename').empty().append(
$('<a>', {href: data.url, download: base_file_name, text: base_file_name}));
} else {
$(base_widget).find('.filename').text(base_file_name);
}
var $remove_button = $(base_widget).find('a.remove');
$remove_button.attr('title', $remove_button[0].dataset.titlePrefix + ' ' + base_file_name);
$(base_widget).find('.fileinfo').show();
$(base_widget).find('input[type=text]').val(data.token);
$(base_widget).parents('form').find('div.buttons button').prop('disabled', false);
$(base_widget).find('[type=file]').hide();
$(base_widget).find('.use-file-from-fargo').hide();
$(base_widget).addClass('has-file').removeClass('has-no-file');
}
}

View File

@ -632,8 +632,9 @@ $(function() {
// to the formatted value, which is expected.
$(widget).find('input[type=hidden]').val(value.content);
$(widget).find('input[type=text]').val(value.text_content);
}
if ($widget.hasClass('CheckboxWidget')) {
} else if ($(widget).is('.FileWithPreviewWidget')) {
$.WcsFileUpload.set_file(widget, value.content);

Voilà, si on est un fichier, on appelle le nouveau set_file().

Voilà, si on est un fichier, on appelle le nouveau set_file().
} else if ($widget.hasClass('CheckboxWidget')) {
// replace checkbox input value
$widget.find('input[type=checkbox]').prop('checked', value.content);
}