Projet

Général

Profil

0001-general-add-support-for-backoffice-fields-8273.patch

Frédéric Péters, 06 juin 2016 23:17

Télécharger (19,2 ko)

Voir les différences:

Subject: [PATCH 1/5] general: add support for backoffice fields (#8273)

 tests/test_admin_pages.py    | 49 +++++++++++++++++++++++++++++++
 wcs/admin/workflows.py       | 59 +++++++++++++++++++++++++++++++++++++
 wcs/backoffice/management.py | 14 ++++-----
 wcs/formdef.py               | 10 ++++++-
 wcs/sql.py                   | 16 +++++-----
 wcs/workflows.py             | 70 +++++++++++++++++++++++++++++++++++++++-----
 6 files changed, 195 insertions(+), 23 deletions(-)
tests/test_admin_pages.py
1556 1556
    assert Workflow.get(1).variables_formdef.fields[0].key == 'string'
1557 1557
    assert Workflow.get(1).variables_formdef.fields[0].varname == '1*1*message'
1558 1558

  
1559
def test_workflows_backoffice_fields(pub):
1560
    create_superuser(pub)
1561
    create_role()
1562

  
1563
    Workflow.wipe()
1564
    workflow = Workflow(name='foo')
1565
    workflow.store()
1566

  
1567
    formdef = FormDef()
1568
    formdef.name = 'form title'
1569
    formdef.workflow_id = workflow.id
1570
    formdef.fields = []
1571
    formdef.store()
1572

  
1573
    app = login(get_app(pub))
1574
    resp = app.get('/backoffice/workflows/1/')
1575
    resp = resp.click(href='backoffice-fields/')
1576
    assert resp.location == 'http://example.net/backoffice/workflows/1/backoffice-fields/fields/'
1577
    resp = resp.follow()
1578

  
1579
    # makes sure we can't add page fields
1580
    assert 'value="New Page"' not in resp.body
1581

  
1582
    # add a simple field
1583
    resp.forms[0]['label'] = 'foobar'
1584
    resp.forms[0]['type'] = 'Text (line)'
1585
    resp = resp.forms[0].submit()
1586
    assert resp.location == 'http://example.net/backoffice/workflows/1/backoffice-fields/fields/'
1587
    resp = resp.follow()
1588

  
1589
    # check it's been saved correctly
1590
    assert 'foobar' in resp.body
1591
    assert len(Workflow.get(1).backoffice_fields_formdef.fields) == 1
1592
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].id.startswith('bo')
1593
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].key == 'string'
1594
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].label == 'foobar'
1595

  
1596
    backoffice_field_id = Workflow.get(1).backoffice_fields_formdef.fields[0].id
1597
    formdef = FormDef.get(formdef.id)
1598
    data_class = formdef.data_class()
1599
    data_class.wipe()
1600
    formdata = data_class()
1601
    formdata.data = {backoffice_field_id: 'HELLO'}
1602
    formdata.status = 'wf-new'
1603
    formdata.store()
1604

  
1605
    assert data_class.get(formdata.id).data[backoffice_field_id] == 'HELLO'
1606

  
1607

  
1559 1608
def test_workflows_functions(pub):
1560 1609
    create_superuser(pub)
1561 1610
    create_role()
wcs/admin/workflows.py
832 832
        return form
833 833

  
834 834

  
835
class WorkflowBackofficeFieldDefPage(FieldDefPage):
836
    section = 'workflows'
837

  
838

  
835 839
class WorkflowVariablesFieldsDirectory(FieldsDirectory):
836 840
    _q_exports = ['', 'update_order', 'new']
837 841

  
......
853 857
        pass
854 858

  
855 859

  
860
class WorkflowBackofficeFieldsDirectory(FieldsDirectory):
861
    _q_exports = ['', 'update_order', 'new']
862

  
863
    section = 'workflows'
864
    field_def_page_class = WorkflowBackofficeFieldDefPage
865
    support_import = False
866
    blacklisted_types = ['page']
867

  
868
    def index_top(self):
869
        r = TemplateIO(html=True)
870
        r += htmltext('<h2>%s - %s - %s</h2>') % (_('Workflow'),
871
                self.objectdef.name, _('Backoffice Fields'))
872
        r += get_session().display_message()
873
        if not self.objectdef.fields:
874
            r += htmltext('<p>%s</p>') % _('There are not yet any backoffice fields.')
875
        return r.getvalue()
876

  
877
    def index_bottom(self):
878
        pass
879

  
880

  
856 881
class VariablesDirectory(Directory):
857 882
    _q_exports = ['', 'fields']
858 883

  
......
869 894
        return Directory._q_traverse(self, path)
870 895

  
871 896

  
897
class BackofficeFieldsDirectory(Directory):
898
    _q_exports = ['', 'fields']
