backoffice: link to individual fields in data source usage pane (#63947) #1408

Merged
fpeters merged 1 commits from wip/63947-data-source-usage-link-to-field into main 2024-04-26 08:29:27 +02:00
8 changed files with 36 additions and 29 deletions

View File

@ -625,7 +625,9 @@ def test_data_sources_view(pub):
resp = app.get('/backoffice/settings/data-sources/%s/' % data_source.id)
assert 'Usage in forms' in resp.text
assert '/backoffice/forms/%s/' % formdef.id in resp.text
assert [x.attrib['href'] for x in resp.pyquery('.usage-in-forms a')] == [
'http://example.net/backoffice/forms/1/fields/1/'
]
# additional formdef types
user_formdef = UserFieldsFormDef(pub)
@ -664,12 +666,14 @@ def test_data_sources_view(pub):
resp = app.get('/backoffice/settings/data-sources/%s/' % data_source.id)
assert 'Usage in forms' in resp.text
assert '/backoffice/forms/%s/' % formdef.id in resp.text
assert '/backoffice/workflows/%s/backoffice-fields/fields/' % workflow.id in resp.text
assert '/backoffice/workflows/%s/variables/fields/' % workflow.id in resp.text
assert '/backoffice/workflows/%s/status/1/items/_x/fields/' % workflow.id in resp.text
assert '/backoffice/settings/users/fields/' in resp.text
assert '/backoffice/cards/%s/' % carddef.id in resp.text
assert sorted([x.attrib['href'] for x in resp.pyquery('.usage-in-forms a')]) == [
f'http://example.net/backoffice/cards/{carddef.id}/fields/1/',
f'http://example.net/backoffice/forms/{formdef.id}/fields/1/',
'http://example.net/backoffice/settings/users/fields/fields/1/',
f'http://example.net/backoffice/workflows/{workflow.id}/backoffice-fields/fields/1/',
f'http://example.net/backoffice/workflows/{workflow.id}/status/1/items/_x/fields/1/',
f'http://example.net/backoffice/workflows/{workflow.id}/variables/fields/1/',
]
# cleanup
user_formdef = UserFieldsFormDef(pub)

View File

@ -1613,15 +1613,15 @@ def test_named_datasource_in_formdef(pub):
assert datasource.slug == 'foobar'
formdef = FormDef()
assert not datasource.is_used_in_formdef(formdef)
assert not any(datasource.usage_in_formdef(formdef))
formdef.fields = [
fields.ItemField(id='0', label='string', data_source={'type': 'foobar'}),
]
assert datasource.is_used_in_formdef(formdef)
assert any(datasource.usage_in_formdef(formdef))
datasource.slug = 'barfoo'
assert not datasource.is_used_in_formdef(formdef)
assert not any(datasource.usage_in_formdef(formdef))
def test_data_source_in_template(pub):

View File

@ -367,7 +367,7 @@ def test_build_agenda_datasources(mock_collect, pub, chrono_url):
fields.ItemField(id='0', label='string', data_source={'type': datasource3.slug}),
]
formdef.store()
assert datasource3.is_used_in_formdef(formdef)
assert any(datasource3.usage_in_formdef(formdef))
datasource3.data_source['value'] = '{{ agendas_url }}api/agenda/events-FOOBAR/datetimes/'
datasource3.store()
build_agenda_datasources(pub)

View File

@ -371,12 +371,11 @@ class NamedDataSourcePage(Directory, DocumentableMixin):
)
def usage_in_formdefs(self):
formdefs = []
fields = []
for formdef in get_formdefs_of_all_kinds():
if self.datasource.is_used_in_formdef(formdef):
formdefs.append(formdef)
formdefs.sort(key=lambda x: x.name.lower())
return formdefs
fields.extend(list(self.datasource.usage_in_formdef(formdef)))
fields.sort(key=lambda x: x._formdef.name.lower())
return fields
def has_preview_block(self):
return bool(self.datasource.type in ('json', 'geojson', 'formula', 'jsonvalue', 'wcs:users'))

View File

@ -1192,18 +1192,18 @@ class NamedDataSource(XmlStorableObject):
from wcs.formdef import get_formdefs_of_all_kinds
for formdef in get_formdefs_of_all_kinds():
if self.is_used_in_formdef(formdef):
if any(self.usage_in_formdef(formdef)):
return True
return False
def is_used_in_formdef(self, formdef):
def usage_in_formdef(self, formdef):
for field in formdef.fields or []:
data_source = getattr(field, 'data_source', None)
if not data_source:
continue
if data_source.get('type') == self.slug:
return True
return False
field._formdef = formdef
yield field
class StubNamedDataSource(NamedDataSource):

View File

@ -256,6 +256,12 @@ class Field:
return ''
return self._formdef.get_field_admin_url(field=self)
def get_admin_url_label(self):
return _('%(form)s, field: "%(field)s"') % {
'form': self._formdef.name,
'field': self.ellipsized_label,
}
@property
def include_in_listing(self):
return 'listings' in (self.display_locations or [])

View File

@ -89,13 +89,13 @@
</div>
{% endif %}
{% with formdefs=view.usage_in_formdefs %}
{% if formdefs %}
<div class="section">
{% with fields=view.usage_in_formdefs %}
{% if fields %}
<div class="section usage-in-forms">
<h3>{% trans "Usage in forms" %}</h3>
<ul class="objects-list single-links">
{% for formdef in formdefs %}
<li><a href="{{ formdef.get_admin_url }}">{{ formdef.name }}</a></li>
{% for field in fields %}
<li><a href="{{ field.get_admin_url }}">{{ field.get_admin_url_label }}</a></li>
{% endfor %}
</ul>
</div>

View File

@ -84,11 +84,9 @@ def grep_strings(string, hit_function):
break
for field in formdef.fields or []:
field._formdef = formdef
url = formdef.get_field_admin_url(field)
source_name = _('%(form)s, field: "%(field)s"') % {
'form': formdef.name,
'field': field.ellipsized_label,
}
source_name = field.get_admin_url_label()
for attr in field.get_admin_attributes():
if check_string(getattr(field, attr, None), source_url=url, source_name=source_name):
break