Access url for interventions

* adds access_token as new attribute
* adds generating of access_token
* adds new form
* adds two new routes for sharing an intervention
* adds translation
* adds render_submit to BaseModalForm which triggers rendering the modal footer
pull/9/head
mipel 3 years ago
parent b7ab9f6f55
commit 2f33e5fba9

@ -15,7 +15,7 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention
from konova.forms import BaseForm
from konova.forms import BaseForm, BaseModalForm
from konova.models import Document
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM
from organisation.models import Organisation
@ -220,6 +220,42 @@ class OpenInterventionForm(EditInterventionForm):
self.disable_form_field(field)
class ShareInterventionForm(BaseModalForm):
url = forms.CharField(
label=_("Share link"),
label_suffix="",
disabled=True,
required=False,
widget=forms.TextInput(
attrs={
"style": "width:100%",
}
)
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("Share")
self.form_caption = _("Send this link to users who you want to have writing access on the data.")
self.template = "modal/modal_form.html"
self.render_submit = False
# Make sure an access_token is set
if self.instance.access_token is None:
self.instance.generate_access_token()
self.share_link = self.request.build_absolute_uri(
reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
)
self.initialize_form_field(
"url",
self.share_link
)
def save(self):
i = 0
class DummyFilterInput(forms.HiddenInput):
""" A dummy input widget

@ -13,6 +13,7 @@ from django.utils.timezone import now
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
from konova.models import BaseObject, Geometry, UuidModel
from konova.utils import generators
from konova.utils.generators import generate_random_string
from organisation.models import Organisation
from user.models import UserActionLogEntry
@ -92,6 +93,12 @@ class Intervention(BaseObject):
# Users having access on this object
users = models.ManyToManyField(User)
access_token = models.CharField(
max_length=255,
null=True,
blank=True,
help_text="Used for sharing access",
)
def __str__(self):
return "{} ({})".format(self.identifier, self.title)
@ -139,6 +146,40 @@ class Intervention(BaseObject):
_str = "{}{}{}".format(curr_month, curr_year, rand_str)
return INTERVENTION_IDENTIFIER_TEMPLATE.format(_str)
def generate_access_token(self, make_unique: bool = False, rec_depth: int = 5):
""" Creates a new access token for the intervention
Tokens are not used for identification of a table row. The share logic checks the intervention id as well
as the given token. Therefore two different interventions can hold the same access_token without problems.
For (possible) future changes to the share logic, the make_unique parameter may be used for checking whether
the access_token is already used in any intervention. If so, tokens will be generated as long as a free token
can be found.
Args:
make_unique (bool): Perform check on uniqueness over all intervention entries
rec_depth (int): How many tries for generating a free random token (only if make_unique)
Returns:
"""
# Make sure we won't end up in an infinite loop of trying to generate access_tokens
rec_depth = rec_depth - 1
if rec_depth < 0 and make_unique:
raise RuntimeError(
"Access token generating for {} does not seem to find a free random token! Aborted!".format(self.id)
)
# Create random token
token = generators.generate_random_string(15)
token_used_in = Intervention.objects.filter(access_token=token)
# Make sure the token is not used anywhere as access_token, yet.
# Make use of QuerySet lazy method for checking if it exists or not.
if token_used_in and make_unique:
self.generate_access_token(make_unique, rec_depth)
else:
self.access_token = token
self.save()
def save(self, *args, **kwargs):
if self.identifier is None or len(self.identifier) == 0:
# Create new identifier

@ -24,11 +24,9 @@
</button>
</a>
{% if has_access %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Share' %}">
{% fa5_icon 'share-alt' %}
</button>
</a>
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}">
{% fa5_icon 'share-alt' %}
</button>
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Run check' %}">
{% fa5_icon 'star' %}

@ -7,7 +7,8 @@ Created on: 30.11.20
"""
from django.urls import path
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
create_share_view
app_name = "intervention"
urlpatterns = [
@ -17,4 +18,6 @@ urlpatterns = [
path('<id>', open_view, name='open'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', share_view, name='share'),
path('<id>/share', create_share_view, name='share-create'),
]

@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest
from django.shortcuts import render, get_object_or_404
from intervention.forms import NewInterventionForm, EditInterventionForm
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm
from intervention.models import Intervention
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
@ -198,3 +198,68 @@ def remove_view(request: HttpRequest, id: str):
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def share_view(request: HttpRequest, id: str, token: str):
""" Performs sharing of an intervention
If token given in url is not valid, the user will be redirected to the dashboard
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
token (str): Access token for intervention
Returns:
"""
user = request.user
intervention = get_object_or_404(Intervention, id=id)
# Check tokens
if intervention.access_token == token:
# Send different messages in case user has already been added to list of sharing users
if intervention.has_access(user):
messages.info(
request,
_("{} has already been shared with you").format(intervention.identifier)
)
else:
messages.success(
request,
_("{} has been shared with you").format(intervention.identifier)
)
intervention.users.add(user)
return redirect("intervention:open", id=id)
else:
messages.error(
request,
_("Share link invalid"),
extra_tags="danger",
)
return redirect("home")
@login_required
def create_share_view(request: HttpRequest, id: str):
""" Renders sharing form for an intervention
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Returns:
"""
user = request.user
intervention = get_object_or_404(Intervention, id=id)
form = ShareInterventionForm(request.POST or None, instance=intervention, request=request)
if request.method != "GET":
raise NotImplementedError
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, form.template, context)

@ -109,6 +109,7 @@ class BaseModalForm(BaseForm, BSModalForm):
"""
is_modal_form = True
render_submit = True
class SimpleGeomForm(BaseForm):

Binary file not shown.

@ -4,16 +4,16 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#: compensation/forms.py:27 compensation/forms.py:32 compensation/forms.py:45
#: intervention/filters.py:25 intervention/filters.py:31
#: intervention/filters.py:38 intervention/filters.py:39 konova/forms.py:78
#: konova/forms.py:154 konova/forms.py:181 konova/forms.py:186
#: konova/forms.py:198 konova/forms.py:209 konova/forms.py:222 user/forms.py:38
#: intervention/filters.py:26 intervention/filters.py:40
#: intervention/filters.py:47 intervention/filters.py:48 konova/forms.py:78
#: konova/forms.py:155 konova/forms.py:182 konova/forms.py:187
#: konova/forms.py:199 konova/forms.py:210 konova/forms.py:223 user/forms.py:38
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-28 08:51+0200\n"
"POT-Creation-Date: 2021-07-30 13:23+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -57,57 +57,97 @@ msgstr "Zahlung"
msgid "Add a payment for intervention '{}'"
msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen"
#: compensation/tables.py:18 compensation/tables.py:71 intervention/forms.py:26
#: intervention/tables.py:23
#: compensation/tables.py:24 compensation/tables.py:164
#: intervention/forms.py:27 intervention/tables.py:23
#: intervention/templates/intervention/detail/related-objects.html:30
msgid "Identifier"
msgstr "Kennung"
#: compensation/tables.py:23 compensation/tables.py:76 intervention/forms.py:33
#: intervention/tables.py:28
#: compensation/tables.py:29 compensation/tables.py:169
#: intervention/forms.py:34 intervention/tables.py:28
#: intervention/templates/intervention/detail/related-documents.html:28
#: intervention/templates/intervention/detail/related-objects.html:33
#: intervention/templates/intervention/detail/view.html:62 konova/forms.py:180
#: intervention/templates/intervention/detail/view.html:60 konova/forms.py:181
msgid "Title"
msgstr "Bezeichnung"
#: compensation/tables.py:28 compensation/tables.py:81 konova/forms.py:185
msgid "Created on"
msgstr "Erstellt"
#: compensation/tables.py:34 intervention/tables.py:33
#: intervention/templates/intervention/detail/view.html:92
msgid "Checked"
msgstr "Geprüft"
#: compensation/tables.py:33 compensation/tables.py:86
msgid "Actions"
msgstr "Aktionen"
#: compensation/tables.py:40 intervention/tables.py:39
#: intervention/templates/intervention/detail/view.html:106
msgid "Recorded"
msgstr "Verzeichnet"
#: compensation/tables.py:46 intervention/tables.py:45
msgid "Editable"
msgstr "Freigegeben"
#: compensation/tables.py:52 intervention/tables.py:51
msgid "Last edit"
msgstr "Zuletzt bearbeitet"
#: compensation/tables.py:44
#: compensation/tables.py:61
#: intervention/templates/intervention/detail/related-objects.html:10
msgid "Compensations"
msgstr "Kompensationen"
#: compensation/tables.py:51 compensation/tables.py:104
#: compensation/tables.py:83 compensation/tables.py:200
#: intervention/tables.py:92 intervention/tables.py:175
msgid "Open {}"
msgstr "Öffne {}"
#: compensation/tables.py:83 compensation/tables.py:197
#: konova/templates/konova/home.html:49 templates/navbar.html:28
msgid "Compensation"
msgstr "Kompensation"
#: compensation/tables.py:54 compensation/tables.py:107
#: intervention/tables.py:92 intervention/tables.py:173
msgid "Open {}"
msgstr "Öffne {}"
#: compensation/tables.py:104 intervention/tables.py:111
msgid "Not checked yet"
msgstr "Noch nicht geprüft"
#: compensation/tables.py:109 intervention/tables.py:116
msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden"
#: compensation/tables.py:128 intervention/tables.py:135
msgid "Not registered yet"
msgstr "Noch nicht verzeichnet"
#: compensation/tables.py:133 intervention/tables.py:140
msgid "Registered on {} by {}"
msgstr "Am {} von {} verzeichnet worden"
#: compensation/tables.py:156 intervention/tables.py:163
msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: compensation/tables.py:156 intervention/tables.py:163
msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: compensation/tables.py:59 compensation/tables.py:112
#: intervention/tables.py:177
#: compensation/tables.py:174 konova/forms.py:186
msgid "Created on"
msgstr "Erstellt"
#: compensation/tables.py:179
msgid "Actions"
msgstr "Aktionen"
#: compensation/tables.py:190
msgid "Eco Accounts"
msgstr "Ökokonten"
#: compensation/tables.py:205 intervention/tables.py:179
msgid "Edit {}"
msgstr "Bearbeite {}"
#: compensation/tables.py:63 compensation/tables.py:116
#: intervention/tables.py:181
#: compensation/tables.py:209 intervention/tables.py:183
msgid "Delete {}"
msgstr "Lösche {}"
#: compensation/tables.py:97
msgid "Eco Accounts"
msgstr "Ökokonten"
#: compensation/views.py:136
msgid "Payment added"
msgstr "Zahlung hinzugefügt"
@ -121,155 +161,127 @@ msgstr "Es gab einen Fehler im Formular."
msgid "Payment removed"
msgstr "Zahlung gelöscht"
#: intervention/filters.py:24
#: intervention/filters.py:25
msgid "Show unshared"
msgstr "Nicht freigegebene anzeigen"
#: intervention/filters.py:30
#: intervention/filters.py:39
msgid "Show recorded"
msgstr "Verzeichnete anzeigen"
#: intervention/filters.py:42
#: intervention/filters.py:51
msgid "District"
msgstr "Gemarkung"
#: intervention/filters.py:43
#: intervention/filters.py:52
msgid "Search for district"
msgstr "Nach Gemarkung suchen"
#: intervention/forms.py:29
#: intervention/forms.py:30
msgid "Generated automatically if none was given"
msgstr "Wird automatisch erzeugt, falls nicht angegeben"
#: intervention/forms.py:38
#: intervention/forms.py:39
msgid "Type"
msgstr "Typ"
#: intervention/forms.py:41
#: intervention/forms.py:42
msgid "Which intervention type is this"
msgstr "Welcher Eingriffstyp"
#: intervention/forms.py:44
#: intervention/templates/intervention/detail/view.html:70
#: intervention/forms.py:45
#: intervention/templates/intervention/detail/view.html:68
msgid "Law"
msgstr "Gesetz"
#: intervention/forms.py:47
#: intervention/forms.py:48
msgid "Based on which law"
msgstr "Basiert auf welchem Recht"
#: intervention/forms.py:50
#: intervention/templates/intervention/detail/view.html:90
#: intervention/forms.py:51
#: intervention/templates/intervention/detail/view.html:88
msgid "Intervention handler"
msgstr "Eingriffsverursacher"
#: intervention/forms.py:53
#: intervention/forms.py:54
msgid "Who performs the intervention"
msgstr "Wer führt den Eingriff durch"
#: intervention/forms.py:56
#: intervention/forms.py:57
msgid "Data provider"
msgstr "Datenbereitsteller"
#: intervention/forms.py:58
#: intervention/forms.py:59
msgid "Who provides the data for the intervention"
msgstr "Wer stellt die Daten für den Eingriff zur Verfügung"
#: intervention/forms.py:63
#: intervention/forms.py:64
msgid "Organization"
msgstr "Organisation"
#: intervention/forms.py:69
#: intervention/forms.py:70
msgid "Data provider details"
msgstr "Datenbereitsteller Details"
#: intervention/forms.py:72
#: intervention/forms.py:73
msgid "Further details"
msgstr "Weitere Details"
#: intervention/forms.py:85
#: intervention/forms.py:86
msgid "Map"
msgstr "Karte"
#: intervention/forms.py:87
#: intervention/forms.py:88
msgid "Where does the intervention take place"
msgstr "Wo findet der Eingriff statt"
#: intervention/forms.py:95
#: intervention/forms.py:96
msgid "Files"
msgstr "Dateien"
#: intervention/forms.py:102
#: intervention/forms.py:103
msgid "New intervention"
msgstr "Neuer Eingriff"
#: intervention/forms.py:145
#: intervention/forms.py:146
msgid "Edit intervention"
msgstr "Eingriff bearbeiten"
#: intervention/tables.py:33
#: intervention/templates/intervention/detail/view.html:94
msgid "Checked"
msgstr "Geprüft"
#: intervention/forms.py:226
msgid "Share link"
msgstr "Freigabelink"
#: intervention/tables.py:39
#: intervention/templates/intervention/detail/view.html:108
msgid "Recorded"
msgstr "Verzeichnet"
#: intervention/tables.py:45
msgid "Editable"
msgstr "Freigegeben"
#: intervention/forms.py:239
#: intervention/templates/intervention/detail/view.html:27
msgid "Share"
msgstr "Freigabe"
#: intervention/tables.py:51
msgid "Last edit"
msgstr "Zuletzt bearbeitet"
#: intervention/forms.py:240
msgid ""
"Send this link to users who you want to have writing access on the data."
msgstr ""
#: intervention/tables.py:70
msgid "Interventions"
msgstr "Eingriffe"
#: intervention/tables.py:92 intervention/tables.py:170
#: intervention/tables.py:92 intervention/tables.py:172
#: intervention/templates/intervention/detail/view.html:12
#: konova/templates/konova/home.html:11 templates/navbar.html:22
msgid "Intervention"
msgstr "Eingriff"
#: intervention/tables.py:111
msgid "Not checked yet"
msgstr "Noch nicht geprüft"
#: intervention/tables.py:115
msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden"
#: intervention/tables.py:134
msgid "Not registered yet"
msgstr "Noch nicht verzeichnet"
#: intervention/tables.py:138
msgid "Registered on {} by {}"
msgstr "Am {} von {} verzeichnet worden"
#: intervention/tables.py:161
msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: intervention/tables.py:161
msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: intervention/templates/intervention/detail/related-documents.html:10
msgid "Documents"
msgstr "Dokumente"
#: intervention/templates/intervention/detail/related-documents.html:15
#: konova/forms.py:221
#: konova/forms.py:222
msgid "Add new document"
msgstr "Neues Dokument hinzufügen"
#: intervention/templates/intervention/detail/related-documents.html:31
#: konova/forms.py:208
#: konova/forms.py:209
msgid "Comment"
msgstr "Kommentar"
@ -310,64 +322,70 @@ msgstr "In LANIS öffnen"
msgid "Public report"
msgstr "Öffentlicher Bericht"
#: intervention/templates/intervention/detail/view.html:28
msgid "Share"
msgstr "Freigabe"
#: intervention/templates/intervention/detail/view.html:33
#: intervention/templates/intervention/detail/view.html:31
msgid "Run check"
msgstr "Prüfung vornehmen"
#: intervention/templates/intervention/detail/view.html:38
#: intervention/templates/intervention/detail/view.html:36
msgid "Record"
msgstr "Verzeichnen"
#: intervention/templates/intervention/detail/view.html:43
#: intervention/templates/intervention/detail/view.html:41
msgid "Edit"
msgstr "Bearbeiten"
#: intervention/templates/intervention/detail/view.html:48
#: intervention/templates/intervention/detail/view.html:46
#: venv/lib/python3.7/site-packages/django/forms/formsets.py:391
msgid "Delete"
msgstr "Löschen"
#: intervention/templates/intervention/detail/view.html:66
#: intervention/templates/intervention/detail/view.html:64
msgid "Process type"
msgstr "Verfahrenstyp"
#: intervention/templates/intervention/detail/view.html:74
#: intervention/templates/intervention/detail/view.html:72
msgid "Registration office"
msgstr "Zulassungsbehörde"
#: intervention/templates/intervention/detail/view.html:78
#: intervention/templates/intervention/detail/view.html:76
msgid "Registration office file number"
msgstr "Aktenzeichen Zulassungsbehörde"
#: intervention/templates/intervention/detail/view.html:82
#: intervention/templates/intervention/detail/view.html:80
msgid "Conservation office"
msgstr "Naturschutzbehörde"
#: intervention/templates/intervention/detail/view.html:86
#: intervention/templates/intervention/detail/view.html:84
msgid "Conversation office file number"
msgstr "Aktenzeichen Naturschutzbehörde"
#: intervention/templates/intervention/detail/view.html:122
#: intervention/templates/intervention/detail/view.html:99
msgid "Checked on "
msgstr "Geprüft am "
#: intervention/templates/intervention/detail/view.html:99
#: intervention/templates/intervention/detail/view.html:113
#: intervention/templates/intervention/detail/view.html:132
msgid "by"
msgstr "von"
#: intervention/templates/intervention/detail/view.html:113
msgid "Recorded on "
msgstr "Verzeichnet am"
#: intervention/templates/intervention/detail/view.html:120
msgid "Registration date"
msgstr "Datum Zulassung bzw. Satzungsbeschluss"
#: intervention/templates/intervention/detail/view.html:126
#: intervention/templates/intervention/detail/view.html:124
msgid "Binding on"
msgstr "Datum Bestandskraft"
#: intervention/templates/intervention/detail/view.html:130
#: intervention/templates/intervention/detail/view.html:128
msgid "Last modified"
msgstr "Zuletzt bearbeitet"
#: intervention/templates/intervention/detail/view.html:134
msgid "by"
msgstr "von"
#: intervention/templates/intervention/detail/view.html:143
#: intervention/templates/intervention/detail/view.html:141
msgid "No geometry added, yet."
msgstr "Keine Geometrie vorhanden"
@ -397,6 +415,18 @@ msgstr ""
msgid "{} edited"
msgstr "{} bearbeitet"
#: intervention/views.py:225
msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben"
#: intervention/views.py:230
msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben"
#: intervention/views.py:237
msgid "Share link invalid"
msgstr "Freigabelink ungültig"
#: konova/decorators.py:29
msgid "You need to be staff to perform this action!"
msgstr "Hierfür müssen Sie Mitarbeiter sein!"
@ -413,11 +443,11 @@ msgstr "Hierfür müssen Sie einer anderen Nutzergruppe angehören!"
msgid "Not editable"
msgstr "Nicht editierbar"
#: konova/forms.py:77 konova/forms.py:153
#: konova/forms.py:77 konova/forms.py:154
msgid "Confirm"
msgstr "Bestätige"
#: konova/forms.py:89 konova/forms.py:162
#: konova/forms.py:89 konova/forms.py:163
msgid "Remove"
msgstr "Löschen"
@ -425,24 +455,24 @@ msgstr "Löschen"
msgid "You are about to remove {} {}"
msgstr "Sie sind dabei {} {} zu löschen"
#: konova/forms.py:163
#: konova/forms.py:164
msgid "Are you sure?"
msgstr ""
#: konova/forms.py:187
#: konova/forms.py:188
msgid "When has this file been created? Important for photos."
msgstr ""
#: konova/forms.py:197
#: konova/forms.py:198
#: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
msgid "File"
msgstr ""
#: konova/forms.py:199
#: konova/forms.py:200
msgid "Must be smaller than 15 Mb"
msgstr ""
#: konova/forms.py:210
#: konova/forms.py:211
msgid "Additional comment on this file"
msgstr "Zusätzlicher Kommentar"
@ -530,7 +560,39 @@ msgstr ""
msgid "Contact"
msgstr "Kontakt"
#: templates/modal/modal_form.html:24
#: templates/generic_index.html:19
msgid "New entry"
msgstr "Neuer Eintrag"
#: templates/generic_index.html:21
msgid "New"
msgstr "Neu"
#: templates/generic_index.html:36
msgid "Search for keywords"
msgstr "Nach Schlagwörtern suchen"
#: templates/generic_index.html:36
msgid "Search"
msgstr "Suchen"
#: templates/generic_index.html:37
msgid "Start search"
msgstr "Starte Suche"
#: templates/generic_index.html:49
msgid "Results per page"
msgstr "Treffer pro Seite"
#: templates/generic_index.html:73 templates/generic_index.html:79
msgid "Filter"
msgstr ""
#: templates/generic_index.html:81
msgid "Apply filter"
msgstr "Filter anwenden"
#: templates/modal/modal_form.html:25
msgid "Continue"
msgstr "Weiter"
@ -574,38 +636,6 @@ msgstr "Einstellungen"
msgid "Logout"
msgstr "Abmelden"
#: templates/table.html:15
msgid "New entry"
msgstr "Neuer Eintrag"
#: templates/table.html:17
msgid "New"
msgstr "Neu"
#: templates/table.html:32
msgid "Search for keywords"
msgstr "Nach Schlagwörtern suchen"
#: templates/table.html:32
msgid "Search"
msgstr "Suchen"
#: templates/table.html:33
msgid "Start search"
msgstr "Starte Suche"
#: templates/table.html:46
msgid "Results per page"
msgstr "Treffer pro Seite"
#: templates/table.html:70 templates/table.html:77
msgid "Filter"
msgstr ""
#: templates/table.html:79
msgid "Apply filter"
msgstr "Filter anwenden"
#: templates/table/generic_table_form.html:23
msgid "Cancel"
msgstr "Abbrechen"

@ -20,7 +20,9 @@
</article>
{% include 'table/generic_table_form_body.html' %}
</div>
{% if form.render_submit %}
<div class="modal-footer">
<button type="submit" class="btn btn-default">{% trans 'Continue' %}</button>
</div>
{% endif %}
</form>
Loading…
Cancel
Save