Projet

Général

Profil

0001-workflows-don-t-overwrite-document-models-when-brows.patch

Frédéric Péters, 19 octobre 2020 17:15

Télécharger (28,1 ko)

Voir les différences:

Subject: [PATCH] workflows: don't overwrite document models when browsing
 snapshots (#47310)

 tests/test_snapshots.py     | 50 +++++++++++++++++++++++++++++++++++++
 wcs/blocks.py               |  2 +-
 wcs/fields.py               | 18 ++++++-------
 wcs/formdef.py              |  3 ++-
 wcs/qommon/xml_storage.py   |  2 +-
 wcs/snapshots.py            |  5 +++-
 wcs/wf/attachment.py        |  2 +-
 wcs/wf/backoffice_fields.py |  4 +--
 wcs/wf/create_formdata.py   |  2 +-
 wcs/wf/dispatch.py          | 12 ++++-----
 wcs/wf/export_to_model.py   |  8 ++++--
 wcs/wf/form.py              |  4 +--
 wcs/wf/jump.py              |  2 +-
 wcs/wf/profile.py           |  4 +--
 wcs/wf/roles.py             |  4 +--
 wcs/wf/wscall.py            |  4 +--
 wcs/workflows.py            | 50 ++++++++++++++++++-------------------
 17 files changed, 117 insertions(+), 59 deletions(-)
tests/test_snapshots.py
1
import os
2
import shutil
3
import xml.etree.ElementTree as ET
4

  
1 5
import pytest
2 6

  
7
from django.utils.six import BytesIO
8
from quixote.http_request import Upload
9

  
3 10
from wcs.blocks import BlockDef
4 11
from wcs.carddef import CardDef
5 12
from wcs.data_sources import NamedDataSource
6 13
from wcs.formdef import FormDef
14
from wcs.qommon.form import UploadedFile
7 15
from wcs.workflows import Workflow
16
from wcs.workflows import ExportToModel
8 17
from wcs.wscalls import NamedWsCall
9 18

  
10 19
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub
......
302 311
    assert 'This workflow is readonly' in resp
303 312

  
304 313

  
314
def test_workflow_with_model_snapshot_browse(pub):
315
    create_superuser(pub)
316
    create_role()
317

  
318
    Workflow.wipe()
319
    if os.path.exists(os.path.join(pub.app_dir, 'models')):
320
        shutil.rmtree(os.path.join(pub.app_dir, 'models'))
321
    workflow = Workflow(name='test')
322
    st1 = workflow.add_status('Status1', 'st1')
323
    export_to = ExportToModel()
324
    export_to.label = 'test'
325
    upload = Upload('/foo/bar', content_type='application/vnd.oasis.opendocument.text')
326
    file_content = b'''PK\x03\x04\x14\x00\x00\x08\x00\x00\'l\x8eG^\xc62\x0c\'\x00'''
327
    upload.fp = BytesIO()
328
    upload.fp.write(file_content)
329
    upload.fp.seek(0)
330
    export_to.model_file = UploadedFile('models', 'tmp', upload)
331
    st1.items.append(export_to)
332
    export_to.parent = st1
333

  
334
    # export/import to get models stored in the expected way
335
    workflow.store()
336
    workflow = Workflow.import_from_xml_tree(
337
            ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True)
338
    assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
339

  
340
    workflow = Workflow.import_from_xml_tree(
341
            ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True)
342
    assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
343

  
344
    app = login(get_app(pub))
345

  
346
    for i in range(3):
347
        # check document model is not overwritten
348
        resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
349
        snapshot = pub.snapshot_class.select_object_history(workflow)[0]
350
        resp = resp.click(href='%s/view/' % snapshot.id)
351
        assert 'This workflow is readonly' in resp
352
        assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 3 + i
353

  
354

  
305 355
def test_wscall_snapshot_browse(pub):
306 356
    create_superuser(pub)
307 357
    create_role()
wcs/blocks.py
157 157
        return blockdef
158 158

  
159 159
    @classmethod
160
    def import_from_xml_tree(cls, tree, include_id=False):
160
    def import_from_xml_tree(cls, tree, include_id=False, snapshot=False):
161 161
        charset = 'utf-8'
162 162
        blockdef = cls()
163 163
        if tree.find('name') is None or not tree.find('name').text:
wcs/fields.py
279 279
                    el.text = str(val)
280 280
        return field
281 281

  
282
    def init_with_xml(self, elem, charset, include_id=False):
282
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
283 283
        for attribute in self.get_admin_attributes():
284 284
            el = elem.find(attribute)
285 285
            if hasattr(self, '%s_init_with_xml' % attribute):
286 286
                getattr(self, '%s_init_with_xml' % attribute)(el, charset,
287
                        include_id=include_id)
287
                        include_id=include_id, snapshot=False)
