diff --git a/opentech/apply/determinations/tests/test_views.py b/opentech/apply/determinations/tests/test_views.py index 5004cb5d5a8f26a7d9d901052907ac764a8c8753..70476edbfc0f7916640e867b49d1cff9fcbd1cbc 100644 --- a/opentech/apply/determinations/tests/test_views.py +++ b/opentech/apply/determinations/tests/test_views.py @@ -50,7 +50,7 @@ class DeterminationFormTestCase(BaseViewTestCase): self.assertContains(response, submission.get_absolute_url()) def test_cant_access_wrong_status(self): - submission = ApplicationSubmissionFactory(status='more_info') + submission = ApplicationSubmissionFactory(status='rejected') response = self.get_page(submission, 'form') self.assertRedirects(response, self.absolute_url(submission.get_absolute_url())) @@ -79,11 +79,12 @@ class DeterminationFormTestCase(BaseViewTestCase): response = self.get_page(submission, 'form') self.assertNotContains(response, 'Update ') - def test_cannot_edit_draft_determination_if_not_lead(self): + def test_can_edit_draft_determination_if_not_lead(self): submission = ApplicationSubmissionFactory(status='in_discussion') determination = DeterminationFactory(submission=submission, author=self.user, accepted=True) response = self.post_page(submission, {'data': 'value', 'outcome': determination.outcome}, 'form') - self.assertRedirects(response, self.url(submission)) + self.assertContains(response, 'Approved') + self.assertRedirects(response, self.absolute_url(submission.get_absolute_url())) def test_sends_message_if_requires_more_info(self): submission = ApplicationSubmissionFactory(status='in_discussion', lead=self.user) diff --git a/opentech/apply/funds/forms.py b/opentech/apply/funds/forms.py index 75ee3336a6f4837501b5e2b75a001e4cd7d00f3e..6465a06e9483548d0d4db849080945edf3dad575 100644 --- a/opentech/apply/funds/forms.py +++ b/opentech/apply/funds/forms.py @@ -34,7 +34,7 @@ class ScreeningSubmissionForm(forms.ModelForm): self.user = kwargs.pop('user') super().__init__(*args, **kwargs) self.should_show = False - if (self.instance.active and self.user.is_apply_staff) or self.user.is_superuser: + if self.user.is_apply_staff: self.should_show = True diff --git a/opentech/apply/funds/models/mixins.py b/opentech/apply/funds/models/mixins.py index 31a5b899affdd6817f197a886a099939f855dd7a..1edb02f148b5acc24dccc451467fbde543acb8ba 100644 --- a/opentech/apply/funds/models/mixins.py +++ b/opentech/apply/funds/models/mixins.py @@ -3,7 +3,9 @@ from django.utils.text import mark_safe from django.core.files import File from django.core.files.storage import get_storage_class -from opentech.apply.stream_forms.blocks import FormFieldBlock +from opentech.apply.stream_forms.blocks import ( + FileFieldBlock, FormFieldBlock, ImageFieldBlock, MultiFileFieldBlock +) from opentech.apply.utils.blocks import SingleIncludeMixin from opentech.apply.stream_forms.blocks import UploadableMediaBlock @@ -112,6 +114,14 @@ class AccessFormData: if isinstance(field.block, FormFieldBlock): yield field_id + @property + def question_text_field_ids(self): + for field_id, field in self.fields.items(): + if isinstance(field.block, (FileFieldBlock, ImageFieldBlock, MultiFileFieldBlock)): + pass + elif isinstance(field.block, FormFieldBlock): + yield field_id + @property def raw_fields(self): # Field ids to field class mapping - similar to raw_data @@ -168,6 +178,14 @@ class AccessFormData: for field_id in self.normal_blocks ] + def render_text_blocks_answers(self): + # Returns a list of the rendered answers of type text + return [ + self.render_answer(field_id, include_question=True) + for field_id in self.question_text_field_ids + if field_id not in self.named_blocks + ] + def output_answers(self): # Returns a safe string of the rendered answers return mark_safe(''.join(self.render_answers())) diff --git a/opentech/apply/funds/templates/funds/includes/review_table_row.html b/opentech/apply/funds/templates/funds/includes/review_table_row.html index 68892e8610feea18c9ad5dc3ca15b81bb0baa5a4..56fb24d3d7b04046b47cdbcb180ad5314d88e689 100644 --- a/opentech/apply/funds/templates/funds/includes/review_table_row.html +++ b/opentech/apply/funds/templates/funds/includes/review_table_row.html @@ -10,6 +10,7 @@ <a href="{% url 'apply:submissions:reviews:review' submission_pk=review.submission.id pk=review.id %}"> <span>{{ review.author }}</span> </a> + <div class="reviews-sidebar__date">{{ review.updated_at|date:"Y-m-d" }}</div> {% else %} <span>{{ review.author }}</span> {% endif %} diff --git a/opentech/apply/funds/tests/test_views.py b/opentech/apply/funds/tests/test_views.py index d71b69897439f30a873485cd0d719c7fe02e6451..dedcd1e4ab0ce50e85916ea068a8110debb523a8 100644 --- a/opentech/apply/funds/tests/test_views.py +++ b/opentech/apply/funds/tests/test_views.py @@ -82,7 +82,7 @@ class TestStaffSubmissionView(BaseSubmissionViewTestCase): self.assertContains(response, submission.title) def test_can_progress_phase(self): - next_status = list(self.submission.get_actions_for_user(self.user))[0][0] + next_status = 'internal_review' self.post_page(self.submission, {'form-submitted-progress_form': '', 'action': next_status}) submission = self.refresh(self.submission) @@ -189,15 +189,6 @@ class TestStaffSubmissionView(BaseSubmissionViewTestCase): submission = self.refresh(self.submission) self.assertEqual(submission.screening_status, screening_outcome) - def test_cant_screen_submission(self): - """ - Now that the submission has been rejected, we cannot screen it as staff - """ - submission = ApplicationSubmissionFactory(rejected=True) - screening_outcome = ScreeningStatusFactory() - response = self.post_page(submission, {'form-submitted-screening_form': '', 'screening_status': screening_outcome.id}) - self.assertEqual(response.context_data['screening_form'].should_show, False) - def test_can_view_submission_screening_block(self): response = self.get_page(self.submission) self.assertContains(response, 'Screening Status') diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 60bcae146afa31720e67c7d0e4b8949dbcd13b16..3d038883af49e7519ad6d777e8980688c4c58176 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -563,11 +563,11 @@ class RevisionCompareView(DetailView): def compare_revisions(self, from_data, to_data): self.object.form_data = from_data.form_data - from_fields = self.object.render_answers() + from_rendered_text_fields = self.object.render_text_blocks_answers() from_required = self.render_required() self.object.form_data = to_data.form_data - to_fields = self.object.render_answers() + to_rendered_text_fields = self.object.render_text_blocks_answers() to_required = self.render_required() # Compare all the required fields @@ -579,12 +579,12 @@ class RevisionCompareView(DetailView): setattr(self.object, 'get_{}_display'.format(field), diff) # Compare all the answers - diffed_answers = [ + diffed_text_fields_answers = [ compare(*fields, should_bleach=False) - for fields in zip(from_fields, to_fields) + for fields in zip(from_rendered_text_fields, to_rendered_text_fields) ] - self.object.output_answers = mark_safe(''.join(diffed_answers)) + self.object.output_answers = mark_safe(''.join(diffed_text_fields_answers)) def render_required(self): return [ diff --git a/opentech/apply/funds/workflow.py b/opentech/apply/funds/workflow.py index a1c4ae8c95012136b8b84197628284f9018649bc..b0a965ccab242409cac75da402486072c01eb284 100644 --- a/opentech/apply/funds/workflow.py +++ b/opentech/apply/funds/workflow.py @@ -85,7 +85,7 @@ class Phase: # For building transition methods on the parent self.transitions = {} - default_permissions = {UserPermissions.STAFF, UserPermissions.ADMIN, UserPermissions.LEAD} + default_permissions = {UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN} for transition_target, action in transitions.items(): transition = dict() @@ -177,10 +177,10 @@ SingleStageDefinition = [ INITIAL_STATE: { 'transitions': { 'internal_review': 'Open Review', - 'rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'rejected': 'Dismiss', 'more_info': 'Request More Information', - 'accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'accepted': 'Accept', + 'determination': 'Ready For Determination', }, 'display': 'Screening', 'public': 'Application Received', @@ -191,12 +191,12 @@ SingleStageDefinition = [ 'transitions': { INITIAL_STATE: { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, - 'accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'accepted': 'Accept', + 'rejected': 'Dismiss', + 'determination': 'Ready For Determination', }, 'display': 'More information required', 'stage': Request, @@ -217,9 +217,9 @@ SingleStageDefinition = [ { 'post_review_discussion': { 'transitions': { - 'accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'accepted': 'Accept', + 'rejected': 'Dismiss', + 'determination': 'Ready For Determination', 'post_review_more_info': 'Request More Information', }, 'display': 'Ready For Discussion', @@ -230,12 +230,12 @@ SingleStageDefinition = [ 'transitions': { 'post_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, - 'accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'accepted': 'Accept', + 'rejected': 'Dismiss', + 'determination': 'Ready For Determination', }, 'display': 'More information required', 'stage': Request, @@ -245,8 +245,8 @@ SingleStageDefinition = [ { 'determination': { 'transitions': { - 'accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'accepted': 'Accept', + 'rejected': 'Dismiss', }, 'display': 'Ready for Determination', 'permissions': hidden_from_applicant_permissions, @@ -273,9 +273,9 @@ SingleStageExternalDefinition = [ INITIAL_STATE: { 'transitions': { 'ext_internal_review': 'Open Review', - 'ext_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_rejected': 'Dismiss', 'ext_more_info': 'Request More Information', - 'ext_determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_determination': 'Ready For Determination', }, 'display': 'Screening', 'public': 'Application Received', @@ -286,7 +286,7 @@ SingleStageExternalDefinition = [ 'transitions': { INITIAL_STATE: { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, }, @@ -310,9 +310,9 @@ SingleStageExternalDefinition = [ 'ext_post_review_discussion': { 'transitions': { 'ext_external_review': 'Open AC review', - 'ext_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_rejected': 'Dismiss', 'ext_post_review_more_info': 'Request More Information', - 'ext_determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_determination': 'Ready For Determination', }, 'display': 'Ready For Discussion', 'stage': RequestExt, @@ -322,7 +322,7 @@ SingleStageExternalDefinition = [ 'transitions': { 'ext_post_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, }, @@ -344,10 +344,10 @@ SingleStageExternalDefinition = [ { 'ext_post_external_review_discussion': { 'transitions': { - 'ext_accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'ext_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_accepted': 'Accept', + 'ext_rejected': 'Dismiss', 'ext_post_external_review_more_info': 'Request More Information', - 'ext_determination': {'display': 'Ready For Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_determination': 'Ready For Determination', }, 'display': 'Ready For Discussion', 'stage': RequestExt, @@ -357,7 +357,7 @@ SingleStageExternalDefinition = [ 'transitions': { 'ext_post_external_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, }, @@ -369,8 +369,8 @@ SingleStageExternalDefinition = [ { 'ext_determination': { 'transitions': { - 'ext_accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'ext_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_accepted': 'Accept', + 'ext_rejected': 'Dismiss', }, 'display': 'Ready for Determination', 'permissions': hidden_from_applicant_permissions, @@ -398,10 +398,10 @@ DoubleStageDefinition = [ INITIAL_STATE: { 'transitions': { 'concept_internal_review': 'Open Review', - 'concept_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'concept_rejected': 'Dismiss', 'concept_more_info': 'Request More Information', - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'concept_determination': {'display': 'Ready For Preliminary Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'invited_to_proposal': 'Invite to Proposal', + 'concept_determination': 'Ready For Preliminary Determination', }, 'display': 'Screening', 'public': 'Concept Note Received', @@ -412,12 +412,12 @@ DoubleStageDefinition = [ 'transitions': { INITIAL_STATE: { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, - 'concept_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'concept_determination': {'display': 'Ready For Preliminary Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'concept_rejected': 'Dismiss', + 'invited_to_proposal': 'Invite to Proposal', + 'concept_determination': 'Ready For Preliminary Determination', }, 'display': 'More information required', 'stage': Concept, @@ -428,7 +428,7 @@ DoubleStageDefinition = [ 'concept_internal_review': { 'transitions': { 'concept_review_discussion': 'Close Review', - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'invited_to_proposal': 'Invite to Proposal', }, 'display': 'Internal Review', 'public': 'OTF Review', @@ -439,10 +439,10 @@ DoubleStageDefinition = [ { 'concept_review_discussion': { 'transitions': { - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'concept_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'invited_to_proposal': 'Invite to Proposal', + 'concept_rejected': 'Dismiss', 'concept_review_more_info': 'Request More Information', - 'concept_determination': {'display': 'Ready For Preliminary Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'concept_determination': 'Ready For Preliminary Determination', }, 'display': 'Ready For Discussion', 'stage': Concept, @@ -452,10 +452,10 @@ DoubleStageDefinition = [ 'transitions': { 'concept_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'invited_to_proposal': 'Invite to Proposal', }, 'display': 'More information required', 'stage': Concept, @@ -465,8 +465,8 @@ DoubleStageDefinition = [ { 'concept_determination': { 'transitions': { - 'invited_to_proposal': {'display': 'Invite to Proposal', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'concept_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'invited_to_proposal': 'Invite to Proposal', + 'concept_rejected': 'Dismiss', }, 'display': 'Ready for Preliminary Determination', 'permissions': hidden_from_applicant_permissions, @@ -481,7 +481,7 @@ DoubleStageDefinition = [ 'draft_proposal': { 'display': 'Progress', 'method': 'progress_application', - 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}, + 'permissions': {UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'conditions': 'not_progressed', }, }, @@ -498,9 +498,9 @@ DoubleStageDefinition = [ 'draft_proposal': { 'transitions': { 'proposal_discussion': {'display': 'Submit', 'permissions': {UserPermissions.APPLICANT}, 'method': 'create_revision'}, - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_rejected': 'Dismiss', 'external_review': 'Open AC review', - 'proposal_determination': {'display': 'Ready For Final Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_determination': 'Ready For Final Determination', }, 'display': 'Invited for Proposal', 'stage': Proposal, @@ -511,9 +511,9 @@ DoubleStageDefinition = [ 'proposal_discussion': { 'transitions': { 'proposal_internal_review': 'Open Review', - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_rejected': 'Dismiss', 'proposal_more_info': 'Request More Information', - 'proposal_determination': {'display': 'Ready For Final Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_determination': 'Ready For Final Determination', 'external_review': 'Open AC review', }, 'display': 'Proposal Received', @@ -524,11 +524,11 @@ DoubleStageDefinition = [ 'transitions': { 'proposal_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'proposal_determination': {'display': 'Ready For Final Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_rejected': 'Dismiss', + 'proposal_determination': 'Ready For Final Determination', 'external_review': 'Open AC review', }, 'display': 'More information required', @@ -551,8 +551,8 @@ DoubleStageDefinition = [ 'post_proposal_review_discussion': { 'transitions': { 'external_review': 'Open AC review', - 'proposal_determination': {'display': 'Ready For Final Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_determination': 'Ready For Final Determination', + 'proposal_rejected': 'Dismiss', 'post_proposal_review_more_info': 'Request More Information', }, 'display': 'Ready For Discussion', @@ -563,7 +563,7 @@ DoubleStageDefinition = [ 'transitions': { 'post_proposal_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, 'external_review': 'Open AC review', @@ -586,9 +586,9 @@ DoubleStageDefinition = [ { 'post_external_review_discussion': { 'transitions': { - 'proposal_accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'proposal_determination': {'display': 'Ready For Final Determination', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_accepted': 'Accept', + 'proposal_rejected': 'Dismiss', + 'proposal_determination': 'Ready For Final Determination', 'post_external_review_more_info': 'Request More Information', }, 'display': 'Ready For Discussion', @@ -599,7 +599,7 @@ DoubleStageDefinition = [ 'transitions': { 'post_external_review_discussion': { 'display': 'Submit', - 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'permissions': {UserPermissions.APPLICANT, UserPermissions.STAFF, UserPermissions.LEAD, UserPermissions.ADMIN}, 'method': 'create_revision', }, }, @@ -611,8 +611,8 @@ DoubleStageDefinition = [ { 'proposal_determination': { 'transitions': { - 'proposal_accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, - 'proposal_rejected': {'display': 'Dismiss', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'proposal_accepted': 'Accept', + 'proposal_rejected': 'Dismiss', }, 'display': 'Ready for Final Determination', 'permissions': hidden_from_applicant_permissions, diff --git a/opentech/apply/review/templates/review/review_detail.html b/opentech/apply/review/templates/review/review_detail.html index 3280e7d1ce9b939020b1974f3421203db16682c2..4a82a07322bf04ed897c272dc3a90bba5ee25c7a 100644 --- a/opentech/apply/review/templates/review/review_detail.html +++ b/opentech/apply/review/templates/review/review_detail.html @@ -5,7 +5,7 @@ <div class="admin-bar"> <div class="admin-bar__inner"> <h1 class="beta heading heading--no-margin heading--bold">Review</h1> - <h5>For <a href="{% url "funds:submissions:detail" review.submission.id %}">{{ review.submission.title }}</a> by {{ review.author }}</h5> + <h5>For <a href="{% url "funds:submissions:detail" review.submission.id %}">{{ review.submission.title }}</a> by {{ review.author }} at {{ review.updated_at|date:"Y-m-d" }}</h5> </div> </div> diff --git a/opentech/apply/stream_forms/blocks.py b/opentech/apply/stream_forms/blocks.py index d911275742eb4d0f1f8444bce96aa649a2d2563f..67730468d785ec6b7f043d4459caca42bafb6f5c 100644 --- a/opentech/apply/stream_forms/blocks.py +++ b/opentech/apply/stream_forms/blocks.py @@ -298,7 +298,9 @@ class UploadableMediaBlock(OptionalFormFieldBlock): def prepare_data(self, value, data, serialize): if serialize: - return data.serialize() + if data: + return data.serialize() + return None return data diff --git a/opentech/public/forms/templates/public_forms/form_page.html b/opentech/public/forms/templates/public_forms/form_page.html index 1e5d2457427e5648edb4f3dcfadac64fd3884c47..9bd0f969a44d777e5ad77129f32d1973789acb8a 100644 --- a/opentech/public/forms/templates/public_forms/form_page.html +++ b/opentech/public/forms/templates/public_forms/form_page.html @@ -1,14 +1,26 @@ {% extends "base.html" %} -{% load wagtailcore_tags %} +{% load static wagtailcore_tags %} {% block content %} - <div> - <h1>{{ page.title }}</h1> - {{ page.intro|richtext }} - <form action="{% pageurl page %}" method="POST"> - {% csrf_token %} - {{ form.as_p }} - <input type="submit"> - </form> - </div> -{% endblock %} \ No newline at end of file +<div class="wrapper wrapper--medium wrapper--light-grey-bg wrapper--form"> + <h1>{{ page.title }}</h1> + {{ page.intro|richtext }} + <p class="wrapper--error message-no-js">You must have Javascript enabled to use this contact form.</p> + <form class="form form__wagtail-form" action="#" data-pageurl="{% pageurl page %}" method="post" enctype="multipart/form-data"> + {% csrf_token %} + {{ form.media }} + {% for field in form %} + {% if field.field %} + {% include "forms/includes/field.html" %} + {% else %} + {{ field }} + {% endif %} + {% endfor %} + <button class="link link--button-secondary" type="submit" disabled>Submit</button> + </form> +</div> +{% endblock %} + +{% block extra_js %} + <script src="{% static 'js/public/wagtail-form.js' %}"></script> +{% endblock %} diff --git a/opentech/public/forms/templates/public_forms/form_page_landing.html b/opentech/public/forms/templates/public_forms/form_page_landing.html index 36a16a6475ae3916bab026ae12ba6fa58923a9e0..66c4e44d04b9f72ec8a43132888978e4466752d6 100644 --- a/opentech/public/forms/templates/public_forms/form_page_landing.html +++ b/opentech/public/forms/templates/public_forms/form_page_landing.html @@ -2,8 +2,10 @@ {% load wagtailcore_tags %} {% block content %} - <div> +<div class="wrapper wrapper--medium wrapper--light-grey-bg wrapper--form"> + <div class="wrapper--sidebar--inner"> <h1>Thank you</h1> <div>{{ page.thank_you_text|richtext }}</div> </div> +</div> {% endblock %} diff --git a/opentech/public/forms/wagtail_hooks.py b/opentech/public/forms/wagtail_hooks.py new file mode 100644 index 0000000000000000000000000000000000000000..7bb7907eda7c3bbd5ff7c56afb3c2718ef5fc0b7 --- /dev/null +++ b/opentech/public/forms/wagtail_hooks.py @@ -0,0 +1,15 @@ +from django.contrib.contenttypes.models import ContentType + +from wagtail.core import hooks + +from opentech.public.forms.models import FormPage + + +@hooks.register('filter_form_submissions_for_user') +def construct_from_wagtail_forms(user, queryset): + """only show wagtail forms (hiding all the ones created from the apply app).""" + + form_page_type = ContentType.objects.get_for_model(FormPage) + queryset = queryset.filter(content_type__pk=form_page_type.pk) + + return queryset diff --git a/opentech/static_src/src/app/src/components/LoadingPanel/index.js b/opentech/static_src/src/app/src/components/LoadingPanel/index.js index cc263937a7eba3378927c1dbd32a1703ce7ca41b..795853300668ffd705c1cc10939b551f443251d4 100644 --- a/opentech/static_src/src/app/src/components/LoadingPanel/index.js +++ b/opentech/static_src/src/app/src/components/LoadingPanel/index.js @@ -1,14 +1,20 @@ import React from 'react' +import OTFLoadingIcon from '@components/OTFLoadingIcon' + import './styles.scss'; -const LoadingIcon = () => { - return ( - <div className="loading-panel"> - <h5>Loading...</h5> - <div className="loading-panel__icon" /> - </div> - ) +const LoadingPanel = () => { + return ( + <div className="loading-panel"> + <div className="loading-panel__text" > + <h5>Loading...</h5> + </div> + <div className="loading-panel__icon" > + <OTFLoadingIcon /> + </div> + </div> + ) } -export default LoadingIcon +export default LoadingPanel diff --git a/opentech/static_src/src/app/src/components/LoadingPanel/styles.scss b/opentech/static_src/src/app/src/components/LoadingPanel/styles.scss index 1a54f514d49bbb1af93c7c127382e14fb21c2302..621045e7ffa8451da1b8c899354d635d38f7c416 100644 --- a/opentech/static_src/src/app/src/components/LoadingPanel/styles.scss +++ b/opentech/static_src/src/app/src/components/LoadingPanel/styles.scss @@ -2,40 +2,16 @@ text-align: center; padding: 20px; - &__icon { - font-size: 10px; - margin: 20px auto; - text-indent: -9999em; - width: 40px; - height: 40px; - border-radius: 50%; - background: linear-gradient(to right, $color--dark-grey 10%, transparent 42%); - position: relative; - animation: spin .4s infinite linear; - transform: translateZ(0); - - &::after { - background: $color--white; - width: 75%; - height: 75%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - } - - @keyframes spin { - 0% { - transform: rotate(0deg); - } + &__text { + width: 100%; + } - 100% { - transform: rotate(360deg); - } + &__icon { + width: 100%; + svg { + width: 80px; + height: 80px; } } + } diff --git a/opentech/static_src/src/app/src/components/OTFLoadingIcon/index.js b/opentech/static_src/src/app/src/components/OTFLoadingIcon/index.js new file mode 100644 index 0000000000000000000000000000000000000000..1ee69b96b078c45d92e75f55597f317b587efb77 --- /dev/null +++ b/opentech/static_src/src/app/src/components/OTFLoadingIcon/index.js @@ -0,0 +1,29 @@ +import React from 'react' + +import './styles.scss' + + +const OTFLoadingIcon = () => ( + <svg className="logo-loading" viewBox="0 0 45 40"> + <g fillRule="nonzero" fill="none"> + <rect className="logo-part logo-part-1" fill="#25AAE1" width='5' height='5' transform='translate(10 0)' /> + <rect className="logo-part logo-part-2" fill="#25AAE1" width='5' height='5' transform='translate(5 5)' /> + <rect className="logo-part logo-part-3" fill="#25AAE1" width='5' height='5' transform='translate(0 10)' /> + <rect className="logo-part logo-part-4" fill="#25AAE1" width='5' height='5' transform='translate(0 15)' /> + <rect className="logo-part logo-part-5" fill="#25AAE1" width='5' height='5' transform='translate(0 20)' /> + <rect className="logo-part logo-part-6" fill="#25AAE1" width='5' height='5' transform='translate(0 25)' /> + <rect className="logo-part logo-part-7" fill="#25AAE1" width='5' height='5' transform='translate(5 30)' /> + <rect className="logo-part logo-part-8" fill="#25AAE1" width='5' height='5' transform='translate(10 35)' /> + <rect className="logo-part logo-part-9" fill="#25AAE1" width='5' height='5' transform='translate(30 0)' /> + <rect className="logo-part logo-part-10" fill="#25AAE1" width='5' height='5' transform='translate(35 5)' /> + <rect className="logo-part logo-part-11" fill="#25AAE1" width='5' height='5' transform='translate(40 10)' /> + <rect className="logo-part logo-part-12" fill="#25AAE1" width='5' height='5' transform='translate(40 15)' /> + <rect className="logo-part logo-part-13" fill="#25AAE1" width='5' height='5' transform='translate(40 20)' /> + <rect className="logo-part logo-part-14" fill="#25AAE1" width='5' height='5' transform='translate(40 25)' /> + <rect className="logo-part logo-part-15" fill="#25AAE1" width='5' height='5' transform='translate(35 30)' /> + <rect className="logo-part logo-part-16" fill="#25AAE1" width='5' height='5' transform='translate(30 35)' /> + </g> + </svg> +) + +export default OTFLoadingIcon; diff --git a/opentech/static_src/src/app/src/components/OTFLoadingIcon/styles.scss b/opentech/static_src/src/app/src/components/OTFLoadingIcon/styles.scss new file mode 100644 index 0000000000000000000000000000000000000000..9095ec59ce88b65c7d6ec78bbf21fec142200de3 --- /dev/null +++ b/opentech/static_src/src/app/src/components/OTFLoadingIcon/styles.scss @@ -0,0 +1,21 @@ +.logo-loading { + @keyframes fade-in-out { + 0% {opacity: 1} + 80%, 100% {opacity: 0.15} + } + + .logo-part { + animation: fade-in-out .8s ease-out infinite; + opacity: 0.15; + + &-1, &-16 {animation-delay: 0s} + &-2, &-15 {animation-delay: .1s} + &-3, &-14 {animation-delay: .2s} + &-4, &-13 {animation-delay: .3s} + &-5, &-12 {animation-delay: .4s} + &-6, &-11 {animation-delay: .5s} + &-7, &-10 {animation-delay: .6s} + &-8, &-9 {animation-delay: .7s} + } + +} diff --git a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js index f28c4c1f1175964710902f8da079ae2fa8544867..b17607d50d550a6ebf1d991368e87dc75c9e5715 100644 --- a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js +++ b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js @@ -33,6 +33,7 @@ class DisplayPanel extends React.Component { const { clearSubmission } = this.props; const isMobile = width < 1024; + const submissionLink = "/apply/submissions/" + submissionID + "/"; const submission = <CurrentSubmissionDisplay /> @@ -62,6 +63,7 @@ class DisplayPanel extends React.Component { <div className="display-panel__column"> <div className="display-panel__header display-panel__header--spacer"></div> <div className="display-panel__body display-panel__body--center"> + <a target="_blank" rel="noopener noreferrer" href={ submissionLink }>Open in new tab</a> { submission } </div> </div> diff --git a/opentech/static_src/src/javascript/public/wagtail-form.js b/opentech/static_src/src/javascript/public/wagtail-form.js new file mode 100644 index 0000000000000000000000000000000000000000..ab59258b7dad3201a4811d65be00d18785dd34fd --- /dev/null +++ b/opentech/static_src/src/javascript/public/wagtail-form.js @@ -0,0 +1,37 @@ +(function ($) { + + 'use strict'; + + $('.form__wagtail-form').each(function () { + var $wagtail_form = $(this); + var $wagtail_form_button = $wagtail_form.find('button[type="submit"]'); + var wagtail_form_action = $wagtail_form.data('pageurl'); + + // Remove the "no javascript" messages + $('.message-no-js').detach(); + + // Wait for a mouse to move, indicating they are human. + $('body').mousemove(function () { + // Unlock the form. + $wagtail_form.attr('action', wagtail_form_action); + $wagtail_form_button.attr('disabled', false); + }); + + // Wait for a touch move event, indicating that they are human. + $('body').on('touchmove', function () { + // Unlock the form. + $wagtail_form.attr('action', wagtail_form_action); + $wagtail_form_button.attr('disabled', false); + }); + + // A tab or enter key pressed can also indicate they are human. + $('body').keydown(function (e) { + if ((e.keyCode === 9) || (e.keyCode === 13)) { + // Unlock the form. + $wagtail_form.attr('action', wagtail_form_action); + $wagtail_form_button.attr('disabled', false); + } + }); + }); + +})(jQuery); diff --git a/opentech/static_src/src/sass/apply/abstracts/_functions.scss b/opentech/static_src/src/sass/apply/abstracts/_functions.scss index 3377d83cce5defbb84b4700d115ea00dc5bcf84e..604d90ef44728574ac325c511a412d25bf401048 100644 --- a/opentech/static_src/src/sass/apply/abstracts/_functions.scss +++ b/opentech/static_src/src/sass/apply/abstracts/_functions.scss @@ -1,3 +1,13 @@ +// Strip the unit from the given value and return the value +@function strip-unit($value) { + @return $value / ($value * 0 + 1); +} + +// Return an em unit based on the pixel value and context +@function rem($px, $context: $base-font-size) { + @return #{strip-unit($px / strip-unit($context))}rem; +} + // Returns the opposite direction of each direction in a list // @param {List} $directions - List of initial directions // @return {List} - List of opposite directions diff --git a/opentech/static_src/src/sass/apply/components/_reviews-sidebar.scss b/opentech/static_src/src/sass/apply/components/_reviews-sidebar.scss index 0b19921b13c944fe8e91397de9beac5be6a219fb..47fc232fecd3598d52557b35f826e720c0628c69 100644 --- a/opentech/static_src/src/sass/apply/components/_reviews-sidebar.scss +++ b/opentech/static_src/src/sass/apply/components/_reviews-sidebar.scss @@ -62,6 +62,10 @@ } } + &__date { + @include font-size(milli); + } + &__no-reviews { color: $color--mid-grey; } diff --git a/opentech/static_src/src/sass/public/components/_messages.scss b/opentech/static_src/src/sass/public/components/_messages.scss index 5ede73fe9645cf2d9a9c3aa6cc1193f43318f0f4..69ae151b8e57fe22635e7cd42e6df7389033a3cf 100644 --- a/opentech/static_src/src/sass/public/components/_messages.scss +++ b/opentech/static_src/src/sass/public/components/_messages.scss @@ -1,46 +1,73 @@ .messages { - position: relative; - right: 50%; - left: 50%; - width: 100vw; - margin-right: -50vw; - margin-left: -50vw; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 20; + pointer-events: none; &__text { position: relative; + padding: 15px 20px; + color: $color--white; + font-size: 14px; + opacity: 1; + transition: opacity, max-height, $transition; + pointer-events: all; max-height: 1000px; - padding: 15px; - padding-right: 35px; - border: solid 1px; + + @include media-query(desktop) { + padding: 15px 30px; + } &--info, &--success { - background: $color--mint; - border-color: darken($color--mint, 20%); + background: $color--dark-blue; } &--error, &--warning { - font-weight: bold; - color: $color--white; - background: $color--error; - border-color: darken($color--error, 20%); + background: darken($color--error, 20%); + } + + &--debug { + background: darken($color--pink, 30%); } &--hide { + opacity: 0; + pointer-events: none; max-height: 0; - padding-top: 0; - padding-bottom: 0; - border: 0 none; - transition: all $transition; // sass-lint:disable-line no-transition-all - transform-origin: top; + padding: 0; } } - &__close { - position: absolute; - top: 15px; - right: 15px; + &__inner { + display: flex; + align-items: center; + max-width: $site-width; + margin: 0 auto; + } + + &__copy { + padding-right: 20px; + margin: 0; + flex: 1; + } + + &__button { + margin-left: auto; + color: $color--dark-blue; + background-color: $color--white; + display: inline-block; + font-weight: $weight--bold; + padding: 2px 20px; + } + &__icon { + width: 25px; + height: 25px; + fill: $color--white; + margin-right: 10px; } } diff --git a/package-lock.json b/package-lock.json index d9f73a8d8ae9bbf64c4dfefce0aef828f32433ac..407c8c2cc2a5689eb9f05e8b66e40a3d8925f530 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8483,14 +8483,25 @@ } }, "react": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz", - "integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.1.tgz", + "integrity": "sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.12.0" + "scheduler": "^0.13.1" + }, + "dependencies": { + "scheduler": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz", + "integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, "react-dom": { diff --git a/package.json b/package.json index ec640e8844fe3bdaffbea8c408ce7f7f8213e7b2..de38ee37b999164cb0a0d60975d79d09e9d0f1ae 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "moment-timezone": "^0.5.23", "node-sass-import-once": "^1.2.0", "prop-types": "^15.6.2", - "react": "^16.7.0", + "react": "^16.8.1", "react-dom": "^16.7.0", "react-redux": "^6.0.0", "react-rte": "^0.16.1", diff --git a/requirements.txt b/requirements.txt index 7890212b56ec2fa36d77f91e712899a73bd78d2a..447eedd7188a382b02e7f547bef194191e9b62e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ responses==0.9.0 flake8 -social_auth_app_django==2.1.0 +social_auth_app_django==3.1.0 django-tables2==1.21.1 django-filter==1.1.0 django_select2==6.0.1