Projet

Général

Profil

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

Frédéric Péters, 11 juin 2016 08:33

Télécharger (30,4 ko)

Voir les différences:

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

 tests/test_admin_pages.py      | 62 ++++++++++++++++++++++++++++++++++
 tests/test_backoffice_pages.py | 37 ++++++++++++++++++++-
 tests/test_workflows.py        | 39 +++++++++++++++++++++-
 wcs/admin/workflows.py         | 59 +++++++++++++++++++++++++++++++++
 wcs/backoffice/management.py   | 14 ++++----
 wcs/formdef.py                 | 10 +++++-
 wcs/forms/common.py            | 44 ++++++++++++++++++-------
 wcs/sql.py                     | 16 ++++-----
 wcs/wf/set_backoffice_field.py | 61 ++++++++++++++++++++++++++++++++++
 wcs/workflows.py               | 75 ++++++++++++++++++++++++++++++++++++++----
 10 files changed, 381 insertions(+), 36 deletions(-)
 create mode 100644 wcs/wf/set_backoffice_field.py
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.add_status(name='baz')
1566
    workflow.store()
1567

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

  
1574
    app = login(get_app(pub))
1575
    resp = app.get('/backoffice/workflows/1/')
1576
    resp = resp.click('baz')
1577
    assert not 'Set Backoffice Field' in resp.body
1578

  
1579
    resp = app.get('/backoffice/workflows/1/')
1580
    resp = resp.click(href='backoffice-fields/')
1581
    assert resp.location == 'http://example.net/backoffice/workflows/1/backoffice-fields/fields/'
1582
    resp = resp.follow()
1583

  
1584
    # makes sure we can't add page fields
1585
    assert 'value="New Page"' not in resp.body
1586

  
1587
    # add a simple field
1588
    resp.forms[0]['label'] = 'foobar'
1589
    resp.forms[0]['type'] = 'Text (line)'
1590
    resp = resp.forms[0].submit()
1591
    assert resp.location == 'http://example.net/backoffice/workflows/1/backoffice-fields/fields/'
1592
    resp = resp.follow()
1593

  
1594
    # check it's been saved correctly
1595
    assert 'foobar' in resp.body
1596
    assert len(Workflow.get(1).backoffice_fields_formdef.fields) == 1
1597
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].id.startswith('bo')
1598
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].key == 'string'
1599
    assert Workflow.get(1).backoffice_fields_formdef.fields[0].label == 'foobar'
1600

  
1601
    backoffice_field_id = Workflow.get(1).backoffice_fields_formdef.fields[0].id
1602
    formdef = FormDef.get(formdef.id)
1603
    data_class = formdef.data_class()
1604
    data_class.wipe()
1605
    formdata = data_class()
1606
    formdata.data = {backoffice_field_id: 'HELLO'}
1607
    formdata.status = 'wf-new'
1608
    formdata.store()
1609

  
1610
    assert data_class.get(formdata.id).data[backoffice_field_id] == 'HELLO'
1611

  
1612
    # check the "set backoffice field" action is now available
1613
    resp = app.get('/backoffice/workflows/1/')
1614
    resp = resp.click('baz')
1615
    resp.forms[0]['type'] = 'Set Backoffice Field'
1616
    resp = resp.forms[0].submit()
1617
    resp = resp.follow()
1618

  
1619
    resp = resp.click('Set Backoffice Field')
1620

  
1559 1621
def test_workflows_functions(pub):
1560 1622
    create_superuser(pub)
1561 1623
    create_role()
tests/test_backoffice_pages.py
20 20
from wcs.roles import Role
21 21
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
22 22
        ChoiceWorkflowStatusItem, EditableWorkflowStatusItem,
23
        JumpOnSubmitWorkflowStatusItem, WorkflowCriticalityLevel)
23
        JumpOnSubmitWorkflowStatusItem, WorkflowCriticalityLevel,
24
        WorkflowBackofficeFieldsFormDef)
24 25
from wcs.wf.dispatch import DispatchWorkflowStatusItem
25 26
from wcs.wf.wscall import WebserviceCallStatusItem
26 27
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
......
2486 2487
    assert (pq('[title="form_var_foo_str_but_non_utf8"]')
2487 2488
            .parents('li').children('div.value span')
2488 2489
            .text() == '\'\\xed\\xa0\\x00\'')
2490

  
2491
def test_backoffice_fields(pub):
2492
    user = create_user(pub)
2493
    create_environment(pub)
2494

  
2495
    wf = Workflow(name='bo fields')
2496
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
2497
    wf.backoffice_fields_formdef.fields = [
2498
        fields.StringField(id='bo1', label='1st backoffice field',
2499
            type='string', varname='backoffice_blah'),
2500
    ]
2501
    st1 = wf.add_status('Status1')
2502
    wf.store()
2503

  
2504
    formdef = FormDef.get_by_urlname('form-title')
2505
    formdef.workflow_id = wf.id
2506
    formdef.store()
2507

  
2508
    formdata = formdef.data_class()()
2509
    formdata.data = {}