288 288
                continue
289 289
            if el is None:
290 290
                continue
......
316 316
            except:
317 317
                pass
318 318

  
319
    def condition_init_with_xml(self, node, charset, include_id=False):
319
    def condition_init_with_xml(self, node, charset, include_id=False, snapshot=False):
320 320
        self.condition = None
321 321
        if node is None:
322 322
            return
......
328 328
        elif node.text:
329 329
            self.condition = {'type': 'python', 'value': force_str(node.text).strip()}
330 330

  
331
    def data_source_init_with_xml(self, node, charset, include_id=False):
331
    def data_source_init_with_xml(self, node, charset, include_id=False, snapshot=False):
332 332
        self.data_source = {}
333 333
        if node is None:
334 334
            return
......
342 342
            elif self.data_source.get('value') is None:
343 343
                del self.data_source['value']
344 344

  
345
    def prefill_init_with_xml(self, node, charset, include_id=False):
345
    def prefill_init_with_xml(self, node, charset, include_id=False, snapshot=False):
346 346
        self.prefill = {}
347 347
        if node is not None and node.findall('type'):
348 348
            self.prefill = {
......
867 867
            changed = True
868 868
        return changed
869 869

  
870
    def init_with_xml(self, element, charset, include_id=False):
870
    def init_with_xml(self, element, charset, include_id=False, snapshot=False):
871 871
        super(StringField, self).init_with_xml(element, charset, include_id=include_id)
872 872
        self.migrate()
873 873

  
......
1263 1263
            self.document_type['mimetypes'] = old_value
1264 1264
        return result
1265 1265

  
1266
    def init_with_xml(self, element, charset, include_id=False):
1266
    def init_with_xml(self, element, charset, include_id=False, snapshot=False):
1267 1267
        super(FileField, self).init_with_xml(element, charset, include_id=include_id)
1268 1268
        # translate fields flattened to strings
1269 1269
        if self.document_type and self.document_type.get('mimetypes'):
......
1443 1443
            changed = True
1444 1444
        return changed
1445 1445

  
1446
    def init_with_xml(self, element, charset, include_id=False):
1446
    def init_with_xml(self, element, charset, include_id=False, snapshot=False):
1447 1447
        super(ItemField, self).init_with_xml(element, charset, include_id=include_id)
1448 1448
        if getattr(element.find('show_as_radio'), 'text', None) == 'True':
1449 1449
            self.display_mode = 'radio'
......
1964 1964

  
1965 1965
    post_conditions = None
1966 1966

  
1967
    def post_conditions_init_with_xml(self, node, charset, include_id=False):
1967
    def post_conditions_init_with_xml(self, node, charset, include_id=False, snapshot=False):
1968 1968
        if node is None:
1969 1969
            return
1970 1970
        self.post_conditions = []
wcs/formdef.py
1071 1071
        return formdef
1072 1072

  
1073 1073
    @classmethod
1074
    def import_from_xml_tree(cls, tree, include_id=False, charset=None, fix_on_error=False):
1074
    def import_from_xml_tree(cls, tree, include_id=False, charset=None,
1075
            fix_on_error=False, snapshot=False):
1075 1076
        if charset is None:
1076 1077
            charset = get_publisher().site_charset
1077 1078
        assert charset == 'utf-8'
wcs/qommon/xml_storage.py
86 86
                include_id=include_id)
87 87

  
88 88
    @classmethod
89
    def import_from_xml_tree(cls, tree, include_id=False, charset=None):
89
    def import_from_xml_tree(cls, tree, include_id=False, charset=None, snapshot=False):
90 90
        if charset is None:
