From d377c711339721a6c5d5d6d7a52d3fcef40b8eec Mon Sep 17 00:00:00 2001
From: Emmanuel Cazenave <ecazenave@entrouvert.com>
Date: Thu, 3 Sep 2020 11:10:56 +0200
Subject: [PATCH] workflows: add some options to add attachement action
 (#37860)

---
 tests/test_form_pages.py | 41 ++++++++++++++++++++++++++++++++----
 wcs/fields.py            | 45 +++++-----------------------------------
 wcs/qommon/misc.py       | 41 ++++++++++++++++++++++++++++++++++++
 wcs/wf/attachment.py     | 27 +++++++++++++++++++++---
 4 files changed, 107 insertions(+), 47 deletions(-)

diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index 43f35d52..5c1461c0 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -3419,7 +3419,7 @@ def test_formdata_attachment_download(pub):
     resp = resp.follow()
     assert 'The form has been recorded' in resp.text
 
-    resp.forms[0]['attachment_attach'] = Upload('test.txt', b'foobar', 'text/plain')
+    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
     resp = resp.forms[0].submit('button_attach')
 
     assert formdef.data_class().count() == 1
@@ -3463,7 +3463,7 @@ def test_formdata_attachment_download_with_substitution_variable(pub):
     resp = resp.follow()
     assert 'The form has been recorded' in resp.text
 
-    resp.forms[0]['attachment_attach'] = Upload('test.txt', b'foobar', 'text/plain')
+    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
     resp = resp.forms[0].submit('button_attach')
 
     assert formdef.data_class().count() == 1
@@ -3529,7 +3529,7 @@ def test_formdata_attachment_download_to_backoffice_file_field(pub):
     resp = resp.follow()
     assert 'The form has been recorded' in resp.text
 
-    resp.forms[0]['attachment_attach'] = Upload('test.txt', b'foobar', 'text/plain')
+    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
     resp = resp.forms[0].submit('button_attach')
 
     # backoffice file field is set
@@ -3581,7 +3581,7 @@ def test_formdata_attachment_download_to_backoffice_file_field_only(pub):
     resp = resp.follow()
     assert 'The form has been recorded' in resp.text
 
-    resp.forms[0]['attachment_attach'] = Upload('test.txt', b'foobar', 'text/plain')
+    resp.forms[0]['attachment_attach$file'] = Upload('test.txt', b'foobar', 'text/plain')
     resp = resp.forms[0].submit('button_attach')
 
     # backoffice file field is set
@@ -3598,6 +3598,39 @@ def test_formdata_attachment_download_to_backoffice_file_field_only(pub):
         assert not evo.parts
 
 
+def test_formdata_attachment_file_options(pub):
+    create_user(pub)
+    wf = Workflow(name='status')
+    st1 = wf.add_status('Status1', 'st1')
+    attach = AddAttachmentWorkflowStatusItem()
+    attach.id = '_attach'
+    attach.by = ['_submitter']
+    attach.document_type = {'label': 'Fichiers vidéo', 'mimetypes': ['video/*'], 'id': '_video'}
+    attach.max_file_size = '3Mo'
+
+    st1.items.append(attach)
+    attach.parent = st1
+    wf.store()
+
+    formdef = create_formdef()
+    formdef.workflow_id = wf.id
+    formdef.fields = []
+    formdef.store()
+    formdef.data_class().wipe()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    resp = resp.forms[0].submit('submit')
+    assert 'Check values then click submit.' in resp.text
+    resp = resp.forms[0].submit('submit')
+    assert resp.status_int == 302
+    resp = resp.follow()
+    assert 'The form has been recorded' in resp.text
+
+    file_input = resp.forms[0]['attachment_attach$file']
+    assert file_input.attrs['accept'] == 'video/*'
+    assert file_input.attrs['data-max-file-size'] == '3000000'
+
+
 def test_formdata_generated_document_download(pub):
     create_user(pub)
     wf = Workflow(name='status')
diff --git a/wcs/fields.py b/wcs/fields.py
index 0a90b1f1..ff085964 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -38,7 +38,7 @@ from django.utils.six.moves.html_parser import HTMLParser
 from .qommon import _, N_, force_str
 from .qommon import evalutils
 from .qommon.form import *
-from .qommon.misc import localstrftime, strftime, date_format, ellipsize, xml_node_text, get_as_datetime
+from .qommon.misc import localstrftime, strftime, date_format, ellipsize, xml_node_text, get_as_datetime, get_document_types, get_document_type_value_options
 from .qommon.ods import NS as OD_NS, clean_text as od_clean_text
 from .qommon.template import Template, TemplateError
 from .qommon import get_cfg, get_logger
@@ -1067,20 +1067,10 @@ class FileField(WidgetField):
 
     def fill_admin_form(self, form):
         WidgetField.fill_admin_form(self, form)
-        document_types = self.get_document_types()
-        cur_dt = self.document_type or {}
-        # SingleSelectWidget compare the value and not the keys, so if we want
-        # the current value not to be hidden, we must reset it with the corresponding
-        # value from settings based on the 'id'
-        document_type_id = cur_dt.get('id')
-        if document_type_id in document_types \
-               and cur_dt != document_types[document_type_id]:
-            cur_dt = document_types[document_type_id]
-        options = [(None, '---', {})]
-        options += [(doc_type, doc_type['label'], key) for key, doc_type in document_types.items()]
+        value, options = get_document_type_value_options(self.document_type)
         form.add(SingleSelectWidget, 'document_type', title=_('File type suggestion'),
-                value=cur_dt, options=options,
-                advanced=not(cur_dt))
+                value=value, options=options,
+                advanced=not(value))
         form.add(FileSizeWidget, 'max_file_size', title=_('Max file size'),
                 value=self.max_file_size,
                 advanced=not(self.max_file_size))
@@ -1215,38 +1205,13 @@ class FileField(WidgetField):
             if value and hasattr(value, 'token'):
                 get_request().form[self.field_key + '$token'] = value.token
 
-    def get_document_types(self):
-        document_types = {
-            '_audio': {
-                'label': _('Sound files'),
-                'mimetypes': ['audio/*'],
-            },
-            '_video': {
-                'label': _('Video files'),
-                'mimetypes': ['video/*'],
-            },
-            '_image': {
-                'label': _('Image files'),
-                'mimetypes': ['image/*'],
-            }
-        }
-        # Local document types
-        document_types.update(get_cfg('filetypes', {}))
-        for key, document_type in document_types.items():
-            document_type['id'] = key
-        # add current file type if it does not exist anymore in the settings
-        cur_dt = self.document_type or {}
-        if cur_dt and cur_dt['id'] not in document_types:
-            document_types[cur_dt['id']] = cur_dt
-        return document_types
-
     def migrate(self):
         changed = super(FileField, self).migrate()
         if 'file_type' in self.__dict__:
             self.document_type = {}
             if self.__dict__['file_type']:
                 file_type = self.__dict__['file_type']
-                document_types = self.get_document_types()
+                document_types = get_document_types(self.document_type)
                 parts = []
                 for key, value in document_types.items():
                     if file_type == value.get('mimetypes'):
diff --git a/wcs/qommon/misc.py b/wcs/qommon/misc.py
index f037729d..8afd06b8 100644
--- a/wcs/qommon/misc.py
+++ b/wcs/qommon/misc.py
@@ -837,3 +837,44 @@ class QLookupRedirect:
 
     def _q_traverse(self, path):
         return redirect(self.url)
