diff --git a/opentech/apply/funds/models/applications.py b/opentech/apply/funds/models/applications.py index 4f69cb77ea91c53f8093c7dfbb38b4dab8a60280..164ad96659d931d9a6b3ac57b7b6afbb009e20d9 100644 --- a/opentech/apply/funds/models/applications.py +++ b/opentech/apply/funds/models/applications.py @@ -17,6 +17,7 @@ from django.db.models import ( When, ) from django.db.models.functions import Coalesce, Length +from django.shortcuts import render from django.http import Http404 from django.utils.functional import cached_property @@ -36,6 +37,7 @@ from wagtail.core.models import Page, PageManager, PageQuerySet from ..admin_forms import RoundBasePageAdminForm, WorkflowFormAdminForm from ..edit_handlers import ReadOnlyPanel, ReadOnlyInlinePanel +from ..workflow import OPEN_CALL_PHASES from .submissions import ApplicationSubmission from .utils import admin_url, EmailForm, SubmittableStreamForm, WorkflowStreamForm, LIMIT_TO_REVIEWERS, LIMIT_TO_STAFF @@ -280,9 +282,78 @@ class RoundBase(WorkflowStreamForm, SubmittableStreamForm): # type: ignore raise ValidationError(error) - def serve(self, request): + def get_initial_data_open_call_submission(self, submission_id): + initial_values = {} + + try: + submission_class = self.get_submission_class() + submission = submission_class.objects.get(id=submission_id) + if submission.status in OPEN_CALL_PHASES and self.get_parent() == submission.page: + title_block_id = submission.named_blocks.get('title') + if title_block_id: + field_data = submission.data(title_block_id) + initial_values[title_block_id] = field_data + ' (please edit)' + + for field_id in submission.first_group_normal_text_blocks: + field_data = submission.data(field_id) + initial_values[field_id] = field_data + + # Select first item in the Group toggle blocks + for toggle_block_id, toggle_field in submission.group_toggle_blocks: + try: + initial_values[toggle_block_id] = toggle_field.value['choices'][0] + except IndexError: + initial_values[toggle_block_id] = 'yes' + except KeyError: + pass + + except (submission_class.DoesNotExist, ValueError): + pass + + return initial_values + + def get_form_parameters(self, submission_id=None): + form_parameters = {} + + if submission_id: + initial_values = self.get_initial_data_open_call_submission(submission_id) + if initial_values: + form_parameters['initial'] = initial_values + + return form_parameters + + def get_form(self, *args, **kwargs): + form_class = self.get_form_class() + submission_id = kwargs.pop('submission_id', None) + if submission_id: + form_params = self.get_form_parameters(submission_id=submission_id) + else: + form_params = self.get_form_parameters() + form_params.update(kwargs) + + return form_class(*args, **form_params) + + def serve(self, request, *args, **kwargs): if hasattr(request, 'is_preview') or hasattr(request, 'show_round'): - return super().serve(request) + # Overriding serve method to pass submission id to get_form method + copy_open_submission = request.GET.get('open_call_submission') + if request.method == 'POST': + form = self.get_form(request.POST, request.FILES, page=self, user=request.user) + + if form.is_valid(): + form_submission = self.process_form_submission(form) + return self.render_landing_page(request, form_submission, *args, **kwargs) + else: + form = self.get_form(page=self, user=request.user, submission_id=copy_open_submission) + + context = self.get_context(request) + context['form'] = form + context['show_all_group_fields'] = True if copy_open_submission else False + return render( + request, + self.get_template(request), + context + ) # We hide the round as only the open round is used which is displayed through the # fund page diff --git a/opentech/apply/funds/models/mixins.py b/opentech/apply/funds/models/mixins.py index 00f253ddfd135175ade8977a6247b8bd80b1633b..cecacf3b64f0b18e193b931b4fef538aaea9d96b 100644 --- a/opentech/apply/funds/models/mixins.py +++ b/opentech/apply/funds/models/mixins.py @@ -4,7 +4,7 @@ from django.core.files import File from django.core.files.storage import get_storage_class from opentech.apply.stream_forms.blocks import ( - FileFieldBlock, FormFieldBlock, ImageFieldBlock, MultiFileFieldBlock + FileFieldBlock, FormFieldBlock, GroupToggleBlock, ImageFieldBlock, MultiFileFieldBlock ) from opentech.apply.utils.blocks import SingleIncludeMixin @@ -124,6 +124,16 @@ class AccessFormData: elif isinstance(field.block, FormFieldBlock): yield field_id + @property + def first_group_question_text_field_ids(self): + for field_id, field in self.fields.items(): + if isinstance(field.block, (FileFieldBlock, ImageFieldBlock, MultiFileFieldBlock)): + continue + elif isinstance(field.block, GroupToggleBlock): + break + elif isinstance(field.block, FormFieldBlock): + yield field_id + @property def raw_fields(self): # Field ids to field class mapping - similar to raw_data @@ -157,6 +167,20 @@ class AccessFormData: if field_id not in self.named_blocks ] + @property + def group_toggle_blocks(self): + for field_id, field in self.fields.items(): + if isinstance(field.block, GroupToggleBlock): + yield field_id, field + + @property + def first_group_normal_text_blocks(self): + return [ + field_id + for field_id in self.first_group_question_text_field_ids + if field_id not in self.named_blocks + ] + def serialize(self, field_id): field = self.field(field_id) data = self.data(field_id) @@ -180,6 +204,12 @@ class AccessFormData: for field_id in self.normal_blocks ] + def render_first_group_text_answers(self): + return [ + self.render_answer(field_id, include_question=True) + for field_id in self.first_group_normal_text_blocks + ] + def render_text_blocks_answers(self): # Returns a list of the rendered answers of type text return [ @@ -191,3 +221,6 @@ class AccessFormData: def output_answers(self): # Returns a safe string of the rendered answers return mark_safe(''.join(self.render_answers())) + + def output_first_group_text_answers(self): + return mark_safe(''.join(self.render_first_group_text_answers())) diff --git a/opentech/apply/funds/templates/funds/application_base.html b/opentech/apply/funds/templates/funds/application_base.html index 481536711302823109b1cfafde4810d0fe22d41d..d368cca23859b223e2d873720d5d1d6a159e7e4c 100644 --- a/opentech/apply/funds/templates/funds/application_base.html +++ b/opentech/apply/funds/templates/funds/application_base.html @@ -35,7 +35,7 @@ {% include "forms/includes/field.html" with is_application=True %} {% else %} {% if field.group_number > 1 %} - <div class="field-group-{{ field.group_number }}" style="display:none;"> + <div class="field-group-{{ field.group_number }}" {% if not show_all_group_fields %}style="display:none;"{% endif %}> {{ field }} </div> {% else %} @@ -52,5 +52,7 @@ {% block extra_js %} <script src="{% static 'js/apply/mailgun-validator.js' %}"></script> <script src="{% static 'js/apply/file-uploads.js' %}"></script> + {% if not show_all_group_fields %} <script src="{% static 'js/apply/form-group-toggle.js' %}"></script> + {% endif %} {% endblock %} diff --git a/opentech/apply/templates/forms/includes/field.html b/opentech/apply/templates/forms/includes/field.html index d80188d6442c3fbec7ada9c298d165a680bcd2ac..3304061107830d2a1ebc2bb08cfdb659498531cb 100644 --- a/opentech/apply/templates/forms/includes/field.html +++ b/opentech/apply/templates/forms/includes/field.html @@ -2,7 +2,7 @@ {% with widget_type=field|widget_type field_type=field|field_type %} -<div class="form__group {% if widget_type == 'checkbox_input' %}form__group--checkbox{% endif %} {% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' %}form__group--file{% endif %} {% if field.help_text %}form__group--wrap{% endif %}{% if field.errors %}form__error{% endif %} {% if is_application and field.field.group_number > 1 %}field-group-{{ field.field.group_number }}{% endif %} {% if is_application and field.field.grouper_for %}form-fields-grouper{% endif %}" {% if is_application and field.field.grouper_for %}data-grouper-for="{{ field.field.grouper_for }}" data-toggle-on="{{ field.field.choices.0.0 }}" data-toggle-off="{{ field.field.choices.1.0 }}"{% endif %} {% if is_application and field.field.group_number > 1 %}style="display:none;"{% endif %}> +<div class="form__group {% if widget_type == 'checkbox_input' %}form__group--checkbox{% endif %} {% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' %}form__group--file{% endif %} {% if field.help_text %}form__group--wrap{% endif %}{% if field.errors %}form__error{% endif %} {% if is_application and field.field.group_number > 1 %}field-group-{{ field.field.group_number }}{% endif %} {% if is_application and field.field.grouper_for %}form-fields-grouper{% endif %}" {% if is_application and field.field.grouper_for %}data-grouper-for="{{ field.field.grouper_for }}" data-toggle-on="{{ field.field.choices.0.0 }}" data-toggle-off="{{ field.field.choices.1.0 }}"{% endif %} {% if is_application and field.field.group_number > 1 and not show_all_group_fields %}style="display:none;"{% endif %}> {% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' %} <span class="form__question">{{ field.label }}</span> <label for="{{ field.id_for_label }}" class="form__question form__question--{{ field_type }} {{ widget_type }}" {% if field.field.required %}required{% endif %}> diff --git a/opentech/public/funds/migrations/0011_opencallindexpage.py b/opentech/public/funds/migrations/0011_opencallindexpage.py new file mode 100644 index 0000000000000000000000000000000000000000..98889d43d209adf912a30660d3e1a356d55a5748 --- /dev/null +++ b/opentech/public/funds/migrations/0011_opencallindexpage.py @@ -0,0 +1,33 @@ +# Generated by Django 2.0.9 on 2019-03-26 12:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('images', '0003_customimage_drupal_id'), + ('wagtailcore', '0040_page_draft_title'), + ('public_funds', '0010_correct_related_page_required'), + ] + + operations = [ + migrations.CreateModel( + name='OpenCallIndexPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ('social_text', models.CharField(blank=True, max_length=255)), + ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)), + ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)), + ('introduction', models.TextField(blank=True)), + ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', models.Model), + ), + ] diff --git a/opentech/public/funds/models.py b/opentech/public/funds/models.py index 0b1f212eb1abad8bba7634a1aa12888d1836db39..ae23e2f4813a39fdd2c35a311d4853bcbf4e398d 100644 --- a/opentech/public/funds/models.py +++ b/opentech/public/funds/models.py @@ -20,6 +20,8 @@ from wagtail.core.fields import StreamField from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index +from opentech.apply.funds.models import ApplicationSubmission +from opentech.apply.funds.workflow import OPEN_CALL_PHASES from opentech.public.utils.models import ( BasePage, RelatedPage, @@ -54,7 +56,7 @@ class BaseApplicationPage(BasePage): ] content_panels = BasePage.content_panels + [ - FieldPanel('introduction'), + FieldPanel('introduction', widget=PagedownWidget()), StreamFieldPanel('body'), InlinePanel('related_pages', label="Related pages"), ] @@ -237,3 +239,37 @@ class LabIndex(BasePage): context = super().get_context(request, *args, **kwargs) context.update(subpages=labs) return context + + +class OpenCallIndexPage(BasePage): + subpage_types = [] + parent_page_types = ['home.HomePage'] + + introduction = models.TextField(blank=True) + + content_panels = BasePage.content_panels + [ + FieldPanel('introduction'), + ] + + search_fields = BasePage.search_fields + [ + index.SearchField('introduction'), + ] + + def get_context(self, request, *args, **kwargs): + context = super().get_context(request, *args, **kwargs) + open_call_submissions = ApplicationSubmission.objects.filter( + status__in=OPEN_CALL_PHASES).select_related('page').order_by('-submit_time') + per_page = settings.DEFAULT_PER_PAGE + page_number = request.GET.get('page') + paginator = Paginator(open_call_submissions, per_page) + + try: + open_call_submissions = paginator.page(page_number) + except PageNotAnInteger: + open_call_submissions = paginator.page(1) + except EmptyPage: + open_call_submissions = paginator.page(paginator.num_pages) + + context['open_call_submissions'] = open_call_submissions + + return context diff --git a/opentech/public/funds/templates/public_funds/open_call_index_page.html b/opentech/public/funds/templates/public_funds/open_call_index_page.html new file mode 100644 index 0000000000000000000000000000000000000000..4c0760619809cd838df4be285218c56844f69537 --- /dev/null +++ b/opentech/public/funds/templates/public_funds/open_call_index_page.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% load wagtailcore_tags wagtailimages_tags markdown_tags bleach_tags %} +{% block body_class %}light-grey-bg{% endblock %} + +{% block content %} +<div class="wrapper wrapper--small wrapper--inner-space-medium"> + {% if page.introduction %} + <h4 class="heading heading--listings-introduction">{{ page.introduction|markdown|bleach }}</h4> + {% endif %} + + <div class="wrapper wrapper--listings"> + + {% if open_call_submissions %} + {% for submission in open_call_submissions %} + <div class="listing listing--with-button"> + <h3 class="title">{{ submission.title }}</h3> + <a class="button listing__button" href="{% pageurl submission.page %}?open_call_submission={{ submission.id }}" target="_blank">Build it</a> + <div class="rich-text rich-text--answers"> + <section> + <h4>Name</h4> + <span>{{ submission.get_full_name_display }}</span> + </section> + + {{ submission.output_first_group_text_answers }} + </div> + </div> + {% endfor %} + + {% include "includes/pagination.html" with paginator_page=open_call_submissions %} + + {% else %} + {# no items on this page #} + {% endif %} + </div> +</div> + +{% endblock %} diff --git a/opentech/static_src/src/sass/public/components/_grid.scss b/opentech/static_src/src/sass/public/components/_grid.scss index 27b722c1118f534bb9559c6ee1b5df613d3ae119..061952f998f6d5bec3c5bfa9a9ae10e001107b6a 100644 --- a/opentech/static_src/src/sass/public/components/_grid.scss +++ b/opentech/static_src/src/sass/public/components/_grid.scss @@ -127,6 +127,23 @@ margin: 0; } + &--proposal-info { + padding-bottom: 30px; + margin: 0 0 30px; + border-bottom: 1px solid $color--mid-grey; + grid-template-columns: 100%; + grid-gap: 10px; // sass-lint:disable-line no-misspelled-properties + + @include media-query(mob-landscape) { + margin: 0 0 30px; + grid-template-columns: 1fr 1fr; + } + + * { + margin: 0; + } + } + &--focus-areas { margin: 20px 0; grid-gap: 20px; // sass-lint:disable-line no-misspelled-properties diff --git a/opentech/static_src/src/sass/public/components/_listing.scss b/opentech/static_src/src/sass/public/components/_listing.scss index fd6d2dc3c48e1ca09732f6617dde4743b27cb36c..b6eab16cb9bca7a3981156826bd34d7d44d7e222 100644 --- a/opentech/static_src/src/sass/public/components/_listing.scss +++ b/opentech/static_src/src/sass/public/components/_listing.scss @@ -52,6 +52,18 @@ } } + &--with-button { + .button { + float: right; + } + + @include media-query(tablet-portrait) { + position: relative; + min-height: 180px; + padding: 30px; + } + } + &__title { margin-bottom: 5px; line-height: 1.2; diff --git a/opentech/static_src/src/sass/public/components/_rich-text.scss b/opentech/static_src/src/sass/public/components/_rich-text.scss index 4cde9575ffc48929b4beb3d5f268e14263fab5c4..0aee52ba6252392311974b6648682583c3d1c07d 100644 --- a/opentech/static_src/src/sass/public/components/_rich-text.scss +++ b/opentech/static_src/src/sass/public/components/_rich-text.scss @@ -1,10 +1,29 @@ .rich-text { margin-bottom: 2rem; + word-break: break-word; .form & { margin-bottom: 0; } + &--answers { + > section { + margin: 0 0 1rem; + + p:first-of-type { + margin-top: 0; + } + + p:empty { + margin: 0; + } + } + + h4 { + margin: 0; + } + } + a { font-weight: $weight--bold; border-bottom: 1px solid transparent;