Add proposal form and view
This commit is contained in:
parent
8283693906
commit
9902039e3c
4 changed files with 162 additions and 7 deletions
|
|
@ -1,11 +1,13 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
from bleach.linkifier import Linker
|
from bleach.linkifier import Linker
|
||||||
from tinymce.widgets import TinyMCE
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
from .models import GovernmentPlan, GovernmentPlanUpdate
|
from .models import GovernmentPlan, GovernmentPlanUpdate, PlanRating, PlanStatus
|
||||||
|
|
||||||
BLEACH_OPTIONS = {
|
BLEACH_OPTIONS = {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|
@ -47,7 +49,9 @@ class BleachField(forms.CharField):
|
||||||
|
|
||||||
|
|
||||||
class GovernmentPlanForm(forms.ModelForm):
|
class GovernmentPlanForm(forms.ModelForm):
|
||||||
description = BleachField(required=False, widget=TinyMCE(attrs={"cols": 80, "rows": 30}))
|
description = BleachField(
|
||||||
|
required=False, widget=TinyMCE(attrs={"cols": 80, "rows": 30})
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = GovernmentPlan
|
model = GovernmentPlan
|
||||||
|
|
@ -55,8 +59,74 @@ class GovernmentPlanForm(forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class GovernmentPlanUpdateForm(forms.ModelForm):
|
class GovernmentPlanUpdateForm(forms.ModelForm):
|
||||||
content = BleachField(required=False, widget=TinyMCE(attrs={"cols": 80, "rows": 30}))
|
content = BleachField(
|
||||||
|
required=False, widget=TinyMCE(attrs={"cols": 80, "rows": 30})
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = GovernmentPlanUpdate
|
model = GovernmentPlanUpdate
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class GovernmentPlanUpdateProposalForm(forms.ModelForm):
|
||||||
|
title = forms.CharField(
|
||||||
|
label=_("title"),
|
||||||
|
help_text=_("Summarize the update in a title."),
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
content = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("details"),
|
||||||
|
help_text=_("Optionally give more details."),
|
||||||
|
widget=forms.Textarea(attrs={"class": "form-control", "rows": "3"}),
|
||||||
|
)
|
||||||
|
url = forms.URLField(
|
||||||
|
label=_("source URL"),
|
||||||
|
help_text=_("Please give provide a link."),
|
||||||
|
widget=forms.URLInput(
|
||||||
|
attrs={"class": "form-control", "placeholder": "https://"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
status = forms.ChoiceField(
|
||||||
|
label=_("status"),
|
||||||
|
help_text=_("Has the status of the plan changed?"),
|
||||||
|
choices=[("", "---")] + PlanStatus.choices,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
rating = forms.ChoiceField(
|
||||||
|
label=_("rating"),
|
||||||
|
help_text=_("What's your rating of the current implementation?"),
|
||||||
|
choices=[("", "---")] + PlanRating.choices,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = GovernmentPlanUpdate
|
||||||
|
fields = (
|
||||||
|
"title",
|
||||||
|
"content",
|
||||||
|
"url",
|
||||||
|
"status",
|
||||||
|
"rating",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self, plan, user):
|
||||||
|
"""
|
||||||
|
This doesn't save instance, but saves
|
||||||
|
the change proposal.
|
||||||
|
"""
|
||||||
|
data = self.cleaned_data
|
||||||
|
plan.proposals = plan.proposals or {}
|
||||||
|
plan.proposals[user.id] = {
|
||||||
|
"data": data,
|
||||||
|
"timestamp": timezone.now().isoformat(),
|
||||||
|
}
|
||||||
|
plan.save()
|
||||||
|
return plan
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
{% load follow_tags %}
|
{% load follow_tags %}
|
||||||
{% load govplan %}
|
{% load govplan %}
|
||||||
{% load fds_ogimage %}
|
{% load fds_ogimage %}
|
||||||
|
{% load form_helper %}
|
||||||
|
{% load content_helper %}
|
||||||
|
|
||||||
{% block title %}{{ object.title }}{% endblock %}
|
{% block title %}{{ object.title }}{% endblock %}
|
||||||
|
|
||||||
|
|
@ -190,6 +192,51 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<div class="col col-12 col-lg-6 d-flex" id="update-{{ update.pk }}">
|
||||||
|
<div class="box-card border-blue shadow-blue">
|
||||||
|
<div>
|
||||||
|
<div class="box-card-header bg-blue-100 d-flex justify-content-center p-3 p-md-4 tight-margin flex-column">
|
||||||
|
<h3 class="h4">Neue Entwicklung melden</h3>
|
||||||
|
</div>
|
||||||
|
<div class="p-3 p-md-4">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<button type="button" class="btn btn-light" data-toggle="modal" data-target="#govplanupdate-proposal">
|
||||||
|
Entwicklung melden
|
||||||
|
</button>
|
||||||
|
<div class="modal" data-teleport="body" tabindex="-1" role="dialog" id="govplanupdate-proposal">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">
|
||||||
|
Neue Entwicklung melden
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form method="post" action="{% url 'govplan:propose_planupdate' gov=object.government.slug plan=object.slug %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% render_form update_proposal_form %}
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
Neue Entwicklung melden
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-light" href="{{ object.get_absolute_url|make_login_redirect_url }}">
|
||||||
|
Bitte einloggen, um eine Entwicklung zu melden
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from django.utils.translation import pgettext_lazy
|
||||||
from .views import (
|
from .views import (
|
||||||
GovPlanDetailOGView,
|
GovPlanDetailOGView,
|
||||||
GovPlanDetailView,
|
GovPlanDetailView,
|
||||||
|
GovPlanProposeUpdateView,
|
||||||
GovPlanSectionDetailView,
|
GovPlanSectionDetailView,
|
||||||
search,
|
search,
|
||||||
)
|
)
|
||||||
|
|
@ -22,6 +23,11 @@ urlpatterns = [
|
||||||
GovPlanDetailOGView.as_view(),
|
GovPlanDetailOGView.as_view(),
|
||||||
name="plan_og",
|
name="plan_og",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
pgettext_lazy("url part", "<slug:gov>/plan/<slug:plan>/propose-update/"),
|
||||||
|
GovPlanProposeUpdateView.as_view(),
|
||||||
|
name="propose_planupdate",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
pgettext_lazy("url part", "<slug:gov>/<slug:section>/"),
|
pgettext_lazy("url part", "<slug:gov>/<slug:section>/"),
|
||||||
GovPlanSectionDetailView.as_view(),
|
GovPlanSectionDetailView.as_view(),
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.contrib import messages
|
||||||
from django.views.generic import DetailView
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import DetailView, UpdateView
|
||||||
|
|
||||||
|
from .forms import GovernmentPlanUpdateProposalForm
|
||||||
from .models import Government, GovernmentPlan, GovernmentPlanSection
|
from .models import Government, GovernmentPlan, GovernmentPlanSection
|
||||||
|
|
||||||
|
|
||||||
class GovernmentMixin:
|
class GovernmentMixin:
|
||||||
def get(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
self.get_government()
|
self.get_government()
|
||||||
return super().get(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
def get_government(self):
|
def get_government(self):
|
||||||
filter_kwarg = {}
|
filter_kwarg = {}
|
||||||
|
|
@ -54,6 +58,8 @@ class GovPlanDetailView(GovernmentMixin, DetailView):
|
||||||
"-timestamp"
|
"-timestamp"
|
||||||
)
|
)
|
||||||
context["section"] = self.get_section()
|
context["section"] = self.get_section()
|
||||||
|
if self.request.user.is_authenticated:
|
||||||
|
context["update_proposal_form"] = GovernmentPlanUpdateProposalForm()
|
||||||
# For CMS toolbar
|
# For CMS toolbar
|
||||||
self.request.govplan = self.object
|
self.request.govplan = self.object
|
||||||
return context
|
return context
|
||||||
|
|
@ -63,6 +69,32 @@ class GovPlanDetailOGView(GovPlanDetailView):
|
||||||
template_name = "froide_govplan/plan_og.html"
|
template_name = "froide_govplan/plan_og.html"
|
||||||
|
|
||||||
|
|
||||||
|
class GovPlanProposeUpdateView(GovernmentMixin, LoginRequiredMixin, UpdateView):
|
||||||
|
template_name = "publicbody/add_proposal.html"
|
||||||
|
slug_url_kwarg = "plan"
|
||||||
|
form_class = GovernmentPlanUpdateProposalForm
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = GovernmentPlan.objects.filter(government=self.government)
|
||||||
|
if self.request.user.is_authenticated and self.request.user.is_staff:
|
||||||
|
return qs
|
||||||
|
return qs.filter(public=True)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.get_absolute_url()
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.save(self.object, self.request.user)
|
||||||
|
messages.add_message(
|
||||||
|
self.request,
|
||||||
|
messages.INFO,
|
||||||
|
_(
|
||||||
|
"Thank you for your proposal. We will send you an email when it has been approved."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return redirect(self.object)
|
||||||
|
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
plans = GovernmentPlan.objects.search(request.GET.get("q", ""))
|
plans = GovernmentPlan.objects.search(request.GET.get("q", ""))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue