diff --git a/froide_govplan/admin.py b/froide_govplan/admin.py index 12e2700..bf294a5 100644 --- a/froide_govplan/admin.py +++ b/froide_govplan/admin.py @@ -1,16 +1,21 @@ from django.contrib import admin, auth from django.contrib.auth.models import Group -from django.urls import reverse_lazy +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import path, reverse, reverse_lazy from django.utils.translation import gettext_lazy as _ from adminsortable2.admin import SortableAdminMixin from froide.follow.admin import FollowerAdmin -from froide.helper.admin_utils import make_choose_object_action +from froide.helper.admin_utils import make_choose_object_action, make_emptyfilter from froide.helper.widgets import TagAutocompleteWidget from froide.organization.models import Organization -from .forms import GovernmentPlanForm, GovernmentPlanUpdateForm +from .forms import ( + GovernmentPlanForm, + GovernmentPlanUpdateAcceptProposalForm, + GovernmentPlanUpdateForm, +) from .models import ( Government, GovernmentPlan, @@ -113,6 +118,17 @@ class GovernmentPlanAdmin(admin.ModelAdmin): actions.update(admin_actions) return actions + def get_urls(self): + urls = super().get_urls() + my_urls = [ + path( + "/accept-proposal/", + self.admin_site.admin_view(self.accept_proposal), + name="froide_govplan-plan_accept_proposal", + ), + ] + return my_urls + urls + def get_list_display(self, request): list_display = [ "title", @@ -135,6 +151,9 @@ class GovernmentPlanAdmin(admin.ModelAdmin): if not has_limited_access(request.user): list_filter.extend( [ + make_emptyfilter( + "proposals", _("Has change proposals"), empty_value=None + ), "organization", "group", "government", @@ -173,6 +192,59 @@ class GovernmentPlanAdmin(admin.ModelAdmin): make_public.short_description = _("Make public") + def accept_proposal(self, request, pk): + obj = get_object_or_404(self.get_queryset(request), pk=pk) + plan_url = reverse( + "admin:froide_govplan_governmentplan_change", + args=(obj.pk,), + current_app=self.admin_site.name, + ) + if not obj.proposals: + return redirect(plan_url) + if request.method == "POST": + proposals = obj.proposals or {} + proposal_id = request.POST.get("proposal_id") + data = proposals[proposal_id]["data"] + form = GovernmentPlanUpdateAcceptProposalForm(data=data, plan=obj) + if form.is_valid(): + update = form.save( + delete_unconfirmed=request.POST.get("delete", "0") == "1", + delete_reason=request.POST.get("delete_reason", ""), + proposal_id=proposal_id, + delete_proposals=request.POST.getlist("proposal_delete"), + ) + if update is None: + self.message_user(request, _("The proposal has been deleted.")) + + return redirect(plan_url) + + self.message_user( + request, + _("An unpublished update has been created."), + ) + update_url = reverse( + "admin:froide_govplan_governmentplanupdate_change", + args=(update.pk,), + current_app=self.admin_site.name, + ) + return redirect(update_url) + else: + form = GovernmentPlanUpdateAcceptProposalForm(plan=obj) + + opts = self.model._meta + context = { + "form": form, + "proposals": form.get_proposals(), + "object": obj, + "app_label": opts.app_label, + "opts": opts, + } + return render( + request, + "froide_govplan/admin/accept_proposal.html", + context, + ) + class GovernmentPlanUpdateAdmin(admin.ModelAdmin): form = GovernmentPlanUpdateForm diff --git a/froide_govplan/forms.py b/froide_govplan/forms.py index 1a477e7..ebc15d0 100644 --- a/froide_govplan/forms.py +++ b/froide_govplan/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.contrib.auth import get_user_model from django.utils import timezone from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ @@ -132,3 +133,95 @@ class GovernmentPlanUpdateProposalForm(forms.ModelForm): } plan.save(update_fields=["proposals"]) return plan + + +class GovernmentPlanUpdateAcceptProposalForm(GovernmentPlanUpdateProposalForm): + def __init__(self, *args, **kwargs): + self.plan = kwargs.pop("plan") + super().__init__(*args, **kwargs) + + def get_proposals(self): + data = dict(self.plan.proposals) + user_ids = self.plan.proposals.keys() + user_map = { + str(u.id): u for u in get_user_model().objects.filter(id__in=user_ids) + } + status_dict = dict(PlanStatus.choices) + rating_dict = dict(PlanRating.choices) + for user_id, v in data.items(): + v["user"] = user_map[user_id] + data[user_id]["data"]["rating_label"] = rating_dict.get( + data[user_id]["data"]["rating"] + ) + data[user_id]["data"]["status_label"] = status_dict.get( + data[user_id]["data"]["status"] + ) + return data + + def save( + self, + proposal_id=None, + delete_proposals=None, + delete_unconfirmed=False, + delete_reason="", + ): + import ipdb + + ipdb.set_trace() + update = super(forms.ModelForm, self).save(commit=False) + update.plan = self.plan + + if delete_proposals is None: + delete_proposals = [] + if proposal_id: + proposals = self.get_proposals() + proposal_user = proposals[proposal_id]["user"] + update.user = proposal_user + # if proposal_user != user: + # proposal_user.send_mail( + # _("Changes to public body ā€œ{}ā€ have been applied").format(pb.name), + # _( + # "Hello,\n\nYou can find the changed public body here:" + # "\n\n{url}\n\nAll the Best,\n{site_name}" + # ).format( + # url=pb.get_absolute_domain_url(), site_name=settings.SITE_NAME + # ), + # priority=False, + # ) + delete_proposals.append(proposal_id) + for pid in delete_proposals: + if pid in self.plan.proposals: + del self.plan.proposals[pid] + if not self.plan.proposals: + self.plan.proposals = None + self.plan.save(update_fields=["proposals"]) + + # if delete_unconfirmed: + # self.delete_proposal(pb, user, delete_reason) + # return None + + update.save() + # PublicBody.change_proposal_accepted.send(sender=pb, user=user) + return update + + # def delete_proposal(self, pb, user, delete_reason=""): + # LogEntry.objects.log_action( + # user_id=user.id, + # content_type_id=ContentType.objects.get_for_model(pb).pk, + # object_id=pb.pk, + # object_repr=str(pb), + # action_flag=DELETION, + # ) + + # creator = pb.created_by + # if creator: + # creator.send_mail( + # _("Your public body proposal ā€œ%sā€ was rejected") % pb.name, + # _( + # "Hello,\n\nA moderator has rejected your proposal for a new " + # "public body.\n\n{delete_reason}\n\nAll the Best,\n{site_name}" + # ).format(delete_reason=delete_reason, site_name=settings.SITE_NAME), + # priority=False, + # ) + # PublicBody.proposal_rejected.send(sender=pb, user=user) + # pb.delete() diff --git a/froide_govplan/templates/admin/froide_govplan/governmentplan/change_form.html b/froide_govplan/templates/admin/froide_govplan/governmentplan/change_form.html new file mode 100644 index 0000000..b512140 --- /dev/null +++ b/froide_govplan/templates/admin/froide_govplan/governmentplan/change_form.html @@ -0,0 +1,13 @@ +{% extends "admin/change_form.html" %} +{% load i18n %} + +{% block object-tools-items %} + {% if original.pk and original.proposals %} +
  • + + {% trans "See update proposals" %} + +
  • + {% endif %} + {{ block.super }} +{% endblock %} diff --git a/froide_govplan/templates/froide_govplan/admin/_proposal.html b/froide_govplan/templates/froide_govplan/admin/_proposal.html new file mode 100644 index 0000000..1f30b9f --- /dev/null +++ b/froide_govplan/templates/froide_govplan/admin/_proposal.html @@ -0,0 +1,77 @@ +{% load i18n %} +{% load form_helper %} +{% load permission_helper %} + +
    + + + + + {% for key, proposal in proposals.items %} + + {% endfor %} + + + + {% if proposals %} + + + {% for key, proposal in proposals.items %} + + {% endfor %} + + {% endif %} + {% for field in form %} + + + {% for key, proposal in proposals.items %} + {% with val=proposal.data|get_item_by_key:field.name %} + + {% endwith %} + {% endfor %} + + {{ form.field }} + {% endfor %} + + {% if proposals %} + + + {% for key, proposal in proposals.items %} + + {% endfor %} + + {% endif %} + + +
    {% trans "Field" %} + + {% if request.user.is_staff and request.user|has_perm:"account.view_user" %} + {{ proposal.user.get_full_name }} ({{ proposal.user.email }}) + {% else %} + {{ proposal.timestamp | date:"SHORT_DATETIME" }} + {% endif %} + +
    + {% trans "Proposal" %} + + +
    {{ field.label }} + {% with label_name=field.name|add:"_label" %} + {% if proposal.data|get_item_by_key:label_name %} + {{ proposal.data|get_item_by_key:label_name }} + {% else %} + {{ val|urlize|linebreaksbr }} + {% endif %} + {% endwith %} +
    + {% trans "Delete proposal" %} + + +
    +
    diff --git a/froide_govplan/templates/froide_govplan/admin/accept_proposal.html b/froide_govplan/templates/froide_govplan/admin/accept_proposal.html new file mode 100644 index 0000000..a754aa7 --- /dev/null +++ b/froide_govplan/templates/froide_govplan/admin/accept_proposal.html @@ -0,0 +1,33 @@ +{% extends "admin/change_form.html" %} +{% load i18n %} + +{% block title %}{{ object.title }} - {{ block.super }}{% endblock %} + + +{% block content %}
    +

    {{ object.title }}

    + +
    + {% csrf_token %} +
    + {% include "froide_govplan/admin/_proposal.html" %} +
    +
    + +
    + +
    +
    +

    {% trans "Delete proposal" %}

    +

    +

    +

    +

    +
    +
    +
    + {% endblock %}