91 91
            charset = get_publisher().site_charset
92 92
        obj = cls()
wcs/snapshots.py
79 79
    def instance(self):
80 80
        if self._instance is None:
81 81
            tree = ET.fromstring(self.serialization)
82
            self._instance = self.get_object_class().import_from_xml_tree(tree, include_id=True)
82
            self._instance = self.get_object_class().import_from_xml_tree(
83
                    tree,
84
                    include_id=True,
85
                    snapshot=True)
83 86
            self._instance.readonly = True
84 87
        return self._instance
85 88

  
wcs/wf/attachment.py
224 224
            ET.SubElement(node, 'mimetype').text = mimetype
225 225
        return node
226 226

  
227
    def document_type_init_with_xml(self, node, charset, include_id=False):
227
    def document_type_init_with_xml(self, node, charset, include_id=False, snapshot=False):
228 228
        self.document_type = {}
229 229
        if node is None:
230 230
            return
wcs/wf/backoffice_fields.py
180 180

  
181 181
        return fields_node
182 182

  
183
    def fields_init_with_xml(self, elem, charset, include_id=False):
183
    def fields_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
184 184
        fields = []
185 185
        if elem is None:
186 186
            return
187 187
        for field_xml_node in elem.findall('field'):
188 188
            field_node = FieldNode()
189 189
            field_node.init_with_xml(field_xml_node, charset,
190
                    include_id=include_id)
190
                    include_id=include_id, snapshot=snapshot)
191 191
            fields.append(field_node.as_dict())
192 192
        if fields:
193 193
            self.fields = fields
wcs/wf/create_formdata.py
465 465
            item.attrib['field_id'] = str(mapping.field_id)
466 466
            item.text = mapping.expression
467 467

  
468
    def mappings_init_with_xml(self, container, charset, include_id=False):
468
    def mappings_init_with_xml(self, container, charset, include_id=False, snapshot=False):
469 469
        self.mappings = []
470 470
        for child in container:
471 471
            field_id = child.attrib.get('field_id', '')
wcs/wf/dispatch.py
73 73
        self._role_export_to_xml('role_id', item, charset,
74 74
                include_id=include_id)
75 75

  
76
    def role_id_init_with_xml(self, elem, charset, include_id=False):
76
    def role_id_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
77 77
        self._role_init_with_xml('role_id', elem, charset,
78
                include_id=include_id)
78
                include_id=include_id, snapshot=snapshot)
79 79

  
80 80

  
81 81
class DispatchWorkflowStatusItem(WorkflowStatusItem):
......
97 97
        self._role_export_to_xml('role_id', item, charset,
98 98
                include_id=include_id)
99 99

  
100
    def role_id_init_with_xml(self, elem, charset, include_id=False):
100
    def role_id_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
101 101
        self._role_init_with_xml('role_id', elem, charset,
102
                include_id=include_id)
102
                include_id=include_id, snapshot=snapshot)
103 103

  
104 104
    def rules_export_to_xml(self, item, charset, include_id=False):
105 105
        if self.dispatch_type != 'automatic' or not self.rules:
......
112 112

  
113 113
        return rules_node
114 114

  
115
    def rules_init_with_xml(self, elem, charset, include_id=False):
115
    def rules_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
116 116
        rules = []
117 117
        if elem is None:
118 118
            return
119 119
        for rule_xml_node in elem.findall('rule'):
120 120
            rule_node = RuleNode()
121 121
            rule_node.init_with_xml(rule_xml_node, charset,
122
                    include_id=include_id)
122
                    include_id=include_id, snapshot=snapshot)
123 123
            rules.append(rule_node.as_dict())
124 124
        if rules:
125 125
            self.rules = rules
wcs/wf/export_to_model.py
606 606
        ET.SubElement(el, 'b64_content').text = force_text(base64.encodebytes(
607 607
                self.model_file.get_file().read()))
608 608

  
609
    def model_file_init_with_xml(self, elem, charset, include_id=False):
609
    def model_file_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
610 610
        if elem is None:
611 611
            return
612 612
        base_filename = elem.find('base_filename').text
......
616 616
        if elem.find('content') is not None:
617 617
            content = elem.find('content').text
618 618

  
619
        if self.parent.parent.id:
619
        if self.parent.parent.id and not snapshot:
620 620
            ids = (self.parent.parent.id, self.parent.id, self.id)
621
        elif snapshot:
622
            # use snapshot prefix so they can eventually be cleaned
623
            # automatically
624
            ids = ('snapshot%i' % random.randint(0, 1000000), self.parent.id, self.id)
621 625
        else:
622 626
            # hopefully this will be random enough.
623 627
            ids = ('i%i' % random.randint(0, 1000000), self.parent.id, self.id)
wcs/wf/form.py
139 139
            fields.append(field.export_to_xml(charset=charset, include_id=include_id))
140 140
        return item
141 141

  
142
    def init_with_xml(self, elem, charset, include_id=False):
142
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
143 143
        WorkflowStatusItem.init_with_xml(self, elem, charset)
144 144
        el = elem.find('formdef')
145 145
        if el is None:
146 146
            return
147 147
        # we can always include id in the formdef export as it lives in
148 148
        # a different space, isolated from other formdefs.
149
        imported_formdef = FormDef.import_from_xml_tree(el, include_id=True)
149
        imported_formdef = FormDef.import_from_xml_tree(el, include_id=True, snapshot=snapshot)
150 150
        self.formdef = WorkflowFormFieldsFormDef(item=self)
151 151
        self.formdef.fields = imported_formdef.fields
152 152
        if self.formdef.max_field_id is None and self.formdef.fields:
wcs/wf/jump.py
119 119
    directory_name = 'jump'
120 120
    directory_class = JumpDirectory
121 121

  
122
    def timeout_init_with_xml(self, elem, charset, include_id=False):
122
    def timeout_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
123 123
        if elem is None or elem.text is None:
124 124
            self.timeout = None
125 125
        else:
wcs/wf/profile.py
132 132

  
133 133
        return fields_node
134 134

  
135
    def fields_init_with_xml(self, elem, charset, include_id=False):
135
    def fields_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
136 136
        fields = []
137 137
        if elem is None:
138 138
            return
139 139
        for field_xml_node in elem.findall('field'):
140 140
            field_node = FieldNode()
141 141
            field_node.init_with_xml(field_xml_node, charset,
142
                    include_id=include_id)
142
                    include_id=include_id, snapshot=snapshot)
143 143
            fields.append(field_node.as_dict())
144 144
        if fields:
145 145
            self.fields = fields
wcs/wf/roles.py
68 68
        self._role_export_to_xml('role_id', item, charset,
69 69
                include_id=include_id)
70 70

  
71
    def role_id_init_with_xml(self, elem, charset, include_id=False):
71
    def role_id_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
72 72
        self._role_init_with_xml('role_id', elem, charset,
73
                include_id=include_id)
73
                include_id=include_id, snapshot=snapshot)
74 74

  
75 75
    def perform(self, formdata):
76 76
        if not self.role_id:
wcs/wf/wscall.py
497 497
        self._kv_data_export_to_xml(xml_item, charset, include_id=include_id,
498 498
                                    attribute='post_data')
499 499

  
500
    def post_data_init_with_xml(self, elem, charset, include_id=False):
500
    def post_data_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
501 501
        self._kv_data_init_with_xml(elem, charset, include_id=include_id, attribute='post_data')
502 502

  
503 503
    def qs_data_export_to_xml(self, xml_item, charset, include_id=False):
504 504
        self._kv_data_export_to_xml(xml_item, charset, include_id=include_id, attribute='qs_data')
505 505

  
506
    def qs_data_init_with_xml(self, elem, charset, include_id=False):
506
    def qs_data_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
507 507
        self._kv_data_init_with_xml(elem, charset, include_id=include_id, attribute='qs_data')
508 508

  
509 509
register_item_class(WebserviceCallStatusItem)
wcs/workflows.py
607 607
        return cls.import_from_xml_tree(tree, include_id=include_id)
608 608

  
609 609
    @classmethod
610
    def import_from_xml_tree(cls, tree, include_id=False):
610
    def import_from_xml_tree(cls, tree, include_id=False, snapshot=False):
611 611
        charset = get_publisher().site_charset
612 612
        workflow = cls()
613 613
        if tree.find('name') is None or not tree.find('name').text:
