inspecteur: faire apparaître les custom views dans l'inspect formdef et carddef et enregistrer un snapshot du formdef/carddef à la modif d'une custom view (#80235) #579

Merged
lguerin merged 4 commits from wip/80235-custom-view-snapshot into main 2023-08-11 11:50:39 +02:00
9 changed files with 214 additions and 35 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
.coverage
/wcs/qommon/static/css/dc2/admin.css
/wcs/qommon/static/css/qommon.css
/wcs/qommon/static/css/item-with-image.css
MANIFEST
build/
coverage.xml

View File

@ -3962,6 +3962,36 @@ def test_admin_form_inspect(pub):
)
assert resp.pyquery('[data-field-id="14"] .parameter-data_source a').attr['href'] == '#invalid-xxx'
assert '>Custom views</button>' not in resp
custom_view_owner = pub.custom_view_class()
custom_view_owner.title = 'card view owner'
custom_view_owner.formdef = formdef
custom_view_owner.visibility = 'owner'
custom_view_owner.store()
custom_view_role = pub.custom_view_class()
custom_view_role.title = 'card view role'
custom_view_role.formdef = formdef
custom_view_role.visibility = 'role'
custom_view_role.store()
custom_view_any = pub.custom_view_class()
custom_view_any.title = 'card view any'
custom_view_any.formdef = formdef
custom_view_any.visibility = 'any'
custom_view_any.store()
custom_view_datasource = pub.custom_view_class()
custom_view_datasource.title = 'card view datasource'
custom_view_datasource.formdef = formdef
custom_view_datasource.visibility = 'datasource'
custom_view_datasource.store()
resp = app.get('/backoffice/forms/%s/inspect' % formdef.id)
assert '>Custom views</button>' in resp
assert '<h4>card view owner</h4>' not in resp
assert '<h4>card view role</h4>' in resp
assert '<h4>card view any</h4>' in resp
assert '<h4>card view datasource</h4>' in resp
def test_admin_form_inspect_validation(pub):
create_superuser(pub)

View File

