diff --git a/opentech/apply/activity/messaging.py b/opentech/apply/activity/messaging.py index a4542d63867523b18453350800ec270585667042..ecdb0584c5be9ce63eb82fd2da9f62a366b3b575 100644 --- a/opentech/apply/activity/messaging.py +++ b/opentech/apply/activity/messaging.py @@ -360,7 +360,7 @@ class SlackAdapter(AdapterBase): MESSAGES.OPENED_SEALED: '{user} has opened the sealed submission: <{link}|{submission.title}>', MESSAGES.REVIEW_OPINION: '{user} {opinion.opinion_display}s with {opinion.review.author}''s review of {submission}', MESSAGES.BATCH_READY_FOR_REVIEW: 'batch_notify_reviewers', - + MESSAGES.DELETE_SUBMISSION: '{user} has deleted {submission.title}', } def __init__(self): diff --git a/opentech/apply/activity/migrations/0020_add_delete_event.py b/opentech/apply/activity/migrations/0020_add_delete_event.py new file mode 100644 index 0000000000000000000000000000000000000000..3dcc5648955156d954541c70e6d6db4e801cf50d --- /dev/null +++ b/opentech/apply/activity/migrations/0020_add_delete_event.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.10 on 2019-04-03 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('activity', '0019_partner_field_event'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='type', + field=models.CharField(choices=[('UPDATE_LEAD', 'Update Lead'), ('EDIT', 'Edit'), ('APPLICANT_EDIT', 'Applicant Edit'), ('NEW_SUBMISSION', 'New Submission'), ('SCREENING', 'Screening'), ('TRANSITION', 'Transition'), ('BATCH_TRANSITION', 'Batch Transition'), ('DETERMINATION_OUTCOME', 'Determination Outcome'), ('BATCH_DETERMINATION_OUTCOME', 'Batch Determination Outcome'), ('INVITED_TO_PROPOSAL', 'Invited To Proposal'), ('REVIEWERS_UPDATED', 'Reviewers Updated'), ('BATCH_REVIEWERS_UPDATED', 'Batch Reviewers Updated'), ('PARTNERS_UPDATED', 'Partners Updated'), ('READY_FOR_REVIEW', 'Ready For Review'), ('BATCH_READY_FOR_REVIEW', 'Batch Ready For Review'), ('NEW_REVIEW', 'New Review'), ('COMMENT', 'Comment'), ('PROPOSAL_SUBMITTED', 'Proposal Submitted'), ('OPENED_SEALED', 'Opened Sealed Submission'), ('REVIEW_OPINION', 'Review Opinion'), ('DELETE_SUBMISSION', 'Delete Submission')], max_length=50), + ), + ] diff --git a/opentech/apply/activity/options.py b/opentech/apply/activity/options.py index 994c50027aab71e8032334c88bb63e29c5677e6b..e1fddf8358183f31e0e2c60b50038e63973a972e 100644 --- a/opentech/apply/activity/options.py +++ b/opentech/apply/activity/options.py @@ -22,6 +22,7 @@ class MESSAGES(Enum): PROPOSAL_SUBMITTED = 'Proposal Submitted' OPENED_SEALED = 'Opened Sealed Submission' REVIEW_OPINION = 'Review Opinion' + DELETE_SUBMISSION = 'Delete Submission' @classmethod def choices(cls): diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_confirm_delete.html b/opentech/apply/funds/templates/funds/applicationsubmission_confirm_delete.html new file mode 100644 index 0000000000000000000000000000000000000000..89e8fc98e4dd60f90fe9611b65ad17057d040a5e --- /dev/null +++ b/opentech/apply/funds/templates/funds/applicationsubmission_confirm_delete.html @@ -0,0 +1,24 @@ +{% extends "base-apply.html" %} +{% load static %} + +{% block title %}Deleting: {{object.title }}{% endblock %} + +{% block content %} +<div class="admin-bar"> + <div class="admin-bar__inner"> + <h2 class="heading heading--no-margin">Deleting: {{ object.title }}</h2> + </div> +</div> + +<div class="wrapper wrapper--light-grey-bg wrapper--form wrapper--sidebar"> + <div class="wrapper--sidebar--inner"> + <form class="form" action="" method="post"> + {% csrf_token %} + <p><strong>Are you sure you want to delete "{{ object }}"?</strong></p> + <p>All content related to this submission will also be deleted. This includes reviews, determinations and comments.</p> + <input class="button button--warning button--submit button--top-space" type="submit"value="Confirm" /> + </form> + </div> +</div> + +{% endblock %} diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html index 214d68db425345b13fce6886c3a6e266cd26cc2b..f6ea15359f228af01789a6e3e8d778bd9a2a87e3 100644 --- a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html @@ -67,6 +67,12 @@ <header class="heading heading--submission-meta heading-text zeta"> <span>Submitted: <strong>{{ object.submit_time.date }} by {{ object.user.get_full_name }}</strong></span> <span>Last edited: <strong>{{ object.live_revision.timestamp.date }} by {{ object.live_revision.author }}</strong></span> + {% if perms.funds.delete_applicationsubmission %} + <a class="link link--edit-submission is-active" href="{% url 'funds:submissions:delete' object.id %}"> + Delete + <svg class="icon icon--pen"><use xlink:href="#pen"></use></svg> + </a> + {% endif %} {% if request.user|has_edit_perm:object %} <a class="link link--edit-submission is-active" href="{% url 'funds:submissions:edit' object.id %}"> Edit diff --git a/opentech/apply/funds/urls.py b/opentech/apply/funds/urls.py index 59161baa1d0f93abe5ca8eba247f40f1b6916a83..72b29d3b792b1860a57f56c853a6e9e896f0c0dc 100644 --- a/opentech/apply/funds/urls.py +++ b/opentech/apply/funds/urls.py @@ -11,6 +11,7 @@ from .views import ( SubmissionListView, SubmissionOverviewView, SubmissionSealedView, + SubmissionDeleteView, ) from .api_views import ( CommentList, @@ -38,6 +39,7 @@ submission_urls = ([ path('', SubmissionDetailView.as_view(), name="detail"), path('edit/', SubmissionEditView.as_view(), name="edit"), path('sealed/', SubmissionSealedView.as_view(), name="sealed"), + path('delete/', SubmissionDeleteView.as_view(), name="delete"), ])), path('<int:submission_pk>/', include([ path('', include('opentech.apply.review.urls', namespace="reviews")), diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index d28a7d595472bf22e540cf6c89893ad096752e13..51c2f5e50287379829e71a1cfd1ff83377b1d52a 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -1,6 +1,6 @@ from copy import copy -from django.contrib.auth.decorators import login_required +from django.contrib.auth.decorators import login_required, permission_required from django.contrib import messages from django.core.exceptions import PermissionDenied from django.db.models import Count, F, Q @@ -10,7 +10,7 @@ from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.text import mark_safe from django.utils.translation import ugettext_lazy as _ -from django.views.generic import DetailView, FormView, ListView, UpdateView +from django.views.generic import DetailView, FormView, ListView, UpdateView, DeleteView from django_filters.views import FilterView from django_tables2.views import SingleTableMixin @@ -750,3 +750,20 @@ class RoundListView(SingleTableMixin, FilterView): def get_queryset(self): return RoundsAndLabs.objects.with_progress() + + +@method_decorator(permission_required('funds.delete_applicationsubmission', raise_exception=True), name='dispatch') +class SubmissionDeleteView(DeleteView): + model = ApplicationSubmission + success_url = reverse_lazy('funds:submissions:list') + + def delete(self, request, *args, **kwargs): + submission = self.get_object() + messenger( + MESSAGES.DELETE_SUBMISSION, + user=request.user, + request=request, + submission=submission, + ) + response = super().delete(request, *args, **kwargs) + return response diff --git a/opentech/apply/funds/wagtail_hooks.py b/opentech/apply/funds/wagtail_hooks.py index 6f29a25ed6ae7e8b574191d81f85d44ca5d19dce..c1e302ee6ee1937c11de18b2770d023cf8e4d7a1 100644 --- a/opentech/apply/funds/wagtail_hooks.py +++ b/opentech/apply/funds/wagtail_hooks.py @@ -1,3 +1,5 @@ +from django.contrib.auth.models import Permission + from wagtail.core import hooks from wagtail.contrib.modeladmin.options import modeladmin_register @@ -23,3 +25,11 @@ def before_copy_round_page(request, page): if isinstance(page.specific, RoundBase) and request.method == 'POST': # Custom view to clear start_date and end_date from the copy being created. return custom_admin_round_copy_view(request, page) + + +@hooks.register('register_permissions') +def register_permissions(): + return Permission.objects.filter( + content_type__app_label='funds', + codename__in=['add_applicationsubmission', 'change_applicationsubmission', 'delete_applicationsubmission'] + )