Compare commits
13 Commits
71a1489c88
...
d5036ed141
Author | SHA1 | Date |
---|---|---|
Pierre Ducroquet | d5036ed141 | |
Pierre Ducroquet | d8ea725cdc | |
Pierre Ducroquet | 496b95910c | |
Frédéric Péters | c191656d4a | |
Frédéric Péters | 4e0b3469f1 | |
Frédéric Péters | b6c83cca37 | |
Frédéric Péters | aa070498e4 | |
Frédéric Péters | 0dc5d3267f | |
Frédéric Péters | 61ce06676d | |
Frédéric Péters | 291fea5b8b | |
Frédéric Péters | 62d178f73c | |
Frédéric Péters | b9b6912385 | |
Frédéric Péters | 992bc9720a |
|
@ -1266,6 +1266,9 @@ def test_api_list_formdata(pub, local_user):
|
|||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on&order_by=-foobar', user=local_user))
|
||||
assert [d['fields']['foobar'] for d in resp.json] == ['FOO BAR %02d' % i for i in range(29, -1, -1)]
|
||||
|
||||
# check 400 on multiple order_by
|
||||
get_app(pub).get(sign_uri('/api/forms/test/list?full=on&order_by=f0,foobar', user=local_user), status=400)
|
||||
|
||||
# check fts
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on&q=foo', user=local_user))
|
||||
assert len(resp.json) == 30
|
||||
|
@ -3048,6 +3051,65 @@ def test_api_geojson_formdata_related_field(pub, local_user):
|
|||
assert properties['item - foobar'] == 'test.txt'
|
||||
|
||||
|
||||
def test_api_geojson_formdata_file_in_block_field(pub, local_user):
|
||||
pub.role_class.wipe()
|
||||
role = pub.role_class(name='test')
|
||||
role.store()
|
||||
|
||||
# add role to user
|
||||
local_user.roles = [role.id]
|
||||
local_user.store()
|
||||
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.FileField(id='123', label='file', varname='foo'),
|
||||
]
|
||||
block.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test'
|
||||
formdef.workflow_roles = {'_receiver': role.id}
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='1', label='test', varname='blockdata', block_slug='foobar', max_items=3),
|
||||
]
|
||||
formdef.geolocations = {'base': 'Location'}
|
||||
formdef.store()
|
||||
|
||||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
|
||||
upload.receive([b'base64me'])
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {'1': {'data': [{'123': upload}], 'schema': {'123': 'file'}}, '1_display': 'test.txt'}
|
||||
formdata.geolocations = {'base': {'lat': 48, 'lon': 2}}
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
# get with blockfield
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?filter=all&1=on', user=local_user))
|
||||
assert len(resp.json['features']) == 1
|
||||
assert resp.json['features'][0]['properties']['id'] == '1-1'
|
||||
assert resp.json['features'][0]['properties']['display_fields'][0]['value'] == 'test.txt'
|
||||
|
||||
# get with file field in block as property
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?filter=all&1-123=on', user=local_user))
|
||||
assert len(resp.json['features']) == 1
|
||||
assert resp.json['features'][0]['properties']['id'] == '1-1'
|
||||
assert resp.json['features'][0]['properties']['display_fields'][0]['value'] == 'test.txt'
|
||||
assert 'download?f=1$0$123' in resp.json['features'][0]['properties']['display_fields'][0]['html_value']
|
||||
|
||||
# check full=on
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?filter=all&full=on', user=local_user))
|
||||
assert len(resp.json['features']) == 1
|
||||
properties = {x['label']: x['value'] for x in resp.json['features'][0]['properties']['display_fields']}
|
||||
assert properties['test'] == 'test.txt'
|
||||
assert 'file' not in properties
|
||||
|
||||
|
||||
def test_api_distance_filter(pub, local_user):
|
||||
pub.role_class.wipe()
|
||||
role = pub.role_class(name='test')
|
||||
|
|
|
@ -2829,3 +2829,55 @@ def test_block_titles_and_empty_block_on_summary_page(pub, emails):
|
|||
assert 'Form Title' not in resp.text
|
||||
assert 'Form Page' not in emails.get('New form (form title)')['msg'].get_payload()[0].get_payload()
|
||||
assert 'Form Title' not in emails.get('New form (form title)')['msg'].get_payload()[0].get_payload()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('logged_user', ['logged', 'anonymous'])
|
||||
@pytest.mark.parametrize('tracking_code', ['with-tracking-code', 'without-tracking-code'])
|
||||
def test_block_multiple_rows_single_draft(pub, logged_user, tracking_code):
|
||||
create_user(pub)
|
||||
FormDef.wipe()
|
||||
BlockDef.wipe()
|
||||
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [fields.StringField(id='123', required=True, label='Test')]
|
||||
block.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='1', label='test', block_slug='foobar', max_items=5),
|
||||
]
|
||||
formdef.enable_tracking_codes = bool(tracking_code == 'with-tracking-code')
|
||||
formdef.store()
|
||||
formdef.data_class().wipe()
|
||||
|
||||
app = get_app(pub)
|
||||
if logged_user == 'logged':
|
||||
login(app, username='foo', password='foo')
|
||||
resp = app.get(formdef.get_url())
|
||||
resp.form['f1$element0$f123'].value = 'Hello World'
|
||||
resp = resp.form.submit('f1$add_element') # add second row
|
||||
|
||||
if logged_user == 'logged' or formdef.enable_tracking_codes:
|
||||
assert formdef.data_class().count() == 1
|
||||
assert formdef.data_class().select()[0].status == 'draft'
|
||||
else:
|
||||
assert formdef.data_class().count() == 0
|
||||
|
||||
resp.form['f1$element1$f123'].value = 'Something else'
|
||||
resp = resp.form.submit('f1$add_element') # add third row
|
||||
|
||||
if logged_user == 'logged' or formdef.enable_tracking_codes:
|
||||
assert formdef.data_class().count() == 1
|
||||
assert formdef.data_class().select()[0].status == 'draft'
|
||||
else:
|
||||
assert formdef.data_class().count() == 0
|
||||
|
||||
resp.form['f1$element2$f123'].value = 'Something else'
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
resp = resp.form.submit('submit') # -> end page
|
||||
resp = resp.follow()
|
||||
|
||||
assert formdef.data_class().count() == 1
|
||||
assert formdef.data_class().select()[0].status == 'wf-new'
|
||||
|
|
|
@ -1396,6 +1396,29 @@ def test_form_item_dynamic_map_data_source(pub, http_requests):
|
|||
assert len(resp_geojson.json['features']) == 2
|
||||
|
||||
|
||||
def test_form_item_map_data_source_initial_position(pub, http_requests):
|
||||
NamedDataSource.wipe()
|
||||
data_source = NamedDataSource(name='foobar')
|
||||
data_source.data_source = {
|
||||
'type': 'geojson',
|
||||
'value': 'http://remote.example.net/geojson',
|
||||
}
|
||||
data_source.id_property = 'id'
|
||||
data_source.label_template_property = '{{ text }}'
|
||||
data_source.cache_duration = '5'
|
||||
data_source.store()
|
||||
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [
|
||||
fields.ItemField(id='1', label='map', display_mode='map', initial_position='geoloc'),
|
||||
]
|
||||
formdef.store()
|
||||
formdef.data_class().wipe()
|
||||
app = get_app(pub)
|
||||
resp = app.get('/test/')
|
||||
assert resp.pyquery('[data-init_with_geoloc="true"]')
|
||||
|
||||
|
||||
def test_form_item_timetable_data_source(pub, http_requests):
|
||||
NamedDataSource.wipe()
|
||||
data_source = NamedDataSource(name='foobar')
|
||||
|
|
|
@ -1331,6 +1331,7 @@ def test_card_custom_id_format(pub):
|
|||
assert data_class.force_valid_id_characters('_Fôô bar-') == '_Foo-bar-'
|
||||
assert data_class.force_valid_id_characters('_Fôô bar☭-') == '_Foo-bar-'
|
||||
assert data_class.force_valid_id_characters('_Fôô bar❗') == '_Foo-bar'
|
||||
assert data_class.force_valid_id_characters(' Foo\'bar') == 'Foo-bar'
|
||||
|
||||
|
||||
def test_card_update_related(pub):
|
||||
|
@ -1519,6 +1520,80 @@ def test_card_update_related_with_custom_view(pub):
|
|||
assert formdata.data['1_display'] == 'view-card1-change1'
|
||||
|
||||
|
||||
def test_card_update_related_with_items_dynamic_custom_view(pub):
|
||||
CardDef.wipe()
|
||||
FormDef.wipe()
|
||||
pub.custom_view_class.wipe()
|
||||
|
||||
carddef = CardDef()
|
||||
carddef.name = 'foo'
|
||||
carddef.fields = [
|
||||
StringField(id='1', label='Test', varname='foo'),
|
||||
StringField(id='2', label='Test2'),
|
||||
]
|
||||
carddef.digest_templates = {
|
||||
'default': '{{ form_var_foo }}',
|
||||
'custom-view:view': 'view-{{ form_var_foo }}',
|
||||
}
|
||||
carddef.store()
|
||||
carddef.data_class().wipe()
|
||||
|
||||
carddata1 = carddef.data_class()()
|
||||
carddata1.data = {'1': 'card1', '2': 'ok'}
|
||||
carddata1.just_created()
|
||||
carddata1.store()
|
||||
|
||||
carddata2 = carddef.data_class()()
|
||||
carddata2.data = {'1': 'card2', '2': 'ok'}
|
||||
carddata2.just_created()
|
||||
carddata2.store()
|
||||
|
||||
custom_view = pub.custom_view_class()
|
||||
custom_view.title = 'view'
|
||||
custom_view.formdef = carddef
|
||||
custom_view.columns = {'list': [{'id': 'id'}]}
|
||||
custom_view.filters = {}
|
||||
custom_view.filters = {'filter-2': 'on', 'filter-2-value': '{{ form_var_data }}'}
|
||||
custom_view.visibility = 'datasource'
|
||||
custom_view.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'foo'
|
||||
formdef.fields = [
|
||||
StringField(id='0', label='Foo', varname='data'),
|
||||
ItemsField(id='1', label='Test', data_source={'type': 'carddef:foo:view'}),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {'0': 'ok', '1': ['1']}
|
||||
formdata.data['1_display'] = 'view-card1'
|
||||
assert formdata.data['1_display'] == 'view-card1'
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
# check usual situation, carddata changed but is still present in the result set
|
||||
pub.cleanup()
|
||||
carddef = carddef.get(carddef.id)
|
||||
carddata1 = carddef.data_class().get(carddata1.id)
|
||||
carddata1.data = {'1': 'card1-change1', '2': 'ok'}
|
||||
carddata1.store()
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.data['1'] == ['1']
|
||||
assert formdata.data['1_display'] == 'view-card1-change1'
|
||||
|
||||
# check with a card that will no longer be part of the custom view result set
|
||||
pub.cleanup()
|
||||
carddef = carddef.get(carddef.id)
|
||||
carddata1 = carddef.data_class().get(carddata1.id)
|
||||
carddata1.data = {'1': 'card1-change2', '2': 'ko'}
|
||||
carddata1.store()
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.data['1_display'] == 'view-card1-change1' # no update, but data still here
|
||||
|
||||
|
||||
def test_card_update_related_cascading(pub):
|
||||
BlockDef.wipe()
|
||||
CardDef.wipe()
|
||||
|
|
|
@ -12,7 +12,7 @@ from wcs.carddef import CardDef
|
|||
from wcs.categories import Category
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.fields import CommentField, ItemField, PageField, StringField
|
||||
from wcs.fields import BlockField, CommentField, ItemField, PageField, StringField
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
from wcs.qommon.form import UploadedFile
|
||||
|
@ -362,6 +362,61 @@ def test_form_snapshot_diff(pub):
|
|||
assert 'Snapshot <a href="%s/view/">%s</a> - (Version 42.0)' % (snapshot3.id, snapshot3.id) in resp
|
||||
|
||||
|
||||
def test_form_snapshot_diff_with_reference_error(pub):
|
||||
create_superuser(pub)
|
||||
create_role(pub)
|
||||
|
||||
BlockDef.wipe()
|
||||
blockdef = BlockDef()
|
||||
blockdef.name = 'testblock'
|
||||
blockdef.fields = []
|
||||
blockdef.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'testform'
|
||||
formdef.fields = [
|
||||
BlockField(id='1', label='block1', varname='foo', block_slug=blockdef.slug),
|
||||
]
|
||||
formdef.store()
|
||||
assert pub.snapshot_class.count() == 2
|
||||
snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
||||
|
||||
formdef.fields.append(StringField(id=2, label='Test'))
|
||||
formdef.store()
|
||||
assert pub.snapshot_class.count() == 3
|
||||
|
||||
formdef.fields = formdef.fields[1:]
|
||||
formdef.store()
|
||||
assert pub.snapshot_class.count() == 4
|
||||
snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id)
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get(
|
||||
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
||||
% (formdef.id, snapshot1.id, snapshot3.id)
|
||||
)
|
||||
assert resp.pyquery('h2').text() == 'Compare snapshots (XML)'
|
||||
resp = app.get(
|
||||
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s&mode=inspect'
|
||||
% (formdef.id, snapshot1.id, snapshot3.id)
|
||||
)
|
||||
assert resp.pyquery('h2').text() == 'Compare snapshots (Inspect)'
|
||||
|
||||
BlockDef.wipe()
|
||||
resp = app.get(
|
||||
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
|
||||
% (formdef.id, snapshot1.id, snapshot3.id)
|
||||
)
|
||||
assert resp.pyquery('h2').text() == 'Compare snapshots (XML)'
|
||||
resp = app.get(
|
||||
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s&mode=inspect'
|
||||
% (formdef.id, snapshot1.id, snapshot3.id)
|
||||
)
|
||||
assert resp.pyquery('h2').text() == 'Error'
|
||||
assert 'Can not display snapshot (Unknown referenced objects)' in resp.text
|
||||
|
||||
|
||||
def test_form_snapshot_comments(pub):
|
||||
create_superuser(pub)
|
||||
create_role(pub)
|
||||
|
|
|
@ -1754,7 +1754,10 @@ class FormDefPage(Directory, TempfileDirectoryMixin, DocumentableMixin):
|
|||
else:
|
||||
role_label = '-'
|
||||
view.role = role_label
|
||||
|
||||
context['custom_views'] = sorted(custom_views, key=lambda x: getattr(x, 'title'))
|
||||
context['is_carddef'] = isinstance(self.formdef, CardDef)
|
||||
|
||||
if not hasattr(self.formdef, 'snapshot_object'):
|
||||
deprecations = DeprecationsDirectory()
|
||||
context['deprecations'] = deprecations.get_deprecations(
|
||||
|
@ -1762,54 +1765,53 @@ class FormDefPage(Directory, TempfileDirectoryMixin, DocumentableMixin):
|
|||
)
|
||||
context['deprecation_titles'] = deprecations.titles
|
||||
|
||||
receipt_time_criteria = GreaterOrEqual(
|
||||
'receipt_time',
|
||||
datetime.datetime.now() - datetime.timedelta(days=self.formdef.get_drafts_lifespan()),
|
||||
)
|
||||
receipt_time_criteria = GreaterOrEqual(
|
||||
'receipt_time',
|
||||
datetime.datetime.now() - datetime.timedelta(days=self.formdef.get_drafts_lifespan()),
|
||||
)
|
||||
|
||||
temp_drafts = defaultdict(int)
|
||||
for formdata in self.formdef.data_class().select_iterator(
|
||||
clause=[Equal('status', 'draft'), receipt_time_criteria], itersize=200
|
||||
):
|
||||
page_id = formdata.page_id if formdata.page_id is not None else '_unknown'
|
||||
temp_drafts[page_id] += 1
|
||||
temp_drafts = defaultdict(int)
|
||||
for formdata in self.formdef.data_class().select_iterator(
|
||||
clause=[Equal('status', 'draft'), receipt_time_criteria], itersize=200
|
||||
):
|
||||
page_id = formdata.page_id if formdata.page_id is not None else '_unknown'
|
||||
temp_drafts[page_id] += 1
|
||||
|
||||
total_drafts = sum(temp_drafts.values()) if temp_drafts else 0
|
||||
drafts = {}
|
||||
special_page_index_mapping = {
|
||||
'_first_page': -1000, # first
|
||||
'_unknown': 1000, # last
|
||||
'_confirmation_page': 999, # second to last
|
||||
}
|
||||
if total_drafts:
|
||||
for page_id, page_index in special_page_index_mapping.items():
|
||||
try:
|
||||
page_total = temp_drafts.pop(page_id)
|
||||
except KeyError:
|
||||
page_total = 0
|
||||
drafts[page_id] = {'total': page_total, 'field': None, 'page_index': page_index}
|
||||
for page_id, page_total in temp_drafts.items():
|
||||
for index, field in enumerate(self.formdef.iter_fields(with_backoffice_fields=False)):
|
||||
if page_id == field.id and isinstance(field, PageField):
|
||||
drafts[page_id] = {
|
||||
'total': page_total,
|
||||
'field': field,
|
||||
'page_index': index,
|
||||
}
|
||||
break
|
||||
else:
|
||||
drafts['_unknown']['total'] += page_total
|
||||
total_drafts = sum(temp_drafts.values()) if temp_drafts else 0
|
||||
drafts = {}
|
||||
special_page_index_mapping = {
|
||||
'_first_page': -1000, # first
|
||||
'_unknown': 1000, # last
|
||||
'_confirmation_page': 999, # second to last
|
||||
}
|
||||
if total_drafts:
|
||||
for page_id, page_index in special_page_index_mapping.items():
|
||||
try:
|
||||
page_total = temp_drafts.pop(page_id)
|
||||
except KeyError:
|
||||
page_total = 0
|
||||
drafts[page_id] = {'total': page_total, 'field': None, 'page_index': page_index}
|
||||
for page_id, page_total in temp_drafts.items():
|
||||
for index, field in enumerate(self.formdef.iter_fields(with_backoffice_fields=False)):
|
||||
if page_id == field.id and isinstance(field, PageField):
|
||||
drafts[page_id] = {
|
||||
'total': page_total,
|
||||
'field': field,
|
||||
'page_index': index,
|
||||
}
|
||||
break
|
||||
else:
|
||||
drafts['_unknown']['total'] += page_total
|
||||
|
||||
for draft_data in drafts.values():
|
||||
draft_data['percent'] = 100 * draft_data['total'] / total_drafts
|
||||
for draft_data in drafts.values():
|
||||
draft_data['percent'] = 100 * draft_data['total'] / total_drafts
|
||||
|
||||
total_formdata = self.formdef.data_class().count([receipt_time_criteria])
|
||||
context['drafts'] = sorted(drafts.items(), key=lambda x: x[1]['page_index'])
|
||||
context['percent_submitted_formdata'] = 100 * (total_formdata - total_drafts) / total_formdata
|
||||
context['total_formdata'] = total_formdata
|
||||
total_formdata = self.formdef.data_class().count([receipt_time_criteria])
|
||||
context['drafts'] = sorted(drafts.items(), key=lambda x: x[1]['page_index'])
|
||||
context['percent_submitted_formdata'] = 100 * (total_formdata - total_drafts) / total_formdata
|
||||
context['total_formdata'] = total_formdata
|
||||
|
||||
context['total_drafts'] = total_drafts
|
||||
context['is_carddef'] = isinstance(self.formdef, CardDef)
|
||||
context['total_drafts'] = total_drafts
|
||||
|
||||
return template.QommonTemplateResponse(
|
||||
templates=[self.inspect_template_name],
|
||||
|
|
|
@ -124,6 +124,9 @@ def geojson_formdatas(formdatas, geoloc_key='base', fields=None):
|
|||
if hasattr(html_value, 'replace'):
|
||||
html_value = html_value.replace('[download]', '%sdownload' % formdata_backoffice_url)
|
||||
value = formdata.get_field_view_value(field)
|
||||
if field.key == 'block':
|
||||
# return display value for block fields, not the internal structure
|
||||
value = formdata.data.get(f'{field.id}_display')
|
||||
if not html_value and not value:
|
||||
continue
|
||||
|
||||
|
@ -133,7 +136,7 @@ def geojson_formdatas(formdatas, geoloc_key='base', fields=None):
|
|||
'value': str(value),
|
||||
'html_value': str(htmlescape(html_value)),
|
||||
}
|
||||
if field.key == 'file':
|
||||
if field.key == 'file' and not getattr(field, 'block_field', None):
|
||||
raw_value = formdata.data.get(field.id)
|
||||
if raw_value.has_redirect_url():
|
||||
geojson_infos['file_url'] = field.get_download_url(file_value=raw_value)
|
||||
|
@ -1724,7 +1727,7 @@ class FormPage(Directory, TempfileDirectoryMixin):
|
|||
self.view.remove_self()
|
||||
return redirect('..')
|
||||
|
||||
def get_formdef_fields(self, include_block_items_fields=False):
|
||||
def get_formdef_fields(self, include_block_fields=True, include_block_items_fields=False):
|
||||
yield filter_fields.IdFilterField(formdef=self.formdef)
|
||||
if self.formdef.default_digest_template:
|
||||
yield filter_fields.DigestFilterField(formdef=self.formdef)
|
||||
|
@ -1744,7 +1747,7 @@ class FormPage(Directory, TempfileDirectoryMixin):
|
|||
field.has_relations = True
|
||||
yield filter_fields.UserRelatedField(field)
|
||||
|
||||
for field in self.formdef.iter_fields(include_block_fields=True):
|
||||
for field in self.formdef.iter_fields(include_block_fields=include_block_fields):
|
||||
if getattr(field, 'block_field', None):
|
||||
if field.key == 'items' and not include_block_items_fields:
|
||||
# not yet
|
||||
|
@ -2689,7 +2692,7 @@ class FormPage(Directory, TempfileDirectoryMixin):
|
|||
selected_filter = self.get_filter_from_query()
|
||||
selected_filter_operator = self.get_filter_operator_from_query()
|
||||
if get_request().form.get('full') == 'on':
|
||||
fields = list(self.get_formdef_fields())
|
||||
fields = list(self.get_formdef_fields(include_block_fields=False))
|
||||
else:
|
||||
fields = self.get_fields_from_query()
|
||||
criterias = self.get_criterias_from_query()
|
||||
|
|
|
@ -136,7 +136,13 @@ class SnapshotsDirectory(Directory):
|
|||
if mode == 'inspect' and not has_inspect:
|
||||
raise errors.TraversalError()
|
||||
|
||||
context = getattr(self, 'get_compare_%s_context' % mode)(snapshot1, snapshot2)
|
||||
from wcs.blocks import BlockdefImportError
|
||||
|
||||
try:
|
||||
context = getattr(self, 'get_compare_%s_context' % mode)(snapshot1, snapshot2)
|
||||
except (BlockdefImportError, FormdefImportError, WorkflowImportError) as e:
|
||||
return template.error_page(_('Can not display snapshot (%s)') % e)
|
||||
|
||||
context.update(
|
||||
{
|
||||
'mode': mode,
|
||||
|
|
|
@ -163,6 +163,7 @@ class UpdateRelationsAfterJob(AfterJob):
|
|||
return
|
||||
|
||||
klass = {'carddef': CardDef, 'formdef': FormDef}
|
||||
publisher = get_publisher()
|
||||
|
||||
# check all known reverse relations
|
||||
for obj_ref in {x['obj'] for x in carddef.reverse_relations}:
|
||||
|
@ -207,6 +208,11 @@ class UpdateRelationsAfterJob(AfterJob):
|
|||
if objdata_seen_key in update_related_seen:
|
||||
# do not allow updates to cycle back
|
||||
continue
|
||||
|
||||
publisher.reset_formdata_state()
|
||||
publisher.substitutions.feed(objdata.formdef)
|
||||
publisher.substitutions.feed(objdata)
|
||||
|
||||
objdata_changed = False
|
||||
for field in fields:
|
||||
if getattr(field, 'block_field', None):
|
||||
|
|
|
@ -613,6 +613,7 @@ class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin, ItemWithImageField
|
|||
title=_('Initial Position'),
|
||||
options=(
|
||||
('', _('Default position (from markers)'), ''),
|
||||
('geoloc', _('Device geolocation'), 'geoloc'),
|
||||
('template', _('From template'), 'template'),
|
||||
),
|
||||
value=self.initial_position or '',
|
||||
|
|
|
@ -501,9 +501,8 @@ class FormData(StorableObject):
|
|||
|
||||
@classmethod
|
||||
def force_valid_id_characters(cls, value):
|
||||
value = unidecode.unidecode(value)
|
||||
value = re.sub(r'[^\w\s\'\-_]', '', unidecode.unidecode(value)).strip()
|
||||
value = re.sub(r'\s+', '-', value)
|
||||
value = re.sub(r'[\s\']+', '-', value)
|
||||
return value
|
||||
|
||||
def set_auto_fields(self, *args, **kwargs):
|
||||
|
|
|
@ -1444,10 +1444,12 @@ class FormPage(Directory, TempfileDirectoryMixin, FormTemplateMixin):
|
|||
# by clicking on a submit widget; for example if an "add row"
|
||||
# button is clicked. [ADD_ROW_BUTTON]
|
||||
if form.has_errors() or form.get_submit() is True:
|
||||
if self.has_draft_support() and not honeypot_error:
|
||||
token_error = form.get_widget('_form_id') and form.get_widget('_form_id').has_error()
|
||||
if self.has_draft_support() and not (honeypot_error or token_error):
|
||||
# save draft during server roundtrip
|
||||
try:
|
||||
self.save_draft(form_data)
|
||||
session.add_magictoken(magictoken, form_data) # make sure draft id is saved
|
||||
except SubmittedDraftException:
|
||||
get_session().message = ('error', self.already_submitted_message)
|
||||
return redirect(
|
||||
|
|
|
@ -1148,7 +1148,7 @@ def get_int_or_400(value):
|
|||
def get_order_by_or_400(value):
|
||||
if value in (None, ''):
|
||||
return None
|
||||
if not re.match(r'-?[a-z0-9_-]+$', value):
|
||||
if not (isinstance(value, str) and re.match(r'-?[a-z0-9_-]+$', value)):
|
||||
raise RequestError()
|
||||
return value
|
||||
|
||||
|
|
|
@ -3223,7 +3223,7 @@ aside .bo-block.documentation {
|
|||
}
|
||||
|
||||
.godo.html-edition,
|
||||
.godo.html-edition--show {
|
||||
.godo.html-edition.is-editable {
|
||||
--padding: 0.5em;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
|
@ -3238,7 +3238,7 @@ aside .bo-block.documentation {
|
|||
}
|
||||
}
|
||||
|
||||
.godo.html-edition--show {
|
||||
.godo.html-edition.is-editable {
|
||||
.godo--editor > :first-child {
|
||||
padding-top: var(--padding);
|
||||
}
|
||||
|
|
|
@ -549,8 +549,6 @@ $(function() {
|
|||
const documentation_save_button = document.querySelector('.bo-block.documentation button.save')
|
||||
var clear_documentation_save_marks_timeout_id = null
|
||||
if (editor_link) {
|
||||
document.querySelector('#documentation-editor .godo--editor').setAttribute('contenteditable', 'false')
|
||||
|
||||
documentation_save_button.addEventListener('click', (e) => {
|
||||
editor.sourceContent = editor.getHTML()
|
||||
var documentation_message = Object()
|
||||
|
@ -594,10 +592,9 @@ $(function() {
|
|||
editor_link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
if (editor_link.getAttribute('aria-pressed') == 'true') {
|
||||
editor.validEdition()
|
||||
editor.editable = false;
|
||||
documentation_save_button.dispatchEvent(new Event('click'))
|
||||
documentation_block.classList.remove('active')
|
||||
document.querySelector('#documentation-editor .godo--editor').setAttribute('contenteditable', 'false')
|
||||
editor_link.setAttribute('aria-pressed', false)
|
||||
if (title_byline) title_byline.style.visibility = 'visible'
|
||||
} else {
|
||||
|
@ -612,8 +609,7 @@ $(function() {
|
|||
}
|
||||
if (title_byline) title_byline.style.visibility = 'hidden'
|
||||
editor_link.setAttribute('aria-pressed', true)
|
||||
document.querySelector('#documentation-editor .godo--editor').setAttribute('contenteditable', 'true')
|
||||
editor.showEdition()
|
||||
editor.editable = true;
|
||||
editor.view.focus()
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1889,7 +1889,7 @@ def purge_obsolete_search_tokens(cur=None):
|
|||
cur.execute(
|
||||
"""DELETE FROM wcs_search_tokens
|
||||
WHERE token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms)
|
||||
AND token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms);"""
|
||||
AND token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM searchable_formdefs);"""
|
||||
)
|
||||
if own_cur:
|
||||
cur.close()
|
||||
|
|
|
@ -2045,7 +2045,7 @@ class CardsSource:
|
|||
|
||||
def __getattr__(self, attr):
|
||||
if attr == 'inspect_collapse':
|
||||
return True
|
||||
return False
|
||||
try:
|
||||
return LazyFormDef(CardDef.get_by_urlname(attr, use_cache=True))
|
||||
except KeyError:
|
||||
|
@ -2062,7 +2062,7 @@ class FormsSource:
|
|||
|
||||
def __getattr__(self, attr):
|
||||
if attr == 'inspect_collapse':
|
||||
return True
|
||||
return False
|
||||
try:
|
||||
return LazyFormDef(FormDef.get_by_urlname(attr, use_cache=True))
|
||||
except KeyError:
|
||||
|
|
Loading…
Reference in New Issue