2510
    formdata.just_created()
2511
    formdata.store()
2512

  
2513
    app = login(get_app(pub))
2514
    resp = app.get(formdata.get_url(backoffice=True))
2515
    assert not 'Backoffice Data' in resp.body
2516
    assert not '1st backoffice field' in resp.body
2517

  
2518
    formdata.data = {'bo1': 'HELLO WORLD'}
2519
    formdata.store()
2520
    resp = app.get(formdata.get_url(backoffice=True))
2521
    assert 'Backoffice Data' in resp.body
2522
    assert '1st backoffice field' in resp.body
2523
    assert 'HELLO WORLD' in resp.body
tests/test_workflows.py
18 18
        SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem,
19 19
        DisplayMessageWorkflowStatusItem,
20 20
        AbortActionException, WorkflowCriticalityLevel,
21
        AttachmentEvolutionPart)
21
        AttachmentEvolutionPart, WorkflowBackofficeFieldsFormDef)
22 22
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem
23 23
from wcs.wf.criticality import ModifyCriticalityWorkflowStatusItem, MODE_INC, MODE_DEC, MODE_SET
24 24
from wcs.wf.dispatch import DispatchWorkflowStatusItem
......
31 31
from wcs.wf.wscall import WebserviceCallStatusItem
32 32
from wcs.wf.export_to_model import transform_to_pdf
33 33
from wcs.wf.geolocate import GeolocateWorkflowStatusItem
34
from wcs.wf.set_backoffice_field import SetBackofficeFieldWorkflowStatusItem
34 35

  
35 36
from utilities import (create_temporary_pub, MockSubstitutionVariables, emails,
36 37
        http_requests, clean_temporary_pub, sms_mocking)
......
1578 1579
    item.fields = [{'field_id': 'plop', 'value': '=form_var_foo'}]
1579 1580
    item.perform(formdata)
1580 1581
    assert pub.user_class.get(user.id).form_data == {'3': 'Plop'}
1582

  
1583
def test_set_backoffice_field(pub):
1584
    Workflow.wipe()
1585
    wf = Workflow(name='xxx')
1586
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
1587
    wf.backoffice_fields_formdef.fields = [
1588
        StringField(id='bo1', label='1st backoffice field',
1589
            type='string', varname='backoffice_blah'),
1590
    ]
1591
    st1 = wf.add_status('Status1')
1592
    wf.store()
1593

  
1594
    formdef = FormDef()
1595
    formdef.name = 'baz'
1596
    formdef.fields = [
1597
        StringField(id='1', label='String', type='string', varname='string'),
1598
    ]
1599
    formdef.workflow_id = wf.id
1600
    formdef.store()
1601

  
1602
    formdata = formdef.data_class()()
1603
    formdata.data = {'1': 'HELLO'}
1604
    formdata.just_created()
1605
    formdata.store()
1606
    pub.substitutions.feed(formdata)
1607

  
1608
    item = SetBackofficeFieldWorkflowStatusItem()
1609
    item.perform(formdata)
1610

  
1611
    item = SetBackofficeFieldWorkflowStatusItem()
1612
    item.field_id = 'bo1'
1613
    item.field_content = '=form_var_string'
1614
    item.perform(formdata)
1615

  
1616
    formdata = formdef.data_class().get(formdata.id)
1617
    assert formdata.data['bo1'] == 'HELLO'
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_all_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_all_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_all_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_all_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_all_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_all_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_all_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_all_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/forms/common.py
420 420
            r += htmltext('<div class="field"><span class="label">%s</span>') % _('User name')
421 421
            r += htmltext('<span class="value">%s</span></div>') % user.display_name
422 422

  
423
        r += self.display_fields(self.formdef.fields, form_url)
424

  
425
        if show_status and self.formdef.is_user_allowed_read_status_and_history(
426
                        get_request().user, self.filled):
427
            wf_status = self.filled.get_visible_status()
428
            if wf_status:
429
                r += htmltext('<div><span class="label">%s</span> ') % _('Status')
430
                r += htmltext('<span class="value">%s</span></div>') % wf_status.name
431

  
432
        r += htmltext('</div>') # .dataview
433
        r += htmltext('</div>') # .bo-block
434

  
435
        return r.getvalue()
436

  
437
    def display_fields(self, fields, form_url=''):
438
        r = TemplateIO(html=True)
423 439
        on_page = False
424 440
        on_disabled_page = False
425
        for f in self.formdef.fields:
426

  
441
        for f in fields:
427 442
            if f.type == 'page':
428 443
                on_disabled_page = False
429 444
                if not f.is_visible(self.filled.data, self.formdef):
......
485 500
        if on_page:
486 501
            r += htmltext('</div></div>')
487 502

  
488
        if show_status and self.formdef.is_user_allowed_read_status_and_history(
489
                        get_request().user, self.filled):
490
            wf_status = self.filled.get_visible_status()
491
            if wf_status:
492
                r += htmltext('<p><span class="label">%s</span> ') % _('Status')
493
                r += htmltext('<span class="value">%s</span></p>') % wf_status.name