899

  
900
    def __init__(self, workflow):
901
        self.workflow = workflow
902

  
903
    def _q_index(self):
904
        return redirect('fields/')
905

  
906
    def _q_traverse(self, path):
907
        get_response().breadcrumb.append(('backoffice-fields/', _('Backoffice Fields')))
908
        self.fields = WorkflowBackofficeFieldsDirectory(
909
                WorkflowBackofficeFieldsFormDef(self.workflow))
910
        return Directory._q_traverse(self, path)
911

  
912

  
913

  
872 914
class FunctionsDirectory(Directory):
873 915
    _q_exports = ['', 'new']
874 916

  
......
1266 1308
class WorkflowPage(Directory):
1267 1309
    _q_exports = ['', 'edit', 'delete', 'newstatus', ('status', 'status_dir'), 'update_order',
1268 1310
            'duplicate', 'export', 'svg', ('variables', 'variables_dir'),
1311
            ('backoffice-fields', 'backoffice_fields_dir'),
1269 1312
            'update_actions_order', 'update_criticality_levels_order',
1270 1313
            ('functions', 'functions_dir'), ('global-actions', 'global_actions_dir'),
1271 1314
            ('criticality-levels', 'criticality_levels_dir'),
......
1280 1323
        self.workflow_ui = WorkflowUI(self.workflow)
1281 1324
        self.status_dir = WorkflowStatusDirectory(self.workflow, html_top)
1282 1325
        self.variables_dir = VariablesDirectory(self.workflow)
1326
        self.backoffice_fields_dir = BackofficeFieldsDirectory(self.workflow)
1283 1327
        self.functions_dir = FunctionsDirectory(self.workflow)
1284 1328
        self.global_actions_dir = GlobalActionsDirectory(self.workflow, html_top)
1285 1329
        self.criticality_levels_dir = CriticalityLevelsDirectory(self.workflow)
......
1425 1469
            r += htmltext('</ul>')
1426 1470
            r += htmltext('</div>')
1427 1471

  
1472
        if not str(self.workflow.id).startswith('_'):
1473
            r += htmltext('<div class="bo-block">')
1474
            r += htmltext('<h3>%s') % _('Backoffice Fields')
1475
            r += htmltext(' <span class="change">(<a href="backoffice-fields/">%s</a>)</span></h3>') % _('change')
1476
            if self.workflow.backoffice_fields_formdef:
1477
                r += htmltext('<ul class="biglist">')
1478
                for field in self.workflow.backoffice_fields_formdef.fields:
1479
                    r += htmltext('<li><a href="backoffice-fields/fields/%s/">%s') % (
1480
                            field.id, field.label)
1481
                    if field.varname:
1482
                        r += htmltext(' (<code>%s</code>)') % field.varname
1483
                    r += htmltext('</a></li>')
1484
                r += htmltext('</ul>')
1485
            r += htmltext('</div>')
1486

  
1428 1487
        r += htmltext('</div>') # .splitcontent-right
1429 1488

  
1430 1489
        r += htmltext('<br style="clear:both;"/>')
wcs/backoffice/management.py
183 183
        r += htmltext('<h2>%s</h2>') % self.user.display_name
184 184
        formdef = UserFieldsFormDef()
185 185
        r += htmltext('<div class="form">')
186
        for field in formdef.fields:
186
        for field in formdef.get_fields():
187 187
            if not hasattr(field, str('get_view_value')):
188 188
                continue
189 189
            value = self.user.form_data.get(field.id)
......
321 321

  
322 322
        formdef = UserFieldsFormDef()
323 323
        criteria_fields = [ILike('name', query), ILike('email', query)]
324
        for field in formdef.fields:
324
        for field in formdef.get_fields():
325 325
            if field.type in ('string', 'text', 'email'):
326 326
                criteria_fields.append(ILike('f%s' % field.id, query))
327 327
        if get_publisher().is_using_postgresql():
......
342 342
            r += htmltext('<th data-field-sort-key="name"><span>%s</span></th>') % _('Name')
343 343
        if include_email_column:
344 344
            r += htmltext('<th data-field-sort-key="email"><span>%s</span></th>') % _('Email')
345
        for field in formdef.fields:
345
        for field in formdef.get_fields():
346 346
            if field.in_listing:
347 347
                r += htmltext('<th data-field-sort-key="f%s"><span>%s</span></th>') % (
348 348
                        field.id, field.label)
......
356 356
                r += htmltext('<td>%s</td>') % (user.name or '')
357 357
            if include_email_column:
358 358
                r += htmltext('<td>%s</td>') % (user.email or '')
359
            for field in formdef.fields:
359
            for field in formdef.get_fields():
360 360
                if field.in_listing:
361 361
                    r += htmltext('<td>%s</td>') % (user.form_data.get(field.id) or '')
362 362
            r += htmltext('</tr>')
......
1036 1036
            fields.append(FakeField('submission_channel', 'submission_channel', _('Channel')))
1037 1037
        fields.append(FakeField('time', 'time', _('Time')))
1038 1038
        fields.append(FakeField('user-label', 'user-label', _('User Label')))
1039
        fields.extend(self.formdef.fields)
1039
        fields.extend(self.formdef.get_fields())
1040 1040
        fields.append(FakeField('status', 'status', _('Status')))
1041 1041
        fields.append(FakeField('anonymised', 'anonymised', _('Anonymised')))
1042 1042

  
......
1046 1046
        field_ids = [x for x in get_request().form.keys()]
1047 1047
        if not field_ids or ignore_form:
1048 1048
            field_ids = ['id', 'time', 'user-label']
1049
            for field in self.formdef.fields:
1049
            for field in self.formdef.get_fields():
1050 1050
                if hasattr(field, str('get_view_value')) and field.in_listing:
1051 1051
                    field_ids.append(field.id)
1052 1052
            field_ids.append('status')
......
1636 1636
        had_page = False
1637 1637
        last_page = None
1638 1638
        last_title = None
1639
        for f in self.formdef.fields:
1639
        for f in self.formdef.get_fields():
1640 1640
            if excluded_fields and f.id in excluded_fields:
1641 1641
                continue
1642 1642
            if f.type == 'page':
wcs/formdef.py
292 292
                    rebuild_global_views=True)
