Compare commits
81 Commits
a372be4af2
...
656a24ef02
| Author | SHA1 | Date | |
|---|---|---|---|
| 656a24ef02 | |||
| 74643db087 | |||
| 3a6fd3cee1 | |||
| 29e9d1bd8c | |||
| 3c5ca9ae00 | |||
| 3d1ad6112d | |||
| b843e67e9b | |||
| 4cab71e8fb | |||
| 969339a95f | |||
| e06efa1539 | |||
| 2fb6d2782f | |||
| f69eccd0e4 | |||
| e20e6d4b1d | |||
| 0352a60e28 | |||
| abeb14601a | |||
| f52225495d | |||
| 797b2c15f7 | |||
| e81618500b | |||
| f7a5da306c | |||
| 92a9b5c6c9 | |||
| 964aeb97a7 | |||
| 474e9eb0f8 | |||
| 7acc2c6eec | |||
| 5a02837d7f | |||
| 7ff0a9b489 | |||
| 9af4b58a4f | |||
| 7a20890f17 | |||
| f6c9e532f8 | |||
| f1698c4fd3 | |||
| cb82aeffde | |||
| e9c1ef2604 | |||
| d8f0f2b3be | |||
| 65f065f5ce | |||
| 5cba64e500 | |||
| 064784a222 | |||
| b890ef3563 | |||
| 962f2ae86c | |||
| c71a1940dd | |||
| 8b2913a8be | |||
| 111ffc2b2e | |||
| 600aa918ef | |||
| 68e13ed176 | |||
| 0fa4330f2c | |||
| c9289b1e8c | |||
| bb3136bfc7 | |||
| b708e9ecaf | |||
| f3619b2881 | |||
| 7572c92da5 | |||
| 5a2b11b44e | |||
| df15ea100b | |||
| 3da6e90f73 | |||
| f784ab0c78 | |||
| ebaa477cff | |||
| b4be21bf45 | |||
| 0df36df9d8 | |||
| a5754b2633 | |||
| 7c6e01a436 | |||
| ad90429ec7 | |||
| 0e36237890 | |||
| 3261f5a90a | |||
| 1551c1bdf2 | |||
| 996bd7af67 | |||
| 21bd34c94d | |||
| fb581c940b | |||
| b428f46213 | |||
| 38fe55dd86 | |||
| 0da6c425fd | |||
| 81962ab9e7 | |||
| 48dd0a6a19 | |||
| 661827a957 | |||
| 242de5f749 | |||
| bd7f940987 | |||
| 0634671c84 | |||
| 1fb5be0cf8 | |||
| 3f9e4265e5 | |||
| de21b8b5e5 | |||
| fd481fef2e | |||
| 70f077e393 | |||
| 1c7d943a21 | |||
| 41873ebfe5 | |||
| fc2dbde064 |
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
# Database
|
||||
notfellchen
|
||||
*.sq3
|
||||
|
||||
# Geojson from imports
|
||||
*.geojson
|
||||
|
||||
74
docs/_ext/drawio.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from docutils import nodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.typing import ExtensionMetadata
|
||||
|
||||
|
||||
class DrawioDirective(SphinxDirective):
|
||||
"""A directive to show a drawio diagram!
|
||||
|
||||
Usage:
|
||||
.. drawio::
|
||||
example-diagram.drawio.html
|
||||
example-diagram.drawio.png
|
||||
:alt: Example of a Draw.io diagram
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
required_arguments = 2 # html and png
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True # indicating if the final argument may contain whitespace
|
||||
option_spec = {
|
||||
"alt": str,
|
||||
}
|
||||
|
||||
def run(self) -> list[nodes.Node]:
|
||||
env = self.state.document.settings.env
|
||||
builder = env.app.builder
|
||||
|
||||
# Resolve paths relative to the document
|
||||
docdir = Path(env.doc2path(env.docname)).parent
|
||||
html_rel = Path(self.arguments[0])
|
||||
png_rel = Path(self.arguments[1])
|
||||
html_path = (docdir / html_rel).resolve()
|
||||
png_path = (docdir / png_rel).resolve()
|
||||
|
||||
alt_text = self.options.get("alt", "")
|
||||
|
||||
container = nodes.container()
|
||||
|
||||
# HTML output -> raw HTML node
|
||||
if builder.format == "html":
|
||||
# Embed the HTML file contents directly
|
||||
try:
|
||||
html_content = html_path.read_text(encoding="utf-8")
|
||||
except OSError as e:
|
||||
msg = self.state_machine.reporter.error(f"Cannot read HTML file: {e}")
|
||||
return [msg]
|
||||
aria_attribute = f' aria-label="{alt_text}"' if alt_text else ""
|
||||
raw_html_node = nodes.raw(
|
||||
"",
|
||||
f'<div class="drawio-diagram"{aria_attribute}>{html_content}</div>',
|
||||
format="html",
|
||||
)
|
||||
container += raw_html_node
|
||||
else:
|
||||
# Other outputs -> PNG image node
|
||||
image_node = nodes.image(uri=png_path)
|
||||
container += image_node
|
||||
|
||||
return [container]
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> ExtensionMetadata:
|
||||
app.add_directive("drawio", DrawioDirective)
|
||||
|
||||
return {
|
||||
"version": "0.2",
|
||||
"parallel_read_safe": True,
|
||||
"parallel_write_safe": True,
|
||||
}
|
||||
12
docs/conf.py
@@ -16,6 +16,10 @@
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path('_ext').resolve()))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
@@ -28,7 +32,6 @@ version = ''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.2.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
@@ -40,6 +43,7 @@ release = '0.2.0'
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.ifconfig',
|
||||
'drawio'
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
@@ -69,7 +73,6 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
@@ -104,7 +107,6 @@ html_static_path = ['_static']
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'notfellchen'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
@@ -133,7 +135,6 @@ latex_documents = [
|
||||
'Julian-Samuel Gebühr', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
@@ -143,7 +144,6 @@ man_pages = [
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
@@ -155,7 +155,6 @@ texinfo_documents = [
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
@@ -173,5 +172,4 @@ epub_title = project
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
||||
|
||||
BIN
docs/user/Screenshot-Moderationstools.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/user/Screenshot-hilfreiche-Links.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
11
docs/user/Tiere-in-Vermittlung-entdecken.drawio.html
Normal file
BIN
docs/user/Tiere-in-Vermittlung-entdecken.drawio.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
docs/user/Vermittlung-Lifecycle.drawio.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
11
docs/user/Vermittlung_Lifecycle.drawio.html
Normal file
@@ -6,14 +6,27 @@ Jede Vermittlung kann abonniert werden. Dafür klickst du auf die Glocke neben d
|
||||
|
||||
.. image:: abonnieren.png
|
||||
|
||||
|
||||
Einstellungen
|
||||
-------------
|
||||
|
||||
Du kannst E-Mail Benachrichtigungen in den Einstellungen deaktivieren.
|
||||
|
||||
.. image::
|
||||
einstellungen-benachrichtigungen.png
|
||||
:alt: Screenshot der Profileinstellungen in Notfellchen. Ein roter Pfeil zeigt auf einen Schalter "E-Mail Benachrichtigungen"
|
||||
|
||||
Auf der Website
|
||||
+++++++++++++++
|
||||
|
||||
.. image::
|
||||
screenshot-benachrichtigungen.png
|
||||
:alt: Screenshot der Menüleiste von Notfellchen.org. Neben dem Symbol einer Glocke steht die Zahl 27.
|
||||
|
||||
|
||||
|
||||
E-Mail
|
||||
++++++
|
||||
|
||||
Mit während deiner :doc:`registrierung` gibst du eine E-Mail Addresse an.
|
||||
|
||||
Benachrichtigungen senden wir per Mail - du kannst das jederzeit in den Einstellungen deaktivieren.
|
||||
Mit während deiner :doc:`registrierung` gibst du eine E-Mail Adresse an. An diese senden wir Benachrichtigungen, außer
|
||||
du deaktiviert dies wie oben beschrieben.
|
||||
BIN
docs/user/einstellungen-benachrichtigungen.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
58
docs/user/erste-schritte.rst
Normal file
@@ -0,0 +1,58 @@
|
||||
Erste Schritte
|
||||
==============
|
||||
|
||||
Tiere zum Adoptieren suchen
|
||||
---------------------------
|
||||
|
||||
Wenn du Tiere zum adoptieren suchst, brauchst du keinen Account. Du kannst bequem die `Suche <https://notfellchen.org/suchen/>`_ nutzen, um Tiere zur Adoption in deiner Nähe zu finden.
|
||||
Wenn dich eine Vermittlung interessiert, kannst du folgendes tun
|
||||
|
||||
* die Vermittlung aufrufen um Details zu sehen
|
||||
* den Link :guilabel:`Weitere Informationen` anklicken um auf der Tierheimwebsite mehr zu erfahren
|
||||
* per Kommentar weitere Informationen erfragen oder hinzufügen
|
||||
|
||||
Wenn du die Tiere tatsächlich informieren willst, folge der Anleitung unter :guilabel:`Adoptionsprozess`.
|
||||
Dieser kann sich je nach Tierschutzorganisation unterscheiden.
|
||||
|
||||
.. image::
|
||||
screenshot-adoptionsprozess.png
|
||||
:alt: Screenshot der Sektion "Adoptionsprozess" einer Vermittlungsanzeige. Der Prozess ist folgendermaßen: 1. Link zu "Weiteren Informationen" prüfen, 2. Organization kontaktieren, 3. Bei erfolgreicher Vermittlung: Vermittlung als geschlossen melden
|
||||
|
||||
Suchen abonnieren
|
||||
+++++++++++++++++
|
||||
|
||||
Es kann sein, dass es in deiner Umgebung keine passenden Tiere für deine Suche gibt. Damit du nicht ständig wieder Suchen musst, gibt es die Funktion "Suche abonnieren".
|
||||
Wenn du eine Suche abonnierst, wirst du für neue Vermittlungen, die den Kriterien der Suche entsprechen, benachrichtigt.
|
||||
|
||||
.. image::
|
||||
screenshot-suche-abonnieren.png
|
||||
:alt: Screenshot der Suchmaske auf Notfellchen.org . Ein roter Pfeil zeigt auf den Button "Suche abonnieren"
|
||||
|
||||
.. important::
|
||||
|
||||
Um Suchen zu abonnieren brauchst du einen Account. Wie du einen Account erstellst erfährst du hier: :doc:`registrierung`.
|
||||
|
||||
.. hint::
|
||||
|
||||
Mehr über Benachrichtigungen findest du hier: :doc:`benachrichtigungen`.
|
||||
|
||||
Vermittlungen hinzufügen
|
||||
------------------------
|
||||
|
||||
Gehe zu `Vermittlung hinzufügen <https://notfellchen.org/vermitteln/>`_ um eine neue Vermittlung einzustellen.
|
||||
Füge alle Informationen die du hast hinzu.
|
||||
|
||||
.. important::
|
||||
|
||||
Um Vermittlungen hinzuzufügen brauchst du einen Account.
|
||||
Wie du einen Account erstellst erfährst du hier: :doc:`registrierung`.
|
||||
|
||||
|
||||
.. important::
|
||||
|
||||
Vermittlungen die du einstellst müssen erst durch Moderator\*innen freigeschaltet werden. Das passiert normalerweise
|
||||
innerhalb von 24 Stunden. Wenn deine Vermittlung dann noch nicht freigeschaltet ist, prüfe bitte dein E-Mail Postfach,
|
||||
es könnte sein, dass die Moderator\*innen Rückfragen haben. Melde dich gerne unter info@notfellchen.org, wenn deine
|
||||
Vermittlung nach 24 Stunden nicht freigeschaltet ist.
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
******************
|
||||
User Dokumentation
|
||||
******************
|
||||
****************
|
||||
Benutzerhandbuch
|
||||
****************
|
||||
|
||||
Im Benutzerhandbuch findest du Informationen zur Benutzung von `notfellchen.org <https://notfellchen.org>`_.
|
||||
Solltest du darüber hinaus Fragen haben, komm gerne auf uns zu: info@notfellchen.org
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Inhalt:
|
||||
|
||||
|
||||
erste-schritte.rst
|
||||
registrierung.rst
|
||||
vermittlungen.rst
|
||||
moderationskonzept.rst
|
||||
benachrichtigungen.rst
|
||||
organisationen-pruefen.rst
|
||||
|
||||
55
docs/user/organisationen-pruefen.rst
Normal file
@@ -0,0 +1,55 @@
|
||||
Tiere in Vermittlung systematisch entdecken & eintragen
|
||||
=======================================================
|
||||
|
||||
Notfellchen hat eine Liste der meisten deutschen Tierheime und anderer Tierschutzorganisationen.
|
||||
Die meisten dieser Organisationen nehmen Tiere auf die bei Notfellchen eingetragen werden können.
|
||||
Es ist daher das Ziel, diese Organisationen alle zwei Wochen auf neue Tiere zu prüfen.
|
||||
|
||||
|
||||
+-------------------------------------------------+---------+----------------------+
|
||||
| Gruppe | Anzahl | Zuletzt aktualisiert |
|
||||
+=================================================+=========+======================+
|
||||
| Tierschutzorganisationen im Verzeichnis | 550 | Oktober 2025 |
|
||||
+-------------------------------------------------+---------+----------------------+
|
||||
| Tierschutzorganisationen in regelmäßigerPrüfung | 412 | Oktober 2025 |
|
||||
+-------------------------------------------------+---------+----------------------+
|
||||
|
||||
.. warning::
|
||||
|
||||
Organisationen auf neue Tiere zu prüfen ist eine Funktion für Moderator\*innen. Falls du Lust hast mitzuhelfen,
|
||||
meld dich unter info@notfellchen.org
|
||||
|
||||
Als Moderator\*in kannst du direkt auf den `Moderations-Check <https://notfellchen.org/organization-check/>`_ zugreifen
|
||||
oder findest ihn in unter :menuselection:`Hilfreiche Links --> Moderationstools`:
|
||||
|
||||
.. image::
|
||||
Screenshot-hilfreiche-Links.png
|
||||
:alt: Screenshot der Hilfreichen Links. Zur Auswahl stehen "Tierheime in der Nähe","Moderationstools" und "Admin-Bereich"
|
||||
|
||||
.. image::
|
||||
Screenshot-Moderationstools.png
|
||||
:alt: Screenshot der Moderationstools. Zur Auswahl stehen "Moderationswarteschlange", "Up-to-Date Check", "Organisations-Check" und "Vermittlung ins Fediverse posten".
|
||||
|
||||
|
||||
Arbeitsmodus
|
||||
------------
|
||||
|
||||
.. drawio::
|
||||
Tiere-in-Vermittlung-entdecken.drawio.html
|
||||
Tiere-in-Vermittlung-entdecken.drawio.png
|
||||
|
||||
Shortcuts
|
||||
---------
|
||||
|
||||
Um die Prüfung schneller zu gestalten, gibt es eine Reihe von Shortcuts die du nutzen kannst. Aus Gründen der
|
||||
Übersichtlichkeit sind im Folgenden auch Shortcuts im Browser aufgeführt.
|
||||
|
||||
+------------------------------------------------------+---------------+
|
||||
| Aktion | Shortcut |
|
||||
+======================================================+===============+
|
||||
| Website der ersten Tierschutzorganisation öffnen | :kbd:`O` |
|
||||
+------------------------------------------------------+---------------+
|
||||
| Tab schließen (Firefox/Chrome) | :kbd:`STRG+W` |
|
||||
+------------------------------------------------------+---------------+
|
||||
| Erste Tierschutzorganisationa als geprüft markieren | :kbd:`C` |
|
||||
+------------------------------------------------------+---------------+
|
||||
BIN
docs/user/screenshot-adoptionsprozess.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/user/screenshot-benachrichtigungen.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
docs/user/screenshot-suche-abonnieren.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/user/screenshot-suche.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -1,7 +1,7 @@
|
||||
Vermittlungen
|
||||
=============
|
||||
|
||||
Vermittlungen können von allen Nutzer*innen mit Account erstellt werden. Vermittlungen normaler Nutzer*innen kommen dann in eine Warteschlange und werden vom Admin & Modertionsteam geprüft und sichtbar geschaltet.
|
||||
Vermittlungen können von allen Nutzer\*innen mit Account erstellt werden. Vermittlungen normaler Nutzer*innen kommen dann in eine Warteschlange und werden vom Admin & Modertionsteam geprüft und sichtbar geschaltet.
|
||||
Tierheime und Pflegestellen können auf Anfrage einen Koordinations-Status bekommen, wodurch sie Vermittlungsanzeigen erstellen können die direkt öffentlich sichtbar sind.
|
||||
|
||||
Jede Vermittlung hat ein "Zuletzt-geprüft" Datum, das anzeigt, wann ein Mensch zuletzt überprüft hat, ob die Anzeige noch aktuell ist.
|
||||
@@ -15,3 +15,114 @@ Die Kommentarfunktion von Vermittlungen ermöglicht es angemeldeten Nutzer*innen
|
||||
Ersteller*innen von Vermittlungen werden über neue Kommentare per Mail benachrichtigt, ebenso alle die die Vermittlung abonniert haben.
|
||||
|
||||
Kommentare können, wie Vermittlungen, gemeldet werden.
|
||||
|
||||
.. drawio::
|
||||
Vermittlung_Lifecycle.drawio.html
|
||||
Vermittlung-Lifecycle.drawio.png
|
||||
:alt: Diagramm das den Prozess der Vermittlungen zeigt.
|
||||
|
||||
|
||||
Adoption Notice Status Choices
|
||||
++++++++++++++++++++++++++++++
|
||||
|
||||
Aktiv
|
||||
-----
|
||||
|
||||
Aktive Vermittlungen die über die Suche auffindbar sind.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:width: 100%
|
||||
:widths: 1 1 2
|
||||
|
||||
* - Value
|
||||
- Label
|
||||
- Description
|
||||
|
||||
* - ``active_searching``
|
||||
- Searching
|
||||
-
|
||||
|
||||
* - ``active_interested``
|
||||
- Interested
|
||||
- Jemand hat bereits Interesse an den Tieren.
|
||||
|
||||
Warte auf Aktion
|
||||
----------------
|
||||
|
||||
Vermittlungen in diesem Status warten darauf, dass ein Mensch sie überprüft. Sie können nicht über die Suche gefunden werden.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:width: 100%
|
||||
:widths: 1 1 2
|
||||
|
||||
* - ``awaiting_action_waiting_for_review``
|
||||
- Waiting for review
|
||||
- Neue Vermittlung die deaktiviert ist bis Moderator*innen sie überprüfen.
|
||||
|
||||
* - ``awaiting_action_needs_additional_info``
|
||||
- Needs additional info
|
||||
- Deaktiviert bis Informationen nachgetragen werden.
|
||||
|
||||
* - ``disabled_unchecked``
|
||||
- Unchecked
|
||||
- Vermittlung deaktiviert bis sie vom Team auf Aktualität geprüft wurde.
|
||||
|
||||
Geschlossen
|
||||
-----------
|
||||
|
||||
Geschlossene Vermittlungen tauchen in keiner Suche auf. Sie werden aber weiterhin angezeigt, wenn der Link zu ihnen direkt aufgerufen wird.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:width: 100%
|
||||
:widths: 1 1 2
|
||||
|
||||
* - ``closed_successful_with_notfellchen``
|
||||
- Successful (with Notfellchen)
|
||||
- Vermittlung erfolgreich abgeschlossen.
|
||||
|
||||
* - ``closed_successful_without_notfellchen``
|
||||
- Successful (without Notfellchen)
|
||||
- Vermittlung erfolgreich abgeschlossen.
|
||||
|
||||
* - ``closed_animal_died``
|
||||
- Animal died
|
||||
- Die zu vermittelnden Tiere sind über die Regenbrücke gegangen.
|
||||
|
||||
* - ``closed_for_other_adoption_notice``
|
||||
- Closed for other adoption notice
|
||||
- Vermittlung wurde zugunsten einer anderen geschlossen.
|
||||
|
||||
* - ``closed_not_open_for_adoption_anymore``
|
||||
- Not open for adoption anymore
|
||||
- Tier(e) stehen nicht mehr zur Vermittlung bereit.
|
||||
|
||||
* - ``closed_link_to_more_info_not_reachable``
|
||||
- Der Link zu weiteren Informationen ist nicht mehr erreichbar.
|
||||
- Der Link zu weiteren Informationen ist nicht mehr erreichbar, die Vermittlung wurde daher automatisch deaktiviert.
|
||||
|
||||
* - ``closed_other``
|
||||
- Other (closed)
|
||||
- Vermittlung geschlossen.
|
||||
|
||||
Deaktiviert
|
||||
-----------
|
||||
|
||||
Deaktivierte Vermittlungen werden nur noch Moderator\*innen und Administrator\*innen angezeigt.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:width: 100%
|
||||
:widths: 1 1 2
|
||||
|
||||
* - ``disabled_against_the_rules``
|
||||
- Against the rules
|
||||
- Vermittlung deaktiviert da sie gegen die Regeln verstößt.
|
||||
|
||||
* - ``disabled_other``
|
||||
- Other (disabled)
|
||||
- Vermittlung deaktiviert.
|
||||
|
||||
|
||||
|
||||
@@ -28,3 +28,6 @@ django_log_level=INFO
|
||||
api_url=https://photon.hyteck.de/api
|
||||
api_format=photon
|
||||
|
||||
[security]
|
||||
totp_issuer="NF Localhost"
|
||||
webauth_allow_insecure_origin=True
|
||||
|
||||
@@ -38,7 +38,8 @@ dependencies = [
|
||||
"celery[redis]",
|
||||
"drf-spectacular[sidecar]",
|
||||
"django-widget-tweaks",
|
||||
"django-super-deduper"
|
||||
"django-super-deduper",
|
||||
"django-allauth[mfa]"
|
||||
]
|
||||
|
||||
dynamic = ["version", "readme"]
|
||||
|
||||
@@ -205,6 +205,8 @@ def main():
|
||||
h = {'Authorization': f'Token {api_token}', "content-type": "application/json"}
|
||||
|
||||
tierheime = overpass_result["features"]
|
||||
stats = {"num_updated_orgs": 0,
|
||||
"num_inserted_orgs": 0}
|
||||
|
||||
for idx, tierheim in enumerate(tqdm(tierheime)):
|
||||
# Check if data is low quality
|
||||
@@ -229,11 +231,13 @@ def main():
|
||||
optional_data = ["email", "phone_number", "website", "description", "fediverse_profile", "facebook",
|
||||
"instagram"]
|
||||
|
||||
# Check if rescue organization exits
|
||||
# Check if rescue organization exists
|
||||
search_data = {"external_source_identifier": "OSM",
|
||||
"external_object_identifier": f"{tierheim["id"]}"}
|
||||
search_result = requests.get(f"{instance}/api/organizations", params=search_data, headers=h)
|
||||
# Rescue organization exits
|
||||
if search_result.status_code == 200:
|
||||
stats["num_updated_orgs"] += 1
|
||||
org_id = search_result.json()[0]["id"]
|
||||
logging.debug(f"{th_data.name} already exists as ID {org_id}.")
|
||||
org_patch_data = {"id": org_id,
|
||||
@@ -248,7 +252,9 @@ def main():
|
||||
if result.status_code != 200:
|
||||
logging.warning(f"Updating {tierheim['properties']['name']} failed:{result.status_code} {result.json()}")
|
||||
continue
|
||||
# Rescue organization does not exist
|
||||
else:
|
||||
stats["num_inserted_orgs"] += 1
|
||||
location = create_location(tierheim, instance, h)
|
||||
org_data = {"name": tierheim["properties"]["name"],
|
||||
"external_object_identifier": f"{tierheim["id"]}",
|
||||
@@ -262,6 +268,7 @@ def main():
|
||||
|
||||
if result.status_code != 201:
|
||||
print(f"{idx} {tierheim["properties"]["name"]}:{result.status_code} {result.json()}")
|
||||
print(f"Upload finished. Inserted {stats['num_inserted_orgs']} new orgs and updated {stats['num_updated_orgs']} orgs.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -7,30 +7,26 @@ from django.utils.html import format_html
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
|
||||
from .models import User, Language, Text, ReportComment, ReportAdoptionNotice, Log, Timestamp, SearchSubscription, \
|
||||
from .models import Language, Text, ReportComment, ReportAdoptionNotice, Log, Timestamp, SearchSubscription, \
|
||||
SpeciesSpecificURL, ImportantLocation, SocialMediaPost
|
||||
|
||||
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions, Notification
|
||||
Comment, Announcement, User, Subscriptions, Notification
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class StatusInline(admin.StackedInline):
|
||||
model = AdoptionNoticeStatus
|
||||
from .tools.model_helpers import AdoptionNoticeStatusChoices
|
||||
|
||||
|
||||
@admin.register(AdoptionNotice)
|
||||
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
||||
search_fields = ("name__icontains", "description__icontains")
|
||||
list_filter = ("owner",)
|
||||
inlines = [
|
||||
StatusInline,
|
||||
]
|
||||
actions = ("activate",)
|
||||
|
||||
def activate(self, request, queryset):
|
||||
for obj in queryset:
|
||||
obj.set_active()
|
||||
obj.adoption_notice_status = AdoptionNoticeStatusChoices.Active.SEARCHING
|
||||
obj.save()
|
||||
|
||||
activate.short_description = _("Ausgewählte Vermittlungen aktivieren")
|
||||
|
||||
@@ -162,10 +158,12 @@ class LocationAdmin(admin.ModelAdmin):
|
||||
ImportantLocationInline,
|
||||
]
|
||||
|
||||
|
||||
@admin.register(SocialMediaPost)
|
||||
class SocialMediaPostAdmin(admin.ModelAdmin):
|
||||
list_filter = ("platform",)
|
||||
|
||||
|
||||
admin.site.register(Animal)
|
||||
admin.site.register(Species)
|
||||
admin.site.register(Rule)
|
||||
@@ -173,7 +171,6 @@ admin.site.register(Image)
|
||||
admin.site.register(ModerationAction)
|
||||
admin.site.register(Language)
|
||||
admin.site.register(Announcement)
|
||||
admin.site.register(AdoptionNoticeStatus)
|
||||
admin.site.register(Subscriptions)
|
||||
admin.site.register(Log)
|
||||
admin.site.register(Timestamp)
|
||||
|
||||
@@ -3,6 +3,21 @@ from rest_framework import serializers
|
||||
import math
|
||||
|
||||
|
||||
class ImageSerializer(serializers.ModelSerializer):
|
||||
width = serializers.SerializerMethodField()
|
||||
height = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = ['id', 'image', 'alt_text', 'width', 'height']
|
||||
|
||||
def get_width(self, obj):
|
||||
return obj.image.width
|
||||
|
||||
def get_height(self, obj):
|
||||
return obj.image.height
|
||||
|
||||
|
||||
class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
location = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Location.objects.all(),
|
||||
@@ -20,17 +35,18 @@ class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
url = serializers.SerializerMethodField()
|
||||
|
||||
photos = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Image.objects.all(),
|
||||
many=True,
|
||||
required=False
|
||||
)
|
||||
photos = ImageSerializer(many=True, read_only=True)
|
||||
|
||||
def get_url(self, obj):
|
||||
return obj.get_full_url()
|
||||
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ['created_at', 'last_checked', "searching_since", "name", "description", "further_information",
|
||||
"group_only", "location", "location_details", "organization", "photos"]
|
||||
"group_only", "location", "location_details", "organization", "photos", "adoption_notice_status",
|
||||
"url"]
|
||||
|
||||
|
||||
class AdoptionNoticeGeoJSONSerializer(serializers.ModelSerializer):
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.urls import path
|
||||
from .views import (
|
||||
AdoptionNoticeApiView,
|
||||
AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView,
|
||||
AdoptionNoticeGeoJSONView, RescueOrgGeoJSONView
|
||||
AdoptionNoticeGeoJSONView, RescueOrgGeoJSONView, AdoptionNoticePerOrgApiView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@@ -14,6 +14,7 @@ urlpatterns = [
|
||||
path("organizations/", RescueOrganizationApiView.as_view(), name="api-organization-list"),
|
||||
path("organizations.geojson", RescueOrgGeoJSONView.as_view(), name="api-organization-list-geojson"),
|
||||
path("organizations/<int:id>/", RescueOrganizationApiView.as_view(), name="api-organization-detail"),
|
||||
path("organizations/<int:id>/adoption-notices", AdoptionNoticePerOrgApiView.as_view(), name="api-organization-adoption-notices"),
|
||||
path("images/", AddImageApiView.as_view(), name="api-add-image"),
|
||||
path("species/", SpeciesApiView.as_view(), name="api-species-list"),
|
||||
path("locations/", LocationApiView.as_view(), name="api-locations-list"),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from django.db.models import Q
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from fellchensammlung.api.serializers import LocationSerializer, AdoptionNoticeGeoJSONSerializer
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from django.db import transaction
|
||||
from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel, Location, AdoptionNoticeStatus
|
||||
from fellchensammlung.models import Log, TrustLevel, Location, AdoptionNoticeStatusChoices
|
||||
from fellchensammlung.tasks import post_adoption_notice_save, post_rescue_org_save
|
||||
from rest_framework import status, serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
@@ -20,7 +21,7 @@ from .serializers import (
|
||||
SpeciesSerializer, RescueOrganizationSerializer,
|
||||
)
|
||||
from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer, OpenApiParameter
|
||||
|
||||
|
||||
class AdoptionNoticeApiView(APIView):
|
||||
@@ -73,9 +74,9 @@ class AdoptionNoticeApiView(APIView):
|
||||
|
||||
# Only set active when user has trust level moderator or higher
|
||||
if request.user_to_notify.trust_level >= TrustLevel.MODERATOR:
|
||||
adoption_notice.set_active()
|
||||
adoption_notice.adoption_notice_status = AdoptionNoticeStatusChoices.Active.SEARCHING
|
||||
else:
|
||||
adoption_notice.set_unchecked()
|
||||
adoption_notice.adoption_notice_status = AdoptionNoticeStatusChoices.AwaitingAction.WAITING_FOR_REVIEW
|
||||
|
||||
# Log the action
|
||||
Log.objects.create(
|
||||
@@ -374,7 +375,7 @@ class LocationApiView(APIView):
|
||||
|
||||
class AdoptionNoticeGeoJSONView(ListAPIView):
|
||||
queryset = AdoptionNotice.objects.select_related('location').filter(location__isnull=False).filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE)
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.Active.values)
|
||||
serializer_class = AdoptionNoticeGeoJSONSerializer
|
||||
renderer_classes = [GeoJSONRenderer]
|
||||
|
||||
@@ -383,3 +384,69 @@ class RescueOrgGeoJSONView(ListAPIView):
|
||||
queryset = RescueOrganization.objects.select_related('location').filter(location__isnull=False)
|
||||
serializer_class = RescueOrgeGeoJSONSerializer
|
||||
renderer_classes = [GeoJSONRenderer]
|
||||
|
||||
|
||||
class AdoptionNoticePerOrgApiView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name='id',
|
||||
required=False,
|
||||
description='ID of the rescue organization from which to retrieve adoption notices.',
|
||||
type=OpenApiTypes.INT
|
||||
),
|
||||
OpenApiParameter(
|
||||
name='in_hierarchy',
|
||||
type=OpenApiTypes.BOOL,
|
||||
required=False,
|
||||
description='Show all Adoption Notices in hierarchy.',
|
||||
),
|
||||
OpenApiParameter(
|
||||
name='status',
|
||||
type=OpenApiTypes.STR,
|
||||
required=False,
|
||||
description='Show all Adoption Notices in a certain status. Comma separated list of values e.g. '
|
||||
'"active,closed"',
|
||||
),
|
||||
],
|
||||
responses={200: AdoptionNoticeSerializer(many=True)}
|
||||
)
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Retrieve adoption notices with their related animals and images.
|
||||
"""
|
||||
org_id = kwargs.get("id")
|
||||
in_hierarchy = request.query_params.get("in_hierarchy")
|
||||
an_status = request.query_params.get("status")
|
||||
try:
|
||||
org = RescueOrganization.objects.get(id=org_id)
|
||||
except RescueOrganization.DoesNotExist:
|
||||
return Response({"error": "Rescue Organization notice not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
if in_hierarchy:
|
||||
adoption_notices = org.adoption_notices_in_hierarchy
|
||||
else:
|
||||
adoption_notices = AdoptionNotice.objects.filter(organization=org)
|
||||
if an_status:
|
||||
status_list = an_status.lower().strip().split(",")
|
||||
temporary_an_storage = []
|
||||
if "active" in status_list:
|
||||
active_ans = [adoption_notice for adoption_notice in adoption_notices if
|
||||
adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Active.values]
|
||||
temporary_an_storage.extend(active_ans)
|
||||
if "closed" in status_list:
|
||||
closed_ans = [adoption_notice for adoption_notice in adoption_notices if
|
||||
adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Closed.values]
|
||||
temporary_an_storage.extend(closed_ans)
|
||||
if "disabled" in status_list:
|
||||
disabled_ans = [adoption_notice for adoption_notice in adoption_notices if
|
||||
adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Disabled.values]
|
||||
temporary_an_storage.extend(disabled_ans)
|
||||
if "awaiting_action" in status_list:
|
||||
awaiting_action_ans = [adoption_notice for adoption_notice in adoption_notices if
|
||||
adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.AwaitingAction.values]
|
||||
temporary_an_storage.extend(awaiting_action_ans)
|
||||
adoption_notices = temporary_an_storage
|
||||
serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
0
src/fellchensammlung/aviews/__init__.py
Normal file
37
src/fellchensammlung/aviews/embeddables.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.views.decorators.clickjacking import xframe_options_exempt
|
||||
|
||||
from fellchensammlung.aviews.helpers import headers
|
||||
from fellchensammlung.models import RescueOrganization, AdoptionNotice, Species
|
||||
|
||||
|
||||
@xframe_options_exempt
|
||||
@headers({"X-Robots-Tag": "noindex"})
|
||||
def list_ans_per_rescue_organization(request, rescue_organization_id, species_slug=None, active=True):
|
||||
expand = request.GET.get("expand")
|
||||
background_color = request.GET.get("background_color")
|
||||
if expand is not None:
|
||||
expand = True
|
||||
else:
|
||||
expand = False
|
||||
org = get_object_or_404(RescueOrganization, pk=rescue_organization_id)
|
||||
|
||||
# Get only active adoption notices or all
|
||||
if active:
|
||||
adoption_notices_of_org = org.adoption_notices_in_hierarchy_divided_by_status[0]
|
||||
else:
|
||||
adoption_notices_of_org = org.adoption_notices
|
||||
|
||||
# Filter for Species if necessary
|
||||
if species_slug is None:
|
||||
adoption_notices = adoption_notices_of_org
|
||||
else:
|
||||
species = get_object_or_404(Species, slug=species_slug)
|
||||
adoption_notices = [adoption_notice for adoption_notice in adoption_notices_of_org if
|
||||
species in adoption_notice.species]
|
||||
|
||||
template = 'fellchensammlung/embeddables/list-adoption-notices.html'
|
||||
return render(request, template,
|
||||
context={"adoption_notices": adoption_notices,
|
||||
"expand": expand,
|
||||
"background_color": background_color})
|
||||
23
src/fellchensammlung/aviews/helpers.py
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
def headers(headers):
|
||||
"""Decorator adding arbitrary HTTP headers to the response.
|
||||
|
||||
This decorator adds HTTP headers specified in the argument (map), to the
|
||||
HTTPResponse returned by the function being decorated.
|
||||
|
||||
Example:
|
||||
|
||||
@headers({'Refresh': '10', 'X-Bender': 'Bite my shiny, metal ass!'})
|
||||
def index(request):
|
||||
....
|
||||
Source: https://djangosnippets.org/snippets/275/
|
||||
"""
|
||||
def headers_wrapper(fun):
|
||||
def wrapped_function(*args, **kwargs):
|
||||
response = fun(*args, **kwargs)
|
||||
for key in headers:
|
||||
response[key] = headers[key]
|
||||
return response
|
||||
return wrapped_function
|
||||
return headers_wrapper
|
||||
|
||||
12
src/fellchensammlung/aviews/urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import embeddables
|
||||
|
||||
urlpatterns = [
|
||||
path("tierschutzorganisationen/<int:rescue_organization_id>/vermittlungen/",
|
||||
embeddables.list_ans_per_rescue_organization,
|
||||
name="list-adoption-notices-for-rescue-organization"),
|
||||
path("tierschutzorganisationen/<int:rescue_organization_id>/vermittlungen/<slug:species_slug>/",
|
||||
embeddables.list_ans_per_rescue_organization,
|
||||
name="list-adoption-notices-for-rescue-organization-species"),
|
||||
]
|
||||
@@ -57,6 +57,14 @@ class AnimalForm(forms.ModelForm):
|
||||
}
|
||||
|
||||
|
||||
class UpdateRescueOrgRegularCheckStatus(forms.ModelForm):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
class Meta:
|
||||
model = RescueOrganization
|
||||
fields = ["regular_check_status"]
|
||||
|
||||
|
||||
class ImageForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'in_flow' in kwargs:
|
||||
@@ -129,6 +137,14 @@ class ModerationActionForm(forms.ModelForm):
|
||||
fields = ('action', 'public_comment', 'private_comment')
|
||||
|
||||
|
||||
class AddedRegistrationForm(forms.Form):
|
||||
captcha = forms.CharField(validators=[animal_validator], label=_("Nenne eine bekannte Tierart"), help_text=_(
|
||||
"Bitte nenne hier eine bekannte Tierart (z.B. ein Tier das an der Leine geführt wird). Das Fragen wir dich um sicherzustellen, dass du kein Roboter bist."))
|
||||
|
||||
def signup(self, request, user):
|
||||
pass
|
||||
|
||||
|
||||
class CustomRegistrationForm(RegistrationForm):
|
||||
class Meta(RegistrationForm.Meta):
|
||||
model = User
|
||||
@@ -153,5 +169,5 @@ class RescueOrgSearchForm(forms.Form):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
location_string = forms.CharField(max_length=100, label=_("Stadt"), required=False)
|
||||
max_distance = forms.ChoiceField(choices=DistanceChoices, initial=DistanceChoices.ONE_HUNDRED,
|
||||
max_distance = forms.ChoiceField(choices=DistanceChoices, initial=DistanceChoices.TWENTY,
|
||||
label=_("Suchradius"))
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Generated by Django 5.2.1 on 2025-08-30 21:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0059_rescueorganization_twenty_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='adoptionnotice',
|
||||
options={'permissions': [('create_active_adoption_notice', 'Can create an active adoption notice')], 'verbose_name': 'Vermittlung', 'verbose_name_plural': 'Vermittlungen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='adoptionnoticestatus',
|
||||
options={'verbose_name': 'Vermittlungsstatus', 'verbose_name_plural': 'Vermittlungsstati'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='animal',
|
||||
options={'verbose_name': 'Tier', 'verbose_name_plural': 'Tiere'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='announcement',
|
||||
options={'verbose_name': 'Banner', 'verbose_name_plural': 'Banner'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='comment',
|
||||
options={'verbose_name': 'Kommentar', 'verbose_name_plural': 'Kommentare'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='image',
|
||||
options={'verbose_name': 'Bild', 'verbose_name_plural': 'Bilder'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='importantlocation',
|
||||
options={'verbose_name': 'Wichtiger Standort', 'verbose_name_plural': 'Wichtige Standorte'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='location',
|
||||
options={'verbose_name': 'Standort', 'verbose_name_plural': 'Standorte'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='moderationaction',
|
||||
options={'verbose_name': 'Moderationsaktion', 'verbose_name_plural': 'Moderationsaktionen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='notification',
|
||||
options={'verbose_name': 'Benachrichtigung', 'verbose_name_plural': 'Benachrichtigungen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='report',
|
||||
options={'verbose_name': 'Meldung', 'verbose_name_plural': 'Meldungen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rescueorganization',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Tierschutzorganisation', 'verbose_name_plural': 'Tierschutzorganisationen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='rule',
|
||||
options={'verbose_name': 'Regel', 'verbose_name_plural': 'Regeln'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='searchsubscription',
|
||||
options={'verbose_name': 'Abonnierte Suche', 'verbose_name_plural': 'Abonnierte Suchen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='speciesspecificurl',
|
||||
options={'verbose_name': 'Tierartspezifische URL', 'verbose_name_plural': 'Tierartspezifische URLs'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='subscriptions',
|
||||
options={'verbose_name': 'Abonnement', 'verbose_name_plural': 'Abonnements'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='timestamp',
|
||||
options={'verbose_name': 'Zeitstempel', 'verbose_name_plural': 'Zeitstempel'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adoptionnotice',
|
||||
name='adoption_notice_status',
|
||||
field=models.TextField(choices=[('active_searching', 'Searching'), ('active_interested', 'Interested'), ('awaiting_action_waiting_for_review', 'Waiting for review'), ('awaiting_action_needs_additional_info', 'Needs additional info'), ('closed_successful_with_notfellchen', 'Successful (with Notfellchen)'), ('closed_successful_without_notfellchen', 'Successful (without Notfellchen)'), ('closed_animal_died', 'Animal died'), ('closed_for_other_adoption_notice', 'Closed for other adoption notice'), ('closed_not_open_for_adoption_anymore', 'Not open for adoption anymore'), ('closed_other', 'Other (closed)'), ('disabled_against_the_rules', 'Against the rules'), ('disabled_unchecked', 'Unchecked'), ('disabled_other', 'Other (disabled)')], default='disabled_other', max_length=64, verbose_name='Status'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,63 @@
|
||||
# Generated by Django 5.2.1 on 2025-08-30 21:51
|
||||
import logging
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def map_status(adoption_notice_status):
|
||||
minor = adoption_notice_status.minor_status
|
||||
|
||||
if minor == "searching":
|
||||
return "active_searching"
|
||||
if minor == "interested":
|
||||
return "active_interested"
|
||||
|
||||
if minor == "waiting_for_review":
|
||||
return "awaiting_action_waiting_for_review"
|
||||
if minor == "needs_additional_info":
|
||||
return "awaiting_action_needs_additional_info"
|
||||
|
||||
if minor == "successful_with_notfellchen":
|
||||
return "closed_successful_with_notfellchen"
|
||||
if minor == "successful_without_notfellchen":
|
||||
return "closed_successful_without_notfellchen"
|
||||
if minor == "animal_died":
|
||||
return "closed_animal_died"
|
||||
if minor == "closed_for_other_adoption_notice":
|
||||
return "closed_for_other_adoption_notice"
|
||||
if minor == "not_open_for_adoption_anymore":
|
||||
return "closed_not_open_for_adoption_anymore"
|
||||
if minor == "other":
|
||||
return "closed_other"
|
||||
|
||||
if minor == "against_the_rules":
|
||||
return "disabled_against_the_rules"
|
||||
if minor == "unchecked":
|
||||
return "disabled_unchecked"
|
||||
if minor in ["missing_information", "technical_error"]:
|
||||
return "disabled_other"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def migrate_status(apps, schema_editor):
|
||||
# We can't import the model directly as it may be a newer
|
||||
# version than this migration expects. We use the historical version.
|
||||
AdoptionNoticeStatus = apps.get_model("fellchensammlung", "AdoptionNoticeStatus")
|
||||
AdoptionNotice = apps.get_model("fellchensammlung", "AdoptionNotice")
|
||||
for ans in AdoptionNoticeStatus.objects.all():
|
||||
adoption_notice = AdoptionNotice.objects.get(id=ans.adoption_notice.id)
|
||||
new_status = map_status(ans)
|
||||
logging.debug(f"{ans.minor_status} -> {new_status}")
|
||||
adoption_notice.adoption_notice_status = map_status(ans)
|
||||
adoption_notice.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('fellchensammlung', '0060_alter_adoptionnotice_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_status),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.2.1 on 2025-08-30 22:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0061_datamigration_status_model_to_field'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='adoptionnotice',
|
||||
name='adoption_notice_status',
|
||||
field=models.TextField(choices=[('active_searching', 'Searching'), ('active_interested', 'Interested'), ('awaiting_action_waiting_for_review', 'Waiting for review'), ('awaiting_action_needs_additional_info', 'Needs additional info'), ('closed_successful_with_notfellchen', 'Successful (with Notfellchen)'), ('closed_successful_without_notfellchen', 'Successful (without Notfellchen)'), ('closed_animal_died', 'Animal died'), ('closed_for_other_adoption_notice', 'Closed for other adoption notice'), ('closed_not_open_for_adoption_anymore', 'Not open for adoption anymore'), ('closed_link_to_more_info_not_reachable', 'Der Link zu weiteren Informationen ist nicht mehr erreichbar.'), ('closed_other', 'Other (closed)'), ('disabled_against_the_rules', 'Against the rules'), ('disabled_unchecked', 'Unchecked'), ('disabled_other', 'Other (disabled)')], max_length=64, verbose_name='Status'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AdoptionNoticeStatus',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-05 14:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0062_alter_adoptionnotice_adoption_notice_status_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='adoptionnotice',
|
||||
name='adoption_process',
|
||||
field=models.TextField(blank=True, choices=[('contact_person_in_an', 'Kontaktiere die Person im Vermittlungstext')], max_length=64, null=True, verbose_name='Adoptionsprozess'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-06 11:11
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0063_adoptionnotice_adoption_process'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='animal',
|
||||
name='name',
|
||||
field=models.CharField(max_length=200, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='animal',
|
||||
name='photos',
|
||||
field=models.ManyToManyField(blank=True, to='fellchensammlung.image', verbose_name='Fotos'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='animal',
|
||||
name='sex',
|
||||
field=models.CharField(choices=[('F', 'Weiblich'), ('M', 'Männlich'), ('M_N', 'Männlich, kastriert'), ('F_N', 'Weiblich, kastriert'), ('I', 'Intergeschlechtlich')], max_length=20, verbose_name='Geschlecht'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='comment',
|
||||
name='adoption_notice',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='alt_text',
|
||||
field=models.TextField(help_text='Beschreibe das Bild für blinde und sehbehinderte Menschen', max_length=2000, verbose_name='Alternativtext'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='city',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Stadt'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='county',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Landkreis'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='housenumber',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Hausnummer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='latitude',
|
||||
field=models.FloatField(verbose_name='Breitengrad'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='longitude',
|
||||
field=models.FloatField(verbose_name='Längengrad'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='postcode',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Postleitzahl'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='street',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Straße'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='user_to_notify',
|
||||
field=models.ForeignKey(help_text='Useraccount der benachrichtigt wird', on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='Empfänger*in'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='report',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='report',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Zuletzt geändert am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rule',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rule',
|
||||
name='language',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fellchensammlung.language', verbose_name='Sprache'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rule',
|
||||
name='rule_identifier',
|
||||
field=models.CharField(help_text='Ein eindeutiger Identifikator der Regel. Ein Regelobjekt derselben Regel in einer anderen Sprache muss den gleichen Identifikator haben', max_length=24, verbose_name='Regel-ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rule',
|
||||
name='rule_text',
|
||||
field=models.TextField(verbose_name='Regeltext'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rule',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Zuletzt geändert am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='searchsubscription',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='searchsubscription',
|
||||
name='sex',
|
||||
field=models.CharField(choices=[('F', 'Weiblich'), ('M', 'Männlich'), ('M_N', 'Männlich, kastriert'), ('F_N', 'Weiblich Kastriert'), ('I', 'Intergeschlechtlich'), ('A', 'Alle')], max_length=20, verbose_name='Geschlecht'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='searchsubscription',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Zuletzt geändert am'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscriptions',
|
||||
name='adoption_notice',
|
||||
field=models.ForeignKey(help_text='Vermittlung die abonniert wurde', on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='text',
|
||||
name='title',
|
||||
field=models.CharField(max_length=100, verbose_name='Titel'),
|
||||
),
|
||||
]
|
||||
18
src/fellchensammlung/migrations/0065_species_slug.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-06 13:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0064_alter_animal_name_alter_animal_photos_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='species',
|
||||
name='slug',
|
||||
field=models.SlugField(null=True, unique=True, verbose_name='Slug'),
|
||||
),
|
||||
]
|
||||
20
src/fellchensammlung/migrations/0066_add_slug_to_species.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-06 13:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_slug(apps, schema_editor):
|
||||
Species = apps.get_model("fellchensammlung", "Species")
|
||||
for species in Species.objects.all():
|
||||
species.slug = f"species-{species.id}"
|
||||
species.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('fellchensammlung', '0065_species_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_slug),
|
||||
]
|
||||
18
src/fellchensammlung/migrations/0067_alter_species_slug.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-06 13:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0066_add_slug_to_species'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='species',
|
||||
name='slug',
|
||||
field=models.SlugField(unique=True, verbose_name='Slug'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.1 on 2025-09-29 15:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0067_alter_species_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='adoptionnotice',
|
||||
name='adoption_notice_status',
|
||||
field=models.TextField(choices=[('active_searching', 'Searching'), ('active_interested', 'Interested'), ('awaiting_action_waiting_for_review', 'Waiting for review'), ('awaiting_action_needs_additional_info', 'Needs additional info'), ('awaiting_action_unchecked', 'Unchecked'), ('closed_successful_with_notfellchen', 'Successful (with Notfellchen)'), ('closed_successful_without_notfellchen', 'Successful (without Notfellchen)'), ('closed_animal_died', 'Animal died'), ('closed_for_other_adoption_notice', 'Closed for other adoption notice'), ('closed_not_open_for_adoption_anymore', 'Not open for adoption anymore'), ('closed_link_to_more_info_not_reachable', 'Der Link zu weiteren Informationen ist nicht mehr erreichbar.'), ('closed_other', 'Other (closed)'), ('disabled_against_the_rules', 'Against the rules'), ('disabled_other', 'Other (disabled)')], max_length=64, verbose_name='Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='image',
|
||||
field=models.ImageField(help_text='Wähle ein Bild aus', upload_to='images', verbose_name='Bild'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-10-20 08:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0068_alter_adoptionnotice_adoption_notice_status_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='rescueorganization',
|
||||
name='regular_check_status',
|
||||
field=models.CharField(choices=[('regular_check', 'Wird regelmäßig geprüft'), ('excluded_no_online_listing', 'Exkludiert: Tiere werden nicht online gelistet'), ('excluded_other_org', 'Exkludiert: Andere Organisation wird geprüft'), ('excluded_scope', 'Exkludiert: Organisation hat nie Notfellchen-relevanten Vermittlungen'), ('excluded_other', 'Exkludiert: Anderer Grund')], default='regular_check', help_text='Organisationen können, durch ändern dieser Einstellung, von der regelmäßigen Prüfung ausgeschlossen werden.', max_length=30, verbose_name='Status der regelmäßigen Prüfung'),
|
||||
),
|
||||
]
|
||||
@@ -1,23 +1,19 @@
|
||||
import uuid
|
||||
from random import choices
|
||||
from tabnanny import verbose
|
||||
|
||||
from django.db import models
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.dispatch import receiver
|
||||
from django.db.models.signals import post_save
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.core.exceptions import ValidationError
|
||||
import base64
|
||||
|
||||
from .tools import misc, geo
|
||||
from notfellchen.settings import MEDIA_URL, base_url
|
||||
from .tools.geo import LocationProxy, Position
|
||||
from .tools.misc import age_as_hr_string, time_since_as_hr_string
|
||||
from .tools.model_helpers import NotificationTypeChoices
|
||||
from .tools.misc import time_since_as_hr_string
|
||||
from .tools.model_helpers import NotificationTypeChoices, AdoptionNoticeStatusChoices, AdoptionProcess, \
|
||||
AdoptionNoticeStatusChoicesDescriptions, RegularCheckStatusChoices
|
||||
from .tools.model_helpers import ndm as NotificationDisplayMapping
|
||||
|
||||
|
||||
@@ -44,14 +40,14 @@ class Language(models.Model):
|
||||
|
||||
class Location(models.Model):
|
||||
place_id = models.CharField(max_length=200) # OSM id
|
||||
latitude = models.FloatField()
|
||||
longitude = models.FloatField()
|
||||
latitude = models.FloatField(verbose_name=_("Breitengrad"))
|
||||
longitude = models.FloatField(verbose_name=_("Längengrad"))
|
||||
name = models.CharField(max_length=2000)
|
||||
city = models.CharField(max_length=200, blank=True, null=True)
|
||||
housenumber = models.CharField(max_length=20, blank=True, null=True)
|
||||
postcode = models.CharField(max_length=20, blank=True, null=True)
|
||||
street = models.CharField(max_length=200, blank=True, null=True)
|
||||
county = models.CharField(max_length=200, blank=True, null=True)
|
||||
city = models.CharField(max_length=200, blank=True, null=True, verbose_name=_('Stadt'))
|
||||
housenumber = models.CharField(max_length=20, blank=True, null=True, verbose_name=_("Hausnummer"))
|
||||
postcode = models.CharField(max_length=20, blank=True, null=True, verbose_name=_("Postleitzahl"))
|
||||
street = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Straße"))
|
||||
county = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Landkreis"))
|
||||
# Country code as per ISO 3166-1 alpha-2
|
||||
# https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
|
||||
countrycode = models.CharField(max_length=2, verbose_name=_("Ländercode"),
|
||||
@@ -135,6 +131,7 @@ class Species(models.Model):
|
||||
"""Model representing a species of animal."""
|
||||
name = models.CharField(max_length=200, help_text=_('Name der Tierart'),
|
||||
verbose_name=_('Name'))
|
||||
slug = models.SlugField(unique=True, verbose_name=_('Slug'))
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@@ -175,6 +172,12 @@ class RescueOrganization(models.Model):
|
||||
exclude_from_check = models.BooleanField(default=False, verbose_name=_('Von Prüfung ausschließen'),
|
||||
help_text=_("Organisation von der manuellen Überprüfung ausschließen, "
|
||||
"z.B. weil Tiere nicht online geführt werden"))
|
||||
regular_check_status = models.CharField(max_length=30, choices=RegularCheckStatusChoices.choices,
|
||||
default=RegularCheckStatusChoices.REGULAR_CHECK,
|
||||
verbose_name=_('Status der regelmäßigen Prüfung'),
|
||||
help_text=_(
|
||||
"Organisationen können, durch ändern dieser Einstellung, von der "
|
||||
"regelmäßigen Prüfung ausgeschlossen werden."))
|
||||
ongoing_communication = models.BooleanField(default=False, verbose_name=_('In aktiver Kommunikation'),
|
||||
help_text=_(
|
||||
"Es findet gerade Kommunikation zwischen Notfellchen und der Organisation statt."))
|
||||
@@ -348,8 +351,9 @@ class User(AbstractUser):
|
||||
|
||||
|
||||
class Image(models.Model):
|
||||
image = models.ImageField(upload_to='images')
|
||||
alt_text = models.TextField(max_length=2000, verbose_name=_('Alternativtext'))
|
||||
image = models.ImageField(upload_to='images', verbose_name=_("Bild"), help_text=_("Wähle ein Bild aus"))
|
||||
alt_text = models.TextField(max_length=2000, verbose_name=_('Alternativtext'),
|
||||
help_text=_("Beschreibe das Bild für blinde und sehbehinderte Menschen"))
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
@@ -365,6 +369,11 @@ class Image(models.Model):
|
||||
def as_html(self):
|
||||
return f'<img src="{MEDIA_URL}/{self.image}" alt="{self.alt_text}">'
|
||||
|
||||
@property
|
||||
def as_base64(self):
|
||||
encoded_string = base64.b64encode(self.image.file.read())
|
||||
return encoded_string.decode("utf-8")
|
||||
|
||||
|
||||
class AdoptionNotice(models.Model):
|
||||
class Meta:
|
||||
@@ -395,6 +404,11 @@ class AdoptionNotice(models.Model):
|
||||
location_string = models.CharField(max_length=200, verbose_name=_("Ortsangabe"))
|
||||
location = models.ForeignKey(Location, blank=True, null=True, on_delete=models.SET_NULL, )
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Creator'))
|
||||
adoption_notice_status = models.TextField(max_length=64, verbose_name=_('Status'),
|
||||
choices=AdoptionNoticeStatusChoices.all_choices())
|
||||
adoption_process = models.TextField(null=True, blank=True,
|
||||
max_length=64, verbose_name=_('Adoptionsprozess'),
|
||||
choices=AdoptionProcess)
|
||||
|
||||
@property
|
||||
def animals(self):
|
||||
@@ -414,6 +428,13 @@ class AdoptionNotice(models.Model):
|
||||
num_per_sex[sex] = self.animals.filter(sex=sex).count()
|
||||
return num_per_sex
|
||||
|
||||
@property
|
||||
def species(self):
|
||||
species = set()
|
||||
for animal in self.animals:
|
||||
species.add(animal.species)
|
||||
return species
|
||||
|
||||
@property
|
||||
def last_checked_hr(self):
|
||||
time_since_last_checked = timezone.now() - self.last_checked
|
||||
@@ -443,12 +464,21 @@ class AdoptionNotice(models.Model):
|
||||
else:
|
||||
return self.location.latitude, self.location.longitude
|
||||
|
||||
@property
|
||||
def description_short(self):
|
||||
def _get_short_description(self, length: int) -> str:
|
||||
if self.description is None:
|
||||
return ""
|
||||
if len(self.description) > 200:
|
||||
return self.description[:200] + f" ... [weiterlesen]({self.get_absolute_url()})"
|
||||
elif len(self.description) > length:
|
||||
return self.description[:length] + f" ... [weiterlesen]({self.get_absolute_url()})"
|
||||
else:
|
||||
return self.description
|
||||
|
||||
@property
|
||||
def description_short(self):
|
||||
return self._get_short_description(200)
|
||||
|
||||
@property
|
||||
def description_100_short(self):
|
||||
return self._get_short_description(90)
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Returns the url to access a detailed page for the adoption notice."""
|
||||
@@ -510,60 +540,34 @@ class AdoptionNotice(models.Model):
|
||||
"""
|
||||
return geo.object_in_distance(self, position, max_distance, unknown_true)
|
||||
|
||||
@staticmethod
|
||||
def _values_of(list_of_enums):
|
||||
return list(map(lambda x: x[0], list_of_enums))
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_active
|
||||
return self.adoption_notice_status in self._values_of(AdoptionNoticeStatusChoices.Active.choices)
|
||||
|
||||
@property
|
||||
def is_disabled(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_disabled
|
||||
return self.adoption_notice_status in self._values_of(AdoptionNoticeStatusChoices.Disabled.choices)
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_closed
|
||||
|
||||
@property
|
||||
def is_interested(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_interested
|
||||
return self.adoption_notice_status in self._values_of(AdoptionNoticeStatusChoices.Closed.choices)
|
||||
|
||||
@property
|
||||
def is_awaiting_action(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_awaiting_action
|
||||
return self.adoption_notice_status in self._values_of(AdoptionNoticeStatusChoices.AwaitingAction.choices)
|
||||
|
||||
@property
|
||||
def is_disabled_unchecked(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
return False
|
||||
return self.adoptionnoticestatus.is_disabled_unchecked
|
||||
|
||||
def set_closed(self, minor_status=None):
|
||||
self.last_checked = timezone.now()
|
||||
self.save()
|
||||
self.adoptionnoticestatus.set_closed(minor_status)
|
||||
|
||||
def set_active(self):
|
||||
self.last_checked = timezone.now()
|
||||
self.save()
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
AdoptionNoticeStatus.create_other(self)
|
||||
self.adoptionnoticestatus.set_active()
|
||||
def status_description(self):
|
||||
return AdoptionNoticeStatusChoicesDescriptions.mapping[self.adoption_notice_status]
|
||||
|
||||
def set_unchecked(self):
|
||||
self.last_checked = timezone.now()
|
||||
self.adoption_notice_status = AdoptionNoticeStatusChoices.AwaitingAction.UNCHECKED
|
||||
self.save()
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
AdoptionNoticeStatus.create_other(self)
|
||||
self.adoptionnoticestatus.set_unchecked()
|
||||
|
||||
for subscription in self.get_subscriptions():
|
||||
notification_title = _("Vermittlung deaktiviert:") + f" {self.name}"
|
||||
@@ -583,124 +587,6 @@ class AdoptionNotice(models.Model):
|
||||
return last_post.created_at
|
||||
|
||||
|
||||
class AdoptionNoticeStatus(models.Model):
|
||||
"""
|
||||
The major status indicates a general state of an adoption notice
|
||||
whereas the minor status is used for reporting
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Vermittlungsstatus')
|
||||
verbose_name_plural = _('Vermittlungsstati')
|
||||
|
||||
ACTIVE = "active"
|
||||
AWAITING_ACTION = "awaiting_action"
|
||||
CLOSED = "closed"
|
||||
DISABLED = "disabled"
|
||||
MAJOR_STATUS_CHOICES = {
|
||||
ACTIVE: "active",
|
||||
AWAITING_ACTION: "in review",
|
||||
CLOSED: "closed",
|
||||
DISABLED: "disabled",
|
||||
}
|
||||
|
||||
MINOR_STATUS_CHOICES = {
|
||||
ACTIVE: {
|
||||
"searching": "searching",
|
||||
"interested": "interested",
|
||||
},
|
||||
AWAITING_ACTION: {
|
||||
"waiting_for_review": "waiting_for_review",
|
||||
"needs_additional_info": "needs_additional_info",
|
||||
},
|
||||
CLOSED: {
|
||||
"successful_with_notfellchen": "successful_with_notfellchen",
|
||||
"successful_without_notfellchen": "successful_without_notfellchen",
|
||||
"animal_died": "animal_died",
|
||||
"closed_for_other_adoption_notice": "closed_for_other_adoption_notice",
|
||||
"not_open_for_adoption_anymore": "not_open_for_adoption_anymore",
|
||||
"other": "other"
|
||||
},
|
||||
DISABLED: {
|
||||
"against_the_rules": "against_the_rules",
|
||||
"missing_information": "missing_information",
|
||||
"technical_error": "technical_error",
|
||||
"unchecked": "unchecked",
|
||||
"other": "other"
|
||||
}
|
||||
}
|
||||
|
||||
major_status = models.CharField(choices=MAJOR_STATUS_CHOICES, max_length=200)
|
||||
minor_choices = {}
|
||||
for key in MINOR_STATUS_CHOICES:
|
||||
minor_choices.update(MINOR_STATUS_CHOICES[key])
|
||||
minor_status = models.CharField(choices=minor_choices, max_length=200)
|
||||
adoption_notice = models.OneToOneField(AdoptionNotice, on_delete=models.CASCADE)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.adoption_notice}: {self.major_status}, {self.minor_status}"
|
||||
|
||||
def as_string(self):
|
||||
return f"{self.major_status}, {self.minor_status}"
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.major_status == self.ACTIVE
|
||||
|
||||
@property
|
||||
def is_disabled(self):
|
||||
return self.major_status == self.DISABLED
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
return self.major_status == self.CLOSED
|
||||
|
||||
@property
|
||||
def is_awaiting_action(self):
|
||||
return self.major_status == self.AWAITING_ACTION
|
||||
|
||||
@property
|
||||
def is_interested(self):
|
||||
return self.major_status == self.ACTIVE and self.minor_status == "interested"
|
||||
|
||||
@property
|
||||
def is_disabled_unchecked(self):
|
||||
return self.major_status == self.DISABLED and self.minor_status == "unchecked"
|
||||
|
||||
@staticmethod
|
||||
def get_minor_choices(major_status):
|
||||
return AdoptionNoticeStatus.MINOR_STATUS_CHOICES[major_status]
|
||||
|
||||
@staticmethod
|
||||
def create_other(an_instance):
|
||||
# Used as empty status to be changed immediately
|
||||
major_status = AdoptionNoticeStatus.DISABLED
|
||||
minor_status = AdoptionNoticeStatus.MINOR_STATUS_CHOICES[AdoptionNoticeStatus.DISABLED]["other"]
|
||||
AdoptionNoticeStatus.objects.create(major_status=major_status,
|
||||
minor_status=minor_status,
|
||||
adoption_notice=an_instance)
|
||||
|
||||
def set_closed(self, minor_status=None):
|
||||
self.major_status = self.MAJOR_STATUS_CHOICES[self.CLOSED]
|
||||
if minor_status is None:
|
||||
self.minor_status = self.MINOR_STATUS_CHOICES[self.CLOSED]["other"]
|
||||
else:
|
||||
self.minor_status = self.MINOR_STATUS_CHOICES[self.CLOSED][minor_status]
|
||||
self.save()
|
||||
|
||||
def set_unchecked(self):
|
||||
self.major_status = self.MAJOR_STATUS_CHOICES[self.DISABLED]
|
||||
self.minor_status = self.MINOR_STATUS_CHOICES[self.DISABLED]["unchecked"]
|
||||
self.save()
|
||||
|
||||
def set_active(self):
|
||||
self.major_status = self.MAJOR_STATUS_CHOICES[self.ACTIVE]
|
||||
self.minor_status = self.MINOR_STATUS_CHOICES[self.ACTIVE]["searching"]
|
||||
self.save()
|
||||
|
||||
|
||||
class SexChoices(models.TextChoices):
|
||||
FEMALE = "F", _("Weiblich")
|
||||
MALE = "M", _("Männlich")
|
||||
@@ -724,13 +610,14 @@ class Animal(models.Model):
|
||||
verbose_name_plural = _('Tiere')
|
||||
|
||||
date_of_birth = models.DateField(verbose_name=_('Geburtsdatum'))
|
||||
name = models.CharField(max_length=200)
|
||||
name = models.CharField(max_length=200, verbose_name=_('Name'))
|
||||
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
|
||||
species = models.ForeignKey(Species, on_delete=models.PROTECT, verbose_name=_("Tierart"))
|
||||
photos = models.ManyToManyField(Image, blank=True)
|
||||
photos = models.ManyToManyField(Image, blank=True, verbose_name=_("Fotos"))
|
||||
sex = models.CharField(
|
||||
max_length=20,
|
||||
choices=SexChoices.choices,
|
||||
verbose_name=_("Geschlecht")
|
||||
)
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE)
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
@@ -795,10 +682,10 @@ class SearchSubscription(models.Model):
|
||||
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
location = models.ForeignKey(Location, on_delete=models.PROTECT, null=True)
|
||||
sex = models.CharField(max_length=20, choices=SexChoicesWithAll.choices)
|
||||
sex = models.CharField(max_length=20, choices=SexChoicesWithAll.choices, verbose_name=_("Geschlecht"))
|
||||
max_distance = models.IntegerField(choices=DistanceChoices.choices, null=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Zuletzt geändert am"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Erstellt am"))
|
||||
|
||||
def __str__(self):
|
||||
if self.location and self.max_distance:
|
||||
@@ -819,12 +706,16 @@ class Rule(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
|
||||
# Markdown is allowed in rule text
|
||||
rule_text = models.TextField()
|
||||
language = models.ForeignKey(Language, on_delete=models.PROTECT)
|
||||
rule_text = models.TextField(verbose_name=_("Regeltext"))
|
||||
language = models.ForeignKey(Language, on_delete=models.PROTECT, verbose_name=_("Sprache"))
|
||||
# Rule identifier allows to translate rules with the same identifier
|
||||
rule_identifier = models.CharField(max_length=24)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
rule_identifier = models.CharField(max_length=24,
|
||||
verbose_name=_("Regel-ID"),
|
||||
help_text=_("Ein eindeutiger Identifikator der Regel. Ein Regelobjekt "
|
||||
"derselben Regel in einer anderen Sprache muss den gleichen "
|
||||
"Identifikator haben"))
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Zuletzt geändert am"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Erstellt am"))
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
@@ -848,8 +739,8 @@ class Report(models.Model):
|
||||
status = models.CharField(max_length=30, choices=STATES)
|
||||
reported_broken_rules = models.ManyToManyField(Rule, verbose_name=_("Regeln gegen die verstoßen wurde"))
|
||||
user_comment = models.TextField(blank=True, verbose_name=_("Kommentar/Zusätzliche Information"))
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Zuletzt geändert am"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Erstellt am"))
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.status}]: {self.user_comment:.20}"
|
||||
@@ -953,7 +844,7 @@ class Text(models.Model):
|
||||
"""
|
||||
Base class to store markdown content
|
||||
"""
|
||||
title = models.CharField(max_length=100)
|
||||
title = models.CharField(max_length=100, verbose_name=_("Titel"))
|
||||
content = models.TextField(verbose_name="Inhalt")
|
||||
language = models.ForeignKey(Language, verbose_name="Sprache", on_delete=models.PROTECT)
|
||||
text_code = models.CharField(max_length=24, verbose_name="Text code", blank=True)
|
||||
@@ -1043,7 +934,7 @@ class Comment(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('AdoptionNotice'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('Vermittlung'))
|
||||
text = models.TextField(verbose_name="Inhalt")
|
||||
reply_to = models.ForeignKey("self", verbose_name="Antwort auf", blank=True, null=True, on_delete=models.CASCADE)
|
||||
|
||||
@@ -1071,7 +962,7 @@ class Notification(models.Model):
|
||||
user_to_notify = models.ForeignKey(User,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Empfänger*in'),
|
||||
help_text=_("Useraccount der Benachrichtigt wird"),
|
||||
help_text=_("Useraccount der benachrichtigt wird"),
|
||||
related_name='user')
|
||||
title = models.CharField(max_length=100, verbose_name=_("Titel"))
|
||||
text = models.TextField(verbose_name="Inhalt")
|
||||
@@ -1113,7 +1004,8 @@ class Subscriptions(models.Model):
|
||||
verbose_name_plural = _("Abonnements")
|
||||
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('AdoptionNotice'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('Vermittlung'),
|
||||
help_text=_("Vermittlung die abonniert wurde"))
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
@@ -1181,7 +1073,7 @@ class SocialMediaPost(models.Model):
|
||||
@staticmethod
|
||||
def get_an_to_post():
|
||||
adoption_notices_without_post = AdoptionNotice.objects.filter(socialmediapost__isnull=True,
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE)
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.Active.values)
|
||||
return adoption_notices_without_post.first()
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -45,6 +45,7 @@ $confirm: hsl(133deg, 100%, calc(41% + 0%));
|
||||
|
||||
p > a {
|
||||
text-decoration: underline;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
p > a.button {
|
||||
@@ -234,7 +235,7 @@ IMAGES
|
||||
|
||||
.thumbnail img {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
height: 70px;
|
||||
object-fit: cover; /* Crops the images */
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -346,3 +347,17 @@ AN Cards
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Embedding Specifics */
|
||||
.embed-main-content {
|
||||
padding: 20px 10px 20px 10px;
|
||||
}
|
||||
|
||||
// FLOATING BUTTON
|
||||
|
||||
.floating {
|
||||
position: fixed;
|
||||
border-radius: 0.3rem;
|
||||
bottom: 4.5rem;
|
||||
right: 1rem;
|
||||
}
|
||||
@@ -1,423 +0,0 @@
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// ------------------------------------------------ functions
|
||||
var show = function (elem) {
|
||||
// Get the natural height of the element
|
||||
var getHeight = function () {
|
||||
elem.style.display = 'block'; // Make it visible
|
||||
var height = elem.scrollHeight + 'px'; // Get its height
|
||||
elem.style.display = ''; // Hide it again
|
||||
return height;
|
||||
};
|
||||
var height = getHeight(); // Get the natural height
|
||||
elem.classList.remove('closed');
|
||||
elem.classList.add('open'); // Make the element visible
|
||||
elem.setAttribute('aria-hidden', 'false');
|
||||
elem.style.height = height; // Update the max-height
|
||||
// Once the transition is complete, remove the inline max-height so the content can scale responsively
|
||||
window.setTimeout(function () {
|
||||
elem.style.height = '';
|
||||
}, 500);
|
||||
};
|
||||
|
||||
var hide = function (elem) {
|
||||
// Give the element a height to change from
|
||||
elem.style.height = elem.scrollHeight + 'px';
|
||||
// Set the height back to 0
|
||||
window.setTimeout(function () {
|
||||
elem.style.height = '0';
|
||||
}, 1);
|
||||
// When the transition is complete, hide it
|
||||
window.setTimeout(function () {
|
||||
elem.classList.remove('open');
|
||||
elem.classList.add('closed');
|
||||
elem.setAttribute('aria-hidden', 'true');
|
||||
}, 500);
|
||||
};
|
||||
|
||||
var toggle = function (elem, timing) {
|
||||
// If the element is visible, hide it
|
||||
if (elem.classList.contains('open')) {
|
||||
hide(elem);
|
||||
return;
|
||||
}
|
||||
// Otherwise, show it
|
||||
show(elem);
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------ build form
|
||||
let orig_form = document.querySelector('form');
|
||||
orig_form.style.display = 'none';
|
||||
|
||||
let an_max = 6;
|
||||
|
||||
let an_fieldset = document.createElement('fieldset');
|
||||
an_fieldset.classList.add('cell');
|
||||
let an_fieldset_legend = document.createElement('legend');
|
||||
an_fieldset_legend.innerHTML = "Allgemeines";
|
||||
an_fieldset.appendChild(an_fieldset_legend);
|
||||
|
||||
let an_name = document.createElement('input');
|
||||
an_name.setAttribute('type', 'text');
|
||||
an_name.setAttribute('name', 'name');
|
||||
an_name.setAttribute('class', 'input');
|
||||
an_name.setAttribute('maxlength', 200);
|
||||
an_name.setAttribute('required', 'required');
|
||||
let an_name_label = document.createElement('label');
|
||||
an_name_label.setAttribute('class', 'label');
|
||||
an_name_label.innerHTML = 'Titel der Vermittlung';
|
||||
an_name_label.appendChild(an_name);
|
||||
|
||||
let an_location_string = document.createElement('input');
|
||||
an_location_string.setAttribute('type', 'text');
|
||||
an_location_string.setAttribute('name', 'location_string');
|
||||
an_location_string.setAttribute('class', 'input');
|
||||
an_location_string.setAttribute('maxlength', 200);
|
||||
an_location_string.setAttribute('required', 'required');
|
||||
let an_location_string_label = document.createElement('label');
|
||||
an_location_string_label.setAttribute('class', 'label');
|
||||
an_location_string_label.innerHTML = 'Ortsangabe';
|
||||
an_location_string_label.appendChild(an_location_string);
|
||||
|
||||
let an_further_information = document.createElement('input');
|
||||
an_further_information.setAttribute('type', 'url');
|
||||
an_further_information.setAttribute('name', 'further_information');
|
||||
an_further_information.setAttribute('class', 'input');
|
||||
an_further_information.setAttribute('maxlength', 200);
|
||||
let an_further_information_label = document.createElement('label');
|
||||
an_further_information_label.setAttribute('class', 'label');
|
||||
an_further_information_label.innerHTML = 'Link zu mehr Informationen';
|
||||
an_further_information_label.appendChild(an_further_information);
|
||||
|
||||
let an_species = document.createElement('select');
|
||||
let an_species_rat = document.createElement('option');
|
||||
an_species_rat.value = 1;
|
||||
an_species_rat.innerHTML = "Farbratte";
|
||||
an_species.appendChild(an_species_rat);
|
||||
an_species.setAttribute('name', 'species');
|
||||
an_species.setAttribute('class', 'input');
|
||||
an_species.setAttribute('required', 'required');
|
||||
let an_species_label = document.createElement('label');
|
||||
an_species_label.setAttribute('class', 'label');
|
||||
an_species_label.innerHTML = 'Tierart';
|
||||
an_species_label.appendChild(an_species);
|
||||
|
||||
let an_number = document.createElement('input');
|
||||
an_number.setAttribute('type', 'number');
|
||||
an_number.setAttribute('name', 'number');
|
||||
an_number.setAttribute('class', 'input');
|
||||
an_number.setAttribute('min', 1);
|
||||
an_number.setAttribute('max', an_max);
|
||||
an_number.setAttribute('required', 'required');
|
||||
let an_number_label = document.createElement('label');
|
||||
an_number_label.setAttribute('class', 'label');
|
||||
an_number_label.innerHTML = 'Anzahl Tiere';
|
||||
an_number_label.appendChild(an_number);
|
||||
|
||||
let an_dateofbirth = document.createElement('input');
|
||||
an_dateofbirth.setAttribute('type', 'date');
|
||||
an_dateofbirth.setAttribute('name', 'dateofbirth');
|
||||
an_dateofbirth.setAttribute('class', 'input');
|
||||
an_dateofbirth.setAttribute('maxlength', 200);
|
||||
an_dateofbirth.setAttribute('required', 'required');
|
||||
let an_dateofbirth_label = document.createElement('label');
|
||||
an_dateofbirth_label.setAttribute('class', 'label');
|
||||
an_dateofbirth_label.innerHTML = 'Geburtsdatum';
|
||||
an_dateofbirth_label.appendChild(an_dateofbirth);
|
||||
|
||||
let an_sex = document.createElement('select');
|
||||
let an_sex_F = document.createElement('option');
|
||||
an_sex_F.value = 'F';
|
||||
an_sex_F.innerHTML = "Weiblich";
|
||||
an_sex.appendChild(an_sex_F);
|
||||
let an_sex_M = document.createElement('option');
|
||||
an_sex_M.value = 'M';
|
||||
an_sex_M.innerHTML = "Männlich";
|
||||
an_sex.appendChild(an_sex_M);
|
||||
let an_sex_F_N = document.createElement('option');
|
||||
an_sex_F_N.value = 'F_N';
|
||||
an_sex_F_N.innerHTML = "Weiblich, kastriert";
|
||||
an_sex.appendChild(an_sex_F_N);
|
||||
let an_sex_M_N = document.createElement('option');
|
||||
an_sex_M_N.value = 'M_N';
|
||||
an_sex_M_N.innerHTML = "Männlich, kastriert";
|
||||
an_sex.appendChild(an_sex_M_N);
|
||||
let an_sex_I = document.createElement('option');
|
||||
an_sex_I.value = 'I';
|
||||
an_sex_I.innerHTML = "Intergeschlechtlich";
|
||||
an_sex.appendChild(an_sex_I);
|
||||
an_sex.setAttribute('name', 'sex');
|
||||
an_sex.setAttribute('class', 'input');
|
||||
an_sex.setAttribute('required', 'required');
|
||||
let an_sex_label = document.createElement('label');
|
||||
an_sex_label.setAttribute('class', 'label');
|
||||
an_sex_label.innerHTML = 'Geschlecht';
|
||||
an_sex_label.appendChild(an_sex);
|
||||
|
||||
let an_searching_since = document.createElement('input');
|
||||
an_searching_since.setAttribute('type', 'date');
|
||||
an_searching_since.setAttribute('name', 'searching_since');
|
||||
an_searching_since.setAttribute('class', 'input');
|
||||
an_searching_since.setAttribute('maxlength', 200);
|
||||
an_searching_since.setAttribute('required', 'required');
|
||||
let an_searching_since_label = document.createElement('label');
|
||||
an_searching_since_label.setAttribute('class', 'label');
|
||||
an_searching_since_label.innerHTML = 'neues Zuhause gesucht seit';
|
||||
an_searching_since_label.appendChild(an_searching_since);
|
||||
|
||||
let an_group_only = document.createElement('select');
|
||||
let an_group_only_yes = document.createElement('option');
|
||||
an_group_only_yes.value = 1;
|
||||
an_group_only_yes.innerHTML = "nur zusammen";
|
||||
let an_group_only_no = document.createElement('option');
|
||||
an_group_only_no.value = 0;
|
||||
an_group_only_no.innerHTML = "auch einzeln";
|
||||
an_group_only.appendChild(an_group_only_yes);
|
||||
an_group_only.appendChild(an_group_only_no);
|
||||
an_group_only.setAttribute('name', 'group_only');
|
||||
an_group_only.setAttribute('class', 'input');
|
||||
an_group_only.setAttribute('required', 'required');
|
||||
let an_group_only_label = document.createElement('label');
|
||||
an_group_only_label.setAttribute('class', 'label');
|
||||
an_group_only_label.innerHTML = 'Gruppenvermittlung';
|
||||
an_group_only_label.appendChild(an_group_only);
|
||||
|
||||
|
||||
let animals = document.createElement('fieldset');
|
||||
animals.classList.add('cell', 'is-col-span-2');
|
||||
let animals_legend = document.createElement('legend');
|
||||
animals_legend.innerHTML = 'Angaben zu den Tieren';
|
||||
animals.appendChild(animals_legend);
|
||||
let noteNumber = document.createElement('p');
|
||||
noteNumber.setAttribute('id', 'noteNumber');
|
||||
noteNumber.innerHTML = 'Bitte Anzahl Tiere angeben';
|
||||
animals.appendChild(noteNumber);
|
||||
|
||||
let an_description = document.createElement('textarea');
|
||||
an_description.setAttribute('name', 'an_description');
|
||||
an_description.classList.add('input', 'textarea');
|
||||
let an_description_label = document.createElement('label');
|
||||
an_description_label.innerHTML = 'Beschreibung der Gruppe';
|
||||
an_description_label.classList.add('label');
|
||||
an_description_label.appendChild(an_description);
|
||||
animals.appendChild(an_group_only_label);
|
||||
animals.appendChild(an_description_label);
|
||||
|
||||
for (let i = 0; i < an_max; i++) {
|
||||
let an_fieldset_$i = document.createElement('fieldset');
|
||||
an_fieldset_$i.classList.add('animal-' + i, 'animal');
|
||||
an_fieldset_$i.appendChild(document.createElement('legend'));
|
||||
an_fieldset_$i.querySelector('legend').innerHTML = 'Tier ' + parseInt(i + 1);
|
||||
let an_name_$i = document.createElement('input');
|
||||
an_name_$i.setAttribute('type', 'text');
|
||||
an_name_$i.setAttribute('name', 'name-' + i);
|
||||
an_name_$i.setAttribute('class', 'input');
|
||||
an_name_$i.setAttribute('maxlength', 200);
|
||||
an_name_$i.setAttribute('required', 'required');
|
||||
let an_name_$i_label = document.createElement('label');
|
||||
an_name_$i_label.setAttribute('class', 'label');
|
||||
an_name_$i_label.innerHTML = 'Name';
|
||||
an_name_$i_label.appendChild(an_name_$i);
|
||||
|
||||
let an_dateofbirth_$i = document.createElement('input');
|
||||
an_dateofbirth_$i.setAttribute('type', 'date');
|
||||
an_dateofbirth_$i.setAttribute('name', 'dateofbirth');
|
||||
an_dateofbirth_$i.setAttribute('class', 'input');
|
||||
an_dateofbirth_$i.setAttribute('maxlength', 200);
|
||||
an_dateofbirth_$i.setAttribute('required', 'required');
|
||||
let an_dateofbirth_$i_label = document.createElement('label');
|
||||
an_dateofbirth_$i_label.setAttribute('class', 'label');
|
||||
an_dateofbirth_$i_label.innerHTML = 'Geburtsdatum';
|
||||
an_dateofbirth_$i_label.appendChild(an_dateofbirth_$i);
|
||||
|
||||
let an_sex_$i = document.createElement('select');
|
||||
let an_sex_F = document.createElement('option');
|
||||
an_sex_F.value = 'F';
|
||||
an_sex_F.innerHTML = "Weiblich";
|
||||
an_sex_$i.appendChild(an_sex_F);
|
||||
let an_sex_M = document.createElement('option');
|
||||
an_sex_M.value = 'M';
|
||||
an_sex_M.innerHTML = "Männlich";
|
||||
an_sex_$i.appendChild(an_sex_M);
|
||||
let an_sex_F_N = document.createElement('option');
|
||||
an_sex_F_N.value = 'F_N';
|
||||
an_sex_F_N.innerHTML = "Weiblich, kastriert";
|
||||
an_sex_$i.appendChild(an_sex_F_N);
|
||||
let an_sex_M_N = document.createElement('option');
|
||||
an_sex_M_N.value = 'M_N';
|
||||
an_sex_M_N.innerHTML = "Männlich, kastriert";
|
||||
an_sex_$i.appendChild(an_sex_M_N);
|
||||
let an_sex_I = document.createElement('option');
|
||||
an_sex_I.value = 'I';
|
||||
an_sex_I.innerHTML = "Intergeschlechtlich";
|
||||
an_sex_$i.appendChild(an_sex_I);
|
||||
an_sex_$i.setAttribute('name', 'sex');
|
||||
an_sex_$i.setAttribute('class', 'input');
|
||||
an_sex_$i.setAttribute('required', 'required');
|
||||
let an_sex_$i_label = document.createElement('label');
|
||||
an_sex_$i_label.setAttribute('class', 'label');
|
||||
an_sex_$i_label.innerHTML = 'Geschlecht';
|
||||
an_sex_$i_label.appendChild(an_sex_$i);
|
||||
|
||||
let an_description_$i = document.createElement('textarea');
|
||||
an_description_$i.setAttribute('name', 'an_description');
|
||||
an_description_$i.classList.add('input', 'textarea');
|
||||
let an_description_$i_label = document.createElement('label');
|
||||
an_description_$i_label.innerHTML = 'Beschreibung';
|
||||
an_description_$i_label.classList.add('label');
|
||||
an_description_$i_label.appendChild(an_description_$i);
|
||||
|
||||
an_fieldset_$i.appendChild(an_description_$i_label);
|
||||
an_fieldset_$i.appendChild(an_name_$i_label);
|
||||
an_fieldset_$i.appendChild(an_dateofbirth_$i_label);
|
||||
an_fieldset_$i.appendChild(an_sex_$i_label);
|
||||
an_fieldset_$i.appendChild(an_description_$i_label);
|
||||
animals.appendChild(an_fieldset_$i);
|
||||
}
|
||||
|
||||
|
||||
an_fieldset.appendChild(an_name_label);
|
||||
an_fieldset.appendChild(an_location_string_label);
|
||||
an_fieldset.appendChild(an_further_information_label);
|
||||
an_fieldset.appendChild(an_species_label);
|
||||
an_fieldset.appendChild(an_number_label);
|
||||
an_fieldset.appendChild(an_dateofbirth_label);
|
||||
an_fieldset.appendChild(an_sex_label);
|
||||
an_fieldset.appendChild(an_searching_since_label);
|
||||
|
||||
let new_form = document.createElement('form');
|
||||
new_form.classList.add('new-animal-ad', 'fixed-grid', 'has-3-cols', 'has-1-cols-mobile');
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('grid');
|
||||
let sButton = document.createElement('button');
|
||||
sButton.classList.add('button');
|
||||
sButton.innerHTML = "Abschicken";
|
||||
|
||||
div.appendChild(an_fieldset);
|
||||
div.appendChild(animals);
|
||||
div.appendChild(sButton);
|
||||
new_form.appendChild(div);
|
||||
document.querySelector('.main-content').appendChild(new_form);
|
||||
|
||||
// ------------------------------------------------ listeners
|
||||
// number of animals
|
||||
let tmpAnimal;
|
||||
an_number.addEventListener('change', function () {
|
||||
if (an_number.value > 0) {
|
||||
hide(noteNumber);
|
||||
} else {
|
||||
show(noteNumber);
|
||||
}
|
||||
if (an_number.value < 2) {
|
||||
hide(an_description_label);
|
||||
hide(an_group_only_label);
|
||||
an_group_only.selectedIndex = 1;
|
||||
} else {
|
||||
show(an_description_label);
|
||||
show(an_group_only_label);
|
||||
an_group_only.selectedIndex = 0;
|
||||
}
|
||||
for (let i = 0; i < an_max; i++) {
|
||||
tmpAnimal = document.querySelector('.animal-' + i);
|
||||
if (i < an_number.value) {
|
||||
tmpAnimal.removeAttribute('disabled');
|
||||
show(tmpAnimal);
|
||||
} else {
|
||||
tmpAnimal.setAttribute('disabled', 'true');
|
||||
hide(tmpAnimal);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// sex
|
||||
an_sex.addEventListener('change', function () {
|
||||
for (let i = 0; i < an_max; i++) {
|
||||
let selList = document.querySelector('.animal-' + i).querySelector('[name="sex"]');
|
||||
for (let j = 0; j < selList.options.length; j++) {
|
||||
if (selList.options[j].value == an_sex.value) {
|
||||
selList.selectedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// date of birth
|
||||
an_dateofbirth.addEventListener('change', function () {
|
||||
for (let i = 0; i < an_max; i++) {
|
||||
document.querySelector('.animal-' + i).querySelector('[name="dateofbirth"]').value = an_dateofbirth.value;
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------ initialise
|
||||
show(noteNumber);
|
||||
hide(an_description_label);
|
||||
hide(an_group_only_label);
|
||||
for (let i = 0; i < an_max; i++) {
|
||||
hide(document.querySelector('.animal-' + i));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------- submit
|
||||
new_form.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
let date = new Date();
|
||||
let postDate = date.toISOString().slice(0, 10);
|
||||
const path = '';
|
||||
|
||||
let elResultsBd = document.createElement('div');
|
||||
elResultsBd.classList.add('feedback-backdrop');
|
||||
let elResults = document.createElement('div');
|
||||
elResults.classList.add('feedback-add-new');
|
||||
elResultsBd.appendChild(elResults);
|
||||
document.querySelector('body').appendChild(elResultsBd);
|
||||
|
||||
let data = JSON.stringify({
|
||||
"created_at": postDate,
|
||||
"searching_since": an_searching_since.value,
|
||||
"name": an_name.value,
|
||||
"description": an_description.value,
|
||||
"further_information": an_further_information.value,
|
||||
"group_only": an_group_only.value,
|
||||
"location_string": an_location_string.value,
|
||||
});
|
||||
|
||||
async function submitAN() {
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
let response = await fetch('http://localhost:8000/api/adoption_notice', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'X-CSRFToken': csrftoken,
|
||||
},
|
||||
body: data,
|
||||
});
|
||||
console.log(response.status);
|
||||
if (response.status === 201) {
|
||||
let result = await response.json();
|
||||
elResults.textContent = result.message + '<br>neue Id: ' + result.id;
|
||||
elResults.classList.add('success');
|
||||
} else {
|
||||
elResults.textContent = 'Fehler! Status Code: ' + response.status;
|
||||
elResults.classList.add('error');
|
||||
}
|
||||
}
|
||||
|
||||
submitAN();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,6 +67,51 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
$el.classList.remove("is-active");
|
||||
});
|
||||
}
|
||||
|
||||
// MODALS //
|
||||
|
||||
function openModal($el) {
|
||||
$el.classList.add('is-active');
|
||||
send("Modal.open", {
|
||||
modal: $el.id
|
||||
});
|
||||
}
|
||||
|
||||
function closeModal($el) {
|
||||
$el.classList.remove('is-active');
|
||||
}
|
||||
|
||||
function closeAllModals() {
|
||||
(document.querySelectorAll('.modal') || []).forEach(($modal) => {
|
||||
closeModal($modal);
|
||||
});
|
||||
}
|
||||
|
||||
// Add a click event on buttons to open a specific modal
|
||||
(document.querySelectorAll('.js-modal-trigger') || []).forEach(($trigger) => {
|
||||
const modal = $trigger.dataset.target;
|
||||
const $target = document.getElementById(modal);
|
||||
|
||||
$trigger.addEventListener('click', () => {
|
||||
openModal($target);
|
||||
});
|
||||
});
|
||||
|
||||
// Add a click event on various child elements to close the parent modal
|
||||
(document.querySelectorAll('.modal-background, .modal-close, .delete, .nf-modal-close') || []).forEach(($close) => {
|
||||
const $target = $close.closest('.modal');
|
||||
|
||||
$close.addEventListener('click', () => {
|
||||
closeModal($target);
|
||||
});
|
||||
});
|
||||
|
||||
// Add a keyboard event to close all modals
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === "Escape") {
|
||||
closeAllModals();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ def task_deactivate_unchecked():
|
||||
@celery_app.task(name="social_media.post_fedi")
|
||||
def task_post_to_fedi():
|
||||
adoption_notice = SocialMediaPost.get_an_to_post()
|
||||
post_an_to_fedi(adoption_notice)
|
||||
if adoption_notice is not None:
|
||||
post_an_to_fedi(adoption_notice)
|
||||
set_timestamp("task_social_media.post_fedi")
|
||||
|
||||
|
||||
|
||||
12
src/fellchensammlung/templates/allauth/elements/badge.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% load allauth %}
|
||||
{% setvar variant %}
|
||||
{% if "primary" in attrs.tags %}
|
||||
is-success
|
||||
{% elif "secondary" in attrs.tags %}
|
||||
is-success is-light
|
||||
{% endif %}
|
||||
{% endsetvar %}
|
||||
<span class="tag{% if variant %} {{ variant }}{% endif %}" {% if attrs.title %}title="{{ attrs.title }}"{% endif %}>
|
||||
{% slot %}
|
||||
{% endslot %}
|
||||
</span>
|
||||
15
src/fellchensammlung/templates/allauth/elements/button.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% load allauth %}
|
||||
{% comment %} djlint:off {% endcomment %}
|
||||
<div class="control">
|
||||
<{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %}
|
||||
class="button is-primary"
|
||||
{% if attrs.form %}form="{{ attrs.form }}"{% endif %}
|
||||
{% if attrs.id %}id="{{ attrs.id }}"{% endif %}
|
||||
{% if attrs.name %}name="{{ attrs.name }}"{% endif %}
|
||||
{% if attrs.value %}value="{{ attrs.value }}"{% endif %}
|
||||
{% if attrs.type %}type="{{ attrs.type }}"{% endif %}
|
||||
>
|
||||
{% slot %}
|
||||
{% endslot %}
|
||||
</{% if attrs.href %}a{% else %}button{% endif %}>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
{% load allauth %}
|
||||
<div class="field is-grouped">
|
||||
{% slot %}
|
||||
{% endslot %}
|
||||
</div>
|
||||
50
src/fellchensammlung/templates/allauth/elements/field.html
Normal file
@@ -0,0 +1,50 @@
|
||||
{% load allauth %}
|
||||
<div class="field">
|
||||
|
||||
{% if attrs.type == "textarea" %}
|
||||
<label class="label" for="{{ attrs.id }}">
|
||||
{% slot label %}
|
||||
{% endslot %}
|
||||
</label>
|
||||
<textarea class="textarea"
|
||||
{% if attrs.required %}required{% endif %}
|
||||
{% if attrs.rows %}rows="{{ attrs.rows }}"{% endif %}
|
||||
{% if attrs.disabled %}disabled{% endif %}
|
||||
{% if attrs.readonly %}readonly{% endif %}
|
||||
{% if attrs.checked %}checked{% endif %}
|
||||
{% if attrs.name %}name="{{ attrs.name }}"{% endif %}
|
||||
{% if attrs.id %}id="{{ attrs.id }}"{% endif %}
|
||||
{% if attrs.placeholder %}placeholder="{{ attrs.placeholder }}"{% endif %}>{% slot value %}{% endslot %}</textarea>
|
||||
{% else %}
|
||||
{% if attrs.type != "checkbox" and attrs.type != "radio" %}
|
||||
<label class="label" for="{{ attrs.id }}">
|
||||
{% slot label %}
|
||||
{% endslot %}
|
||||
</label>
|
||||
{% endif %}
|
||||
<input {% if attrs.type != "checkbox" and attrs.type != "radio" %}class="input"{% endif %}
|
||||
{% if attrs.required %}required{% endif %}
|
||||
{% if attrs.disabled %}disabled{% endif %}
|
||||
{% if attrs.readonly %}readonly{% endif %}
|
||||
{% if attrs.checked %}checked{% endif %}
|
||||
{% if attrs.name %}name="{{ attrs.name }}"{% endif %}
|
||||
{% if attrs.id %}id="{{ attrs.id }}"{% endif %}
|
||||
{% if attrs.placeholder %}placeholder="{{ attrs.placeholder }}"{% endif %}
|
||||
{% if attrs.autocomplete %}autocomplete="{{ attrs.autocomplete }}"{% endif %}
|
||||
{% if attrs.value is not None %}value="{{ attrs.value }}"{% endif %}
|
||||
type="{{ attrs.type }}">
|
||||
{% if attrs.type == "checkbox" or attrs.type == "radio" %}
|
||||
<label for="{{ attrs.id }}">
|
||||
{% slot label %}
|
||||
{% endslot %}
|
||||
</label>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if slots.help_text %}
|
||||
<p class="help is-danger">
|
||||
{% slot help_text %}
|
||||
{% endslot %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="help is-danger">{{ attrs.errors }}</p>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
{{ attrs.form }}
|
||||
12
src/fellchensammlung/templates/allauth/elements/form.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% load allauth %}
|
||||
<div class="block">
|
||||
<form method="{{ attrs.method }}"
|
||||
{% if attrs.action %}action="{{ attrs.action }}"{% endif %}>
|
||||
{% slot body %}
|
||||
{% endslot %}
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
{% slot actions %}
|
||||
{% endslot %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
1
src/fellchensammlung/templates/allauth/elements/h1.html
Normal file
@@ -0,0 +1 @@
|
||||
{% comment %} djlint:off {% endcomment %}{% load allauth %}<h1 class="title is-1">{% slot %}{% endslot %}</h1>
|
||||
1
src/fellchensammlung/templates/allauth/elements/h2.html
Normal file
@@ -0,0 +1 @@
|
||||
{% comment %} djlint:off {% endcomment %}{% load allauth %}<h2 class="title is-2">{% slot %}{% endslot %}</h2>
|
||||
1
src/fellchensammlung/templates/allauth/elements/p.html
Normal file
@@ -0,0 +1 @@
|
||||
{% comment %} djlint:off {% endcomment %}{% load allauth %}<p class="content">{% slot %}{% endslot %}</p>
|
||||
18
src/fellchensammlung/templates/allauth/elements/panel.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% load allauth %}
|
||||
<section class="block">
|
||||
<h2 class="title is-2">
|
||||
{% slot title %}
|
||||
{% endslot %}
|
||||
</h2>
|
||||
{% slot body %}
|
||||
{% endslot %}
|
||||
{% if slots.actions %}
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
{% for action in slots.actions %}
|
||||
<div class="control">
|
||||
{{ action }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
1
src/fellchensammlung/templates/allauth/layouts/base.html
Normal file
@@ -0,0 +1 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
@@ -27,104 +27,106 @@
|
||||
|
||||
{% block content %}
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice-status.html" %}
|
||||
<div class="columns">
|
||||
<div class="column is-two-thirds">
|
||||
<!--- Title level (including action dropdown) -->
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<p class="title is-3 is-size-4-mobile">{{ adoption_notice.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!--- Title level (including action dropdown) -->
|
||||
<div class="block is-flex is-justify-content-space-between">
|
||||
<div class="">
|
||||
<h2 class="title is-3 is-size-4-mobile">{{ adoption_notice.name }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<div class="dropdown is-right">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu4">
|
||||
<span><i class="fas fa-gear" aria-label="{% trans 'Aktionen' %}"></i></span>
|
||||
<span class="icon is-small">
|
||||
<div class="">
|
||||
<div class="dropdown is-right">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu4">
|
||||
<span><i class="fas fa-gear" aria-label="{% trans 'Aktionen' %}"></i></span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<!--- Action menu (dropdown) --->
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
{% if is_subscribed %}
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="unsubscribe">
|
||||
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-bell-slash fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Deabonnieren' %}
|
||||
</button>
|
||||
</div>
|
||||
<!--- Action menu (dropdown) --->
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
{% if is_subscribed %}
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="unsubscribe">
|
||||
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-bell-slash fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Deabonnieren' %}
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="subscribe">
|
||||
|
||||
</form>
|
||||
{% else %}
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="subscribe">
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-bell fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Abonnieren' %}
|
||||
</button>
|
||||
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-bell fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Abonnieren' %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<hr class="dropdown-divider">
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
<hr class="dropdown-divider">
|
||||
|
||||
{% if has_edit_permission %}
|
||||
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="checked_active">
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-check fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Als aktiv bestätigen' %}
|
||||
</button>
|
||||
</form>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-edit' adoption_notice_id=adoption_notice.pk %}">
|
||||
<i class="fas fa-pencil fa-fw"
|
||||
aria-hidden="true"></i> {% translate 'Bearbeiten' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-add-photo' adoption_notice.pk %}">
|
||||
<i class="fas fa-image fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Bilder hinzufügen' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-add-animal' adoption_notice.pk %}">
|
||||
<i class="fas fa-plus fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Tier hinzufügen' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-close' adoption_notice_id=adoption_notice.pk %}">
|
||||
<i class="fas fa-circle-xmark fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Deaktivieren' %}
|
||||
</a>
|
||||
<hr class="dropdown-divider">
|
||||
{% endif %}
|
||||
<a class="dropdown-item" href="{{ adoption_notice.get_report_url }}">
|
||||
<i class="fas fa-flag"
|
||||
aria-hidden="true"></i> {% trans 'Melden' %}
|
||||
</a>
|
||||
{% if request.user.is_superuser %}
|
||||
<hr class="dropdown-divider">
|
||||
<a class="dropdown-item is-warning"
|
||||
href="{% url adoption_notice_meta|admin_urlname:'change' adoption_notice.pk %}">
|
||||
<i class="fa-solid fa-tools fa-fw"></i> Admin interface
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if has_edit_permission %}
|
||||
<form class="dropdown-item" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="checked_active">
|
||||
<button type="submit" id="submit">
|
||||
<i class="fas fa-check fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Als aktiv bestätigen' %}
|
||||
</button>
|
||||
</form>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-edit' adoption_notice_id=adoption_notice.pk %}">
|
||||
<i class="fas fa-pencil fa-fw"
|
||||
aria-hidden="true"></i> {% translate 'Bearbeiten' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-add-photo' adoption_notice.pk %}">
|
||||
<i class="fas fa-image fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Bilder hinzufügen' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-add-animal' adoption_notice.pk %}">
|
||||
<i class="fas fa-plus fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Tier hinzufügen' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-close' adoption_notice_id=adoption_notice.pk %}">
|
||||
<i class="fas fa-circle-xmark fa-fw"
|
||||
aria-hidden="true"></i> {% trans 'Deaktivieren' %}
|
||||
</a>
|
||||
<hr class="dropdown-divider">
|
||||
{% endif %}
|
||||
<a class="dropdown-item" href="{{ adoption_notice.get_report_url }}">
|
||||
<i class="fas fa-flag"
|
||||
aria-hidden="true"></i> {% trans 'Melden' %}
|
||||
</a>
|
||||
{% if request.user.is_superuser %}
|
||||
{% if oxitraffic_base_url %}
|
||||
<hr class="dropdown-divider">
|
||||
<a class="dropdown-item"
|
||||
href="{{ oxitraffic_base_url }}/stats?path={{ adoption_notice.get_absolute_url }}">
|
||||
<i class="fas fa-chart-line fa-fw"></i> {% trans 'Aufrufe' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<hr class="dropdown-divider">
|
||||
<a class="dropdown-item is-warning"
|
||||
href="{% url adoption_notice_meta|admin_urlname:'change' adoption_notice.pk %}">
|
||||
<i class="fa-solid fa-tools fa-fw"></i> Admin interface
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-two-thirds">
|
||||
<!--- General Information --->
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
@@ -157,7 +159,7 @@
|
||||
</div>
|
||||
|
||||
<!--- Images and Description --->
|
||||
<div class="columns">
|
||||
<div class="columns block">
|
||||
<!--- Images --->
|
||||
{% if adoption_notice.get_photos %}
|
||||
<div class="column block">
|
||||
@@ -218,13 +220,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
{% include 'fellchensammlung/partials/adoption_process/base.html' %}
|
||||
</div>
|
||||
<div class="block">
|
||||
{% if adoption_notice.further_information %}
|
||||
<form method="get" action="{% url 'external-site' %}">
|
||||
<input type="hidden" name="url" value="{{ adoption_notice.further_information }}">
|
||||
<button class="button is-primary is-fullwidth" type="submit" id="submit">
|
||||
{{ adoption_notice.further_information | domain }} <i
|
||||
class="fa-solid fa-arrow-up-right-from-square fa-fw"></i>
|
||||
<button class="button is-warning is-fullwidth" type="submit" id="submit">
|
||||
{% translate 'Weitere Informationen' %}: {{ adoption_notice.further_information | domain }}
|
||||
<i
|
||||
class="fa-solid fa-arrow-up-right-from-square fa-fw"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
@@ -238,7 +244,7 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
<div class="block">
|
||||
|
||||
@@ -19,53 +19,10 @@
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="block">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-header-title">{{ org.name }}</h1>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="block">
|
||||
<b><i class="fa-solid fa-location-dot"></i></b>
|
||||
{% if org.location %}
|
||||
{{ org.location }}
|
||||
{% else %}
|
||||
{{ org.location_string }}
|
||||
{% endif %}
|
||||
{% if org.description %}
|
||||
<div class="block content">
|
||||
<p>{{ org.description | render_markdown }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if org.specializations %}
|
||||
<div class="block">
|
||||
<h3 class="title is-5">{% translate 'Spezialisierung' %}</h3>
|
||||
<div class="content">
|
||||
<ul>
|
||||
{% for specialization in org.specializations.all %}
|
||||
<li>{{ specialization }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if org.parent_org %}
|
||||
<div class="block">
|
||||
<h3 class="title is-5">{% translate 'Übergeordnete Organisation' %}</h3>
|
||||
<p>
|
||||
<span>
|
||||
<i class="fa-solid fa-building fa-fw"
|
||||
aria-label="{% trans 'Tierschutzorganisation' %}"></i>
|
||||
<a href="{{ org.parent_org.get_absolute_url }}"> {{ org.parent_org }}</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-basic-info-card.html" %}
|
||||
</div>
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/partials/partial-rescue-organization-contact.html" %}
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-rescue-organization-contact.html" %}
|
||||
</div>
|
||||
<div class="block">
|
||||
<a class="button is-warning is-fullwidth" href="{% url org_meta|admin_urlname:'change' org.pk %}">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load account %}
|
||||
|
||||
{% block title %}<title>{{ user.get_full_name }}</title>{% endblock %}
|
||||
{% block title %}<title>{% user_display user %}</title>{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
@@ -13,7 +14,7 @@
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<form class="" action="{% url 'logout' %}" method="post">
|
||||
<form class="" action="{% url 'account_logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="button" type="submit">
|
||||
<i aria-hidden="true" class="fas fa-sign-out fa-fw"></i> Logout
|
||||
@@ -25,69 +26,87 @@
|
||||
|
||||
<div class="block">
|
||||
<h2 class="title is-2">{% trans 'Profil verwalten' %}</h2>
|
||||
<p><strong>{% translate "E-Mail" %}:</strong> {{ user.email }}</p>
|
||||
<div class="">
|
||||
<p>
|
||||
<a class="button is-warning" href="{% url 'password_change' %}">{% translate "Change password" %}</a>
|
||||
<a class="button is-info" href="{% url 'user-me-export' %}">{% translate "Daten exportieren" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.id is request.user.id %}
|
||||
<div class="block"><strong>{% translate "E-Mail" %}:</strong> {{ user.email }}</div>
|
||||
<div class="block">
|
||||
<h2 class="title is-2">{% trans 'Einstellungen' %}</h2>
|
||||
<form class="block" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
{% if user.email_notifications %}
|
||||
<label class="toggle">
|
||||
<input type="submit" class="toggle-checkbox checked" name="toggle_email_notifications">
|
||||
<div class="toggle-switch round "></div>
|
||||
<span class="slider-label">
|
||||
{% translate 'E-Mail Benachrichtigungen' %}
|
||||
</span>
|
||||
</label>
|
||||
{% else %}
|
||||
<label class="toggle">
|
||||
<input type="submit" class="toggle-checkbox" name="toggle_email_notifications">
|
||||
<div class="toggle-switch round"></div>
|
||||
<span class="slider-label">
|
||||
{% translate 'E-Mail Benachrichtigungen' %}
|
||||
</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</form>
|
||||
<details>
|
||||
<summary><strong>{% trans 'Erweiterte Einstellungen' %}</strong></summary>
|
||||
<div class="block">
|
||||
{% if token %}
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<p class="text-muted"><strong>{% translate "API token:" %}</strong> {{ token }}</p>
|
||||
<input class="button is-danger" type="submit" name="delete_token"
|
||||
value={% translate "Delete API token" %}>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% translate "Kein API-Token vorhanden." %}</p>
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<input class="button is-primary" type="submit" name="create_token"
|
||||
value={% translate "Create API token" %}>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<a class="button is-warning"
|
||||
href="{% url 'account_change_password' %}">{% translate "Passwort ändern" %}</a>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="control">
|
||||
<a class="button is-warning"
|
||||
href="{% url 'account_email' %}">
|
||||
{% translate "E-Mail Adresse ändern" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-warning"
|
||||
href="{% url 'mfa_index' %}">
|
||||
{% translate "2-Faktor Authentifizierung" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-info" href="{% url 'user-me-export' %}">
|
||||
{% translate "Daten exportieren" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="title is-2">{% translate 'Benachrichtigungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-notifications.html" %}
|
||||
{% if user.id is request.user.id %}
|
||||
<div class="block">
|
||||
<h2 class="title is-2">{% trans 'Einstellungen' %}</h2>
|
||||
<form class="block" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
{% if user.email_notifications %}
|
||||
<label class="toggle">
|
||||
<input type="submit" class="toggle-checkbox checked" name="toggle_email_notifications">
|
||||
<div class="toggle-switch round "></div>
|
||||
<span class="slider-label">
|
||||
{% translate 'E-Mail Benachrichtigungen' %}
|
||||
</span>
|
||||
</label>
|
||||
{% else %}
|
||||
<label class="toggle">
|
||||
<input type="submit" class="toggle-checkbox" name="toggle_email_notifications">
|
||||
<div class="toggle-switch round"></div>
|
||||
<span class="slider-label">
|
||||
{% translate 'E-Mail Benachrichtigungen' %}
|
||||
</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</form>
|
||||
<details>
|
||||
<summary><strong>{% trans 'Erweiterte Einstellungen' %}</strong></summary>
|
||||
<div class="block">
|
||||
{% if token %}
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<p class="text-muted"><strong>{% translate "API token:" %}</strong> {{ token }}</p>
|
||||
<input class="button is-danger" type="submit" name="delete_token"
|
||||
value={% translate "Delete API token" %}>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% translate "Kein API-Token vorhanden." %}</p>
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<input class="button is-primary" type="submit" name="create_token"
|
||||
value={% translate "Create API token" %}>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<h2 class="title is-2">{% translate 'Abonnierte Suchen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-search-subscriptions.html" %}
|
||||
</div>
|
||||
|
||||
<h2 class="title is-2">{% translate 'Meine Vermittlungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
<h2 class="title is-2">{% translate 'Benachrichtigungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-notifications.html" %}
|
||||
|
||||
{% endif %}
|
||||
<h2 class="title is-2">{% translate 'Abonnierte Suchen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-search-subscriptions.html" %}
|
||||
|
||||
<h2 class="title is-2">{% translate 'Meine Vermittlungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,52 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{% block title %}{% endblock %}
|
||||
{% block og_title %}{% endblock %}
|
||||
{% block description %}{% endblock %}
|
||||
{% block og_description %}{% endblock %}
|
||||
{% block og_image %}{% endblock %}
|
||||
<link rel="canonical" href="{% block canonical_url %}{% endblock %}">
|
||||
|
||||
<!-- Add additional CSS in static file -->
|
||||
<link rel="stylesheet" href="{% static 'fellchensammlung/css/main.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fellchensammlung/css/photoswipe.css' %}">
|
||||
<link href="{% static 'fontawesomefree/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/brands.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/solid.css' %}" rel="stylesheet" type="text/css">
|
||||
|
||||
{% if background_color %}
|
||||
<style>
|
||||
body {
|
||||
background: #{{ background_color }};
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
|
||||
<script src="{% static 'fellchensammlung/js/custom.js' %}"></script>
|
||||
<script src="{% static 'fellchensammlung/js/toggles.js' %}"></script>
|
||||
<script src="{% static 'fellchensammlung/js/jquery.min.js' %}"></script>
|
||||
<script type="module" src="{% static 'fellchensammlung/js/photoswipe.js' %}"></script>
|
||||
{% block additional_scrips %}{% endblock %}
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'fellchensammlung/favicon/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'fellchensammlung/favicon/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'fellchensammlung/favicon/favicon-16x16.png' %}">
|
||||
{% get_oxitraffic_script_if_enabled %}
|
||||
<base target="_parent"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="embed-main-content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends "fellchensammlung/embeddables/embedding-base.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -2,7 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "403 Forbidden" %}</title>{% endblock %}
|
||||
{% block title %}<title>{% translate "404 Forbidden" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">404 Not Found</h1>
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
{% translate 'Das Notfellchen Projekt' %}
|
||||
</a>
|
||||
<br/>
|
||||
<a href="{% url "contact" %}">
|
||||
{% translate 'Kontakt' %}
|
||||
</a>
|
||||
<br/>
|
||||
<a href="{% url "buying" %}">
|
||||
{% translate 'Ratten kaufen' %}
|
||||
</a>
|
||||
|
||||
@@ -81,6 +81,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-organization">
|
||||
{{ form.organization.label }}
|
||||
{% if form.organization.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
<div class="select">
|
||||
{{ form.organization|attr:"id:an-organization" }}
|
||||
</div>
|
||||
<div class="help">
|
||||
{{ form.organization.help_text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-info">
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
{% load i18n %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
{% blocktrans %}
|
||||
Als {{ user }} kommentieren
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="comment">
|
||||
<div class="field">
|
||||
{{ comment_form.text |add_class:"input textarea"|attr:"rows:3"|attr:"placeholder:Neuer Kommentar" }}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="comment">
|
||||
{{ comment_form }}
|
||||
<input type="submit" class="button is-primary" value="{% trans 'Kommentieren' %}">
|
||||
</form>
|
||||
<div class="control">
|
||||
<input type="submit" class="button is-primary" value="{% trans 'Kommentieren' %}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,35 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load widget_tweaks %}
|
||||
{% load admin_urls %}
|
||||
|
||||
{% block title %}
|
||||
<title>Organisation {{ org }} von regelmäßiger Prüfung ausschließen</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">Organisation {{ org }} von regelmäßiger Prüfung ausschließen</h1>
|
||||
<div class="columns block">
|
||||
<div class="column">
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-basic-info-card.html" %}
|
||||
</div>
|
||||
<div class="column">
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-rescue-organization-contact.html" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
|
||||
<input class="button is-primary" type="submit" name="delete"
|
||||
value="{% translate "Aktualisieren" %}">
|
||||
<a class="button" href="{% url 'organization-check' %}">{% translate "Zurück" %}</a>
|
||||
</form>
|
||||
</div>
|
||||
<div class="block">
|
||||
<a class="button is-warning is-fullwidth" href="{% url org_meta|admin_urlname:'change' org.pk %}">
|
||||
<i class="fa-solid fa-tools fa-fw"></i> Admin interface
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -4,26 +4,84 @@
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
{% blocktranslate %}
|
||||
Lade hier ein Foto hoch - wähle den Titel wie du willst und mach bitte eine Bildbeschreibung,
|
||||
damit die Fotos auch für blinde und sehbehinderte Personen zugänglich sind.
|
||||
{% endblocktranslate %}
|
||||
<p><a class="button" target="_blank"
|
||||
href="https://www.dbsv.org/bildbeschreibung-4-regeln.html">{% translate 'Anleitung für Bildbeschreibungen' %}</a>
|
||||
<div class="block">
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Lade hier ein Foto hoch. Füge bitte eine Bildbeschreibung hinzu,
|
||||
damit die Fotos auch für blinde und sehbehinderte Personen zugänglich sind.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label class="label" for="image">{{ form.image.label }}</label>
|
||||
{{ form.image|add_class:"input"|attr:"id:image" }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="alt-text">{{ form.alt_text.label }}</label>
|
||||
{{ form.alt_text|add_class:"textarea"|attr:"id:alt-text"|attr:"rows:3" }}
|
||||
<div class="is-danger">{{ form.alt_text.errors }}</div>
|
||||
</div>
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
</form>
|
||||
{% endblock %}
|
||||
<div class="block">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="file has-name is-boxed is-primary is-">
|
||||
<label class="file-label" for="image">
|
||||
{{ form.image|add_class:"file-input"|attr:"id:image" }}
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
</span>
|
||||
<span class="file-label">{{ form.image.help_text }}</span>
|
||||
</span>
|
||||
<span class="file-name" id="image-upload-filename">...</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!--- Image Preview: Only shown if image gets selected --->
|
||||
<div class="field" id="image-preview-wrapper" style="display: none;">
|
||||
<label class="label">{% translate "Vorschau" %}</label>
|
||||
<div class="box has-text-centered">
|
||||
<figure class="image is-256x256 is-inline-block">
|
||||
<img id="image-preview" src="" alt="{% translate 'Bildvorschau' %}" class="is-rounded">
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="alt-text">{{ form.alt_text.label }}</label>
|
||||
{{ form.alt_text|add_class:"textarea"|attr:"id:alt-text"|attr:"rows:3" }}
|
||||
<div class="help">
|
||||
{{ form.alt_text.help_text }}
|
||||
</div>
|
||||
<div class="is-danger">{{ form.alt_text.errors }}</div>
|
||||
</div>
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<a class="button is-info is-warning is-fullwidth" target="_blank"
|
||||
href="https://www.dbsv.org/bildbeschreibung-4-regeln.html">
|
||||
<i class="fa-solid fa-link fa-fw"></i> {% translate 'Anleitung für Bildbeschreibungen' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const input = document.getElementById("image");
|
||||
const previewWrapper = document.getElementById("image-preview-wrapper");
|
||||
const preview = document.getElementById("image-preview");
|
||||
const filename = document.getElementById("image-upload-filename");
|
||||
|
||||
input.addEventListener("change", function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
preview.src = e.target.result;
|
||||
previewWrapper.style.display = "block";
|
||||
filename.innerText = file.name;
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
preview.src = "";
|
||||
previewWrapper.style.display = "none";
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{{ field|add_class:"input" }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="help">
|
||||
<div class="help content">
|
||||
{{ field.help_text }}
|
||||
</div>
|
||||
<div class="help is-danger">
|
||||
|
||||
@@ -49,10 +49,10 @@
|
||||
{% else %}
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary" href="{% url "django_registration_register" %}">
|
||||
<a class="button is-primary" href="{% url "account_signup" %}">
|
||||
<strong>{% translate "Registrieren" %}</strong>
|
||||
</a>
|
||||
<a class="button is-light" href="{% url "login" %}">
|
||||
<a class="button is-light" href="{% url "account_login" %}">
|
||||
<strong>{% translate "Login" %}</strong>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="264.58334mm"
|
||||
height="264.58334mm"
|
||||
viewBox="0 0 264.58334 264.58334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="drawing.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.51136354"
|
||||
inkscape:cx="1504.8003"
|
||||
inkscape:cy="472.26675"
|
||||
inkscape:window-width="2048"
|
||||
inkscape:window-height="1208"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:page
|
||||
x="0"
|
||||
y="0"
|
||||
width="264.58334"
|
||||
height="264.58334"
|
||||
id="page1"
|
||||
margin="0"
|
||||
bleed="0"/>
|
||||
<inkscape:page
|
||||
x="274.58334"
|
||||
y="0"
|
||||
width="264.58334"
|
||||
height="264.58334"
|
||||
id="page2"
|
||||
margin="0"
|
||||
bleed="0"/>
|
||||
<inkscape:page
|
||||
x="549.16669"
|
||||
y="0"
|
||||
width="264.58334"
|
||||
height="264.58334"
|
||||
id="page4"
|
||||
margin="0"
|
||||
bleed="0"/>
|
||||
</sodipodi:namedview>
|
||||
<defs id="defs1"/>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#6cd4ff;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill-opacity:0.73333335"
|
||||
id="rect1"
|
||||
width="264.58337"
|
||||
height="264.58337"
|
||||
x="0"
|
||||
y="0"/>
|
||||
<rect
|
||||
style="fill:#6cd4ff;fill-opacity:0.733333;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect1-2"
|
||||
width="264.58337"
|
||||
height="264.58337"
|
||||
x="274.58334"
|
||||
y="0"/>
|
||||
<rect
|
||||
style="fill:#6cd4ff;fill-opacity:0.733333;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect1-0"
|
||||
width="264.58337"
|
||||
height="264.58337"
|
||||
x="549.16669"
|
||||
y="0"/>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:19.7556px;line-height:1.3;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono, Normal';text-align:start;letter-spacing:0px;word-spacing:-0.574146px;writing-mode:lr-tb;direction:ltr;text-anchor:start;white-space:pre;inline-size:230.276;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
x="16.116602"
|
||||
y="81.350471"
|
||||
id="text4"
|
||||
transform="translate(-7.9696277,63.01184)"><tspan
|
||||
x="16.116602"
|
||||
y="81.350471"
|
||||
id="tspan2">{{ adoption_notice.short_description }}</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.17108;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect4"
|
||||
width="192.30475"
|
||||
height="93.450798"
|
||||
x="42.798157"
|
||||
y="14.846257"/>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:19.7556px;line-height:1.1;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono, Normal';text-align:start;letter-spacing:0px;word-spacing:-0.79286px;writing-mode:lr-tb;direction:ltr;text-anchor:start;white-space:pre;inline-size:192.796;fill:#000000;fill-opacity:0.733333;stroke:none;stroke-width:2.76188;stroke-linecap:round;stroke-linejoin:round"
|
||||
x="98.301682"
|
||||
y="49.274101"
|
||||
id="text1"
|
||||
transform="translate(-58.467048,-14.731528)"><tspan
|
||||
x="98.301682"
|
||||
y="49.274101"
|
||||
id="tspan4"><tspan
|
||||
dx="0 0.79285997 -0.79286003 0 0 0 0 0 0.79285902 -0.79285526 0 0.79285902"
|
||||
style="text-align:center;text-anchor:middle"
|
||||
id="tspan3">{{ adoption_notice.name }}</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect4-8"
|
||||
width="175.74771"
|
||||
height="40.675236"
|
||||
x="309.5625"
|
||||
y="17.4625"/>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:36.6612px;line-height:1.3;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono, Normal';text-align:start;letter-spacing:0px;word-spacing:-1.06546px;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:0.733333;stroke:none;stroke-width:3.71147;stroke-linecap:round;stroke-linejoin:round"
|
||||
x="328.02808"
|
||||
y="47.813782"
|
||||
id="text1-7">
|
||||
<tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1-9"
|
||||
style="fill:#000000;fill-opacity:0.733333;stroke-width:3.71147"
|
||||
x="328.02808"
|
||||
y="47.813782">
|
||||
Ratte 1
|
||||
</tspan>
|
||||
</text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect4-7"
|
||||
width="175.74771"
|
||||
height="40.675236"
|
||||
x="597.69373"
|
||||
y="18.520834"/>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:36.6612px;line-height:1.3;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono, Normal';text-align:start;letter-spacing:0px;word-spacing:-1.06546px;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:0.733333;stroke:none;stroke-width:3.71147;stroke-linecap:round;stroke-linejoin:round"
|
||||
x="616.1593"
|
||||
y="48.872116"
|
||||
id="text1-5">
|
||||
<tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1-92"
|
||||
style="fill:#000000;fill-opacity:0.733333;stroke-width:3.71147"
|
||||
x="616.1593"
|
||||
y="48.872116">Ratte 2</tspan>
|
||||
</text>
|
||||
<image
|
||||
width="116.45744"
|
||||
height="145.62663"
|
||||
preserveAspectRatio="none"
|
||||
xlink:href="data:image/jpeg;base64,{{ adoption_notice.get_photo.as_base64 }};"
|
||||
id="image1"
|
||||
x="339.55661"
|
||||
y="87.612373"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
@@ -3,10 +3,23 @@
|
||||
<div class="grid is-col-min-10">
|
||||
{% for adoption_notice in adoption_notices %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}
|
||||
{% if expand %}
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice.html" %}
|
||||
{% else %}
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||
<article class="message is-warning">
|
||||
<div class="message-header">
|
||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
{% blocktranslate %}
|
||||
Versuche es zu einem späteren Zeitpunkt erneut.
|
||||
{% endblocktranslate %}
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% if rescue_organizations %}
|
||||
{% for rescue_organization in rescue_organizations %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-rescue-organization.html" %}
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-rescue-organization.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select id="reason_for_closing" name="reason_for_closing">
|
||||
<option value="successful_with_notfellchen">{% translate 'Vermittelt mit Hilfe von Notfellchen' %}</option>
|
||||
<option value="successful_without_notfellchen">{% translate 'Vermittelt ohne Hilfe von Notfellchen' %}</option>
|
||||
<option value="closed_successful_with_notfellchen">{% translate 'Vermittelt mit Hilfe von Notfellchen' %}</option>
|
||||
<option value="closed_successful_without_notfellchen">{% translate 'Vermittelt ohne Hilfe von Notfellchen' %}</option>
|
||||
<option value="closed_for_other_adoption_notice">{% translate 'Vermittlung zugunsten einer anderen geschlossen' %}</option>
|
||||
<option value="not_open_for_adoption_anymore">{% translate 'Nicht mehr zu vermitteln (z.B. aufgrund von Krankheit)' %}</option>
|
||||
<option value="animal_died">{% translate 'Tod des Tiers' %}</option>
|
||||
<option value="other">{% translate 'Anderer Grund' %}</option>
|
||||
<option value="closed_not_open_for_adoption_anymore">{% translate 'Nicht mehr zu vermitteln (z.B. aufgrund von Krankheit)' %}</option>
|
||||
<option value="closed_animal_died">{% translate 'Tod des Tiers' %}</option>
|
||||
<option value="closed_other">{% translate 'Anderer Grund' %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-header-title">
|
||||
{% translate 'Adoptionsprozess' %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{% include adoption_process_template %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="content">
|
||||
{% translate 'Kontaktiere die Person mit den Kontaktdaten in der Beschreibung.' %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="content">
|
||||
<ol class="adoption-process">
|
||||
{% if adoption_notice.organization %}
|
||||
{% if adoption_notice.further_information %}
|
||||
<li>{% translate 'Link zu "Weiteren Informationen" prüfen' %}</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{% translate 'Organization kontaktieren' %}<br>
|
||||
<a class="button is-info is-small is-fullwidth"
|
||||
href="{{ adoption_notice.organization.get_absolute_url }}">
|
||||
{% translate 'Kontaktdaten' %}
|
||||
</a>
|
||||
</li>
|
||||
<li>{% translate 'Bei erfolgreicher Vermittlung: Vermittlung als geschlossen melden' %}</li>
|
||||
{% else %}
|
||||
{% if adoption_notice.further_information %}
|
||||
<li>{% translate 'Link zu "Weiteren Informationen" prüfen' %}</li>
|
||||
<li>{% translate 'Person, die die Vermittlung eingestellt hat, kontaktieren' %}</li>
|
||||
{% else %}
|
||||
<li>
|
||||
{% translate 'Person, die die Vermittlung eingestellt hat, per Kommentar kontaktieren' %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>{% translate 'Bei erfolgreicher Vermittlung: Vermittlung als geschlossen melden' %}</li>
|
||||
{% endif %}
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{% load i18n %}
|
||||
<div id="modal-shortcuts" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{% trans 'Shortcuts' %}</p>
|
||||
<button class="delete" aria-label="{% trans 'Schließen' %}"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li>{% trans 'Website öffnen' %}: <code>O</code></li>
|
||||
<li>{% trans 'Website schließen' %}: <code>{% trans 'STRG' %} + W</code></li>
|
||||
<li>{% trans 'Organisation als geprüft markieren' %}: <code>{% trans 'STRG' %} + W</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<div class="buttons">
|
||||
<button class="button is-success nf-modal-close">{% translate 'Verstanden' %}</button>
|
||||
<button class="button">{% translate 'Details' %}</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="button is-info floating js-modal-trigger" data-target="modal-shortcuts">
|
||||
<i class="fa-solid fa-keyboard fa-fw"></i> {% trans 'Shortcuts' %}
|
||||
</button>
|
||||
@@ -54,20 +54,27 @@
|
||||
</a>
|
||||
</div>
|
||||
{% endwith %}
|
||||
|
||||
<div class="thumbnail-row minimal">
|
||||
{% for photo in adoption_notice.get_photos|slice:"1:4" %}
|
||||
<div class="thumbnail">
|
||||
<a href="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
data-pswp-width="{{ photo.image.width }}"
|
||||
data-pswp-height="{{ photo.image.height }}"
|
||||
target="_blank">
|
||||
<img src="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
alt="{{ photo.alt_text }}">
|
||||
</a>
|
||||
{% if adoption_notice.get_photos|length > 1 %}
|
||||
<div class="thumbnail-row minimal">
|
||||
{% for photo in adoption_notice.get_photos|slice:"1:4" %}
|
||||
<div class="thumbnail">
|
||||
<a href="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
data-pswp-width="{{ photo.image.width }}"
|
||||
data-pswp-height="{{ photo.image.height }}"
|
||||
target="_blank">
|
||||
<img src="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
alt="{{ photo.alt_text }}">
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% if adoption_notice.description_100_short %}
|
||||
<div class="content">
|
||||
{{ adoption_notice.description_100_short | render_markdown }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% if adoption_notice.description_short %}
|
||||
|
||||
@@ -4,14 +4,10 @@
|
||||
{% if adoption_notice.is_closed %}
|
||||
<article class="message is-warning">
|
||||
<div class="message-header">
|
||||
<p>{% translate 'Vermittlung deaktiviert' %}</p>
|
||||
<p>{% translate 'Vermittlung geschlossen' %}</p>
|
||||
</div>
|
||||
<div class="message-body content">
|
||||
{% blocktranslate %}
|
||||
Diese Vermittlung wurde deaktiviert. Typischerweise passiert das, wenn die Tiere erfolgreich
|
||||
vermittelt wurden.
|
||||
In den Kommentaren findest du ggf. mehr Informationen.
|
||||
{% endblocktranslate %}
|
||||
{{ adoption_notice.status_description }}
|
||||
</div>
|
||||
</article>
|
||||
{% elif adoption_notice.is_interested %}
|
||||
@@ -29,14 +25,10 @@
|
||||
{% elif adoption_notice.is_awaiting_action %}
|
||||
<article class="message is-warning">
|
||||
<div class="message-header">
|
||||
<p>{% translate 'Warten auf Aktivierung' %}</p>
|
||||
<p>{% translate 'Wartet auf Freigabe von Moderator*innen' %}</p>
|
||||
</div>
|
||||
<div class="message-body content">
|
||||
{% blocktranslate %}
|
||||
Diese Vermittlung muss noch durch Moderator*innen aktiviert werden und taucht daher nicht auf der
|
||||
Startseite auf.
|
||||
Ggf. fehlen noch relevante Informationen.
|
||||
{% endblocktranslate %}
|
||||
{{ adoption_notice.status_description }}
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,40 +1,84 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
<div class="card an-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-header-title">
|
||||
<a href="{{ adoption_notice.get_absolute_url }}"> {{ adoption_notice.name }}</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<!--- General Information --->
|
||||
<div class="grid">
|
||||
|
||||
<div class="card">
|
||||
<div>
|
||||
<div class="header-card-adoption-notice">
|
||||
<h1><a class="heading-card-adoption-notice"
|
||||
href="{{ adoption_notice.get_absolute_url }}"> {{ adoption_notice.name }}</a></h1>
|
||||
<a class="adoption-card-report-link" href="{{ adoption_notice.get_report_url }}"><i
|
||||
class="fa-solid fa-flag"></i></a>
|
||||
</div>
|
||||
<p><b>{% translate "Notfellchen" %}:</b> {{ adoption_notice.animals.all|join_link:", " | safe }}
|
||||
</p>
|
||||
<p>
|
||||
<b>Ort</b>
|
||||
{% if adoption_notice.location %}
|
||||
{{ adoption_notice.location.str }}
|
||||
{% else %}
|
||||
{{ adoption_notice.location_string }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="content">
|
||||
{% if adoption_notice.description %}
|
||||
{{ adoption_notice.description | render_markdown }}
|
||||
{% else %}
|
||||
<p>
|
||||
{% translate "Keine Beschreibung" %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if adoption_notice.get_photo %}
|
||||
<div class="adoption-notice-img">
|
||||
<img src="{{ MEDIA_URL }}/{{ adoption_notice.get_photo.image }}"
|
||||
alt="{{ adoption_notice.get_photo.alt_text }}">
|
||||
<div class="cell">
|
||||
|
||||
{% if adoption_notice.organization %}
|
||||
<div class="cell">
|
||||
<span>
|
||||
<i class="fa-solid fa-building fa-fw" aria-label="{% trans 'Tierschutzorganisation' %}"></i>
|
||||
<a href="{{ adoption_notice.organization.get_absolute_url }}"> {{ adoption_notice.organization }}</a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<i class="fa-solid fa-location-dot fa-fw"></i>
|
||||
{% if adoption_notice.location %}
|
||||
{{ adoption_notice.location }}
|
||||
{% else %}
|
||||
{{ adoption_notice.location_string }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/sex-overview.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if adoption_notice.get_photos %}
|
||||
<!--- Images -->
|
||||
<div class="gallery block">
|
||||
{% with photo=adoption_notice.get_photos.0 %}
|
||||
<div class="main-photo minimal">
|
||||
<a href="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
data-pswp-width="{{ photo.image.width }}"
|
||||
data-pswp-height="{{ photo.image.height }}"
|
||||
target="_blank">
|
||||
<img src="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
alt="{{ photo.alt_text }}">
|
||||
</a>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% if adoption_notice.get_photos|length > 1 %}
|
||||
<div class="thumbnail-row minimal">
|
||||
{% for photo in adoption_notice.get_photos|slice:"1:4" %}
|
||||
<div class="thumbnail">
|
||||
<a href="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
data-pswp-width="{{ photo.image.width }}"
|
||||
data-pswp-height="{{ photo.image.height }}"
|
||||
target="_blank">
|
||||
<img src="{{ MEDIA_URL }}{{ photo.image }}"
|
||||
alt="{{ photo.alt_text }}">
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!--- Description -->
|
||||
{% if adoption_notice.description_short %}
|
||||
<div class="content block">
|
||||
{{ adoption_notice.description_short | render_markdown }}
|
||||
</div>
|
||||
{% else %}
|
||||
{% translate "Keine Foto" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@
|
||||
{% else %}
|
||||
<p class="is-italic">{% translate 'Noch keine Kommentare' %}</p>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<hr>
|
||||
{% include "fellchensammlung/forms/form-comment.html" %}
|
||||
{% else %}
|
||||
<div class="card-footer-item">
|
||||
{% translate 'Du musst dich einloggen um Kommentare zu hinterlassen' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
{% if user.is_authenticated %}
|
||||
{% include "fellchensammlung/forms/form-comment.html" %}
|
||||
{% else %}
|
||||
<p class="card-footer-item">
|
||||
{% translate 'Du musst dich einloggen um Kommentare zu hinterlassen' %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if org.website %}
|
||||
<a class="panel-block is-active" href="{{ org.website }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-globe" aria-label="{% translate "Website" %}"></i>
|
||||
</span>
|
||||
{{ org.website }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.phone_number %}
|
||||
<a class="panel-block is-active" href="tel:{{ org.phone_number }}">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-phone" aria-label="{% translate "Telefonnummer" %}"></i>
|
||||
</span>
|
||||
{{ org.phone_number }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.email %}
|
||||
<a class="panel-block is-active" href="mailto:{{ org.email }}">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-envelope" aria-label="{% translate "E-Mail" %}"></i>
|
||||
</span>
|
||||
{{ org.email }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.fediverse_profile %}
|
||||
<a class="panel-block is-active" href="{{ org.fediverse_profile }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-hashtag" aria-label="{% translate "Fediverse Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.fediverse_profile }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.instagram %}
|
||||
<a class="panel-block is-active" href="{{ org.instagram }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fa-brands fa-instagram" aria-label="{% translate "Instagram Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.instagram }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.facebook %}
|
||||
<a class="panel-block is-active" href="{{ org.facebook }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fa-brands fa-facebook" aria-label="{% translate "Facebook Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.facebook }}
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -1,65 +0,0 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class="panel block">
|
||||
<p class="panel-heading">{% trans "Kontaktdaten" %}</p>
|
||||
{% if org.has_contact_data %}
|
||||
{% if org.website %}
|
||||
<a class="panel-block is-active" href="{{ org.website }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-globe" aria-label="{% translate "Website" %}"></i>
|
||||
</span>
|
||||
{{ org.website }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if org.phone_number %}
|
||||
<a class="panel-block is-active" href="tel:{{ org.phone_number }}">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-phone" aria-label="{% translate "Telefonnummer" %}"></i>
|
||||
</span>
|
||||
{{ org.phone_number }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if org.email %}
|
||||
<a class="panel-block is-active" href="mailto:{{ org.email }}">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-envelope" aria-label="{% translate "E-Mail" %}"></i>
|
||||
</span>
|
||||
{{ org.email }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.fediverse_profile %}
|
||||
<a class="panel-block is-active" href="{{ org.fediverse_profile }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-hashtag" aria-label="{% translate "Fediverse Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.fediverse_profile }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if org.instagram %}
|
||||
<a class="panel-block is-active" href="{{ org.instagram }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fa-brands fa-instagram" aria-label="{% translate "Instagram Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.instagram }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if org.facebook %}
|
||||
<a class="panel-block is-active" href="{{ org.facebook }}" target="_blank">
|
||||
<span class="panel-icon">
|
||||
<i class="fa-brands fa-facebook" aria-label="{% translate "Facebook Profil" %}"></i>
|
||||
</span>
|
||||
{{ org.facebook }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="panel-block is-active">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-x" aria-label="{% translate "E-Mail" %}"></i>
|
||||
</span>
|
||||
{% translate 'Keine Kontaktdaten hinterlegt' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-header-title">{{ org.name }}</h1>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="block">
|
||||
<b><i class="fa-solid fa-location-dot"></i></b>
|
||||
{% if org.location %}
|
||||
{{ org.location }}
|
||||
{% else %}
|
||||
{{ org.location_string }}
|
||||
{% endif %}
|
||||
{% if org.description %}
|
||||
<div class="block content">
|
||||
<p>{{ org.description | render_markdown }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if org.specializations %}
|
||||
<div class="block">
|
||||
<h3 class="title is-5">{% translate 'Spezialisierung' %}</h3>
|
||||
<div class="content">
|
||||
<ul>
|
||||
{% for specialization in org.specializations.all %}
|
||||
<li>{{ specialization }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if org.parent_org %}
|
||||
<div class="block">
|
||||
<h3 class="title is-5">{% translate 'Übergeordnete Organisation' %}</h3>
|
||||
<p>
|
||||
<span>
|
||||
<i class="fa-solid fa-building fa-fw"
|
||||
aria-label="{% trans 'Tierschutzorganisation' %}"></i>
|
||||
<a href="{{ org.parent_org.get_absolute_url }}"> {{ org.parent_org }}</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,15 +75,7 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer-item is-danger">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden"
|
||||
name="rescue_organization_id"
|
||||
value="{{ rescue_org.pk }}">
|
||||
<input type="hidden" name="action" value="exclude">
|
||||
<button class="" type="submit">{% translate "Von Check exkludieren" %}</button>
|
||||
|
||||
</form>
|
||||
<a href="{% url 'rescue-organization-exclude' rescue_organization_id=rescue_org.pk %}">{% translate "Von Check exkludieren" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class="panel block">
|
||||
<p class="panel-heading">{% trans "Kontaktdaten" %}</p>
|
||||
{% if org.has_contact_data %}
|
||||
{% include "fellchensammlung/partials/partial-in-panel-contact-data.html" %}
|
||||
{% elif org.parent_org.has_contact_data %}
|
||||
{% with org=org.parent_org %}
|
||||
{% include "fellchensammlung/partials/partial-in-panel-contact-data.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<div class="panel-block is-active">
|
||||
<span class="panel-icon">
|
||||
<i class="fas fa-x" aria-label="{% translate "E-Mail" %}"></i>
|
||||
</span>
|
||||
{% translate 'Keine Kontaktdaten hinterlegt' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-header-title"><a
|
||||
href="{{ rescue_organization.get_absolute_url }}"> {{ rescue_organization.name }}</a></h2>
|
||||
<h2 class="card-header-title">
|
||||
<a href="{{ rescue_organization.get_absolute_url }}"> {{ rescue_organization.name }}
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
<div class="card-content">
|
||||
@@ -16,10 +18,10 @@
|
||||
{{ rescue_organization.location_string }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="block content">
|
||||
{% if rescue_organization.description_short %}
|
||||
{% if rescue_organization.description_short %}
|
||||
<div class="block content">
|
||||
{{ rescue_organization.description_short | render_markdown }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -23,9 +23,11 @@
|
||||
</div>
|
||||
<div class="column">
|
||||
{% if dq %}
|
||||
<a class="button is-info" href="{% url 'organization-check' %}">{% translate 'Datenergänzung deaktivieren' %}</a>
|
||||
<a class="button is-info"
|
||||
href="{% url 'organization-check' %}">{% translate 'Datenergänzung deaktivieren' %}</a>
|
||||
{% else %}
|
||||
<a class="button is-info is-light" href="{% url 'organization-check-dq' %}">{% translate 'Datenergänzung aktivieren' %}</a>
|
||||
<a class="button is-info is-light"
|
||||
href="{% url 'organization-check-dq' %}">{% translate 'Datenergänzung aktivieren' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,7 +38,7 @@
|
||||
<div class="grid is-col-min-15">
|
||||
{% for rescue_org in rescue_orgs_to_check %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-check-rescue-org.html" %}
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-check-rescue-org.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -48,7 +50,7 @@
|
||||
<div class="grid is-col-min-15">
|
||||
{% for rescue_org in rescue_orgs_with_ongoing_communication %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-check-rescue-org.html" %}
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-check-rescue-org.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -60,9 +62,10 @@
|
||||
<div class="grid is-col-min-15">
|
||||
{% for rescue_org in rescue_orgs_last_checked %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-check-rescue-org.html" %}
|
||||
{% include "fellchensammlung/partials/rescue_orgs/partial-check-rescue-org.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "fellchensammlung/partials/modal-shortcuts.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not subscribed_search %}
|
||||
<div class="block">{% translate 'Wenn du die Suche abbonierst, wirst du für neue Vermittlungen, die den Kriterien entsprechen, benachrichtigt.' %}</div>
|
||||
<div class="block">{% translate 'Wenn du die Suche abonnierst, wirst du für neue Vermittlungen, die den Kriterien entsprechen, benachrichtigt.' %}</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="button is-primary is-fullwidth" type="submit" value="search" name="search">
|
||||
|
||||
43
src/fellchensammlung/templates/mfa/recovery_codes/index.html
Normal file
@@ -0,0 +1,43 @@
|
||||
{% extends "mfa/recovery_codes/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load allauth %}
|
||||
{% block content %}
|
||||
{% element h1 %}
|
||||
{% translate "Recovery Codes" %}
|
||||
{% endelement %}
|
||||
{% element p %}
|
||||
{% blocktranslate count unused_count=unused_codes|length %}There is {{ unused_count }} out of {{ total_count }}
|
||||
recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes
|
||||
available.{% endblocktranslate %}
|
||||
{% endelement %}
|
||||
<div class="block">
|
||||
{% element field id="recovery_codes" type="textarea" disabled=True rows=unused_codes|length readonly=True %}
|
||||
{% slot label %}
|
||||
{% translate "Unused codes" %}
|
||||
{% endslot %}
|
||||
{% comment %} djlint:off {% endcomment %}
|
||||
{% slot value %}{% for code in unused_codes %}{% if forloop.counter0 %}
|
||||
{% endif %}{{ code }}{% endfor %}{% endslot %}
|
||||
{% comment %} djlint:on {% endcomment %}
|
||||
{% endelement %}
|
||||
</div>
|
||||
<div class="block">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
{% if unused_codes %}
|
||||
{% url 'mfa_download_recovery_codes' as download_url %}
|
||||
<div class="control">
|
||||
{% element button href=download_url %}
|
||||
{% translate "Download codes" %}
|
||||
{% endelement %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% url 'mfa_generate_recovery_codes' as generate_url %}
|
||||
|
||||
<div class="control">
|
||||
{% element button href=generate_url %}
|
||||
{% translate "Generate new codes" %}
|
||||
{% endelement %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,78 @@
|
||||
{% extends "mfa/webauthn/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load allauth %}
|
||||
{% load humanize %}
|
||||
{% block content %}
|
||||
{% element h1 %}
|
||||
{% trans "Security Keys" %}
|
||||
{% endelement %}
|
||||
{% if authenticators|length == 0 %}
|
||||
{% element p %}
|
||||
{% blocktranslate %}No security keys have been added.{% endblocktranslate %}
|
||||
{% endelement %}
|
||||
{% else %}
|
||||
<article class="panel">
|
||||
<p class="panel-heading">{% trans "Security Keys" %}</p>
|
||||
{% for authenticator in authenticators %}
|
||||
<div class="panel-block">
|
||||
<div class="level" style="width: 100%;">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
{% if authenticator.wrap.is_passwordless is True %}
|
||||
{% element badge tags="mfa,key,primary" %}
|
||||
{% translate "Passkey" %}
|
||||
{% endelement %}
|
||||
{% elif authenticator.wrap.is_passwordless is False %}
|
||||
{% element badge tags="mfa,key,secondary" %}
|
||||
{% translate "Security key" %}
|
||||
{% endelement %}
|
||||
{% else %}
|
||||
{% element badge title=_("This key does not indicate whether it is a passkey.") tags="mfa,key,warning" %}
|
||||
{% translate "Unspecified" %}
|
||||
{% endelement %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<strong>
|
||||
{{ authenticator }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
{% blocktranslate with created_at=authenticator.created_at|date:"SHORT_DATE_FORMAT" %}
|
||||
Added
|
||||
on {{ created_at }}{% endblocktranslate %}.
|
||||
</div>
|
||||
<div class="level-item">
|
||||
{% if authenticator.last_used_at %}
|
||||
{% blocktranslate with last_used=authenticator.last_used_at|naturaltime %}Last used
|
||||
{{ last_used }}{% endblocktranslate %}
|
||||
{% else %}
|
||||
Not used.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
{% url 'mfa_edit_webauthn' pk=authenticator.pk as edit_url %}
|
||||
{% element button tags="mfa,authenticator,edit,tool" href=edit_url %}
|
||||
{% translate "Edit" %}
|
||||
{% endelement %}
|
||||
</div>
|
||||
<div class="level-item">
|
||||
{% url 'mfa_remove_webauthn' pk=authenticator.pk as remove_url %}
|
||||
{% element button tags="mfa,authenticator,danger,delete,tool" href=remove_url %}
|
||||
{% translate "Remove" %}
|
||||
{% endelement %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</article>
|
||||
{% endif %}
|
||||
{% url 'mfa_add_webauthn' as add_url %}
|
||||
{% element button href=add_url %}
|
||||
{% translate "Add" %}
|
||||
{% endelement %}
|
||||
{% endblock %}
|
||||
@@ -49,7 +49,7 @@ def render_markdown(value):
|
||||
@register.simple_tag
|
||||
def get_oxitraffic_script_if_enabled():
|
||||
if settings.OXITRAFFIC_ENABLED:
|
||||
return mark_safe(f'<script type="module" src="https://{settings.OXITRAFFIC_BASE_URL}/count.js"></script>')
|
||||
return mark_safe(f'<script type="module" src="{settings.OXITRAFFIC_BASE_URL}/count.js"></script>')
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import logging
|
||||
|
||||
from random import randint
|
||||
|
||||
from fellchensammlung.tools.model_helpers import AdoptionNoticeStatusChoices
|
||||
from notfellchen import settings
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
@@ -10,7 +12,7 @@ from django.template.loader import render_to_string
|
||||
from django.core import mail
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from fellchensammlung.models import AdoptionNotice, Location, RescueOrganization, AdoptionNoticeStatus, Log, \
|
||||
from fellchensammlung.models import AdoptionNotice, Location, RescueOrganization, Log, \
|
||||
Notification, NotificationTypeChoices
|
||||
from fellchensammlung.tools.misc import is_404
|
||||
|
||||
@@ -86,7 +88,8 @@ def deactivate_404_adoption_notices():
|
||||
for adoption_notice in get_active_adoption_notices():
|
||||
if adoption_notice.further_information and adoption_notice.further_information != "":
|
||||
if is_404(adoption_notice.further_information):
|
||||
adoption_notice.set_closed()
|
||||
adoption_notice.adoption_notice_status = AdoptionNoticeStatusChoices.Closed.LINK_TO_MORE_INFO_NOT_REACHABLE
|
||||
adoption_notice.save()
|
||||
logging_msg = f"Automatically set Adoption Notice {adoption_notice.id} closed as link to more information returened 404"
|
||||
logging.info(logging_msg)
|
||||
Log.objects.create(action="automated", text=logging_msg)
|
||||
|
||||
9
src/fellchensammlung/tools/img.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from fellchensammlung.models import AdoptionNotice
|
||||
|
||||
|
||||
def export_svg(adoption_notice):
|
||||
result = render_to_string(template_name="fellchensammlung/images/adoption-notice.svg",
|
||||
context={"adoption_notice": adoption_notice, })
|
||||
return result
|
||||
@@ -1,4 +1,22 @@
|
||||
from fellchensammlung.models import User, AdoptionNotice, AdoptionNoticeStatus
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from fellchensammlung.models import User, AdoptionNotice, AdoptionNoticeStatusChoices, Animal, RescueOrganization
|
||||
|
||||
|
||||
def get_rescue_org_check_stats():
|
||||
timeframe = timezone.now().date() - timedelta(days=14)
|
||||
num_rescue_orgs_to_check = RescueOrganization.objects.filter(exclude_from_check=False).filter(
|
||||
last_checked__lt=timeframe).count()
|
||||
num_rescue_orgs_checked = RescueOrganization.objects.filter(exclude_from_check=False).filter(
|
||||
last_checked__gte=timeframe).count()
|
||||
|
||||
try:
|
||||
percentage_checked = 100 * num_rescue_orgs_checked / (num_rescue_orgs_to_check + num_rescue_orgs_checked)
|
||||
except ZeroDivisionError:
|
||||
percentage_checked = 100
|
||||
return num_rescue_orgs_to_check, num_rescue_orgs_checked, percentage_checked
|
||||
|
||||
|
||||
def gather_metrics_data():
|
||||
@@ -9,14 +27,14 @@ def gather_metrics_data():
|
||||
"""Adoption notices"""
|
||||
num_adoption_notices = AdoptionNotice.objects.count()
|
||||
adoption_notices_active = AdoptionNotice.objects.filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE)
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.Active.values)
|
||||
num_adoption_notices_active = adoption_notices_active.count()
|
||||
num_adoption_notices_closed = AdoptionNotice.objects.filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.CLOSED).count()
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.Closed.values).count()
|
||||
num_adoption_notices_disabled = AdoptionNotice.objects.filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.DISABLED).count()
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.Disabled.values).count()
|
||||
num_adoption_notices_awaiting_action = AdoptionNotice.objects.filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.AWAITING_ACTION).count()
|
||||
adoption_notice_status__in=AdoptionNoticeStatusChoices.AwaitingAction.values).count()
|
||||
|
||||
adoption_notices_without_location = AdoptionNotice.objects.filter(location__isnull=True).count()
|
||||
|
||||
@@ -32,6 +50,10 @@ def gather_metrics_data():
|
||||
active_animals_per_sex[sex] = number_of_animals
|
||||
active_animals += number_of_animals
|
||||
|
||||
num_animal_shelters = RescueOrganization.objects.all().count()
|
||||
|
||||
num_rescue_orgs_to_check, num_rescue_orgs_checked, percentage_checked = get_rescue_org_check_stats()
|
||||
|
||||
data = {
|
||||
'users': num_user,
|
||||
'staff': num_staff,
|
||||
@@ -45,6 +67,12 @@ def gather_metrics_data():
|
||||
},
|
||||
'adoption_notices_without_location': adoption_notices_without_location,
|
||||
'active_animals': active_animals,
|
||||
'active_animals_per_sex': active_animals_per_sex
|
||||
'active_animals_per_sex': active_animals_per_sex,
|
||||
'rescue_organizations': num_animal_shelters,
|
||||
'rescue_organization_check': {
|
||||
'rescue_orgs_to_check': num_rescue_orgs_to_check,
|
||||
'rescue_orgs_checked': num_rescue_orgs_checked,
|
||||
'percentage_checked': percentage_checked,
|
||||
}
|
||||
}
|
||||
return data
|
||||
|
||||
@@ -17,10 +17,10 @@ def pluralize(number, letter="e"):
|
||||
|
||||
|
||||
def age_as_hr_string(age: datetime.timedelta) -> str:
|
||||
days = age.days
|
||||
weeks = age.days / 7
|
||||
months = age.days / 30
|
||||
years = age.days / 365
|
||||
days = int(age.days)
|
||||
weeks = int(age.days / 7)
|
||||
months = int(age.days / 30)
|
||||
years = int(age.days / 365)
|
||||
if years >= 1:
|
||||
months = months - 12 * years
|
||||
return f'{years:.0f} Jahr{pluralize(years)} und {months:.0f} Monat{pluralize(months)}'
|
||||
@@ -52,9 +52,9 @@ def time_since_as_hr_string(age: datetime.timedelta) -> str:
|
||||
elif weeks >= 3:
|
||||
text = _("vor %(weeks)d Wochen") % {"weeks": weeks}
|
||||
elif days >= 1:
|
||||
text = ngettext("vor einem Tag","vor %(count)d Tagen", days,) % {"count": days,}
|
||||
text = ngettext("vor einem Tag", "vor %(count)d Tagen", days, ) % {"count": days, }
|
||||
elif hours >= 1:
|
||||
text = ngettext("vor einer Stunde", "vor %(count)d Stunden", hours,) % {"count": hours,}
|
||||
text = ngettext("vor einer Stunde", "vor %(count)d Stunden", hours, ) % {"count": hours, }
|
||||
elif minutes >= 1:
|
||||
text = ngettext("vor einer Minute", "vor %(count)d Minuten", minutes, ) % {"count": minutes, }
|
||||
else:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from django.db.models.enums import TextChoices
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
@@ -30,8 +31,8 @@ report_mapping = NotificationDisplayMapping(
|
||||
)
|
||||
# ndm = notification display mapping
|
||||
ndm = {NotificationTypeChoices.NEW_USER: NotificationDisplayMapping(
|
||||
email_html_template='fellchensammlung/mail/notifications/report.html',
|
||||
email_plain_template="fellchensammlung/mail/notifications/report.txt",
|
||||
email_html_template='fellchensammlung/mail/notifications/new-user.html',
|
||||
email_plain_template="fellchensammlung/mail/notifications/new-user.txt",
|
||||
web_partial="fellchensammlung/partials/notifications/body-new-user.html"),
|
||||
NotificationTypeChoices.NEW_COMMENT: NotificationDisplayMapping(
|
||||
email_html_template='fellchensammlung/mail/notifications/new-comment.html',
|
||||
@@ -55,3 +56,81 @@ ndm = {NotificationTypeChoices.NEW_USER: NotificationDisplayMapping(
|
||||
web_partial='fellchensammlung/partials/notifications/body-an-for-search.html'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class AdoptionNoticeStatusChoices:
|
||||
class Active(TextChoices):
|
||||
SEARCHING = "active_searching", _("Searching")
|
||||
INTERESTED = "active_interested", _("Interested")
|
||||
|
||||
class AwaitingAction(TextChoices):
|
||||
WAITING_FOR_REVIEW = "awaiting_action_waiting_for_review", _("Waiting for review")
|
||||
NEEDS_ADDITIONAL_INFO = "awaiting_action_needs_additional_info", _("Needs additional info")
|
||||
UNCHECKED = "awaiting_action_unchecked", _("Unchecked")
|
||||
|
||||
class Closed(TextChoices):
|
||||
SUCCESSFUL_WITH_NOTFELLCHEN = "closed_successful_with_notfellchen", _("Successful (with Notfellchen)")
|
||||
SUCCESSFUL_WITHOUT_NOTFELLCHEN = "closed_successful_without_notfellchen", _("Successful (without Notfellchen)")
|
||||
ANIMAL_DIED = "closed_animal_died", _("Animal died")
|
||||
FOR_OTHER_ADOPTION_NOTICE = "closed_for_other_adoption_notice", _("Closed for other adoption notice")
|
||||
NOT_OPEN_ANYMORE = "closed_not_open_for_adoption_anymore", _("Not open for adoption anymore")
|
||||
LINK_TO_MORE_INFO_NOT_REACHABLE = "closed_link_to_more_info_not_reachable", _(
|
||||
"Der Link zu weiteren Informationen ist nicht mehr erreichbar.")
|
||||
OTHER = "closed_other", _("Other (closed)")
|
||||
|
||||
class Disabled(TextChoices):
|
||||
AGAINST_RULES = "disabled_against_the_rules", _("Against the rules")
|
||||
OTHER = "disabled_other", _("Other (disabled)")
|
||||
|
||||
@classmethod
|
||||
def all_choices(cls):
|
||||
"""Return all subgroup choices as a single list for use in models."""
|
||||
return (
|
||||
cls.Active.choices
|
||||
+ cls.AwaitingAction.choices
|
||||
+ cls.Closed.choices
|
||||
+ cls.Disabled.choices
|
||||
)
|
||||
|
||||
|
||||
class AdoptionNoticeStatusChoicesDescriptions:
|
||||
_ansc = AdoptionNoticeStatusChoices # Mapping for readability
|
||||
mapping = {_ansc.Active.SEARCHING.value: "",
|
||||
_ansc.Active.INTERESTED: _("Jemand hat bereits Interesse an den Tieren."),
|
||||
_ansc.Closed.SUCCESSFUL_WITH_NOTFELLCHEN: _("Vermittlung erfolgreich abgeschlossen."),
|
||||
_ansc.Closed.SUCCESSFUL_WITHOUT_NOTFELLCHEN: _("Vermittlung erfolgreich abgeschlossen."),
|
||||
_ansc.Closed.ANIMAL_DIED: _("Die zu vermittelnden Tiere sind über die Regenbrücke gegangen."),
|
||||
_ansc.Closed.FOR_OTHER_ADOPTION_NOTICE: _("Vermittlung wurde zugunsten einer anderen geschlossen."),
|
||||
_ansc.Closed.NOT_OPEN_ANYMORE: _("Tier(e) stehen nicht mehr zur Vermittlung bereit."),
|
||||
_ansc.Closed.LINK_TO_MORE_INFO_NOT_REACHABLE: _(
|
||||
"Der Link zu weiteren Informationen ist nicht mehr erreichbar,"
|
||||
"die Vermittlung wurde daher automatisch deaktiviert"),
|
||||
_ansc.Closed.OTHER: _("Vermittlung geschlossen."),
|
||||
|
||||
_ansc.AwaitingAction.WAITING_FOR_REVIEW: _(
|
||||
"Deaktiviert bis Moderator*innen die Vermittlung prüfen können."),
|
||||
_ansc.AwaitingAction.NEEDS_ADDITIONAL_INFO: _("Deaktiviert bis Informationen nachgetragen werden."),
|
||||
_ansc.AwaitingAction.UNCHECKED: _(
|
||||
"Vermittlung deaktiviert bis sie vom Team auf Aktualität geprüft wurde."),
|
||||
|
||||
_ansc.Disabled.AGAINST_RULES: _("Vermittlung deaktiviert da sie gegen die Regeln verstößt."),
|
||||
_ansc.Disabled.OTHER: _("Vermittlung deaktiviert.")
|
||||
}
|
||||
|
||||
|
||||
class AdoptionProcess(TextChoices):
|
||||
CONTACT_PERSON_IN_AN = "contact_person_in_an", _("Kontaktiere die Person im Vermittlungstext")
|
||||
|
||||
|
||||
class AdoptionNoticeProcessTemplates:
|
||||
_bp = "fellchensammlung/partials/adoption_process/" # Base path for ease
|
||||
mapping = {AdoptionProcess.CONTACT_PERSON_IN_AN: f"{_bp}contact_person_in_an.html",
|
||||
}
|
||||
|
||||
|
||||
class RegularCheckStatusChoices(models.TextChoices):
|
||||
REGULAR_CHECK = "regular_check", _("Wird regelmäßig geprüft")
|
||||
EXCLUDED_NO_ONLINE_LISTING = "excluded_no_online_listing", _("Exkludiert: Tiere werden nicht online gelistet")
|
||||
EXCLUDED_OTHER_ORG = "excluded_other_org", _("Exkludiert: Andere Organisation wird geprüft")
|
||||
EXCLUDED_SCOPE = "excluded_scope", _("Exkludiert: Organisation hat nie Notfellchen-relevanten Vermittlungen")
|
||||
EXCLUDED_OTHER = "excluded_other", _("Exkludiert: Anderer Grund")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from fellchensammlung.models import User, Notification, TrustLevel
|
||||
from fellchensammlung.models import User, Notification, TrustLevel, AdoptionNoticeStatusChoices
|
||||
from fellchensammlung.models import NotificationTypeChoices as ntc
|
||||
|
||||
|
||||
def notify_of_AN_to_be_checked(adoption_notice):
|
||||
if adoption_notice.is_disabled_unchecked:
|
||||
if adoption_notice.adoption_notice_status == AdoptionNoticeStatusChoices.AwaitingAction.WAITING_FOR_REVIEW:
|
||||
users_to_notify = set(User.objects.filter(trust_level__gt=TrustLevel.MODERATOR))
|
||||
users_to_notify.add(adoption_notice.owner)
|
||||
for user in users_to_notify:
|
||||
|
||||