494

  
495
        r += htmltext('</div>') # .dataview
496
        r += htmltext('</div>') # .bo-block
497 503
        return r.getvalue()
498 504

  
505
    def backoffice_fields_section(self):
506
        backoffice_fields = self.formdef.workflow.get_backoffice_fields()
507
        if not backoffice_fields:
508
            return
509
        content = self.display_fields(backoffice_fields)
510
        if not len(content):
511
            return
512
        r = TemplateIO(html=True)
513
        r += htmltext('<div class="bo-block">')
514
        r += htmltext('<h2 class="foldable">%s</h2>') % _('Backoffice Data')
515
        r += htmltext('<div class="dataview">')
516
        r += content
517
        r += htmltext('</div>')
518
        r += htmltext('</div>')
519
        return r.getvalue()
499 520

  
500 521
    def status(self):
501 522
        object_key = 'formdata-%s-%s' % (self.formdef.url_name, self.filled.id)
......
545 566
                    break
546 567

  
547 568
        r += self.receipt(always_include_user=True, folded=folded)
569
        r += self.backoffice_fields_section()
548 570

  
549 571
        r += self.history()
550 572

  
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_all_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_all_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_all_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_all_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_all_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_all_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_all_fields():
1496 1496
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
1497 1497
            if sql_type is None:
1498 1498
                continue
wcs/wf/set_backoffice_field.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2016  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
from quixote import get_publisher
18

  
19
from qommon import get_logger
20
from qommon.form import SingleSelectWidget, ComputedExpressionWidget
21
from wcs.workflows import WorkflowStatusItem, register_item_class
22

  
23
class SetBackofficeFieldWorkflowStatusItem(WorkflowStatusItem):
24
    description = N_('Set Backoffice Field')
25
    key = 'set-backoffice-field'
26

  
27
    field_id = None
28
    field_content = None
29

  
30
    @classmethod
31
    def is_available(cls, workflow=None):
32
        return bool(workflow and getattr(workflow.backoffice_fields_formdef, 'fields', None))
33

  
34
    def get_parameters(self):
35
        return ('field_id', 'field_content')
36

  
37
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
38
        if 'field_id' in parameters:
39
            fields = getattr(self.parent.parent.backoffice_fields_formdef, 'fields', [])
40
            options = [(str(x.id), x.label) for x in fields]
41
            if not options:
42
                options = [('', '')]
43
            form.add(SingleSelectWidget, '%sfield_id' % prefix,
44
                    title=_('Field'), value=str(self.field_id),
45
                    required=True, options=options)
46
        if 'field_content' in parameters:
47
            form.add(ComputedExpressionWidget, '%sfield_content' % prefix, size=50,
48
                    title=_('Content'), value=self.field_content)
49

  
50
    def perform(self, formdata):
51
        if not (self.field_id and self.field_content):
52
            return
53
        try:
54
            formdata.data['%s' % self.field_id] = self.compute(
55
                    self.field_content, raises=True)
56
            formdata.store()
57
        except:
58
            get_publisher().notify_of_exception(sys.exc_info())
59

  
60

  
61
register_item_class(SetBackofficeFieldWorkflowStatusItem)
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):
......
337 376
                return status
338 377
        raise KeyError()
339 378

  
379
    def get_backoffice_fields(self):
380
        if self.backoffice_fields_formdef:
381
            return self.backoffice_fields_formdef.fields or []
382
        return []
383

  
340 384
    def add_global_action(self, name, id=None):
341 385
        if [x for x in self.global_actions if x.name == name]:
342 386
            raise DuplicateGlobalActionNameError()
......
472 516
            for field in self.variables_formdef.fields:
473 517
                fields.append(field.export_to_xml(charset=charset, include_id=include_id))
474 518

  
519
        if self.backoffice_fields_formdef:
520
            variables = ET.SubElement(root, 'backoffice-fields')
521
            formdef = ET.SubElement(variables, 'formdef')
522
            ET.SubElement(formdef, 'name').text = '-' # required by formdef xml import
523
            fields = ET.SubElement(formdef, 'fields')
524
            for field in self.backoffice_fields_formdef.fields:
525
                fields.append(field.export_to_xml(charset=charset, include_id=include_id))
526

  
475 527
        return root
476 528

  
477 529
    @classmethod
......
543 595
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
544 596
            workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
545 597
            workflow.variables_formdef.fields = imported_formdef.fields
598

  
599
        variables = tree.find('backoffice-fields')
600
        if variables is not None:
601
            formdef = variables.find('backoffice-fields')
602
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
603
            workflow.backoffice_fields_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
604
            workflow.backoffice_fields_formdef.fields = imported_formdef.fields
605

  
546 606
        return workflow
547 607

  
548 608
    def get_list_of_roles(self, include_logged_in_users=True):
......
2230 2290
    import wf.resubmit
2231 2291
    import wf.criticality
2232 2292
    import wf.profile
2293
    import wf.set_backoffice_field
2233 2294

  
2234 2295
from wf.export_to_model import ExportToModel
2235
-