Projet

Général

Profil

0001-workflows-allow-attachments-in-emails-8274.patch

Thomas Noël, 02 octobre 2017 14:17

Télécharger (15,3 ko)

Voir les différences:

Subject: [PATCH] workflows: allow attachments in emails (#8274)

 tests/test_admin_pages.py |  74 ++++++++++++++++++++++++++++++-
 tests/test_workflows.py   | 111 ++++++++++++++++++++++++++++++++++++++++++++++
 wcs/workflows.py          |  63 +++++++++++++++++++++++++-
 3 files changed, 246 insertions(+), 2 deletions(-)
tests/test_admin_pages.py
1919 1919
    role = create_role()
1920 1920
    Workflow.wipe()
1921 1921
    workflow = Workflow(name='foo')
1922
    workflow.add_status(name='baz')
1922
    st1 = workflow.add_status(name='baz')
1923 1923
    workflow.store()
1924 1924

  
1925 1925
    app = login(get_app(pub))
......
1957 1957
        resp = resp.form.submit('submit')
1958 1958
        assert 'error in template' in resp.body and 'unmatched [end]' in resp.body
1959 1959

  
1960
    # attachments without backoffice fields: python expressions
1961
    resp = app.get(item_url)
1962
    assert "Attachments (Python expressions)" in resp.body
1963
    resp.form['attachments$element0'] = 'form_var_upload_raw'
1964
    resp = resp.form.submit('submit')
1965
    assert resp.location
1966
    resp = app.get(item_url)
1967
    assert "Attachments (Python expressions)" in resp.body
1968
    assert resp.form['attachments$element0'].value == 'form_var_upload_raw'
1969
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
1970
    assert sendmail.attachments == ['form_var_upload_raw']
1971

  
1972
    # attachments with backoffice fields: select-with-other inputs
1973
    workflow = Workflow.get(workflow.id)
1974
    workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
1975
    workflow.backoffice_fields_formdef.fields = [
1976
        fields.FileField(id='bo1', label='bo field 1', type='file', varname='upload'),
1977
        fields.FileField(id='bo2', label='bo field 2', type='file', varname='upload2'),
1978
        fields.FileField(id='bo3', label='bo field varnameless', type='file'),
1979
    ]
1980
    workflow.store()
1981
    resp = app.get(item_url)
1982
    assert "Attachments" in resp.body
1983
    assert "Attachments (Python expressions)" not in resp.body
1984
    assert resp.form['attachments$element0$choice'].value == 'form_var_upload_raw'
1985
    assert len(resp.form['attachments$element0$choice'].options) == 5
1986
    resp.form['attachments$element1$choice'] = 'form_var_upload2_raw'
1987
    resp = resp.form.submit('submit')
1988
    assert resp.location
1989
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
1990
    assert sendmail.attachments == ['form_var_upload_raw', 'form_var_upload2_raw']
1991

  
1992
    resp = app.get(item_url)
1993
    resp.form['attachments$element2$choice'] = 'form_fbo3'
1994
    resp = resp.form.submit('submit')
1995
    assert resp.location
1996
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
1997
    assert sendmail.attachments == ['form_var_upload_raw', 'form_var_upload2_raw', 'form_fbo3']
1998

  
1999
    resp = app.get(item_url)
2000
    resp.form['attachments$element3$choice'] = '__other'
2001
    resp.form['attachments$element3$other'] = '{"content":"foo", "filename":"bar.txt"}'
2002
    resp = resp.form.submit('submit')
2003
    assert resp.location
2004
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
2005
    assert sendmail.attachments == ['form_var_upload_raw', 'form_var_upload2_raw', 'form_fbo3',
2006
                                    '{"content":"foo", "filename":"bar.txt"}']
2007

  
2008
    # remove some backoffice fields: varnameless fbo3 disapear
2009
    workflow = Workflow.get(workflow.id)
2010
    workflow.backoffice_fields_formdef.fields = [
2011
        fields.FileField(id='bo2', label='bo field 2', type='file', varname='upload2'),
2012
    ]
2013
    workflow.store()
2014
    resp = app.get(item_url)
2015
    resp = resp.form.submit('submit')
2016
    assert resp.location
2017
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
2018
    assert sendmail.attachments == ['form_var_upload_raw', 'form_var_upload2_raw',
2019
                                    '{"content":"foo", "filename":"bar.txt"}']
2020
    # remove all backoffice fields
2021
    workflow = Workflow.get(workflow.id)
2022
    workflow.backoffice_fields_formdef.fields = []
2023
    workflow.store()
2024
    resp = app.get(item_url)
2025
    assert "Attachments (Python expressions)" in resp.body
2026
    resp = resp.form.submit('submit')
2027
    assert resp.location
2028
    sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0]