......
641 641
        for status in tree.find('possible_status'):
642 642
            status_o = WorkflowStatus()
643 643
            status_o.parent = workflow
644
            status_o.init_with_xml(status, charset, include_id=include_id)
644
            status_o.init_with_xml(status, charset, include_id=include_id, snapshot=snapshot)
645 645
            workflow.possible_status.append(status_o)
646 646

  
647 647
        workflow.global_actions = []
......
650 650
            for action in global_actions:
651 651
                action_o = WorkflowGlobalAction()
652 652
                action_o.parent = workflow
653
                action_o.init_with_xml(action, charset, include_id=include_id)
653
                action_o.init_with_xml(action, charset, include_id=include_id, snapshot=snapshot)
654 654
                workflow.global_actions.append(action_o)
655 655

  
656 656
        workflow.criticality_levels = []
......
664 664
        variables = tree.find('variables')
665 665
        if variables is not None:
666 666
            formdef = variables.find('formdef')
667
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
667
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True, snapshot=snapshot)
668 668
            workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
669 669
            workflow.variables_formdef.fields = imported_formdef.fields
670 670

  
671 671
        variables = tree.find('backoffice-fields')
672 672
        if variables is not None:
673 673
            formdef = variables.find('formdef')
674
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True)
674
            imported_formdef = FormDef.import_from_xml_tree(formdef, include_id=True, snapshot=snapshot)
675 675
            workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow=workflow)
676 676
            workflow.backoffice_fields_formdef.fields = imported_formdef.fields
677 677

  
......
905 905
                    el.text = str(val)
906 906
        return node
907 907

  
908
    def init_with_xml(self, elem, charset, include_id=False):
908
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
909 909
        if include_id and elem.attrib.get('id'):
910 910
            self.id = elem.attrib.get('id')
911 911
        for attribute in self.get_parameters():
912 912
            el = elem.find(attribute)
913 913
            if getattr(self, '%s_init_with_xml' % attribute, None):
914 914
                getattr(self, '%s_init_with_xml' % attribute)(el, charset,
915
                        include_id=include_id)
915
                        include_id=include_id, snapshot=snapshot)
916 916
                continue
917 917
            if el is None:
918 918
                continue
......
957 957
            sub.attrib['role_id'] = role_id
958 958
            sub.text = role
959 959

  
960
    def _roles_init_with_xml(self, attribute, elem, charset, include_id=False):
960
    def _roles_init_with_xml(self, attribute, elem, charset, include_id=False, snapshot=False):
961 961
        if elem is None:
962 962
            setattr(self, attribute, [])
963 963
        else:
......
1021 1021
        role.store()
1022 1022
        return role.id
1023 1023

  
1024
    def _role_init_with_xml(self, attribute, elem, charset, include_id=False):
1024
    def _role_init_with_xml(self, attribute, elem, charset, include_id=False, snapshot=False):
1025 1025
        setattr(self, attribute, self._get_role_id_from_xml(elem, charset,
1026 1026
            include_id=include_id))
1027 1027

  
......
1071 1071
    def roles_export_to_xml(self, item, charset, include_id=False):
1072 1072
        self._roles_export_to_xml('roles', item, charset, include_id=include_id)
1073 1073

  
1074
    def roles_init_with_xml(self, elem, charset, include_id=False):
1075
        self._roles_init_with_xml('roles', elem, charset, include_id=include_id)
1074
    def roles_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
1075
        self._roles_init_with_xml('roles', elem, charset, include_id=include_id, snapshot=snapshot)
1076 1076

  
1077 1077

  
1078 1078
class WorkflowGlobalActionTimeoutTriggerMarker(object):
......
1413 1413

  
1414 1414
        return status
1415 1415

  
1416
    def init_with_xml(self, elem, charset, include_id=False):
1416
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
1417 1417
        self.id = xml_node_text(elem.find('id'))
1418 1418
        self.name = xml_node_text(elem.find('name'))
1419 1419
        if elem.find('backoffice_info_text') is not None:
......
1425 1425
            self.append_item(item_type)
1426 1426
            item_o = self.items[-1]
1427 1427
            item_o.parent = self
1428
            item_o.init_with_xml(item, charset, include_id=include_id)
1428
            item_o.init_with_xml(item, charset, include_id=include_id, snapshot=snapshot)
1429 1429

  
1430 1430
        self.triggers = []
1431 1431
        for trigger in elem.find('triggers'):
......
1433 1433
            self.append_trigger(trigger_type)
1434 1434
            trigger_o = self.triggers[-1]
1435 1435
            trigger_o.parent = self
1436
            trigger_o.init_with_xml(trigger, charset, include_id=include_id)
1436
            trigger_o.init_with_xml(trigger, charset, include_id=include_id, snapshot=snapshot)
1437 1437

  
1438 1438

  
1439 1439
class WorkflowCriticalityLevel(object):
......
1454 1454
            ET.SubElement(level, 'colour').text = force_text(self.colour, charset)
1455 1455
        return level
1456 1456

  
1457
    def init_with_xml(self, elem, charset, include_id=False):
1457
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
1458 1458
        self.id = xml_node_text(elem.find('id'))
1459 1459
        self.name = xml_node_text(elem.find('name'))
1460 1460
        if elem.find('colour') is not None:
......
1732 1732
                include_id=include_id))
1733 1733
        return status
1734 1734

  
1735
    def init_with_xml(self, elem, charset, include_id=False):
1735
    def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
1736 1736
        self.id = xml_node_text(elem.find('id'))
1737 1737
        self.name = xml_node_text(elem.find('name'))
1738 1738
        if elem.find('colour') is not None:
......
1754 1754
            self.append_item(item_type)
1755 1755
            item_o = self.items[-1]
1756 1756
            item_o.parent = self
1757
            item_o.init_with_xml(item, charset, include_id=include_id)
1757
            item_o.init_with_xml(item, charset, include_id=include_id, snapshot=snapshot)
1758 1758

  
1759 1759
    def __repr__(self):
1760 1760
        return '<%s %s %r>' % (self.__class__.__name__, self.id, self.name)
......
2086 2086
    def by_export_to_xml(self, item, charset, include_id=False):
2087 2087
        self._roles_export_to_xml('by', item, charset, include_id=include_id)
2088 2088

  
2089
    def by_init_with_xml(self, elem, charset, include_id=False):
2090
        self._roles_init_with_xml('by', elem, charset, include_id=include_id)
2089
    def by_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
2090
        self._roles_init_with_xml('by', elem, charset, include_id=include_id, snapshot=snapshot)
2091 2091

  
2092 2092
    def to_export_to_xml(self, item, charset, include_id=False):
2093 2093
        self._roles_export_to_xml('to', item, charset, include_id=include_id)
2094 2094

  
2095
    def to_init_with_xml(self, elem, charset, include_id=False):
2096
        self._roles_init_with_xml('to', elem, charset, include_id)
2095
    def to_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
2096
        self._roles_init_with_xml('to', elem, charset, include_id, snapshot=snapshot)
2097 2097

  
2098
    def condition_init_with_xml(self, node, charset, include_id=False):
2098
    def condition_init_with_xml(self, node, charset, include_id=False, snapshot=False):
2099 2099
        self.condition = None
2100 2100
        if node is None:
2101 2101
            return
......
2117 2117
            del odict['parent']
2118 2118
        return odict
2119 2119

  
2120
    def mail_template_init_with_xml(self, elem, charset, include_id=False):
2120
    def mail_template_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
2121 2121
        if elem is None:
2122 2122
            self.mail_template = None
2123 2123
            return
......
2128 2128
        self.mail_template = value
2129 2129
        return
2130 2130

  
2131
    def attachments_init_with_xml(self, elem, charset, include_id=False):
2131
    def attachments_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
2132 2132
        if elem is None:
2133 2133
            self.attachments = None
2134 2134
        else:
......
2396 2396
            el = ET.SubElement(xml_item, 'button_label')
2397 2397
            el.text = self.button_label
2398 2398

  
2399
    def button_label_init_with_xml(self, element, charset, include_id=False):
2399
    def button_label_init_with_xml(self, element, charset, include_id=False, snapshot=False):
2400 2400
        if element is None:
2401 2401
            return
2402 2402
        # this can be None if element is self-closing, <button_label />, which
2403
-