#7 New forms WIP
* adds saving functionality for new intervention form * refactors new identifier generating, so a pre-generated identifier from a new element form will be checked again before saving * adds css fixes for disturbing input field:focus bugs * adds missing csrf token to new collapsible form * adds/updates translations * introduces mark_as_deleted as only marking instead of using delete() which will really delete from the db
This commit is contained in:
@@ -13,8 +13,8 @@ from bootstrap_modal_forms.utils import is_ajax
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.gis.forms import GeometryField, OSMWidget
|
||||
from django.contrib.gis.geos import Polygon
|
||||
from django.contrib.gis.forms import OSMWidget, MultiPolygonField
|
||||
from django.contrib.gis.geos import Polygon, MultiPolygon
|
||||
from django.db import transaction
|
||||
from django.http import HttpRequest, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
@@ -25,7 +25,8 @@ from compensation.models import EcoAccount, Compensation, EcoAccountDocument, Co
|
||||
from ema.models import Ema, EmaDocument
|
||||
from intervention.models import Intervention, Revocation, RevocationDocument, InterventionDocument
|
||||
from konova.contexts import BaseContext
|
||||
from konova.models import BaseObject
|
||||
from konova.models import BaseObject, Geometry
|
||||
from konova.settings import DEFAULT_SRID
|
||||
from konova.utils.message_templates import FORM_INVALID
|
||||
from user.models import UserActionLogEntry, UserAction
|
||||
|
||||
@@ -187,8 +188,7 @@ class BaseModalForm(BaseForm, BSModalForm):
|
||||
full_width_fields = False
|
||||
template = "modal/modal_form.html"
|
||||
|
||||
def __init__(self, full_width_fields: bool = True, *args, **kwargs):
|
||||
self.full_width_fields = full_width_fields
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.full_width_fields:
|
||||
# Automatically add bootstrap w-100 class for maximum width of form fields in modals
|
||||
@@ -243,18 +243,26 @@ class SimpleGeomForm(BaseForm):
|
||||
""" A geometry form for rendering geometry read-only using a widget
|
||||
|
||||
"""
|
||||
geom = GeometryField(
|
||||
geom = MultiPolygonField(
|
||||
srid=DEFAULT_SRID,
|
||||
label=_("Geometry"),
|
||||
help_text=_(""),
|
||||
label_suffix="",
|
||||
required=False,
|
||||
disabled=True,
|
||||
disabled=False,
|
||||
widget=OSMWidget(
|
||||
attrs={
|
||||
"map_width": 600,
|
||||
"map_height": 400,
|
||||
# default_zoom defines the nearest possible zoom level from which the JS automatically
|
||||
# zooms out if geometry requires a larger view port. So define a larger range for smaller geometries
|
||||
"default_zoom": 25,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
read_only = kwargs.pop("read_only", True)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Initialize geometry
|
||||
@@ -267,8 +275,17 @@ class SimpleGeomForm(BaseForm):
|
||||
geom = Polygon.from_bbox([0, 0, 0, 0])
|
||||
# Zoom out to a very high level, so the user can see directly that there is no geometry for this entry
|
||||
self.fields["geom"].widget.attrs["default_zoom"] = 1
|
||||
self.initialize_form_field("geom", geom)
|
||||
self.area = geom.area
|
||||
if read_only:
|
||||
self.initialize_form_field("geom", geom)
|
||||
self.area = geom.area
|
||||
self.fields["geom"].disabled = True
|
||||
|
||||
def save(self, action: UserActionLogEntry):
|
||||
geometry = Geometry.objects.create(
|
||||
geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)),
|
||||
created=action,
|
||||
)
|
||||
return geometry
|
||||
|
||||
|
||||
class RemoveModalForm(BaseModalForm):
|
||||
@@ -294,15 +311,7 @@ class RemoveModalForm(BaseModalForm):
|
||||
|
||||
def save(self):
|
||||
if isinstance(self.instance, BaseObject):
|
||||
with transaction.atomic():
|
||||
action = UserActionLogEntry.objects.create(
|
||||
user=self.user,
|
||||
timestamp=timezone.now(),
|
||||
action=UserAction.DELETED,
|
||||
)
|
||||
self.instance.deleted = action
|
||||
self.instance.log.add(action)
|
||||
self.instance.save()
|
||||
self.instance.mark_as_deleted(self.user)
|
||||
else:
|
||||
# If the class does not provide restorable delete functionality, we must delete the entry finally
|
||||
self.instance.delete()
|
||||
@@ -360,6 +369,9 @@ class NewDocumentForm(BaseModalForm):
|
||||
Ema: EmaDocument,
|
||||
}
|
||||
|
||||
# Define w-100 for all form fields
|
||||
full_width_fields = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("Add new document")
|
||||
|
||||
@@ -9,6 +9,8 @@ import os
|
||||
import uuid
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.gis.db.models import MultiPolygonField
|
||||
@@ -61,8 +63,20 @@ class BaseResource(UuidModel):
|
||||
abstract = True
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
if self.created:
|
||||
""" Base deletin of a resource
|
||||
|
||||
Args:
|
||||
using ():
|
||||
keep_parents ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
try:
|
||||
self.created.delete()
|
||||
except ObjectDoesNotExist:
|
||||
# Object does not exist anymore - we can skip this
|
||||
pass
|
||||
super().delete()
|
||||
|
||||
|
||||
@@ -81,14 +95,13 @@ class BaseObject(BaseResource):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
""" Custom delete functionality
|
||||
def mark_as_deleted(self, user: User):
|
||||
""" Mark an entry as deleted
|
||||
|
||||
Does not delete from database but sets a timestamp for being deleted on and which user deleted the object
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
user (User): The performing user
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -97,13 +110,14 @@ class BaseObject(BaseResource):
|
||||
# Nothing to do here
|
||||
return
|
||||
|
||||
_user = kwargs.get("user", None)
|
||||
with transaction.atomic():
|
||||
action = UserActionLogEntry.objects.create(
|
||||
user=_user,
|
||||
action=UserAction.DELETED
|
||||
user=user,
|
||||
action=UserAction.DELETED,
|
||||
timestamp=timezone.now()
|
||||
)
|
||||
self.deleted = action
|
||||
self.log.add(action)
|
||||
self.save()
|
||||
|
||||
def add_log_entry(self, action: UserAction, user: User, comment: str):
|
||||
|
||||
@@ -60,8 +60,18 @@ a {
|
||||
color: var(--rlp-red);
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
input[type=text], input[type=date] {
|
||||
border: 1px solid gray;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus{
|
||||
outline: none;
|
||||
border-color: var(--rlp-red);
|
||||
box-shadow: 0 0 3px var(--rlp-red);
|
||||
-moz-box-shadow: 0 0 3px var(--rlp-red);
|
||||
-webkit-box-shadow: 0 0 3px var(--rlp-red);
|
||||
}
|
||||
|
||||
.body-content{
|
||||
@@ -137,6 +147,13 @@ input[type=text] {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
/**
|
||||
Overwrites bootstrap .btn:focus box shadow color
|
||||
*/
|
||||
.btn:focus{
|
||||
box-shadow: 0 0 5px .2rem var(--rlp-gray-light);
|
||||
}
|
||||
|
||||
.btn-default{
|
||||
color: white;
|
||||
background-color: var(--rlp-red);
|
||||
@@ -175,13 +192,6 @@ input[type=text] {
|
||||
background-color: var(--rlp-gray-light);
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus{
|
||||
border-color: var(--rlp-red) !important;
|
||||
box-shadow: 0 0 3px var(--rlp-red) !important;
|
||||
-moz-box-shadow: 0 0 3px var(--rlp-red) !important;
|
||||
-webkit-box-shadow: 0 0 3px var(--rlp-red) !important;
|
||||
}
|
||||
|
||||
.check-star{
|
||||
color: goldenrod;
|
||||
}
|
||||
@@ -219,4 +229,7 @@ No other approach worked to get the autocomplete fields to full width of parent
|
||||
*/
|
||||
.select2-container{
|
||||
width: 100% !important;
|
||||
}
|
||||
.select2-results__option--highlighted{
|
||||
background-color: var(--rlp-red) !important;
|
||||
}
|
||||
Reference in New Issue
Block a user