2029
    assert sendmail.attachments == ['form_var_upload_raw', 'form_var_upload2_raw',
2030
                                    '{"content":"foo", "filename":"bar.txt"}']
2031

  
1960 2032
def test_workflows_edit_sms_action(pub):
1961 2033
    create_superuser(pub)
1962 2034
    role = create_role()
tests/test_workflows.py
1 1
import datetime
2
import os
2 3
import pytest
3 4
import shutil
4 5
import StringIO
......
765 766
    assert emails.count() == 1
766 767
    assert emails.get('foobar').get('from') == 'foobar@localhost'
767 768

  
769

  
770
def test_email_attachments(pub, emails):
771
    formdef = FormDef()
772
    formdef.name = 'baz'
773
    formdef.fields = [
774
        FileField(id='3', label='File', type='file', varname='file'),
775
    ]
776
    formdef.store()
777

  
778
    upload = PicklableUpload('test.jpeg', 'image/jpeg')
779
    jpg = open(os.path.join(os.path.dirname(__file__), 'image-with-gps-data.jpeg')).read()
780
    upload.receive([jpg])
781
    formdata = formdef.data_class()()
782
    formdata.data = {'3': upload}
783
    formdata.just_created()
784
    formdata.store()
785
    pub.substitutions.feed(formdata)
786

  
787
    sendmail = SendmailWorkflowStatusItem()
788
    sendmail.subject = 'foobar'
789
    sendmail.body = '<p>force html</p>'
790
    sendmail.to = ['to@example.net']
791
    sendmail.attachments = ['form_var_file_raw']
792
    sendmail.perform(formdata)
793
    get_response().process_after_jobs()
794
    assert emails.count() == 1
795
    assert emails.emails['foobar']['msg'].is_multipart()
796
    assert emails.emails['foobar']['msg'].get_content_subtype() == 'mixed'
797
    assert emails.emails['foobar']['msg'].get_payload()[0].get_content_type() == 'text/html'
798
    assert emails.emails['foobar']['msg'].get_payload()[1].get_content_type() == 'image/jpeg'
799

  
800
    # build a backoffice field
801
    Workflow.wipe()
802
    FormDef.wipe()
803
    wf = Workflow(name='email with attachments')
804
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
805
    wf.backoffice_fields_formdef.fields = [
806
        FileField(id='bo1', label='bo field 1', type='file', varname='backoffice_file1'),
807
        FileField(id='bo2', label='bo field 2', type='file', varname='backoffice_file2'),
808
    ]
809
    st1 = wf.add_status('Status1')
810
    wf.store()
811
    formdef = FormDef()
812
    formdef.name = 'baz'
813
    formdef.fields = [
814
        FileField(id='1', label='File', type='file', varname='frontoffice_file'),
815
    ]
816
    formdef.workflow_id = wf.id
817
    formdef.store()
818
    formdata = formdef.data_class()()
819
    formdata.data = {'1': upload}
820
    formdata.just_created()
821
    formdata.store()
822
    pub.substitutions.feed(formdata)
823
    # store file in backoffice field form_fbo1 / form_var_backoffice_file_raw
824
    setbo = SetBackofficeFieldsWorkflowStatusItem()
825
    setbo.parent = st1
826
    setbo.fields = [{'field_id': 'bo1', 'value': '=form_var_frontoffice_file_raw'}]
827
    setbo.perform(formdata)
828

  
829
    emails.empty()
830
    sendmail.attachments = ['form_fbo1']
831
    sendmail.perform(formdata)
832
    get_response().process_after_jobs()
833
    assert emails.count() == 1
834
    assert emails.emails['foobar']['msg'].is_multipart()
835
    assert emails.emails['foobar']['msg'].get_content_subtype() == 'mixed'
836
    assert emails.emails['foobar']['msg'].get_payload()[0].get_content_type() == 'text/html'
837
    assert emails.emails['foobar']['msg'].get_payload()[1].get_content_type() == 'image/jpeg'
838

  
839
    emails.empty()
840
    sendmail.attachments = ['form_var_backoffice_file1_raw']
841
    sendmail.perform(formdata)
842
    get_response().process_after_jobs()
843
    assert emails.count() == 1
844
    assert emails.emails['foobar']['msg'].is_multipart()
845
    assert emails.emails['foobar']['msg'].get_content_subtype() == 'mixed'
846
    assert emails.emails['foobar']['msg'].get_payload()[0].get_content_type() == 'text/html'
847
    assert emails.emails['foobar']['msg'].get_payload()[1].get_content_type() == 'image/jpeg'
848

  
849
    emails.empty()
850
    sendmail.attachments = ['form_var_backoffice_file1_raw', 'form_var_backoffice_file2_raw']
851
    sendmail.perform(formdata)
852
    get_response().process_after_jobs()
853
    assert emails.count() == 1
854
    assert emails.emails['foobar']['msg'].is_multipart()
855
    assert emails.emails['foobar']['msg'].get_content_subtype() == 'mixed'
856
    assert emails.emails['foobar']['msg'].get_payload()[0].get_content_type() == 'text/html'
857
    assert emails.emails['foobar']['msg'].get_payload()[1].get_content_type() == 'image/jpeg'
858
    # backoffice_file2 is unset, no more parts :
859
    assert len(emails.emails['foobar']['msg'].get_payload()) == 2
860

  
861
    # set backoffice_file2 and retry
862
    setbo.fields = [{'field_id': 'bo2',
863
                     'value': '={"content": "blah", "filename": "hello.txt", '
864
                              '"content_type": "text/plain"}'}]
865
    setbo.perform(formdata)
866
    emails.empty()
867
    sendmail.perform(formdata)
868
    get_response().process_after_jobs()
869
    assert emails.count() == 1
870
    assert emails.emails['foobar']['msg'].is_multipart()
871
    assert emails.emails['foobar']['msg'].get_content_subtype() == 'mixed'
872
    assert emails.emails['foobar']['msg'].get_payload()[0].get_content_type() == 'text/html'
873
    assert emails.emails['foobar']['msg'].get_payload()[1].get_content_type() == 'image/jpeg'
874
    assert emails.emails['foobar']['msg'].get_payload()[2].get_content_type() == 'text/plain'
875
    assert emails.emails['foobar']['msg'].get_payload()[2].get_payload() == 'blah'
876
    assert len(emails.emails['foobar']['msg'].get_payload()) == 3
877

  
878

  
768 879
def test_webservice_call(pub):
769 880
    pub.substitutions.feed(MockSubstitutionVariables())
770 881

  
wcs/workflows.py
38 38
import qommon.errors
39 39

  
40 40
from wcs.roles import Role, logged_users_role, get_user_roles
41
from wcs.fields import FileField
41 42
from wcs.formdef import FormDef
42 43
from wcs.formdata import Evolution
43 44

  
......
2015 2016
    subject = None
2016 2017
    body = None
2017 2018
    custom_from = None
2019
    attachments = None
2018 2020

  
2019 2021
    comment = None
2020 2022

  
......
2053 2055
            return _('Send mail (not completed)')
2054 2056

  
2055 2057
    def get_parameters(self):
2056
        return ('to', 'subject', 'body', 'custom_from')
2058
        return ('to', 'subject', 'body', 'attachments', 'custom_from')
2057 2059

  
2058 2060
    def fill_admin_form(self, form):
2059 2061
        self.add_parameters_widgets(form, self.get_parameters())