@ -1743,3 +1743,96 @@ def test_backoffice_folded_data_sources(pub):
resp = resp.click('datasource view')
assert resp.pyquery('.sidebar-custom-views').length == 2
assert resp.pyquery('fieldset.foldable:not(.folded) .sidebar-custom-views').length == 1
@pytest.mark.parametrize('formdef_class', [FormDef, CardDef])
def test_backoffice_custom_view_and_snapshots(pub, formdef_class):
user = create_superuser(pub)
pub.custom_view_class.wipe()
pub.snapshot_class.wipe()
formdef_class.wipe()
formdef = formdef_class()
formdef.name = 'foo'
formdef.fields = [
fields.StringField(id='1', label='Test', varname='foo'),
]
formdef.backoffice_submission_roles = user.roles
formdef.workflow_roles = {'_editor': user.roles[0], '_receiver': 1}
formdef.digest_templates = {
'default': 'plop',
}
formdef.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get(formdef.get_backoffice_url())
resp.forms['listing-settings']['filter-1'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view - owner'
resp.forms['save-custom-view']['visibility'] = 'owner'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/user-custom-test-view-owner/')
resp = resp.follow()
custom_view = pub.custom_view_class.select()[-1]
assert custom_view.visibility == 'owner'
assert pub.custom_view_class.count() == 1
assert pub.snapshot_class.count() == 1 # owner custom view, no store on formdef
resp = resp.click('Delete')
resp = resp.form.submit().follow()
assert pub.custom_view_class.count() == 0
assert pub.snapshot_class.count() == 1 # owner custom view, no store on formdef
resp.forms['listing-settings']['filter-1'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view - role'
resp.forms['save-custom-view']['role'] = user.roles[0]
resp.forms['save-custom-view']['visibility'] = 'role'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/custom-test-view-role/')
resp = resp.follow()
custom_view = pub.custom_view_class.select()[-1]
assert custom_view.visibility == 'role'
assert pub.custom_view_class.count() == 1
assert pub.snapshot_class.count() == 2 # role custom view, store formdef
resp = resp.click('Delete')
resp = resp.form.submit().follow()
assert pub.custom_view_class.count() == 0
assert pub.snapshot_class.count() == 3 # role custom view, store formdef
resp.forms['listing-settings']['filter-1'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view - any'
resp.forms['save-custom-view']['visibility'] = 'any'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/custom-test-view-any/')
resp = resp.follow()
custom_view = pub.custom_view_class.select()[-1]
assert custom_view.visibility == 'any'
assert pub.custom_view_class.count() == 1
assert pub.snapshot_class.count() == 4 # any custom view, store formdef
resp = resp.click('Delete')
resp = resp.form.submit().follow()
assert pub.custom_view_class.count() == 0
assert pub.snapshot_class.count() == 5 # any custom view, store formdef
if formdef_class == CardDef:
resp.forms['listing-settings']['filter-1'].checked = True
resp = resp.forms['listing-settings'].submit()
resp.forms['save-custom-view']['title'] = 'custom test view - datasource'
resp.forms['save-custom-view']['visibility'] = 'datasource'
resp = resp.forms['save-custom-view'].submit()
assert resp.location.endswith('/custom-test-view-datasource/')
resp = resp.follow()
custom_view = pub.custom_view_class.select()[-1]
assert custom_view.visibility == 'datasource'
assert pub.custom_view_class.count() == 1
assert pub.snapshot_class.count() == 6 # datasource custom view, store formdef
resp = resp.click('Delete')
resp = resp.form.submit().follow()
assert pub.custom_view_class.count() == 0
assert pub.snapshot_class.count() == 7 # datasource custom view, store formdef

View File

@ -1683,6 +1683,31 @@ class FormDefPage(Directory):
if str(x.id) in self.formdef.tracking_code_verify_fields
]
)
if hasattr(self.formdef, '_custom_views'):
# loaded from snapshot
custom_views = self.formdef._custom_views
else:
custom_views = []
for view in get_publisher().custom_view_class.select():
if view.match(user=None, formdef=self.formdef, for_export=True):
custom_views.append(view)
for view in custom_views:
view.digest_template = (self.formdef.digest_templates or {}).get(
'custom-view:%s' % view.get_url_slug()
)
if view.visibility == 'role':
role_id = view.role_id
if role_id:
try:
role = get_publisher().role_class.get(role_id)
role_label = role.name
except KeyError:
# removed role ?
role_label = _('Unknown role (%s)') % role_id
else:
role_label = '-'
view.role = role_label
context['custom_views'] = sorted(custom_views, key=lambda x: getattr(x, 'title'))
return template.QommonTemplateResponse(templates=[self.inspect_template_name], context=context)

View File

@ -1691,6 +1691,20 @@ class FormPage(FormdefDirectoryBase):
custom_view.group_by = form.get_widget('group_by').parse()
custom_view.store()
if custom_view.is_default and custom_view.visibility != 'datasource':
# need to clean other views to have only one default per owner/any visibility
for view in self.get_custom_views():
if view.id == custom_view.id:
continue
if (
custom_view.visibility == view.visibility
and view.is_default
and view.role_id == custom_view.role_id
):
view.is_default = False
view.store()
formdef_stored = False
if form.get_widget('digest_template') and custom_view.visibility != 'owner':
if not self.formdef.digest_templates:
self.formdef.digest_templates = {}
@ -1701,6 +1715,7 @@ class FormPage(FormdefDirectoryBase):
'custom-view:%s' % custom_view.get_url_slug()
] = form.get_widget('digest_template').parse()
self.formdef.store()
formdef_stored = True
if self.formdef.data_class().count():
get_response().add_after_job(UpdateDigestAfterJob(formdefs=[self.formdef]))
elif (
@ -1715,19 +1730,12 @@ class FormPage(FormdefDirectoryBase):
if old_view_digest_key in (self.formdef.digest_templates or {}):
del self.formdef.digest_templates[old_view_digest_key]
self.formdef.store()
if custom_view.is_default and custom_view.visibility != 'datasource':
# need to clean other views to have only one default per owner/any visibility
for view in self.get_custom_views():
if view.id == custom_view.id:
continue
if (
custom_view.visibility == view.visibility
and view.is_default
and view.role_id == custom_view.role_id
):
view.is_default = False
view.store()
formdef_stored = True
if custom_view.visibility != 'owner':
# store to always have a snapshot, except if owner view
if not formdef_stored:
# a snapshot will be stored only if there is changes
self.formdef.store()
if self.view:
return redirect('../' + custom_view.get_url_slug() + '/')

View File

@ -69,6 +69,7 @@ class CustomView(StorableObject):
self.formdef_type = value.xml_root_node
def remove_self(self):
super().remove_self()
try:
formdef = self.formdef
except KeyError:
@ -78,7 +79,9 @@ class CustomView(StorableObject):
if view_digest_key in (formdef.digest_templates or {}):
del formdef.digest_templates[view_digest_key]
formdef.store()
super().remove_self()
elif self.visibility != 'owner':
# a snapshot will be stored only if there is changes
formdef.store()
def match(self, user, formdef, for_export=False):
if self.formdef_type != formdef.xml_root_node:

View File

@ -2196,18 +2196,6 @@ div.mail-body {
}
}
.snapshots-table {
table-layout: fixed;
}
.snapshots-list .collapsed {
display: none;
}
p.snapshots-navigation {
text-align: center;
}
.sidebar-custom-views {
.active {
font-weight: bold;
@ -2467,9 +2455,16 @@ div.timetable-widget {
}
}
.snapshots-list .counter {
display: inline-block;
min-width: 4em;
.snapshots-table {
table-layout: fixed;
}
.snapshots-list .collapsed {
display: none;
}
p.snapshots-navigation {
text-align: center;
}
div.diff {

View File

@ -11,6 +11,9 @@
<button role="tab" aria-selected="false" aria-controls="inspect-workflow" id="tab-workflow" tabindex="-1">{% trans "Workflow" %}</button>
<button role="tab" aria-selected="false" aria-controls="inspect-options" id="tab-options" tabindex="-1">{% trans "Options" %}</button>
<button role="tab" aria-selected="false" aria-controls="inspect-fields" id="tab-fields" tabindex="-1">{% trans "Fields" %}</button>
{% if custom_views %}
<button role="tab" aria-selected="false" aria-controls="inspect-customviews" id="tab-customviews" tabindex="-1">{% trans "Custom views" %}</button>
{% endif %}
</div>
<div class="pk-tabs--container">
@ -81,6 +84,33 @@
{% include "wcs/backoffice/includes/inspect-field.html" with path=formdef.get_admin_url|add:"fields/" %}
{% endfor %}
</div>
<div id="inspect-customviews" role="tabpanel" tabindex="0" aria-labelledby="tab-customviews" hidden>
<div>
{% for custom_view in custom_views %}
<h4>{{ custom_view.title }}</h4>
<ul>
<li>
<span class='parameter'>{% trans "Visibility" %}{% trans ":" %}</span>
{% if custom_view.visibility == 'any' %}
{% trans "to any users" %}
{% elif custom_view.visibility == 'datasource' %}
{% trans "as data source" %}
{% elif custom_view.visibility == 'role' %}
{% trans "to role" %} ({{ custom_view.role|default:'-' }})
{% endif %}</li>
<li><span class='parameter'>{% trans "Default view" %}{% trans ":" %}</span> {{ custom_view.is_default|yesno }}</li>

% trans "Default vue" %}

Default view.

> % trans "Default vue" %} Default view.

corrigé

corrigé
<li><span class='parameter'>{% trans "Digest" %}{% trans ":" %}</span> {{ custom_view.digest_template|default:'-' }}</li>
<li><span class='parameter'>{% trans "Columns" %}{% trans ":" %}</span> {{ custom_view.columns|default:'-' }}</li>
<li><span class='parameter'>{% trans "Filters" %}{% trans ":" %}</span> {{ custom_view.filters|default:'-' }}</li>
<li><span class='parameter'>{% trans "Order by" %}{% trans ":" %}</span> {{ custom_view.order_by|default:'-' }}</li>

Comme j'ai ajouté le regroupement, il pourrait être repris ici :

{% if custom_view.visibility == 'datasource' %}
<li><span class='parameter'>{% trans "Group by" %}{% trans ":" %}</span> {{ custom_view.group_by|default:'-' }}</li> 
{% endif %}
Comme j'ai ajouté le regroupement, il pourrait être repris ici : ``` {% if custom_view.visibility == 'datasource' %} <li><span class='parameter'>{% trans "Group by" %}{% trans ":" %}</span> {{ custom_view.group_by|default:'-' }}</li> {% endif %} ```

ajouté

ajouté
{% if custom_view.visibility == 'datasource' %}
<li><span class='parameter'>{% trans "Group by" %}{% trans ":" %}</span> {{ custom_view.group_by|default:'-' }}</li>
{% endif %}
</ul>
{% endfor %}
</div>
</div>
</div>
</div>

View File

@ -68,8 +68,6 @@
{% endfor %}
</tbody>
</table>
<ul class="objects-list snapshots-list">
</ul>
</form>
</div>
{% else %}
@ -85,10 +83,6 @@
$('.snapshots-list tr[data-day="' + day + '"]:not(.new-day)').toggleClass('collapsed');
return false;
});
$('input[name="version1"]').on('click', function() {
var next = $(this).parent('tr').next();
$('input[name="version2"]', next).prop('checked', true);
});

Cette partie pourrait plutôt être corrigée,

- var next = $(this).parent('tr').next();
+ var next = $(this).parents('tr').next()
Cette partie pourrait plutôt être corrigée, ``` - var next = $(this).parent('tr').next(); + var next = $(this).parents('tr').next() ```

Je ne sais pas: ça n'a pas l'air d'avoir manqué, et le système de comparaison de version fait de toute façon l'inversion si version1 > version2 (ou le contraire, je ne sais plus).
Autant laisser l'utilisateur cliquer là où il veut sans que ça change sa sélection ?

Et je n'ai pas repris ce JS dans combo :)

Je ne sais pas: ça n'a pas l'air d'avoir manqué, et le système de comparaison de version fait de toute façon l'inversion si version1 > version2 (ou le contraire, je ne sais plus). Autant laisser l'utilisateur cliquer là où il veut sans que ça change sa sélection ? Et je n'ai pas repris ce JS dans combo :)
});
</script>