293 293
        return t
294 294

  
295
    def rebuild_views(self):
295
    def get_fields(self):
296
        workflow_fields = []
297
        try:
298
            workflow_fields = self.workflow.backoffice_fields_formdef.fields
299
        except AttributeError:
300
            pass
301
        return (self.fields or []) + workflow_fields
302

  
303
    def rebuild(self):
296 304
        if get_publisher().is_using_postgresql():
297 305
            import sql
298 306
            sql.do_formdef_tables(self, rebuild_views=True,
wcs/sql.py
378 378
        cur.execute('''ALTER TABLE %s ADD COLUMN criticality_level integer NOT NULL DEFAULT(0)''' % table_name)
379 379

  
380 380
    # add new fields
381
    for field in formdef.fields:
381
    for field in formdef.get_fields():
382 382
        assert field.id is not None
383 383
        sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
384 384
        if sql_type is None:
......
461 461
    from admin.settings import UserFieldsFormDef
462 462
    formdef = UserFieldsFormDef()
463 463

  
464
    for field in formdef.fields:
464
    for field in formdef.get_fields():
465 465
        sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
466 466
        if sql_type is None:
467 467
            continue
......
602 602
    view_fields = get_view_fields(formdef)
603 603

  
604 604
    column_names = {}
605
    for field in formdef.fields:
605
    for field in formdef.get_fields():
606 606
        field_key = 'f%s' % field.id
607 607
        if field.type in ('page', 'title', 'subtitle', 'comment'):
608 608
            continue
......
900 900

  
901 901
    def get_sql_dict_from_data(self, data, formdef):
902 902
        sql_dict = {}
903
        for field in formdef.fields:
903
        for field in formdef.get_fields():
904 904
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
905 905
            if sql_type is None:
906 906
                continue
......
932 932
        i = len(cls._table_static_fields)
933 933
        if formdef.geolocations:
934 934
            i += len(formdef.geolocations.keys())
935
        for field in formdef.fields:
935
        for field in formdef.get_fields():
936 936
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
937 937
            if sql_type is None:
938 938
                continue
......
1220 1220
        fts_strings = [str(self.id)]
1221 1221
        if self.tracking_code:
1222 1222
            fts_strings.append(self.tracking_code)
1223
        for field in self._formdef.fields:
1223
        for field in self._formdef.get_fields():
1224 1224
            if not self.data.get(field.id):
1225 1225
                continue
1226 1226
            value = None
......
1275 1275
    @classmethod
1276 1276
    def get_data_fields(cls):
1277 1277
        data_fields = ['geoloc_%s' % x for x in (cls._formdef.geolocations or {}).keys()]
1278
        for field in cls._formdef.fields:
1278
        for field in cls._formdef.get_fields():
1279 1279
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
1280 1280
            if sql_type is None:
1281 1281
                continue
......
1492 1492
    @classmethod
1493 1493
    def get_data_fields(cls):
1494 1494
        data_fields = []
1495
        for field in cls.get_formdef().fields:
1495
        for field in cls.get_formdef().get_fields():
1496 1496
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
1497 1497
            if sql_type is None:
1498 1498
                continue
wcs/workflows.py
246 246
        self.workflow.store()
247 247

  
248 248

  
249
class WorkflowBackofficeFieldsFormDef(FormDef):
250
    '''Class to handle workflow backoffice fields, it loads and saves from/to
251
       the workflow object 'backoffice_fields_formdef' attribute.'''
252

  
253
    def __init__(self, workflow):
254
        self.id = None
255
        self.name = workflow.name
256
        self.workflow = workflow
257
        if workflow.backoffice_fields_formdef and workflow.backoffice_fields_formdef.fields:
258
            self.fields = self.workflow.backoffice_fields_formdef.fields
259
            self.max_field_id = max([lax_int(x.id) for x in self.fields or []])
260
        else:
261
            self.fields = []
262

  
263
    def get_new_field_id(self):
264
        if self.max_field_id is None:
265
            field_id = 1
266
        else:
267
            field_id = self.max_field_id + 1
268
        self.max_field_id = field_id
269
        return 'bo%s' % field_id
270

  
271
    def store(self):
272
        self.workflow.backoffice_fields_formdef = self
273
        self.workflow.store()
274

  
275

  
249 276
class Workflow(StorableObject):
250 277
    _names = 'workflows'
251 278
    name = None
252 279
    possible_status = None
253 280
    roles = None
254 281
    variables_formdef = None
282
    backoffice_fields_formdef = None
255 283
    global_actions = None
256 284
    criticality_levels = None
257 285

  
......
283 311
            self.store()
284 312

  
285 313
    def store(self):
286
        must_update_views = False
314
        must_update = False
287 315
        if self.id:
288 316
            old_self = self.get(self.id, ignore_errors=True, ignore_migration=True)
289 317
            if old_self:
290 318
                old_endpoints = set([x.id for x in old_self.get_endpoint_status()])
291 319
                if old_endpoints != set([x.id for x in self.get_endpoint_status()]):
292
                    must_update_views = True
320
                    must_update = True
293 321
                old_criticality_levels = len(old_self.criticality_levels or [0])
294 322
                if old_criticality_levels != len(self.criticality_levels or [0]):
295
                    must_update_views = True
323
                    must_update = True
324
                try:
325
                    old_backoffice_fields = old_self.backoffice_fields_formdef.fields
326
                except AttributeError:
327
                    old_backoffice_fields = []
328
                try:
329
                    new_backoffice_fields = self.backoffice_fields_formdef.fields
330
                except AttributeError:
331
                    new_backoffice_fields = []
332
                if len(old_backoffice_fields) != len(new_backoffice_fields):
333
                    must_update = True
334
        elif self.backoffice_fields_formdef:
335
            must_update = True
296 336

  
297 337
        self.last_modification_time = time.localtime()
298 338
        if get_request() and get_request().user:
......
301 341
            self.last_modification_user_id = None
302 342
        StorableObject.store(self)
303 343

  
304
        # instruct all related formdefs to update their security rules, and
305
        # their views if endpoints have changed.
344
        # instruct all related formdefs to update.
306 345
        for form in FormDef.select(lambda x: x.workflow_id == self.id, ignore_migration=True):
307 346
            form.data_class().rebuild_security()
308
            if must_update_views:
309
                form.rebuild_views()
347
            if must_update:
348
                form.rebuild()
310 349

  
311 350
    @classmethod
312 351
    def get(cls, id, ignore_errors=False, ignore_migration=False):
......
472 511
            for field in self.variables_formdef.fields:
473 512
                fields.append(field.export_to_xml(charset=charset, include_id=include_id))
474 513

  
514
        if self.backoffice_fields_formdef:
515
            variables = ET.SubElement(root, 'backoffice-fields')
516
            formdef = ET.SubElement(variables, 'formdef')
517
            ET.SubElement(formdef, 'name').text = '-' # required by formdef xml import
518
            fields = ET.SubElement(formdef, 'fields')
519
            for field in self.backoffice_fields_formdef.fields:
520
                fields.append(field.export_to_xml(charset=charset, include_id=include_id))
521

  
475 522
        return root
476 523

  
477 524
    @classmethod
......
543 590
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
544 591
            workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
545 592
            workflow.variables_formdef.fields = imported_formdef.fields
593

  
594
        variables = tree.find('backoffice-fields')
595
        if variables is not None:
596
            formdef = variables.find('backoffice-fields')
597
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
598
            workflow.backoffice_fields_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
599
            workflow.backoffice_fields_formdef.fields = imported_formdef.fields
600

  
546 601
        return workflow
547 602

  
548 603
    def get_list_of_roles(self, include_logged_in_users=True):
......
2229 2284
    import wf.resubmit
2230 2285
    import wf.criticality
2231 2286
    import wf.profile
2287
    import wf.set_backoffice_variable
2232 2288

  
2233 2289
from wf.export_to_model import ExportToModel
2234
-