2060 2062

  
2063
    def get_attachments_options(self):
2064
        attachments_options = [(None, '---', None)]
2065
        varnameless = []
2066
        for field in self.parent.parent.get_backoffice_fields():
2067
            if field.key != 'file':
2068
                continue
2069
            if field.varname:
2070
                codename = 'form_var_%s_raw' % field.varname
2071
            else:
2072
                codename = 'form_f%s' % field.id  # = form_fbo<n>
2073
                varnameless.append(codename)
2074
            attachments_options.append((codename, field.label, codename))
2075
        # filter: do not consider removed fields without varname
2076
        attachments = [attachment for attachment in self.attachments or []
2077
                       if ((not attachment.startswith('form_fbo')) or
2078
                           (attachment in varnameless))]
2079
        return attachments_options, attachments
2080

  
2061 2081
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
2062 2082
        if 'to' in parameters:
2063 2083
            form.add(WidgetList, '%sto' % prefix, title=_('To'),
......
2076 2096
                     value=self.body, cols=80, rows=10,
2077 2097
                     validation_function=ComputedExpressionWidget.validate_ezt,
2078 2098
                     hint=_('Available variables: url, url_status, details, name, number, comment, field_NAME'))
2099

  
2100
        if 'attachments' in parameters:
2101
            attachments_options, attachments = self.get_attachments_options()
2102
            if len(attachments_options) > 1:
2103
                form.add(WidgetList, '%sattachments' % prefix, title=_('Attachments'),
2104
                         element_type=SingleSelectWidgetWithOther,
2105
                         value=attachments,
2106
                         add_element_label=_('Add attachment'),
2107
                         element_kwargs={'render_br': False, 'options': attachments_options})
2108
            else:
2109
                form.add(WidgetList, '%sattachments' % prefix,
2110
                         title=_('Attachments (Python expressions)'),
2111
                         element_type=StringWidget,
2112
                         value=attachments,
2113
                         add_element_label=_('Add attachment'),
2114
                         element_kwargs={'render_br': False, 'size': 50},
2115
                         advanced=not(bool(attachments)))
2116

  
2079 2117
        if 'custom_from' in parameters:
2080 2118
            form.add(ComputedExpressionWidget, '%scustom_from' % prefix,
2081 2119
                     title=_('Custom From Address'), value=self.custom_from,
......
2152 2190
        if self.custom_from:
2153 2191
            email_from = self.compute(self.custom_from)
2154 2192

  
2193
        attachments = []
2194
        if self.attachments:
2195
            global_eval_dict = get_publisher().get_global_eval_dict()
2196
            local_eval_dict = get_publisher().substitutions.get_context_variables()
2197
            for attachment in self.attachments:
2198
                try:
2199
                    picklableupload = eval(attachment, global_eval_dict, local_eval_dict)
2200
                except:
2201
                    get_publisher().notify_of_exception(sys.exc_info(),
2202
                                                        context='[Sendmail/attachments]')
2203
                    continue
2204
                if not picklableupload:
2205
                    continue
2206
                try:
2207
                    picklableupload = FileField.convert_value_from_anything(picklableupload)
2208
                except ValueError:
2209
                    get_publisher().notify_of_exception(sys.exc_info(),
2210
                                                        context='[Sendmail/attachments]')
2211
                    continue
2212
                attachments.append(picklableupload)
2213

  
2155 2214
        if len(addresses) > 1:
2156 2215
            emails.email(mail_subject, mail_body, email_rcpt=None,
2157 2216
                    bcc=addresses, email_from=email_from,
2158 2217
                    exclude_current_user=False,
2218
                    attachments=attachments,
2159 2219
                    fire_and_forget=True)
2160 2220
        else:
2161 2221
            emails.email(mail_subject, mail_body, email_rcpt=addresses,
2162 2222
                    email_from=email_from, exclude_current_user=False,
2223
                    attachments=attachments,
2163 2224
                    fire_and_forget=True)
2164 2225
register_item_class(SendmailWorkflowStatusItem)
2165 2226

  
2166
-