Simplify access/appraisal rule UI for simplified profile
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 13 Oct 2016 18:05:26 +0200
changeset 1741 13b39d6ff207
parent 1740 d53885d29c38
child 1742 6493df257368
Simplify access/appraisal rule UI for simplified profile Considering we want one and only one rule and associated start date. We need yet to consider this rule to allow or not to mark an existing profile as simplified, and may still enhance rule inheriting management at some point. Related to #15524229
entities/__init__.py
test/test_views.py
views/simplified.py
--- a/entities/__init__.py	Wed Oct 12 12:29:55 2016 +0200
+++ b/entities/__init__.py	Thu Oct 13 18:05:26 2016 +0200
@@ -89,6 +89,8 @@
 def simplified_profile(cls, req, rset=None, entity=None, **kwargs):
     """Predicate returning 1 score if context entity is within a simplified profile."""
     entity = _transfer_from_context(rset, entity)
+    if entity is None:
+        return 0
     return int(entity.simplified_profile)
 
 
--- a/test/test_views.py	Wed Oct 12 12:29:55 2016 +0200
+++ b/test/test_views.py	Thu Oct 13 18:05:26 2016 +0200
@@ -230,5 +230,67 @@
             self.assertEqual(exporter.__class__, SEDAExportView)
 
 
+class SimplifiedFormsTC(CubicWebTC):
+    """Functional test case about forms in the web interface."""
+
+    def setup_database(self):
+        with self.admin_access.repo_cnx() as cnx:
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'diagnosis testing',
+                                         simplified_profile=True)
+            cnx.commit()
+            self.transfer_eid = transfer.eid
+
+    def rule_form(self, req, etype):
+        # add minimal information in for to detect container even if edited entity isn't
+        # actually created
+        req.form['__linkto'] = 'x:{0}:y'.format(self.transfer_eid)
+        rule = req.vreg['etypes'].etype_class(etype)(req)
+        return self.vreg['forms'].select('edition', req, entity=rule)
+
+    def assertInlinedFields(self, form, expected):
+        """Assert the given inlined form as the expected set of inlined forms, each defined by its
+        field name and associated view class'name.
+        """
+        # text_type around field.name because it may be a relation schema instead of a string
+        inlined_fields = [(text_type(field.name), field.view.__class__.__name__)
+                          for field in form.fields if hasattr(field, 'view')]
+        self.assertEqual(set(inlined_fields), set(expected))
+
+    def assertNoRemove(self, form, field_name, role):
+        """Assert the given inlined form field will be rendered without a link to remove it."""
+        field = form.field_by_name(field_name, role=role)
+        self.assertEqual(field.view._get_removejs(), None)
+
+    def assertNoTitle(self, form, field_name, role):
+        """Assert the given inlined form field will be rendered without form title."""
+        field = form.field_by_name(field_name, role=role)
+        self.assertEqual(field.view.form_renderer_id, 'notitle')
+
+    def test_rule_form(self):
+        with self.admin_access.web_request() as req:
+            for rule_type in ('access', 'appraisal'):
+                form = self.rule_form(req, 'SEDA{0}Rule'.format(rule_type.capitalize()))
+                self.assertInlinedFields(form, [
+                    ('seda_seq_{0}_rule_rule'.format(rule_type),
+                     'RuleRuleInlineEntityCreationFormView'),
+                    ('seda_alt_{0}_rule_prevent_inheritance'.format(rule_type),
+                     'InlineAddNewLinkView'),
+                ])
+                self.assertNoRemove(form, 'seda_seq_{0}_rule_rule'.format(rule_type), 'subject')
+
+    def test_rule_rule_form(self):
+        with self.admin_access.web_request() as req:
+            for rule_type in ('access', 'appraisal'):
+                form = self.rule_form(req, 'SEDASeq{0}RuleRule'.format(rule_type.capitalize()))
+                other_fields = [text_type(field.name)
+                                for field in form.fields if not hasattr(field, 'view')]
+                self.assertNotIn('user_cardinality', other_fields)
+                self.assertInlinedFields(form, [
+                    ('seda_start_date', 'StartDateInlineEntityCreationFormView'),
+                ])
+                self.assertNoRemove(form, 'seda_start_date', 'object')
+                self.assertNoTitle(form, 'seda_start_date', 'object')
+
+
 if __name__ == '__main__':
     unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/views/simplified.py	Thu Oct 13 18:05:26 2016 +0200
@@ -0,0 +1,125 @@
+# copyright 2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+"""cubicweb-seda views configuration / overrides for simplified profiles."""
+
+from logilab.common.registry import objectify_predicate
+
+from cubicweb.predicates import is_instance
+from cubicweb.web.views import uicfg, autoform
+
+from cubes.seda.entities import simplified_profile
+from cubes.seda.views import copy_rtag
+# ensure those are registered first
+from cubes.seda.views import mgmt_rules, archivetransfer, dataobject, archiveunit, content  # noqa
+
+
+afs = uicfg.autoform_section
+simplified_afs = copy_rtag(afs, __name__, simplified_profile())
+
+# appraisal/access rules have a single top level cardinality in simplified profile, as well as
+# always a start date. This implies:
+# 1. force one and only one eg AppraisalRuleRule,
+# 2. force start date, but hide it,
+# 3. hide eg AppraisalRuleRule's cardinality.
+
+# 3. hide rule rule's cardinality
+simplified_afs.tag_attribute(('SEDASeqAppraisalRuleRule', 'user_cardinality'),
+                             'main', 'hidden')
+simplified_afs.tag_attribute(('SEDASeqAccessRuleRule', 'user_cardinality'),
+                             'main', 'hidden')
+# 2. hide start date's cardinality
+simplified_afs.tag_attribute(('SEDAStartDate', 'user_cardinality'),
+                             'main', 'hidden')
+
+
+class RuleAutomaticEntityForm(autoform.AutomaticEntityForm):
+    __select__ = (is_instance('SEDAAppraisalRule', 'SEDAAccessRule')
+                  & simplified_profile())
+
+    def should_display_inline_creation_form(self, rschema, existing, card):
+        # 1. force creation of one appraisal/access rule
+        if not existing and rschema in ('seda_seq_appraisal_rule_rule',
+                                        'seda_seq_access_rule_rule'):
+            return True
+        return super(RuleAutomaticEntityForm, self).should_display_inline_creation_form(
+            rschema, existing, card)
+
+    def should_display_add_new_relation_link(self, rschema, existing, card):
+        # 1. don't allow creation of more than one appraisal/access rule
+        if rschema in ('seda_seq_appraisal_rule_rule',
+                       'seda_seq_access_rule_rule'):
+            return False
+        return super(RuleAutomaticEntityForm, self).should_display_add_new_relation_link(
+            rschema, existing, card)
+
+
+class SeqRuleRuleAutomaticEntityForm(autoform.AutomaticEntityForm):
+    __select__ = (is_instance('SEDASeqAppraisalRuleRule', 'SEDASeqAccessRuleRule')
+                  & simplified_profile())
+
+    def should_display_inline_creation_form(self, rschema, existing, card):
+        # 2. force start date
+        if not existing and rschema == 'seda_start_date':
+            return True
+        return super(SeqRuleRuleAutomaticEntityForm, self).should_display_inline_creation_form(
+            rschema, existing, card)
+
+
+@objectify_predicate
+def simplified_rule_rule(cls, req, rtype=None, pform=None, **kwargs):
+    # check we're within a simplified profile
+    if isinstance(pform, RuleAutomaticEntityForm) and rtype in ('seda_seq_appraisal_rule_rule',
+                                                                'seda_seq_access_rule_rule'):
+        return 1
+    return 0
+
+
+# 1. don't allow deletion of our appraisal/access rule
+
+class RuleRuleInlineEntityEditionFormView(autoform.InlineEntityEditionFormView):
+    __select__ = (autoform.InlineEntityEditionFormView.__select__
+                  & simplified_rule_rule())
+    removejs = None
+
+
+class RuleRuleInlineEntityCreationFormView(autoform.InlineEntityCreationFormView):
+    __select__ = (autoform.InlineEntityCreationFormView.__select__
+                  & simplified_rule_rule())
+    removejs = None
+
+
+# 2. hide start date
+
+@objectify_predicate
+def simplified_start_date(cls, req, rtype=None, pform=None, **kwargs):
+    # check we're within a simplified profile
+    if isinstance(pform, SeqRuleRuleAutomaticEntityForm) and rtype == 'seda_start_date':
+        return 1
+    return 0
+
+
+class StartDateInlineEntityEditionFormView(autoform.InlineEntityEditionFormView):
+    __select__ = (autoform.InlineEntityEditionFormView.__select__
+                  & simplified_start_date())
+    removejs = None
+    form_renderer_id = 'notitle'
+
+
+class StartDateInlineEntityCreationFormView(autoform.InlineEntityCreationFormView):
+    __select__ = (autoform.InlineEntityCreationFormView.__select__
+                  & simplified_start_date())
+    removejs = None
+    form_renderer_id = 'notitle'