From 3d8ebb6322745709ac911c72f4df380280a03729 Mon Sep 17 00:00:00 2001
From: Todd Dembrey <todd.dembrey@torchbox.com>
Date: Wed, 26 Sep 2018 14:48:53 +0100
Subject: [PATCH] Add the concept of single include block as alternative to
 required

---
 opentech/apply/funds/blocks.py | 16 ++++++++++++++--
 opentech/apply/utils/blocks.py | 27 ++++++++++++++++++---------
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/opentech/apply/funds/blocks.py b/opentech/apply/funds/blocks.py
index 614bf4918..d0d8b78ad 100644
--- a/opentech/apply/funds/blocks.py
+++ b/opentech/apply/funds/blocks.py
@@ -6,7 +6,16 @@ from django.utils.translation import ugettext_lazy as _
 from addressfield.fields import AddressField
 from opentech.apply.categories.blocks import CategoryQuestionBlock
 from opentech.apply.stream_forms.blocks import FormFieldsBlock
-from opentech.apply.utils.blocks import MustIncludeFieldBlock, CustomFormFieldsBlock, RichTextFieldBlock
+from opentech.apply.utils.blocks import (
+    CustomFormFieldsBlock,
+    MustIncludeFieldBlock,
+    RichTextFieldBlock,
+    SingleIncludeBlock,
+)
+
+
+class ApplicationSingleIncludeFieldBlock(SingleIncludeBlock):
+    pass
 
 
 class ApplicationMustIncludeFieldBlock(MustIncludeFieldBlock):
@@ -36,7 +45,7 @@ class EmailBlock(ApplicationMustIncludeFieldBlock):
         icon = 'mail'
 
 
-class AddressFieldBlock(ApplicationMustIncludeFieldBlock):
+class AddressFieldBlock(ApplicationSingleIncludeFieldBlock):
     name = 'address'
     description = 'The postal address of the user'
 
@@ -105,6 +114,9 @@ class ApplicationCustomFormFieldsBlock(CustomFormFieldsBlock, FormFieldsBlock):
     category = CategoryQuestionBlock(group=_('Custom'))
     rich_text = RichTextFieldBlock(group=_('Fields'))
     required_blocks = ApplicationMustIncludeFieldBlock.__subclasses__()
+    single_blocks = ApplicationSingleIncludeFieldBlock.__subclasses__()
 
 
 REQUIRED_BLOCK_NAMES = [block.name for block in ApplicationMustIncludeFieldBlock.__subclasses__()]
+
+SINGLE_BLOCK_NAMES = [block.name for block in ApplicationSingleIncludeFieldBlock.__subclasses__()]
diff --git a/opentech/apply/utils/blocks.py b/opentech/apply/utils/blocks.py
index 898e0487a..124911764 100644
--- a/opentech/apply/utils/blocks.py
+++ b/opentech/apply/utils/blocks.py
@@ -8,7 +8,7 @@ from django.utils.text import mark_safe
 
 from wagtail.core.blocks import StaticBlock, StreamValue, StreamBlock
 
-from opentech.apply.stream_forms.blocks import FormFieldBlock, TextFieldBlock
+from opentech.apply.stream_forms.blocks import FormFieldBlock, OptionalFormFieldBlock, TextFieldBlock
 from opentech.apply.utils.options import RICH_TEXT_WIDGET
 
 
@@ -45,10 +45,13 @@ class RichTextFieldBlock(TextFieldBlock):
 class CustomFormFieldsBlock(StreamBlock):
     rich_text = RichTextFieldBlock(group=_('Fields'))
     required_blocks = []
+    single_blocks = []
 
     def __init__(self, *args, **kwargs):
         child_blocks = [(block.name, block(group=_('Required'))) for block in self.required_blocks]
+        child_blocks += [(block.name, block(group=_('Custom'))) for block in self.single_blocks]
         self.required_block_names = [block.name for block in self.required_blocks]
+        self.single_block_names = [block.name for block in self.single_blocks] + self.required_block_names
 
         super().__init__(child_blocks, *args, **kwargs)
 
@@ -65,7 +68,7 @@ class CustomFormFieldsBlock(StreamBlock):
 
         duplicates = [
             name for name in find_duplicates(block_types)
-            if name in self.required_block_names
+            if name in self.single_block_names
         ]
 
         all_errors = list()
@@ -76,7 +79,7 @@ class CustomFormFieldsBlock(StreamBlock):
 
         if duplicates:
             all_errors.append(
-                'You have duplicates of the following required fields: {}'.format(', '.join(prettify_names(duplicates)))
+                'You have duplicates of the following non duplicate fields: {}'.format(', '.join(prettify_names(duplicates)))
             )
             for i, block_name in enumerate(block_types):
                 if block_name in duplicates:
@@ -110,7 +113,7 @@ class CustomFormFieldsBlock(StreamBlock):
         return StreamValue(self, value, is_lazy=True)
 
 
-class MustIncludeStatic(StaticBlock):
+class SingleIncludeStatic(StaticBlock):
     """Helper block which displays additional information about the must include block and
     helps display the error in a noticeable way.
     """
@@ -137,15 +140,21 @@ class MustIncludeStatic(StaticBlock):
         return ('wagtail.core.blocks.static_block.StaticBlock', (), {})
 
 
-class MustIncludeFieldBlock(FormFieldBlock):
-    """Any block inheriting from this will need to be included in the application forms
-    This data will also be available to query on the submission object
-    """
+class SingleIncludeMixin:
     def __init__(self, *args, **kwargs):
         info_name = f'{self.name.title()} Field'
-        child_blocks = [('info', MustIncludeStatic(label=info_name, description=self.description))]
+        child_blocks = [('info', SingleIncludeStatic(label=info_name, description=self.description))]
         super().__init__(child_blocks, *args, **kwargs)
 
+
+class SingleIncludeBlock(SingleIncludeMixin, OptionalFormFieldBlock):
+    """A block that is only allowed to be included once in the form, but is optional"""
+
+
+class MustIncludeFieldBlock(SingleIncludeMixin, FormFieldBlock):
+    """Any block inheriting from this will need to be included in the application forms
+    This data will also be available to query on the submission object
+    """
     def get_field_kwargs(self, struct_value):
         kwargs = super().get_field_kwargs(struct_value)
         kwargs['required'] = True
-- 
GitLab