+
+
+def get_document_types(document_type):
+    document_types = {
+        '_audio': {
+            'label': _('Sound files'),
+            'mimetypes': ['audio/*'],
+        },
+        '_video': {
+            'label': _('Video files'),
+            'mimetypes': ['video/*'],
+        },
+        '_image': {
+            'label': _('Image files'),
+            'mimetypes': ['image/*'],
+        }
+    }
+    # Local document types
+    document_types.update(get_cfg('filetypes', {}))
+    for key, document_type in document_types.items():
+        document_type['id'] = key
+            # add current file type if it does not exist anymore in the settings
+    cur_dt = document_type or {}
+    if cur_dt and cur_dt['id'] not in document_types:
+        document_types[cur_dt['id']] = cur_dt
+    return document_types
+
+
+def get_document_type_value_options(document_type):
+    document_types = get_document_types(document_type)
+    cur_dt = document_type or {}
+    # SingleSelectWidget compare the value and not the keys, so if we want
+    # the current value not to be hidden, we must reset it with the corresponding
+    # value from settings based on the 'id'
+    document_type_id = cur_dt.get('id')
+    if document_type_id in document_types \
+           and cur_dt != document_types[document_type_id]:
+        cur_dt = document_types[document_type_id]
+    options = [(None, '---', {})]
+    options += [(doc_type, doc_type['label'], key) for key, doc_type in document_types.items()]
+    return cur_dt, options
diff --git a/wcs/wf/attachment.py b/wcs/wf/attachment.py
index c241a535..5cd09946 100644
--- a/wcs/wf/attachment.py
+++ b/wcs/wf/attachment.py
@@ -20,7 +20,9 @@ from quixote import redirect
 
 from ..qommon import _, N_
 from wcs.workflows import *
+from ..qommon import get_cfg
 from ..qommon.errors import *
+from ..qommon.misc import get_document_type_value_options
 
 from wcs.forms.common import FormStatusPage, FileDirectory
 from wcs.portfolio import has_portfolio, push_document
@@ -86,6 +88,12 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
     backoffice_filefield_id = None
     attach_to_history = True  # legacy choice
     push_to_portfolio = False
+    document_type = None
+    max_file_size = None
+
+    def __init__(self, *args, **kwargs):
+        super(AddAttachmentWorkflowStatusItem, self).__init__(*args, **kwargs)
+        self.document_type = self.document_type or {}
 
     @classmethod
     def init(cls):
@@ -106,8 +114,10 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
             title = self.title or _('Upload File')
         else:
             title = None
-        form.add(FileWidget, 'attachment%s' % self.id, title=title,
-                    required=self.required, hint=self.hint)
+        file_type = self.document_type.get('mimetypes')
+        form.add(FileWithPreviewWidget, 'attachment%s' % self.id, title=title,
+                    required=self.required, hint=self.hint, file_type=file_type,
+                    max_file_size=self.max_file_size)
         if self.display_button:
             form.add_submit('button%s' % self.id, self.button_label or _('Upload File'))
             form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text
@@ -139,7 +149,8 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
     def get_parameters(self):
         parameters = ('by', 'required', 'title', 'display_title', 'button_label',
                 'display_button', 'hint', 'backoffice_info_text',
-                'backoffice_filefield_id', 'varname', 'attach_to_history')
+                'backoffice_filefield_id', 'varname', 'attach_to_history', 'document_type',
+                'max_file_size')
         if has_portfolio():
             parameters += ('push_to_portfolio',)
         parameters += ('condition',)
@@ -191,6 +202,16 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
             form.add(CheckboxWidget, '%spush_to_portfolio' % prefix,
                      title=_('Push to portfolio'),
                      value=self.push_to_portfolio)
+        if 'document_type' in parameters:
+            value, options = get_document_type_value_options(self.document_type)
+            form.add(SingleSelectWidget, 'document_type', title=_('File type suggestion'),
+                     value=value, options=options,
+                     advanced=not(value))
+        if 'max_file_size' in parameters:
+            form.add(FileSizeWidget, 'max_file_size', title=_('Max file size'),
+                     value=self.max_file_size,
+                     advanced=not(self.max_file_size))
+
 
 
 register_item_class(AddAttachmentWorkflowStatusItem)
-- 
2.20.1

