Compare commits
282 Commits
88987a973e
...
20cbb0397a
Author | SHA1 | Date | |
---|---|---|---|
20cbb0397a | |||
26f999c4cf | |||
9858dfc1bd | |||
2d1df879dc | |||
2f12dc6a5e | |||
4b3286f12d | |||
d53c5707b8 | |||
ba64661217 | |||
554de17e9e | |||
5bc4b538e6 | |||
8a691d59e7 | |||
e99798ba5c | |||
caa962700b | |||
648466aa70 | |||
25f84bf2ad | |||
ac2147095a | |||
4856c720b1 | |||
ba8ff743f2 | |||
1e827af2dd | |||
7d107abc6a | |||
3bb1cd29cd | |||
0388367c7a | |||
a8843dfc8f | |||
9d1264b6e6 | |||
8df04519b9 | |||
112a731cd6 | |||
8b4ff83921 | |||
2249b615f4 | |||
fbfc800453 | |||
752aaf9b89 | |||
f1a0d5f475 | |||
fb5f38b3e6 | |||
ded5299387 | |||
090548905f | |||
12a89b6927 | |||
fc4c348ff9 | |||
1cd133e335 | |||
b5dc6ca97d | |||
f7b98c9dfe | |||
8e9f4e2b2e | |||
b3faa06c4c | |||
165aeb6dcc | |||
24bae28cec | |||
65182d4c2f | |||
1d879993c9 | |||
f3fbf3ba1d | |||
5a24f32327 | |||
d72bd22f6d | |||
0eb94038f6 | |||
3a69397c0a | |||
d49cb5a783 | |||
c13805dd75 | |||
bcf8a0e6e4 | |||
81f9398da4 | |||
32c90aecd3 | |||
01d6c1e0f6 | |||
607a442e22 | |||
b2a79b3547 | |||
63c692d46b | |||
3d2ef9e735 | |||
aee8b0c1e8 | |||
d5e28ba3d9 | |||
1edaf4df14 | |||
b6d31e3c3b | |||
ed7b55c090 | |||
e66e9ad888 | |||
4ad8b30e04 | |||
ae2ac5c462 | |||
2330542a85 | |||
7363e1ab30 | |||
d2131b2c91 | |||
78866c86cd | |||
f5dbccb9c4 | |||
8ab38cc71b | |||
7dfcbfe38f | |||
cd8471036c | |||
c3ec477a6e | |||
5a6294adf6 | |||
1ba44cdd67 | |||
dfeb88f980 | |||
44a724809f | |||
1acd4be953 | |||
930a5383c4 | |||
eda6da7f12 | |||
448fc395d8 | |||
7e8b665c7c | |||
7be61bc9b8 | |||
6828af76dc | |||
fe92d762be | |||
b91a17e950 | |||
aabc549bcf | |||
4bb4d0386b | |||
a74af5e4d3 | |||
a939d53286 | |||
35c6aae552 | |||
83bd6cf7e6 | |||
17a0cfbde0 | |||
dbcad42da0 | |||
1b48022b63 | |||
7b8e3061d5 | |||
3f27564075 | |||
e7c2746eab | |||
1e243496fb | |||
53bc433aaa | |||
6f5e75a1b3 | |||
f83851b694 | |||
3f5a5dceb5 | |||
f9c7dd8c39 | |||
f3e437dbd1 | |||
5ee1e61eac | |||
abd34ec7cb | |||
b73f6db7b6 | |||
3b9f10dad7 | |||
![]() |
53c0e8b3b8 | ||
![]() |
7d264fe131 | ||
![]() |
c968b39657 | ||
![]() |
ebf116f347 | ||
8227866e7a | |||
6f5e73b533 | |||
a302a36fd4 | |||
1307b2ff7b | |||
d9730e765e | |||
8420c698d4 | |||
14be917d43 | |||
20da09fb96 | |||
19de7d3e4c | |||
41d821b86e | |||
b758b54233 | |||
c650266c6b | |||
dae9bb0916 | |||
2f2371d8df | |||
baaf4b70ac | |||
3b1cd800f6 | |||
0f5f7216ac | |||
c40872379e | |||
897ac5ceef | |||
eb3dbb3e45 | |||
9eb6042ba7 | |||
075833aa25 | |||
f84d800bff | |||
20f814b0ef | |||
4376a63e93 | |||
ee3d316175 | |||
452113b4bf | |||
e9b28ea1c1 | |||
ba07533667 | |||
1ab5c4885e | |||
8c977cf255 | |||
d4c6014e17 | |||
078e5e28cc | |||
7010b4f3d2 | |||
43f38b88ce | |||
7a12a1a4d6 | |||
d784f14c4c | |||
339cdf3ea9 | |||
060be3b486 | |||
9f93a19d51 | |||
c131c07afe | |||
42dbf5c6f7 | |||
2e9039a569 | |||
64b48efafb | |||
37e8dc4bdc | |||
61deb96961 | |||
3a6ce1d38b | |||
82fb73ae59 | |||
4e71c8704f | |||
0c1edf647b | |||
9b38898a8a | |||
25348e45e0 | |||
631c2360e6 | |||
6798cf3477 | |||
cc873d6029 | |||
5d147a4fc9 | |||
640862d8ee | |||
99bb53a7a3 | |||
4f05dc18b9 | |||
2a2df3bf52 | |||
c2b15c2175 | |||
edc27b899e | |||
59d96e36a4 | |||
2c976f926c | |||
671c6ec6f5 | |||
ef9ac58c0f | |||
60e6fdf4e4 | |||
06e6455ba0 | |||
007eb3b5a9 | |||
f3333f2da4 | |||
96b40c5169 | |||
d81408b79c | |||
5ae5e90461 | |||
2534ef3319 | |||
0c2e774891 | |||
895bb3c901 | |||
fca5445aa7 | |||
31d2b85b2f | |||
a8b3214c49 | |||
ccdfd388c4 | |||
cad6acd125 | |||
8dc9c1b9e7 | |||
cd4de2528f | |||
3ef4b98c1c | |||
349917e887 | |||
6c200ba076 | |||
f16aa845d2 | |||
3bccb1e690 | |||
10ae697e33 | |||
baf0d2db72 | |||
b30123a890 | |||
44c34d2daa | |||
e010fa413b | |||
e79aca4efa | |||
037f6529fd | |||
14752d9746 | |||
a8b2bd4e90 | |||
60ae971f14 | |||
1920d72821 | |||
01aa8baadd | |||
f2f526c9de | |||
4d490690e2 | |||
d9c7aa8c49 | |||
040299b90c | |||
0bd321e5ec | |||
c038370602 | |||
c08f7fc792 | |||
4dd35c3866 | |||
8bd041d7ea | |||
d450ad42c0 | |||
d34dcada09 | |||
d8448de419 | |||
35ef6676a2 | |||
e132b1c9f6 | |||
5511d8275c | |||
accf877375 | |||
9ac362fa58 | |||
9253fde2e5 | |||
975c962025 | |||
ba72b4e59f | |||
89e001bd17 | |||
623ca8bc0a | |||
0b483ce630 | |||
16998b85d5 | |||
b55952ac67 | |||
30967dac33 | |||
3166faa7eb | |||
9bba81be22 | |||
18a2d16bf6 | |||
9265cdaea9 | |||
fcb9b60656 | |||
599702f50a | |||
c8453db69d | |||
6ad93abe3b | |||
3c60782ae7 | |||
736f645bf0 | |||
b0887ab731 | |||
ada194122d | |||
b3d1ec142b | |||
e7a8a163f1 | |||
c3ef54a267 | |||
da3b43a713 | |||
8cfddd7882 | |||
80eafbb014 | |||
cc2a659767 | |||
cacfeff3fe | |||
9379728b71 | |||
d30d15c0d4 | |||
5343f53661 | |||
3126b2b962 | |||
43c671018b | |||
7a37377a09 | |||
19d9dea8b1 | |||
c50e0b18b5 | |||
4c07c0feb2 | |||
cf15b60bef | |||
328f64aa51 | |||
fdf4e79a69 | |||
bbc8732112 | |||
17dbe85219 | |||
3dc011a22c | |||
f5b89456ab | |||
2e4f63b250 | |||
3b261ff240 | |||
f06b00fb9d |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,10 +3,16 @@
|
||||
# Database
|
||||
notfellchen
|
||||
|
||||
# Geojson from imports
|
||||
*.geojson
|
||||
|
||||
# Media storage
|
||||
/static
|
||||
media
|
||||
|
||||
# Compiled CSS
|
||||
/src/fellchensammlung/static/fellchensammlung/css/main.css
|
||||
/src/fellchensammlung/static/fellchensammlung/css/main.css.map
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
@@ -161,3 +167,4 @@ dmypy.json
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
/node_modules/
|
||||
|
@@ -1,6 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
# Use 3.11 to avoid django.core.exceptions.ImproperlyConfigured: Error loading psycopg2 or psycopg module
|
||||
MAINTAINER Julian-Samuel Gebühr
|
||||
LABEL org.opencontainers.image.authors="Julian-Samuel Gebühr"
|
||||
|
||||
ENV DOCKER_BUILD=true
|
||||
|
||||
|
51
README.md
51
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[notfellchen.org](https://notfellchen.org) ist eine Sammelstelle für Tier-Vermittlungen. Die Idee entstand, da in der
|
||||
deutschsprachigen Rattencommunity ein wilder Mix aus Websites, Foren und Facebookgruppen besteht die Ratten vermitteln.
|
||||
Diese Website soll die bestehende Communities NICHT ersetzten, jedoch ermöglichen, dass Menschen die Ratten aufnehmen
|
||||
Diese Website soll die bestehende Communitys NICHT ersetzten, jedoch ermöglichen, dass Menschen die Ratten aufnehmen
|
||||
wollen Informationen einfach finden und nicht bereits in jeder Gruppe sein müssen.
|
||||
|
||||
Wir nehmen Angebote auf die
|
||||
@@ -57,10 +57,50 @@ Therefore, a solution is used where a number of predefined texts per site are su
|
||||
|
||||
# Developer Notes
|
||||
|
||||
## Getting started
|
||||
|
||||
### Clone the project
|
||||
|
||||
```
|
||||
git clone https://codeberg.org/moanos/notfellchen.git
|
||||
```
|
||||
|
||||
### Install dependencies
|
||||
```
|
||||
pip install -e '.[all]'
|
||||
```
|
||||
|
||||
### Create the database
|
||||
|
||||
```
|
||||
nf migrate
|
||||
```
|
||||
|
||||
Because of a wired bug the initial migrations must run two times as the first time the permissions
|
||||
for `create_active_adoption_notice` are created but can not yet be accessed and on the second time this permission will
|
||||
be added to groups.
|
||||
|
||||
### Start the server
|
||||
|
||||
```
|
||||
nf runserver
|
||||
```
|
||||
|
||||
### Build the docs
|
||||
|
||||
```
|
||||
sphinx-autobuild ./docs ./docs/_build/html
|
||||
```
|
||||
|
||||
|
||||
## Styling
|
||||
|
||||
Bulma is used for styling, including related SCSS. All styles should eventually be migrated to SCSS.
|
||||
|
||||
Use `npm run build-bulma` to generate the css file from SCSS.
|
||||
You can use `npm start` during development so that the file is re-generated upon change.
|
||||
|
||||
|
||||
## Docker
|
||||
|
||||
Build latest image
|
||||
@@ -77,6 +117,7 @@ docker push moanos/notfellchen:latest
|
||||
docker run -p8000:7345 moanos/notfellchen:latest
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
Tests can be run with
|
||||
@@ -144,17 +185,17 @@ Start beat
|
||||
|
||||
# Contributing
|
||||
|
||||
This project is currently solely developed by me, moanos. I'd like that to change and will be very happy for contributions
|
||||
This project is currently mainly developed by me, moanos. I'd like that to change and will be very happy for contributions
|
||||
and shared responsibilities. Some ideas where you can look for contributing first
|
||||
|
||||
* CSS structure: It's a hot mess right now, and I'm happy it somehow works. As you might see, there is much room for improvement. Refactoring this and streamlining the look across the app would be amazing.
|
||||
* UI improvements: Since a major redesign I'm much happier but the UI could use many, many little tweaks
|
||||
* Docker: If you know how to build a docker container that is a) smaller or b) utilizes staged builds this would be amazing. Any improvement welcome
|
||||
* Testing: Writing tests is always welcome, and it's likely you discover a few bugs
|
||||
|
||||
I'm also very happy for all other contributions. Before you do large refactoring efforts or features, best write a short
|
||||
issue for it before you spend a lot of work.
|
||||
|
||||
Send PRs either to [codeberg](https://codeberg.org/moanos/notfellchen) (preferred) or [Github](https://github.com/moan0s/notfellchen).
|
||||
CI (currently only for dcumentation) runs via [git.hyteck.de](https://git.hyteck.de), you can also ask moanos for an account there.
|
||||
Send PRs either to [codeberg](https://codeberg.org/moanos/notfellchen) (preferred) or [GitHub](https://github.com/moan0s/notfellchen).
|
||||
CI (currently only for documentation) runs via [git.hyteck.de](https://git.hyteck.de), you can also ask moanos for an account there.
|
||||
|
||||
Also welcome are new issues with suggestions or bugs and additions to the documentation.
|
||||
|
@@ -14,7 +14,8 @@ Via browser
|
||||
-----------
|
||||
|
||||
When a user is logged in, they can easily access the API in their browser, authenticated by their session.
|
||||
The API endpoint can be found at http://notfellchen.org/api/adoption_notices
|
||||
|
||||
For example: You can check all current adoption notices here: https://notfellchen.org/api/adoption_notice
|
||||
|
||||
Via token
|
||||
---------
|
||||
@@ -28,9 +29,9 @@ An application can then send this token in the request header for authorization.
|
||||
|
||||
|
||||
.. warning::
|
||||
Usage or creation of content still has to follow the terms of Notfellchen.org
|
||||
Usage or creation of content still has to follow the terms of notfellchen.org.
|
||||
Copyright of content is often held by rescue organizations, so you are not allowed to simply mirror content.
|
||||
Talk to the Notfellchen-Team if you want develop such things.
|
||||
Talk to the notfellchen team if you want develop such things.
|
||||
|
||||
|
||||
Endpoints
|
||||
@@ -45,7 +46,8 @@ Get Adoption Notices
|
||||
++++++++++++++++++++
|
||||
|
||||
.. code-block::
|
||||
curl --request GET \
|
||||
|
||||
curl --request GET \
|
||||
--url https://notfellchen.org/api/adoption_notice \
|
||||
--header 'Authorization: {{token}}'
|
||||
|
||||
@@ -53,7 +55,8 @@ Create Adoption Notice
|
||||
++++++++++++++++++++++
|
||||
|
||||
.. code-block::
|
||||
curl --request POST \
|
||||
|
||||
curl --request POST \
|
||||
--url https://notfellchen.org/api/adoption_notice \
|
||||
--header 'Authorization: {{token}}' \
|
||||
--header 'content-type: multipart/form-data' \
|
||||
@@ -68,6 +71,7 @@ Add Animal to Adoption Notice
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block::
|
||||
|
||||
curl --request POST \
|
||||
--url https://notfellchen.org/api/animals/ \
|
||||
--header 'Authorization: {{token}}' \
|
||||
@@ -83,6 +87,7 @@ Add picture to Animal or Adoption Notice
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block::
|
||||
|
||||
curl -X POST https://notfellchen.org/api/images/ \
|
||||
-H "Authorization: Token {{token}}" \
|
||||
-F "image=@256-256-crop.jpg" \
|
||||
@@ -96,6 +101,7 @@ Species
|
||||
Getting available species is mainly important when creating animals
|
||||
|
||||
.. code-block::
|
||||
|
||||
curl --request GET \
|
||||
--url https://notfellchen.org/api/species \
|
||||
--header 'Authorization: {{token}}'
|
||||
|
@@ -67,5 +67,6 @@ Healthchecks
|
||||
You can configure notfellchen to give a hourly ping to a healthchecks server. If this ping is not received, you will get notified and cna check why the celery jobs are no running.
|
||||
Add the following to your `notfellchen.cfg` and adjust the URL to match your check.
|
||||
.. code::
|
||||
|
||||
[monitoring]
|
||||
healthchecks_url=https://health.example.org/ping/5fa7c9b2-753a-4cb3-bcc9-f982f5bc68e8
|
||||
|
@@ -18,7 +18,7 @@ media=./media
|
||||
static=./static
|
||||
|
||||
[mail]
|
||||
console-only=true
|
||||
console_only=true
|
||||
|
||||
[logging]
|
||||
app_log_level=INFO
|
||||
|
497
package-lock.json
generated
Normal file
497
package-lock.json
generated
Normal file
@@ -0,0 +1,497 @@
|
||||
{
|
||||
"name": "notfellchen",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"bulma": "^1.0.4",
|
||||
"sass": "^1.89.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"is-glob": "^4.0.3",
|
||||
"micromatch": "^4.0.5",
|
||||
"node-addon-api": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher-android-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||
"@parcel/watcher-freebsd-x64": "2.5.1",
|
||||
"@parcel/watcher-linux-arm-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-arm-musl": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-musl": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-musl": "2.5.1",
|
||||
"@parcel/watcher-win32-arm64": "2.5.1",
|
||||
"@parcel/watcher-win32-ia32": "2.5.1",
|
||||
"@parcel/watcher-win32-x64": "2.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-android-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-ia32": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
|
||||
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/bulma": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
|
||||
"integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"detect-libc": "bin/detect-libc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
|
||||
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.89.2",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz",
|
||||
"integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
package.json
Normal file
10
package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"bulma": "^1.0.4",
|
||||
"sass": "^1.89.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build-bulma": "sass --load-path=node_modules src/fellchensammlung/static/fellchensammlung/css/main.scss src/fellchensammlung/static/fellchensammlung/css/main.css --style compressed",
|
||||
"start": "npm run build-bulma -- --watch"
|
||||
}
|
||||
}
|
@@ -6,14 +6,14 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "notfellchen"
|
||||
description = "A tool to help."
|
||||
description = "A website to help animals to find a loving home. It features organized input of adoption notices and related animals including automated lifecycle, location-based search, roles, and support for easy checking of rescue organizations."
|
||||
authors = [
|
||||
{ name = "moanos", email = "julian-samuel@gebuehr.net" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "moanos", email = "julian-samuel@gebuehr.net" },
|
||||
]
|
||||
keywords = ["animal", "adoption", "django", "rescue", ]
|
||||
keywords = ["animal", "adoption", "django", "rescue", "rats" ]
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
classifiers = [
|
||||
"Environment :: Web",
|
||||
@@ -25,8 +25,6 @@ classifiers = [
|
||||
dependencies = [
|
||||
"Django",
|
||||
"codecov",
|
||||
"sphinx",
|
||||
"sphinx-rtd-theme",
|
||||
"gunicorn",
|
||||
"fontawesomefree",
|
||||
"whitenoise",
|
||||
@@ -38,7 +36,9 @@ dependencies = [
|
||||
"crispy-bootstrap4",
|
||||
"djangorestframework",
|
||||
"celery[redis]",
|
||||
"drf-spectacular[sidecar]"
|
||||
"drf-spectacular[sidecar]",
|
||||
"django-widget-tweaks",
|
||||
"django-super-deduper"
|
||||
]
|
||||
|
||||
dynamic = ["version", "readme"]
|
||||
@@ -49,6 +49,11 @@ develop = [
|
||||
"coverage",
|
||||
"model_bakery",
|
||||
]
|
||||
docs = [
|
||||
"sphinx",
|
||||
"sphinx-rtd-theme",
|
||||
"sphinx-autobuild"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://notfellchen.org"
|
||||
|
@@ -1,18 +1,29 @@
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from types import SimpleNamespace
|
||||
|
||||
import requests
|
||||
# TODO: consider using OSMPythonTools instead of requests or overpass library
|
||||
from osmtogeojson import osmtogeojson
|
||||
from tqdm import tqdm
|
||||
|
||||
DEFAULT_OSM_DATA_FILE = "export.geojson"
|
||||
# Search area must be the official name, e.g. "Germany" is not a valid area name in Overpass API
|
||||
# Consider instead finding & using the code within the query itself, e.g. "ISO3166-1"="DE"
|
||||
DEFAULT_OVERPASS_SEARCH_AREA = "Deutschland"
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command-line arguments."""
|
||||
parser = argparse.ArgumentParser(description="Upload animal shelter data to the Notfellchen API.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Download animal shelter data from the Overpass API to the Notfellchen API.")
|
||||
parser.add_argument("--api-token", type=str, help="API token for authentication.")
|
||||
parser.add_argument("--area", type=str, help="Area to search for animal shelters (default: Deutschland).")
|
||||
parser.add_argument("--instance", type=str, help="API instance URL.")
|
||||
parser.add_argument("--data-file", type=str, help="Path to the GeoJSON file containing (only) animal shelters.")
|
||||
parser.add_argument("--use-cached", action='store_true', help="Use the stored GeoJSON file")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -21,16 +32,26 @@ def get_config():
|
||||
args = parse_args()
|
||||
|
||||
api_token = args.api_token or os.getenv("NOTFELLCHEN_API_TOKEN")
|
||||
# TODO: document new environment variable NOTFELLCHEN_AREA
|
||||
area = args.area or os.getenv("NOTFELLCHEN_AREA", DEFAULT_OVERPASS_SEARCH_AREA)
|
||||
instance = args.instance or os.getenv("NOTFELLCHEN_INSTANCE")
|
||||
data_file = args.data_file or os.getenv("NOTFELLCHEN_DATA_FILE", DEFAULT_OSM_DATA_FILE)
|
||||
use_cached = args.use_cached or os.getenv("NOTFELLCHEN_USE_CACHED", False)
|
||||
|
||||
if not api_token or not instance:
|
||||
raise ValueError("API token and instance URL must be provided via environment variables or CLI arguments.")
|
||||
|
||||
return api_token, instance, data_file
|
||||
return api_token, area, instance, data_file, use_cached
|
||||
|
||||
|
||||
def get_or_none(data, key):
|
||||
if key in data["properties"].keys():
|
||||
return data["properties"][key]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_or_empty(data, key):
|
||||
if key in data["properties"].keys():
|
||||
return data["properties"][key]
|
||||
else:
|
||||
@@ -68,37 +89,174 @@ def https(value):
|
||||
return None
|
||||
|
||||
|
||||
def calc_coordinate_center(coordinates):
|
||||
"""
|
||||
Calculates the center as the arithmetic mean of the list of coordinates.
|
||||
|
||||
Not perfect because earth is a sphere (citation needed) but good enough.
|
||||
"""
|
||||
if not coordinates:
|
||||
return None, None
|
||||
|
||||
lon_sum = 0.0
|
||||
lat_sum = 0.0
|
||||
count = 0
|
||||
|
||||
for lon, lat in coordinates:
|
||||
lon_sum += lon
|
||||
lat_sum += lat
|
||||
count += 1
|
||||
|
||||
return lon_sum / count, lat_sum / count
|
||||
|
||||
|
||||
def get_center_coordinates(geometry):
|
||||
"""
|
||||
Given a GeoJSON geometry dict, return (longitude, latitude)
|
||||
|
||||
If a shape, calculate the center, else reurn the point
|
||||
"""
|
||||
geom_type = geometry["type"]
|
||||
coordinates = geometry["coordinates"]
|
||||
|
||||
if geom_type == "Point":
|
||||
return coordinates[0], coordinates[1]
|
||||
|
||||
elif geom_type == "LineString":
|
||||
return calc_coordinate_center(coordinates)
|
||||
|
||||
elif geom_type == "Polygon":
|
||||
outer_ring = coordinates[0]
|
||||
return calc_coordinate_center(outer_ring)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported geometry type: {geom_type}")
|
||||
|
||||
|
||||
# TODO: take note of new get_overpass_result function which does the bulk of the new overpass query work
|
||||
def get_overpass_result(area, data_file):
|
||||
"""Build the Overpass query for fetching animal shelters in the specified area."""
|
||||
overpass_endpoint = "https://overpass-api.de/api/interpreter"
|
||||
overpass_query = f"""
|
||||
[out:json][timeout:25];
|
||||
area[name="{area}"]->.searchArea;
|
||||
nwr["amenity"="animal_shelter"](area.searchArea);
|
||||
out body;
|
||||
>;
|
||||
out skel qt;
|
||||
"""
|
||||
r = requests.get(overpass_endpoint, params={'data': overpass_query})
|
||||
if r.status_code == 200:
|
||||
rjson = r.json()
|
||||
result = osmtogeojson.process_osm_json(rjson)
|
||||
with open(data_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, ensure_ascii=False)
|
||||
return result
|
||||
|
||||
|
||||
def add_if_available(base_data, keys, result):
|
||||
# Loads the data into the org if available
|
||||
for key in keys:
|
||||
if getattr(base_data, key) is not None:
|
||||
result[key] = getattr(base_data, key)
|
||||
return result
|
||||
|
||||
|
||||
def create_location(tierheim, instance, headers):
|
||||
location_data = {
|
||||
"place_id": tierheim["id"],
|
||||
"longitude": get_center_coordinates(tierheim["geometry"])[0],
|
||||
"latitude": get_center_coordinates(tierheim["geometry"])[1],
|
||||
"name": tierheim["properties"]["name"],
|
||||
"city": tierheim["properties"]["addr:city"],
|
||||
"housenumber": get_or_empty(tierheim, "addr:housenumber"),
|
||||
"postcode": get_or_empty(tierheim, "addr:postcode"),
|
||||
"street": get_or_empty(tierheim, "addr:street"),
|
||||
"countrycode": get_or_empty(tierheim, "addr:country"),
|
||||
}
|
||||
|
||||
location_result = requests.post(f"{instance}/api/locations/", json=location_data, headers=headers)
|
||||
|
||||
if location_result.status_code != 201:
|
||||
print(
|
||||
f"Location for {tierheim["properties"]["name"]}:{location_result.status_code} {location_result.json()} not created")
|
||||
return location_result.json()
|
||||
|
||||
|
||||
def main():
|
||||
api_token, instance, data_file = get_config()
|
||||
api_token, area, instance, data_file, use_cached = get_config()
|
||||
if not use_cached:
|
||||
# Query shelters
|
||||
overpass_result = get_overpass_result(area, data_file)
|
||||
if overpass_result is None:
|
||||
print("Error: get_overpass_result returned None")
|
||||
return
|
||||
else:
|
||||
with open(data_file, 'r', encoding='utf-8') as f:
|
||||
overpass_result = json.load(f)
|
||||
|
||||
# Set headers and endpoint
|
||||
endpoint = f"{instance}/api/organizations/"
|
||||
h = {'Authorization': f'Token {api_token}', "content-type": "application/json"}
|
||||
|
||||
with open(data_file, encoding="utf8") as f:
|
||||
d = json.load(f)
|
||||
|
||||
for idx, tierheim in tqdm(enumerate(d["features"])):
|
||||
tierheime = overpass_result["features"]
|
||||
|
||||
for idx, tierheim in enumerate(tqdm(tierheime)):
|
||||
# Check if data is low quality
|
||||
if "name" not in tierheim["properties"].keys() or "addr:city" not in tierheim["properties"].keys():
|
||||
continue
|
||||
|
||||
data = {"name": tierheim["properties"]["name"],
|
||||
"location_string": f"{get_or_none(tierheim, "addr:street")} {get_or_none(tierheim, "addr:housenumber")}, {get_or_none(tierheim, "addr:postcode")} {tierheim["properties"]["addr:city"]}",
|
||||
"phone_number": choose(("contact:phone", "phone"), tierheim["properties"], replace=True),
|
||||
"fediverse_profile": get_or_none(tierheim, "contact:mastodon"),
|
||||
"facebook": https(add(get_or_none(tierheim, "contact:facebook"), "facebook")),
|
||||
"instagram": https(add(get_or_none(tierheim, "contact:instagram"), "instagram")),
|
||||
"website": https(choose(("contact:website", "website"), tierheim["properties"])),
|
||||
"email": choose(("contact:email", "email"), tierheim["properties"]),
|
||||
"description": get_or_none(tierheim, "opening_hours"),
|
||||
"external_object_identifier": f"{tierheim["id"]}",
|
||||
"external_source_identifier": "OSM"
|
||||
}
|
||||
# Load TH data in for easier accessing
|
||||
th_data = SimpleNamespace(
|
||||
name=tierheim["properties"]["name"],
|
||||
email=choose(("contact:email", "email"), tierheim["properties"]),
|
||||
phone_number=choose(("contact:phone", "phone"), tierheim["properties"], replace=True),
|
||||
fediverse_profile=get_or_none(tierheim, "contact:mastodon"),
|
||||
facebook=https(add(get_or_empty(tierheim, "contact:facebook"), "facebook")),
|
||||
instagram=https(add(get_or_empty(tierheim, "contact:instagram"), "instagram")),
|
||||
website=https(choose(("contact:website", "website"), tierheim["properties"])),
|
||||
description=get_or_none(tierheim, "opening_hours"),
|
||||
external_object_identifier=tierheim["id"],
|
||||
EXTERNAL_SOURCE_IDENTIFIER="OSM",
|
||||
)
|
||||
|
||||
result = requests.post(endpoint, json=data, headers=h)
|
||||
# Define here for later
|
||||
optional_data = ["email", "phone_number", "website", "description", "fediverse_profile", "facebook",
|
||||
"instagram"]
|
||||
|
||||
if result.status_code != 201:
|
||||
print(f"{idx} {tierheim["properties"]["name"]}:{result.status_code} {result.json()}")
|
||||
# Check if rescue organization exits
|
||||
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)
|
||||
if search_result.status_code == 200:
|
||||
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,
|
||||
"name": th_data.name}
|
||||
if search_result.json()[0]["location"] is None:
|
||||
location = create_location(tierheim, instance, h)
|
||||
org_patch_data["location"] = location["id"]
|
||||
|
||||
org_patch_data = add_if_available(th_data, optional_data, org_patch_data)
|
||||
|
||||
result = requests.patch(endpoint, json=org_patch_data, headers=h)
|
||||
if result.status_code != 200:
|
||||
logging.warning(f"Updating {tierheim['properties']['name']} failed:{result.status_code} {result.json()}")
|
||||
continue
|
||||
else:
|
||||
location = create_location(tierheim, instance, h)
|
||||
org_data = {"name": tierheim["properties"]["name"],
|
||||
"external_object_identifier": f"{tierheim["id"]}",
|
||||
"external_source_identifier": "OSM",
|
||||
"location": location["id"]
|
||||
}
|
||||
|
||||
org_data = add_if_available(th_data, optional_data, org_data)
|
||||
|
||||
result = requests.post(endpoint, json=org_data, headers=h)
|
||||
|
||||
if result.status_code != 201:
|
||||
print(f"{idx} {tierheim["properties"]["name"]}:{result.status_code} {result.json()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@@ -1,16 +1,17 @@
|
||||
import csv
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import EmptyFieldListFilter
|
||||
from django.http import HttpResponse
|
||||
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, \
|
||||
SpeciesSpecificURL, ImportantLocation
|
||||
SpeciesSpecificURL, ImportantLocation, SpeciesSpecialization
|
||||
|
||||
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions, BaseNotification
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions, Notification
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
@@ -99,13 +100,19 @@ class SpeciesSpecificURLInline(admin.StackedInline):
|
||||
model = SpeciesSpecificURL
|
||||
|
||||
|
||||
class SpeciesSpecializationInline(admin.StackedInline):
|
||||
model = SpeciesSpecialization
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(RescueOrganization)
|
||||
class RescueOrganizationAdmin(admin.ModelAdmin):
|
||||
search_fields = ("name", "description", "internal_comment", "location_string")
|
||||
search_fields = ("name", "description", "internal_comment", "location_string", "location__city")
|
||||
list_display = ("name", "trusted", "allows_using_materials", "website")
|
||||
list_filter = ("allows_using_materials", "trusted",)
|
||||
list_filter = ("allows_using_materials", "trusted", ("external_source_identifier", EmptyFieldListFilter))
|
||||
|
||||
inlines = [
|
||||
SpeciesSpecializationInline,
|
||||
SpeciesSpecificURLInline,
|
||||
]
|
||||
|
||||
@@ -120,9 +127,9 @@ class CommentAdmin(admin.ModelAdmin):
|
||||
list_filter = ("user",)
|
||||
|
||||
|
||||
@admin.register(BaseNotification)
|
||||
@admin.register(Notification)
|
||||
class BaseNotificationAdmin(admin.ModelAdmin):
|
||||
list_filter = ("user", "read")
|
||||
list_filter = ("user_to_notify", "read")
|
||||
|
||||
|
||||
@admin.register(SearchSubscription)
|
||||
|
33
src/fellchensammlung/api/renderers.py
Normal file
33
src/fellchensammlung/api/renderers.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from rest_framework.renderers import BaseRenderer
|
||||
import json
|
||||
|
||||
|
||||
class GeoJSONRenderer(BaseRenderer):
|
||||
media_type = 'application/json'
|
||||
format = 'geojson'
|
||||
charset = 'utf-8'
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
features = []
|
||||
for item in data:
|
||||
coords = item["coordinates"]
|
||||
if coords:
|
||||
feature = {
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": coords
|
||||
},
|
||||
"properties": {
|
||||
k: v for k, v in item.items()
|
||||
},
|
||||
"id": f"{item['id']}"
|
||||
}
|
||||
features.append(feature)
|
||||
|
||||
geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"generator": "notfellchen",
|
||||
"features": features
|
||||
}
|
||||
return json.dumps(geojson)
|
@@ -1,5 +1,6 @@
|
||||
from ..models import Animal, RescueOrganization, AdoptionNotice, Species, Image, Location
|
||||
from rest_framework import serializers
|
||||
import math
|
||||
|
||||
|
||||
class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
@@ -32,19 +33,95 @@ class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
"group_only", "location", "location_details", "organization", "photos"]
|
||||
|
||||
|
||||
class AdoptionNoticeGeoJSONSerializer(serializers.ModelSerializer):
|
||||
species = serializers.SerializerMethodField()
|
||||
title = serializers.CharField(source='name')
|
||||
url = serializers.SerializerMethodField()
|
||||
location_hr = serializers.SerializerMethodField()
|
||||
coordinates = serializers.SerializerMethodField()
|
||||
image_url = serializers.SerializerMethodField()
|
||||
image_alt = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ('id', 'species', 'title', 'description', 'url', 'location_hr', 'coordinates', 'image_url',
|
||||
'image_alt')
|
||||
|
||||
def get_species(self, obj):
|
||||
return "rat"
|
||||
|
||||
def get_url(self, obj):
|
||||
return obj.get_absolute_url()
|
||||
|
||||
def get_image_url(self, obj):
|
||||
photo = obj.get_photo()
|
||||
if photo is not None:
|
||||
return obj.get_photo().image.url
|
||||
return None
|
||||
|
||||
def get_image_alt(self, obj):
|
||||
photo = obj.get_photo()
|
||||
if photo is not None:
|
||||
return obj.get_photo().alt_text
|
||||
return None
|
||||
|
||||
def get_coordinates(self, obj):
|
||||
"""
|
||||
Coordinates are randomly moved around real location, roughly in a circle. The object id is used as angle so that
|
||||
points are always displayed at the same location (as if they were a seed for a random function).
|
||||
|
||||
It's not exactly a circle, because the earth is round.
|
||||
"""
|
||||
if obj.location:
|
||||
longitude_addition = math.sin(obj.id) / 2000
|
||||
latitude_addition = math.cos(obj.id) / 2000
|
||||
return [obj.location.longitude + longitude_addition, obj.location.latitude + latitude_addition]
|
||||
return None
|
||||
|
||||
def get_location_hr(self, obj):
|
||||
if obj.location:
|
||||
return f"{obj.location}"
|
||||
return None
|
||||
|
||||
|
||||
class RescueOrgeGeoJSONSerializer(serializers.ModelSerializer):
|
||||
name = serializers.CharField()
|
||||
url = serializers.SerializerMethodField()
|
||||
location_hr = serializers.SerializerMethodField()
|
||||
coordinates = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ('id', 'name', 'description', 'url', 'location_hr', 'coordinates')
|
||||
|
||||
def get_url(self, obj):
|
||||
return obj.get_absolute_url()
|
||||
|
||||
def get_coordinates(self, obj):
|
||||
"""
|
||||
Coordinates are randomly moved around real location, roughly in a circle. The object id is used as angle so that
|
||||
points are always displayed at the same location (as if they were a seed for a random function).
|
||||
|
||||
It's not exactly a circle, because the earth is round.
|
||||
"""
|
||||
if obj.location:
|
||||
return [obj.location.longitude, obj.location.latitude]
|
||||
return None
|
||||
|
||||
def get_location_hr(self, obj):
|
||||
if obj.location.city:
|
||||
return f"{obj.location.city}"
|
||||
elif obj.location:
|
||||
return f"{obj.location}"
|
||||
return None
|
||||
|
||||
|
||||
class AnimalCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Animal
|
||||
fields = ["name", "date_of_birth", "description", "species", "sex", "adoption_notice"]
|
||||
|
||||
|
||||
class RescueOrgSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RescueOrganization
|
||||
fields = ["name", "location_string", "instagram", "facebook", "fediverse_profile", "email", "phone_number",
|
||||
"website", "description", "external_object_identifier", "external_source_identifier"]
|
||||
|
||||
|
||||
class AnimalGetSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Animal
|
||||
|
@@ -1,15 +1,18 @@
|
||||
from django.urls import path
|
||||
from .views import (
|
||||
AdoptionNoticeApiView,
|
||||
AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView
|
||||
AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView,
|
||||
AdoptionNoticeGeoJSONView, RescueOrgGeoJSONView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("adoption_notice", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-list"),
|
||||
path("adoption_notice.geojson", AdoptionNoticeGeoJSONView.as_view(), name="api-adoption-notice-list-geojson"),
|
||||
path("adoption_notice/<int:id>/", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-detail"),
|
||||
path("animals/", AnimalApiView.as_view(), name="api-animal-list"),
|
||||
path("animals/<int:id>/", AnimalApiView.as_view(), name="api-animal-detail"),
|
||||
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("images/", AddImageApiView.as_view(), name="api-add-image"),
|
||||
path("species/", SpeciesApiView.as_view(), name="api-species-list"),
|
||||
|
@@ -1,23 +1,26 @@
|
||||
from django.db.models import Q
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from fellchensammlung.api.serializers import LocationSerializer
|
||||
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
|
||||
from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel, Location, AdoptionNoticeStatus
|
||||
from fellchensammlung.tasks import post_adoption_notice_save, post_rescue_org_save
|
||||
from rest_framework import status
|
||||
from rest_framework import status, serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from .renderers import GeoJSONRenderer
|
||||
from .serializers import (
|
||||
AnimalGetSerializer,
|
||||
AnimalCreateSerializer,
|
||||
RescueOrganizationSerializer,
|
||||
RescueOrgeGeoJSONSerializer,
|
||||
AdoptionNoticeSerializer,
|
||||
ImageCreateSerializer,
|
||||
SpeciesSerializer, RescueOrgSerializer,
|
||||
SpeciesSerializer, RescueOrganizationSerializer,
|
||||
)
|
||||
from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
|
||||
|
||||
class AdoptionNoticeApiView(APIView):
|
||||
@@ -63,22 +66,22 @@ class AdoptionNoticeApiView(APIView):
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
adoption_notice = serializer.save(owner=request.user)
|
||||
adoption_notice = serializer.save(owner=request.user_to_notify)
|
||||
|
||||
# Add the location
|
||||
post_adoption_notice_save.delay_on_commit(adoption_notice.pk)
|
||||
|
||||
# Only set active when user has trust level moderator or higher
|
||||
if request.user.trust_level >= TrustLevel.MODERATOR:
|
||||
if request.user_to_notify.trust_level >= TrustLevel.MODERATOR:
|
||||
adoption_notice.set_active()
|
||||
else:
|
||||
adoption_notice.set_unchecked()
|
||||
|
||||
# Log the action
|
||||
Log.objects.create(
|
||||
user=request.user,
|
||||
user=request.user_to_notify,
|
||||
action="add_adoption_notice",
|
||||
text=f"{request.user} added adoption notice {adoption_notice.pk} via API",
|
||||
text=f"{request.user_to_notify} added adoption notice {adoption_notice.pk} via API",
|
||||
)
|
||||
|
||||
# Return success response with new adoption notice details
|
||||
@@ -91,6 +94,9 @@ class AdoptionNoticeApiView(APIView):
|
||||
class AnimalApiView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
responses=AnimalGetSerializer
|
||||
)
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Get list of animals or a specific animal by ID.
|
||||
@@ -107,6 +113,16 @@ class AnimalApiView(APIView):
|
||||
serializer = AnimalGetSerializer(animals, many=True, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@transaction.atomic
|
||||
@extend_schema(
|
||||
request=AnimalCreateSerializer,
|
||||
responses={201: inline_serializer(
|
||||
name='Animal',
|
||||
fields={
|
||||
'id': serializers.IntegerField(),
|
||||
"message": serializers.Field()}),
|
||||
400: "json"}
|
||||
)
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
@@ -114,7 +130,7 @@ class AnimalApiView(APIView):
|
||||
"""
|
||||
serializer = AnimalCreateSerializer(data=request.data, context={"request": request})
|
||||
if serializer.is_valid():
|
||||
animal = serializer.save(owner=request.user)
|
||||
animal = serializer.save(owner=request.user_to_notify)
|
||||
return Response(
|
||||
{"message": "Animal created successfully!", "id": animal.id},
|
||||
status=status.HTTP_201_CREATED,
|
||||
@@ -197,24 +213,27 @@ class RescueOrganizationApiView(APIView):
|
||||
Q(location__name__icontains=search_query) |
|
||||
Q(location__city__icontains=search_query)
|
||||
)
|
||||
if organizations.count() == 0:
|
||||
return Response({"error": "No organizations found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
serializer = RescueOrganizationSerializer(organizations, many=True, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@transaction.atomic
|
||||
@extend_schema(
|
||||
request=RescueOrgSerializer,
|
||||
request=RescueOrganizationSerializer,
|
||||
responses={201: 'Rescue organization created successfully!'}
|
||||
)
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Create or update a rescue organization.
|
||||
"""
|
||||
serializer = RescueOrgSerializer(data=request.data, context={"request": request})
|
||||
serializer = RescueOrganizationSerializer(data=request.data, context={"request": request})
|
||||
if serializer.is_valid():
|
||||
rescue_org = serializer.save()
|
||||
# Add the location
|
||||
post_rescue_org_save.delay_on_commit(rescue_org.pk)
|
||||
if rescue_org.location is None:
|
||||
# Add the location
|
||||
post_rescue_org_save.delay_on_commit(rescue_org.pk)
|
||||
return Response(
|
||||
{"message": "Rescue organization created successfully!", "id": rescue_org.id},
|
||||
status=status.HTTP_201_CREATED,
|
||||
@@ -224,14 +243,14 @@ class RescueOrganizationApiView(APIView):
|
||||
|
||||
@transaction.atomic
|
||||
@extend_schema(
|
||||
request=RescueOrgSerializer,
|
||||
request=RescueOrganizationSerializer,
|
||||
responses={200: 'Rescue organization updated successfully!'}
|
||||
)
|
||||
def patch(self, request, *args, **kwargs):
|
||||
"""
|
||||
Partially update a rescue organization.
|
||||
"""
|
||||
org_id = kwargs.get("id")
|
||||
org_id = request.data.get("id")
|
||||
if not org_id:
|
||||
return Response({"error": "ID is required for updating."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -240,13 +259,14 @@ class RescueOrganizationApiView(APIView):
|
||||
except RescueOrganization.DoesNotExist:
|
||||
return Response({"error": "Organization not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
serializer = RescueOrgSerializer(organization, data=request.data, partial=True, context={"request": request})
|
||||
serializer = RescueOrganizationSerializer(organization, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response({"message": "Rescue organization updated successfully!"}, status=status.HTTP_200_OK)
|
||||
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class AddImageApiView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@@ -269,7 +289,7 @@ class AddImageApiView(APIView):
|
||||
raise ValueError("Unknown attach_to_type given, should not happen. Check serializer")
|
||||
serializer.validated_data.pop('attach_to_type', None)
|
||||
serializer.validated_data.pop('attach_to', None)
|
||||
image = serializer.save(owner=request.user)
|
||||
image = serializer.save(owner=request.user_to_notify)
|
||||
object_to_attach_to.photos.add(image)
|
||||
return Response(
|
||||
{"message": "Image added successfully!", "id": image.id},
|
||||
@@ -292,6 +312,7 @@ class SpeciesApiView(APIView):
|
||||
serializer = SpeciesSerializer(species, many=True, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class LocationApiView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@@ -339,9 +360,9 @@ class LocationApiView(APIView):
|
||||
|
||||
# Log the action
|
||||
Log.objects.create(
|
||||
user=request.user,
|
||||
user=request.user_to_notify,
|
||||
action="add_location",
|
||||
text=f"{request.user} added adoption notice {location.pk} via API",
|
||||
text=f"{request.user_to_notify} added adoption notice {location.pk} via API",
|
||||
)
|
||||
|
||||
# Return success response with new adoption notice details
|
||||
@@ -351,3 +372,14 @@ class LocationApiView(APIView):
|
||||
)
|
||||
|
||||
|
||||
class AdoptionNoticeGeoJSONView(ListAPIView):
|
||||
queryset = AdoptionNotice.objects.select_related('location').filter(location__isnull=False).filter(
|
||||
adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE)
|
||||
serializer_class = AdoptionNoticeGeoJSONSerializer
|
||||
renderer_classes = [GeoJSONRenderer]
|
||||
|
||||
|
||||
class RescueOrgGeoJSONView(ListAPIView):
|
||||
queryset = RescueOrganization.objects.select_related('location').filter(location__isnull=False)
|
||||
serializer_class = RescueOrgeGeoJSONSerializer
|
||||
renderer_classes = [GeoJSONRenderer]
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from django import forms
|
||||
|
||||
from .models import AdoptionNotice, Animal, Image, ReportAdoptionNotice, ReportComment, ModerationAction, User, Species, \
|
||||
Comment, SexChoicesWithAll, DistanceChoices
|
||||
Comment, SexChoicesWithAll, DistanceChoices, SpeciesSpecificURL, RescueOrganization
|
||||
from django_registration.forms import RegistrationForm
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit, Layout, Fieldset, HTML, Row, Column, Field, Hidden
|
||||
@@ -22,105 +22,39 @@ class DateInput(forms.DateInput):
|
||||
input_type = 'date'
|
||||
|
||||
|
||||
class BulmaAdoptionNoticeForm(forms.ModelForm):
|
||||
class AdoptionNoticeForm(forms.ModelForm):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string",
|
||||
"organization"]
|
||||
|
||||
|
||||
class AdoptionNoticeForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'in_adoption_notice_creation_flow' in kwargs:
|
||||
in_flow = kwargs.pop('in_adoption_notice_creation_flow')
|
||||
else:
|
||||
in_flow = False
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
|
||||
self.helper.form_id = 'form-adoption-notice'
|
||||
self.helper.form_class = 'card'
|
||||
self.helper.form_method = 'post'
|
||||
|
||||
if in_flow:
|
||||
submit = Submit('save-and-add-another-animal', _('Speichern'))
|
||||
|
||||
else:
|
||||
submit = Submit('submit', _('Speichern'))
|
||||
|
||||
self.helper.layout = Layout(
|
||||
Fieldset(
|
||||
_('Vermittlungsdetails'),
|
||||
'name',
|
||||
'species',
|
||||
'num_animals',
|
||||
'date_of_birth',
|
||||
'sex',
|
||||
'group_only',
|
||||
'searching_since',
|
||||
'location_string',
|
||||
'organization',
|
||||
'description',
|
||||
'further_information',
|
||||
),
|
||||
submit)
|
||||
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string",
|
||||
"organization"]
|
||||
|
||||
|
||||
class AdoptionNoticeFormWithDateWidget(AdoptionNoticeForm):
|
||||
class Meta:
|
||||
model = AdoptionNotice
|
||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string",
|
||||
"organization"]
|
||||
widgets = {
|
||||
'searching_since': DateInput(),
|
||||
'searching_since': DateInput(format=('%Y-%m-%d')),
|
||||
}
|
||||
|
||||
|
||||
class AnimalForm(forms.ModelForm):
|
||||
class AdoptionNoticeFormAutoAnimal(AdoptionNoticeForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'in_adoption_notice_creation_flow' in kwargs:
|
||||
adding = kwargs.pop('in_adoption_notice_creation_flow')
|
||||
else:
|
||||
adding = False
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = 'form-animal card'
|
||||
if adding:
|
||||
self.helper.add_input(Submit('save-and-add-another-animal', _('Speichern und weiteres Tier hinzufügen')))
|
||||
self.helper.add_input(Submit('save-and-finish', _('Speichern und beenden')))
|
||||
else:
|
||||
self.helper.add_input(Submit('submit', _('Speichern'), css_class="btn"))
|
||||
|
||||
class Meta:
|
||||
model = Animal
|
||||
fields = ["name", "date_of_birth", "species", "sex", "description"]
|
||||
|
||||
|
||||
class AnimalFormWithDateWidget(AnimalForm):
|
||||
class Meta:
|
||||
model = Animal
|
||||
fields = ["name", "date_of_birth", "species", "sex", "description"]
|
||||
widgets = {
|
||||
'date_of_birth': DateInput(),
|
||||
}
|
||||
|
||||
|
||||
class AdoptionNoticeFormWithDateWidgetAutoAnimal(AdoptionNoticeFormWithDateWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AdoptionNoticeFormWithDateWidgetAutoAnimal, self).__init__(*args, **kwargs)
|
||||
super(AdoptionNoticeFormAutoAnimal, self).__init__(*args, **kwargs)
|
||||
self.fields["num_animals"] = forms.fields.IntegerField(min_value=1, max_value=30, label=_("Zahl der Tiere"))
|
||||
animal_form = AnimalForm()
|
||||
self.fields["species"] = animal_form.fields["species"]
|
||||
self.fields["sex"] = animal_form.fields["sex"]
|
||||
self.fields["date_of_birth"] = animal_form.fields["date_of_birth"]
|
||||
self.fields["date_of_birth"].widget = DateInput()
|
||||
self.fields["date_of_birth"].widget = DateInput(format=('%Y-%m-%d'))
|
||||
|
||||
|
||||
class AnimalForm(forms.ModelForm):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
class Meta:
|
||||
model = Animal
|
||||
fields = ["name", "date_of_birth", "species", "sex", "description"]
|
||||
|
||||
widgets = {
|
||||
'date_of_birth': DateInput(format=('%Y-%m-%d'))
|
||||
}
|
||||
|
||||
|
||||
class ImageForm(forms.ModelForm):
|
||||
@@ -156,30 +90,39 @@ class ImageForm(forms.ModelForm):
|
||||
|
||||
|
||||
class ReportAdoptionNoticeForm(forms.ModelForm):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
class Meta:
|
||||
model = ReportAdoptionNotice
|
||||
fields = ('reported_broken_rules', 'user_comment')
|
||||
|
||||
|
||||
class ReportCommentForm(forms.ModelForm):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
class Meta:
|
||||
model = ReportComment
|
||||
fields = ('reported_broken_rules', 'user_comment')
|
||||
|
||||
|
||||
class CommentForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_class = 'form-comments'
|
||||
self.helper.add_input(Hidden('action', 'comment'))
|
||||
self.helper.add_input(Submit('submit', _('Kommentieren'), css_class="button is-primary"))
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ('text',)
|
||||
|
||||
|
||||
class SpeciesURLForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = SpeciesSpecificURL
|
||||
fields = ('species', 'url')
|
||||
|
||||
|
||||
class RescueOrgInternalComment(forms.ModelForm):
|
||||
class Meta:
|
||||
model = RescueOrganization
|
||||
fields = ('internal_comment',)
|
||||
|
||||
|
||||
class ModerationActionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ModerationAction
|
||||
@@ -190,17 +133,11 @@ class CustomRegistrationForm(RegistrationForm):
|
||||
class Meta(RegistrationForm.Meta):
|
||||
model = User
|
||||
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
||||
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 __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_id = 'form-registration'
|
||||
self.helper.form_class = 'card'
|
||||
|
||||
self.helper.add_input(Submit('submit', _('Registrieren'), css_class="btn"))
|
||||
|
||||
|
||||
class AdoptionNoticeSearchForm(forms.Form):
|
||||
template_name = "fellchensammlung/forms/form_snippets.html"
|
||||
|
@@ -1,43 +1,60 @@
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
from fellchensammlung.models import User, CommentNotification, BaseNotification, TrustLevel
|
||||
from notfellchen.settings import host
|
||||
from fellchensammlung.models import User, Notification, TrustLevel, NotificationTypeChoices
|
||||
from notfellchen.settings import base_url
|
||||
|
||||
NEWLINE = "\r\n"
|
||||
|
||||
|
||||
def mail_admins_new_report(report):
|
||||
subject = _("Neue Meldung")
|
||||
"""
|
||||
Sends an e-mail to all users that should handle the report.
|
||||
"""
|
||||
for moderator in User.objects.filter(trust_level__gt=TrustLevel.MODERATOR):
|
||||
greeting = _("Moin,") + "{NEWLINE}"
|
||||
new_report_text = _("es wurde ein Regelverstoß gemeldet.") + "{NEWLINE}"
|
||||
if len(report.reported_broken_rules.all()) > 0:
|
||||
reported_rules_text = (f"Ein Verstoß gegen die folgenden Regeln wurde gemeldet:{NEWLINE}"
|
||||
f"- {f'{NEWLINE} - '.join([str(r) for r in report.reported_broken_rules.all()])}{NEWLINE}")
|
||||
else:
|
||||
reported_rules_text = f"Es wurden keine Regeln angegeben gegen die Verstoßen wurde.{NEWLINE}"
|
||||
if report.user_comment:
|
||||
comment_text = f'Kommentar zum Report: "{report.user_comment}"{NEWLINE}'
|
||||
else:
|
||||
comment_text = f"Es wurde kein Kommentar hinzugefügt.{NEWLINE}"
|
||||
report_url = base_url + report.get_absolute_url()
|
||||
context = {"report_url": report_url,
|
||||
"user_comment": report.user_comment, }
|
||||
|
||||
report_url = "https://" + host + report.get_absolute_url()
|
||||
link_text = f"Um alle Details zu sehen, geh bitte auf: {report_url}"
|
||||
body_text = greeting + new_report_text + reported_rules_text + comment_text + link_text
|
||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
||||
message.send()
|
||||
subject = _("Neue Meldung")
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
|
||||
mail.send_mail(subject,
|
||||
plain_message,
|
||||
from_email="info@notfellchen.org",
|
||||
recipient_list=[moderator.email],
|
||||
html_message=html_message)
|
||||
|
||||
|
||||
def send_notification_email(notification_pk):
|
||||
try:
|
||||
notification = CommentNotification.objects.get(pk=notification_pk)
|
||||
except CommentNotification.DoesNotExist:
|
||||
notification = BaseNotification.objects.get(pk=notification_pk)
|
||||
subject = f"🔔 {notification.title}"
|
||||
body_text = notification.text
|
||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [notification.user.email])
|
||||
message.send()
|
||||
notification = Notification.objects.get(pk=notification_pk)
|
||||
|
||||
subject = f"{notification.title}"
|
||||
context = {"notification": notification, }
|
||||
if notification.notification_type == NotificationTypeChoices.NEW_REPORT_COMMENT or notification.notification_type == NotificationTypeChoices.NEW_REPORT_AN:
|
||||
context["user_comment"] = notification.report.user_comment
|
||||
context["report_url"] = f"{base_url}{notification.report.get_absolute_url()}"
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.NEW_USER:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/new-user.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_IS_TO_BE_CHECKED:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/an-to-be-checked.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_WAS_DEACTIVATED:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/an-deactivated.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_FOR_SEARCH_FOUND:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/an-for-search-found.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.NEW_COMMENT:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/new-comment.html', context)
|
||||
else:
|
||||
raise NotImplementedError("Unknown notification type")
|
||||
|
||||
plain_message = strip_tags(html_message)
|
||||
mail.send_mail(subject, plain_message, settings.DEFAULT_FROM_EMAIL,
|
||||
[notification.user_to_notify.email],
|
||||
html_message=html_message)
|
||||
|
10
src/fellchensammlung/management/commands/dedup_locations.py
Normal file
10
src/fellchensammlung/management/commands/dedup_locations.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.core.management import BaseCommand
|
||||
from fellchensammlung.tools.admin import dedup_locations
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Deduplicate locations based on place_id'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dedup_locations()
|
||||
|
11
src/fellchensammlung/management/commands/export_contacts.py
Normal file
11
src/fellchensammlung/management/commands/export_contacts.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.core.management import BaseCommand
|
||||
|
||||
from fellchensammlung.tools.admin import export_orgs_as_vcf
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Export organizations with phone number as contacts in vcf format'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
export_orgs_as_vcf()
|
||||
|
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-23 16:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0046_alter_importantlocation_location'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='adoptionnotice',
|
||||
name='further_information',
|
||||
field=models.URLField(blank=True, help_text='Verlinke hier die Quelle der Vermittlung (z.B. die Website des Tierheims', null=True, verbose_name='Link zu mehr Informationen'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adoptionnotice',
|
||||
name='name',
|
||||
field=models.CharField(max_length=200, verbose_name='Titel der Vermittlung'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rescueorganization',
|
||||
name='location_string',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Ort der Organisation'),
|
||||
),
|
||||
]
|
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-06-19 15:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0047_alter_adoptionnotice_further_information_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='adoptionnotice',
|
||||
name='further_information',
|
||||
field=models.URLField(blank=True, help_text='Verlinke hier die Quelle der Vermittlung (z.B. die Website des Tierheims)', null=True, verbose_name='Link zu mehr Informationen'),
|
||||
),
|
||||
]
|
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-06-19 21:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0048_alter_adoptionnotice_further_information'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='rescueorganization',
|
||||
name='exclude_from_check',
|
||||
field=models.BooleanField(default=False, help_text='Organisation von der manuellen Überprüfung ausschließen, z.B. weil Tiere nicht online geführt werden', verbose_name='Von Prüfung ausschließen'),
|
||||
),
|
||||
]
|
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.1 on 2025-06-20 16:52
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0049_rescueorganization_exclude_from_check'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='speciesspecificurl',
|
||||
old_name='rescues_organization',
|
||||
new_name='rescue_organization',
|
||||
),
|
||||
]
|
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-03 09:28
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0050_rename_rescues_organization_speciesspecificurl_rescue_organization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='rescueorganization',
|
||||
name='parent_org',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='fellchensammlung.rescueorganization'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SpeciesSpecialization',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('rescue_organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.rescueorganization', verbose_name='Tierschutzorganisation')),
|
||||
('species', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.species', verbose_name='Tierart')),
|
||||
],
|
||||
),
|
||||
]
|
@@ -0,0 +1,54 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-11 09:01
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0051_rescueorganization_parent_org_speciesspecialization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='basenotification',
|
||||
name='user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='commentnotification',
|
||||
name='basenotification_ptr',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='commentnotification',
|
||||
name='comment',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Notification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('read_at', models.DateTimeField(blank=True, null=True, verbose_name='Gelesen am')),
|
||||
('notification_type', models.CharField(choices=[('new_user', 'Useraccount wurde erstellt'), ('new_report_an', 'Vermittlung wurde gemeldet'), ('new_report_comment', 'Kommentar wurde gemeldet'), ('an_is_to_be_checked', 'Vermittlung muss überprüft werden'), ('an_was_deactivated', 'Vermittlung wurde deaktiviert'), ('an_for_search_found', 'Vermittlung für Suche gefunden')], max_length=200, verbose_name='Benachrichtigungsgrund')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Titel')),
|
||||
('text', models.TextField(verbose_name='Inhalt')),
|
||||
('read', models.BooleanField(default=False)),
|
||||
('adoption_notice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung')),
|
||||
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.comment', verbose_name='Antwort')),
|
||||
('report', models.ForeignKey(help_text='Report auf den sich die Benachrichtigung bezieht.', on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.report', verbose_name='Report')),
|
||||
('user_related', models.ForeignKey(help_text='Useraccount auf den sich die Benachrichtigung bezieht.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Verwandter Useraccount')),
|
||||
('user_to_notify', 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='Nutzer*in')),
|
||||
],
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AdoptionNoticeNotification',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='BaseNotification',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='CommentNotification',
|
||||
),
|
||||
]
|
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-11 09:10
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0052_remove_basenotification_user_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='adoption_notice',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='notification_type',
|
||||
field=models.CharField(choices=[('new_user', 'Useraccount wurde erstellt'), ('new_report_an', 'Vermittlung wurde gemeldet'), ('new_report_comment', 'Kommentar wurde gemeldet'), ('an_is_to_be_checked', 'Vermittlung muss überprüft werden'), ('an_was_deactivated', 'Vermittlung wurde deaktiviert'), ('an_for_search_found', 'Vermittlung für Suche gefunden'), ('new_comment', 'Neuer Kommentar')], max_length=200, verbose_name='Benachrichtigungsgrund'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='report',
|
||||
field=models.ForeignKey(blank=True, help_text='Report auf den sich die Benachrichtigung bezieht.', null=True, on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.report', verbose_name='Report'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='user_related',
|
||||
field=models.ForeignKey(blank=True, help_text='Useraccount auf den sich die Benachrichtigung bezieht.', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Verwandter Useraccount'),
|
||||
),
|
||||
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='Nutzer*in'),
|
||||
),
|
||||
]
|
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-11 11:47
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0053_alter_notification_adoption_notice_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='comment',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.comment', verbose_name='Antwort'),
|
||||
),
|
||||
]
|
@@ -11,9 +11,10 @@ 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
|
||||
|
||||
from .tools import misc, geo
|
||||
from notfellchen.settings import MEDIA_URL
|
||||
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
|
||||
|
||||
@@ -61,16 +62,12 @@ class Location(models.Model):
|
||||
if self.city and self.postcode:
|
||||
return f"{self.city} ({self.postcode})"
|
||||
else:
|
||||
return f"{self.name} ({self.latitude:.5}, {self.longitude:.5})"
|
||||
return f"{self.name}"
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return (self.latitude, self.longitude)
|
||||
|
||||
@property
|
||||
def str_hr(self):
|
||||
return f"{self.name.split(',')[0]}"
|
||||
|
||||
@staticmethod
|
||||
def get_location_from_string(location_string):
|
||||
try:
|
||||
@@ -131,7 +128,7 @@ class RescueOrganization(models.Model):
|
||||
default=AllowUseOfMaterialsChices.USE_MATERIALS_NOT_ASKED,
|
||||
choices=AllowUseOfMaterialsChices.choices,
|
||||
verbose_name=_('Erlaubt Nutzung von Inhalten'))
|
||||
location_string = models.CharField(max_length=200, verbose_name=_("Ort der Organisation"))
|
||||
location_string = models.CharField(max_length=200, verbose_name=_("Ort der Organisation"), null=True, blank=True, )
|
||||
location = models.ForeignKey(Location, on_delete=models.PROTECT, blank=True, null=True)
|
||||
instagram = models.URLField(null=True, blank=True, verbose_name=_('Instagram Profil'))
|
||||
facebook = models.URLField(null=True, blank=True, verbose_name=_('Facebook Profil'))
|
||||
@@ -149,10 +146,19 @@ class RescueOrganization(models.Model):
|
||||
external_source_identifier = models.CharField(max_length=200, null=True, blank=True,
|
||||
choices=ExternalSourceChoices.choices,
|
||||
verbose_name=_('External Source Identifier'))
|
||||
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"))
|
||||
parent_org = models.ForeignKey("RescueOrganization", on_delete=models.PROTECT, blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('external_object_identifier', 'external_source_identifier',)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.location is None and self.location_string is None:
|
||||
raise ValidationError(_('Location or Location String must be set'))
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("rescue-organization-detail", args=[str(self.pk)])
|
||||
|
||||
@@ -173,6 +179,8 @@ class RescueOrganization(models.Model):
|
||||
return ""
|
||||
if len(self.description) > 200:
|
||||
return self.description[:200] + _(f" ... [weiterlesen]({self.get_absolute_url()})")
|
||||
else:
|
||||
return self.description
|
||||
|
||||
def set_checked(self):
|
||||
self.last_checked = timezone.now()
|
||||
@@ -185,7 +193,18 @@ class RescueOrganization(models.Model):
|
||||
|
||||
@property
|
||||
def species_urls(self):
|
||||
return SpeciesSpecificURL.objects.filter(organization=self)
|
||||
return SpeciesSpecificURL.objects.filter(rescue_organization=self)
|
||||
|
||||
@property
|
||||
def has_contact_data(self):
|
||||
"""
|
||||
Returns true if at least one type of contact data is available.
|
||||
"""
|
||||
return self.instagram or self.facebook or self.website or self.phone_number or self.email or self.fediverse_profile
|
||||
|
||||
def set_exclusion_from_checks(self):
|
||||
self.exclude_from_check = True
|
||||
self.save()
|
||||
|
||||
|
||||
# Admins can perform all actions and have the highest trust associated with them
|
||||
@@ -237,10 +256,10 @@ class User(AbstractUser):
|
||||
return self.get_absolute_url()
|
||||
|
||||
def get_unread_notifications(self):
|
||||
return BaseNotification.objects.filter(user=self, read=False)
|
||||
return Notification.objects.filter(user=self, read=False)
|
||||
|
||||
def get_num_unread_notifications(self):
|
||||
return BaseNotification.objects.filter(user=self, read=False).count()
|
||||
return Notification.objects.filter(user=self, read=False).count()
|
||||
|
||||
@property
|
||||
def adoption_notices(self):
|
||||
@@ -303,7 +322,9 @@ class AdoptionNotice(models.Model):
|
||||
verbose_name=_('Organisation'))
|
||||
further_information = models.URLField(null=True, blank=True,
|
||||
verbose_name=_('Link zu mehr Informationen'),
|
||||
help_text=_("Verlinke hier die Quelle der Vermittlung (z.B. die Website des Tierheims"))
|
||||
help_text=_(
|
||||
"Verlinke hier die Quelle der Vermittlung (z.B. die Website des "
|
||||
"Tierheims)"))
|
||||
group_only = models.BooleanField(default=False, verbose_name=_('Ausschließlich Gruppenadoption'))
|
||||
photos = models.ManyToManyField(Image, blank=True)
|
||||
location_string = models.CharField(max_length=200, verbose_name=_("Ortsangabe"))
|
||||
@@ -368,9 +389,9 @@ class AdoptionNotice(models.Model):
|
||||
"""Returns the url to access a detailed page for the adoption notice."""
|
||||
return reverse('adoption-notice-detail', args=[str(self.id)])
|
||||
|
||||
def get_absolute_url_bulma(self):
|
||||
"""Returns the url to access a detailed page for the adoption notice."""
|
||||
return reverse('adoption-notice-detail-bulma', args=[str(self.id)])
|
||||
def get_full_url(self):
|
||||
"""Returns the url including protocol and domain"""
|
||||
return f"{base_url}{self.get_absolute_url()}"
|
||||
|
||||
def get_report_url(self):
|
||||
"""Returns the url to report an adoption notice."""
|
||||
@@ -428,13 +449,6 @@ class AdoptionNotice(models.Model):
|
||||
distance = geo.calculate_distance_between_coordinates(self.position, position)
|
||||
return distance < max_distance
|
||||
|
||||
@property
|
||||
def link_to_more_information(self):
|
||||
from urllib.parse import urlparse
|
||||
|
||||
domain = urlparse(self.further_information).netloc
|
||||
return f"<a href='{self.further_information}'>{domain}</a>"
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
if not hasattr(self, 'adoptionnoticestatus'):
|
||||
@@ -469,7 +483,11 @@ class AdoptionNotice(models.Model):
|
||||
for subscription in self.get_subscriptions():
|
||||
notification_title = _("Vermittlung deaktiviert:") + f" {self.name}"
|
||||
text = _("Die folgende Vermittlung wurde deaktiviert: ") + f"[{self.name}]({self.get_absolute_url()})"
|
||||
BaseNotification.objects.create(user=subscription.owner, text=text, title=notification_title)
|
||||
Notification.objects.create(user_to_notify=subscription.owner,
|
||||
notification_type=NotificationTypeChoices.AN_WAS_DEACTIVATED,
|
||||
adoption_notice=self,
|
||||
text=text,
|
||||
title=notification_title)
|
||||
|
||||
|
||||
class AdoptionNoticeStatus(models.Model):
|
||||
@@ -892,20 +910,48 @@ class Comment(models.Model):
|
||||
return self.adoption_notice.get_absolute_url()
|
||||
|
||||
|
||||
class BaseNotification(models.Model):
|
||||
class NotificationTypeChoices(models.TextChoices):
|
||||
NEW_USER = "new_user", _("Useraccount wurde erstellt")
|
||||
NEW_REPORT_AN = "new_report_an", _("Vermittlung wurde gemeldet")
|
||||
NEW_REPORT_COMMENT = "new_report_comment", _("Kommentar wurde gemeldet")
|
||||
AN_IS_TO_BE_CHECKED = "an_is_to_be_checked", _("Vermittlung muss überprüft werden")
|
||||
AN_WAS_DEACTIVATED = "an_was_deactivated", _("Vermittlung wurde deaktiviert")
|
||||
AN_FOR_SEARCH_FOUND = "an_for_search_found", _("Vermittlung für Suche gefunden")
|
||||
NEW_COMMENT = "new_comment", _("Neuer Kommentar")
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
read_at = models.DateTimeField(blank=True, null=True, verbose_name=_("Gelesen am"))
|
||||
notification_type = models.CharField(max_length=200,
|
||||
choices=NotificationTypeChoices.choices,
|
||||
verbose_name=_('Benachrichtigungsgrund'))
|
||||
title = models.CharField(max_length=100, verbose_name=_("Titel"))
|
||||
text = models.TextField(verbose_name="Inhalt")
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
user_to_notify = models.ForeignKey(User,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Nutzer*in'),
|
||||
help_text=_("Useraccount der Benachrichtigt wird"),
|
||||
related_name='user')
|
||||
read = models.BooleanField(default=False)
|
||||
comment = models.ForeignKey(Comment, blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Antwort'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Vermittlung'))
|
||||
user_related = models.ForeignKey(User,
|
||||
blank=True, null=True,
|
||||
on_delete=models.CASCADE, verbose_name=_('Verwandter Useraccount'),
|
||||
help_text=_("Useraccount auf den sich die Benachrichtigung bezieht."))
|
||||
report = models.ForeignKey(Report,
|
||||
blank=True, null=True,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Report'),
|
||||
help_text=_("Report auf den sich die Benachrichtigung bezieht."))
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.user}] {self.title} ({self.created_at})"
|
||||
return f"[{self.user_to_notify}] {self.title} ({self.created_at})"
|
||||
|
||||
def get_absolute_url(self):
|
||||
self.user.get_notifications_url()
|
||||
self.user_to_notify.get_notifications_url()
|
||||
|
||||
def mark_read(self):
|
||||
self.read = True
|
||||
@@ -913,22 +959,6 @@ class BaseNotification(models.Model):
|
||||
self.save()
|
||||
|
||||
|
||||
class CommentNotification(BaseNotification):
|
||||
comment = models.ForeignKey(Comment, on_delete=models.CASCADE, verbose_name=_('Antwort'))
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.comment.get_absolute_url
|
||||
|
||||
|
||||
class AdoptionNoticeNotification(BaseNotification):
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('Vermittlung'))
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.adoption_notice.get_absolute_url
|
||||
|
||||
|
||||
class Subscriptions(models.Model):
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('AdoptionNotice'))
|
||||
@@ -961,7 +991,7 @@ class Timestamp(models.Model):
|
||||
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("Zeitstempel"))
|
||||
data = models.CharField(max_length=2000, blank=True, null=True)
|
||||
|
||||
def ___str__(self):
|
||||
def __str__(self):
|
||||
return f"[{self.key}] - {self.timestamp.strftime('%H:%M:%S %d-%m-%Y ')} - {self.data}"
|
||||
|
||||
|
||||
@@ -970,6 +1000,18 @@ class SpeciesSpecificURL(models.Model):
|
||||
Model that allows to specify a URL for a rescue organization where a certain species can be found
|
||||
"""
|
||||
species = models.ForeignKey(Species, on_delete=models.CASCADE, verbose_name=_("Tierart"))
|
||||
rescues_organization = models.ForeignKey(RescueOrganization, on_delete=models.CASCADE,
|
||||
verbose_name=_("Tierschutzorganisation"))
|
||||
rescue_organization = models.ForeignKey(RescueOrganization, on_delete=models.CASCADE,
|
||||
verbose_name=_("Tierschutzorganisation"))
|
||||
url = models.URLField(verbose_name=_("Tierartspezifische URL"))
|
||||
|
||||
|
||||
class SpeciesSpecialization(models.Model):
|
||||
"""
|
||||
Model that allows to specify if a rescue organization has a specialization for dedicated species
|
||||
"""
|
||||
species = models.ForeignKey(Species, on_delete=models.CASCADE, verbose_name=_("Tierart"))
|
||||
rescue_organization = models.ForeignKey(RescueOrganization, on_delete=models.CASCADE,
|
||||
verbose_name=_("Tierschutzorganisation"))
|
||||
|
||||
def __str__(self):
|
||||
return f"{_('Spezialisierung')} {self.species}"
|
||||
|
@@ -1,23 +1,20 @@
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from fellchensammlung.models import BaseNotification, CommentNotification, User, TrustLevel, RescueOrganization
|
||||
from fellchensammlung.models import Notification, User, TrustLevel, RescueOrganization, \
|
||||
NotificationTypeChoices
|
||||
from .tasks import task_send_notification_email
|
||||
from notfellchen.settings import host
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
@receiver(post_save, sender=CommentNotification)
|
||||
def comment_notification_receiver(sender, instance: BaseNotification, created: bool, **kwargs):
|
||||
base_notification_receiver(sender, instance, created, **kwargs)
|
||||
|
||||
|
||||
@receiver(post_save, sender=BaseNotification)
|
||||
def base_notification_receiver(sender, instance: BaseNotification, created: bool, **kwargs):
|
||||
if not created or not instance.user.email_notifications:
|
||||
@receiver(post_save, sender=Notification)
|
||||
def base_notification_receiver(sender, instance: Notification, created: bool, **kwargs):
|
||||
if not created or not instance.user_to_notify.email_notifications:
|
||||
return
|
||||
else:
|
||||
task_send_notification_email.delay(instance.pk)
|
||||
|
||||
|
||||
@receiver(post_save, sender=RescueOrganization)
|
||||
def rescue_org_receiver(sender, instance: RescueOrganization, created: bool, **kwargs):
|
||||
if instance.location:
|
||||
@@ -40,5 +37,9 @@ def notification_new_user(sender, instance: User, created: bool, **kwargs):
|
||||
link_text = f"Um alle Details zu sehen, geh bitte auf: {user_url}"
|
||||
body_text = new_user_text + user_detail_text + link_text
|
||||
for moderator in User.objects.filter(trust_level__gt=TrustLevel.MODERATOR):
|
||||
notification = BaseNotification.objects.create(title=subject, text=body_text, user=moderator)
|
||||
notification = Notification.objects.create(title=subject,
|
||||
text=body_text,
|
||||
notification_type=NotificationTypeChoices.NEW_USER,
|
||||
user_to_notify=moderator,
|
||||
user_related=instance)
|
||||
notification.save()
|
||||
|
29
src/fellchensammlung/registration_views.py
Normal file
29
src/fellchensammlung/registration_views.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from django.utils.html import strip_tags
|
||||
from django_registration.backends.activation.views import RegistrationView
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class HTMLMailRegistrationView(RegistrationView):
|
||||
def send_activation_email(self, user):
|
||||
"""
|
||||
overwrites the function in django registration
|
||||
"""
|
||||
activation_key = self.get_activation_key(user)
|
||||
context = self.get_email_context(activation_key)
|
||||
context["user"] = user
|
||||
subject = render_to_string(
|
||||
template_name=self.email_subject_template,
|
||||
context=context,
|
||||
request=self.request,
|
||||
)
|
||||
# Force subject to a single line to avoid header-injection issues.
|
||||
subject = "".join(subject.splitlines())
|
||||
message = render_to_string(
|
||||
template_name=self.email_body_template,
|
||||
context=context,
|
||||
request=self.request,
|
||||
)
|
||||
plain_message = strip_tags(message)
|
||||
user.email_user(subject, plain_message, settings.DEFAULT_FROM_EMAIL, html_message=message)
|
@@ -1,60 +0,0 @@
|
||||
/***************/
|
||||
/* MAIN COLORS */
|
||||
/***************/
|
||||
|
||||
:root {
|
||||
--primary-light-one: #5daa68;
|
||||
--primary-light-two: #4a9455;
|
||||
--primary-semidark-one: #356c3c;
|
||||
--primary-dark-one: #17311b;
|
||||
--secondary-light-one: #faf1cf;
|
||||
--secondary-light-two: #e1d7b5;
|
||||
--background-one: var(--primary-light-one);
|
||||
--background-two: var(--primary-light-two);
|
||||
--background-three: var(--secondary-light-one);
|
||||
--background-four: var(--primary-dark-one);
|
||||
--highlight-one: var(--primary-dark-one);
|
||||
--highlight-one-text: var(--secondary-light-one);
|
||||
--highlight-two: var(--primary-semidark-one);
|
||||
--text-one: var(--secondary-light-one);
|
||||
--shadow-one: var(--primary-dark-one);
|
||||
--text-two: var(--primary-dark-one);
|
||||
--text-three: var(--primary-light-one);
|
||||
--shadow-three: var(--primary-dark-one);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/*******/
|
||||
/* MAP */
|
||||
/*******/
|
||||
|
||||
.map {
|
||||
border-radius: 8px;
|
||||
width:100%;
|
||||
height:100%
|
||||
}
|
||||
|
||||
.marker {
|
||||
background-image: url('../img/logo_transparent.png');
|
||||
background-size: cover;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.animal-shelter-marker {
|
||||
background-image: url('../img/animal_shelter.png');
|
||||
!important;
|
||||
}
|
||||
|
||||
.maplibregl-popup {
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
.map-in-content #map {
|
||||
max-height: 500px;
|
||||
width: 90%;
|
||||
}
|
File diff suppressed because one or more lines are too long
322
src/fellchensammlung/static/fellchensammlung/css/main.scss
Normal file
322
src/fellchensammlung/static/fellchensammlung/css/main.scss
Normal file
@@ -0,0 +1,322 @@
|
||||
$primary: #6CD4FF;
|
||||
$link: #292a2c;
|
||||
$grey-light: #c4c6ce;
|
||||
$grey-dark: #262728;
|
||||
$confirm: hsl(133deg, 100%, calc(41% + 0%));
|
||||
|
||||
// Path to Bulma's sass folder
|
||||
@use "bulma/sass" with (
|
||||
$family-primary: '"Nunito", sans-serif',
|
||||
$grey-dark: $grey-dark,
|
||||
$grey-light: $grey-light,
|
||||
$primary: $primary,
|
||||
$link: $link,
|
||||
$control-border-width: 2px,
|
||||
$input-shadow: none
|
||||
);
|
||||
|
||||
@use "bulma/sass/utilities/css-variables" as cv;
|
||||
|
||||
@include cv.system-theme($name: "dark") {
|
||||
.navbar-item > img {
|
||||
background-color: $grey-light !important;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.card-header {
|
||||
background-color: $grey-dark;
|
||||
}
|
||||
a.card-footer-item.is-danger {
|
||||
color: black;
|
||||
}
|
||||
.tag {
|
||||
color: $grey-dark;
|
||||
background-color: $grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// General Styles
|
||||
|
||||
.main-content {
|
||||
margin: auto;
|
||||
max-width: 80em;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
p > a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
p > a.button {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Cards
|
||||
|
||||
.card-header {
|
||||
background-color: $primary;
|
||||
}
|
||||
|
||||
|
||||
// Search form suggestion dropdown
|
||||
|
||||
#location-result-list {
|
||||
display: inline; //ensures that the dropdown is not restricted in width WTF
|
||||
}
|
||||
|
||||
.result-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.result-item:hover {
|
||||
background-color: #b2aaaa;
|
||||
}
|
||||
|
||||
|
||||
// Toggle switch
|
||||
|
||||
.toggle-switch {
|
||||
display: inline-block;
|
||||
background: #ccc;
|
||||
border-radius: 16px;
|
||||
width: 58px;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
transition: background 0.25s;
|
||||
}
|
||||
|
||||
.toggle-switch:before, .toggle-switch:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.toggle-switch:before {
|
||||
display: block;
|
||||
background: linear-gradient(to bottom, #fff 0%, #eee 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
transition: left 0.25s;
|
||||
}
|
||||
|
||||
.toggle:hover .toggle-switch:before {
|
||||
background: linear-gradient(to bottom, #fff 0%, #fff 100%);
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.checked + .toggle-switch {
|
||||
background: #56c080;
|
||||
}
|
||||
|
||||
.checked + .toggle-switch:before {
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.toggle-checkbox {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.slider-label {
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
|
||||
// Button in card footer
|
||||
.card-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-footer .card-footer-item.is-confirm {
|
||||
background-color: $confirm;
|
||||
}
|
||||
|
||||
.card-footer .card-footer-item.is-confirm:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.card-footer .card-footer-item.is-danger {
|
||||
background-color: sass.$danger;
|
||||
}
|
||||
|
||||
.card-footer .card-footer-item.is-danger:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
|
||||
.card-footer .card-footer-item.is-warning {
|
||||
background-color: sass.$warning;
|
||||
}
|
||||
|
||||
.card-footer .card-footer-item.is-warning:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
/*******/
|
||||
/* MAP */
|
||||
/*******/
|
||||
|
||||
.map {
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.marker {
|
||||
background-image: url('../img/logo_transparent.png');
|
||||
background-size: cover;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.animal-shelter-marker {
|
||||
background-image: url('../img/animal_shelter.png');
|
||||
}
|
||||
|
||||
.map-in-content #map {
|
||||
max-height: 500px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
.maplibregl-popup {
|
||||
max-width: 280px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.maplibregl-popup {
|
||||
max-width: 150px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.maplibregl-popup-close-button {
|
||||
all: unset; /* Remove all inherited styles */
|
||||
font-size: 1.2rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.5rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
|
||||
/*****
|
||||
IMAGES
|
||||
*****/
|
||||
|
||||
.gallery .main-photo img {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover; /* Crops the images */
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.thumbnail-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.thumbnail img {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
object-fit: cover; /* Crops the images */
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Ensure each thumbnail takes equal width */
|
||||
.thumbnail {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
AN Cards
|
||||
*/
|
||||
|
||||
.an-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
// Fonts
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../fonts/nunito.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
|
||||
.new-animal-ad fieldset {
|
||||
border-top: 4px solid var(--bulma-text-weak);
|
||||
margin-top: 2em;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.new-animal-ad * {
|
||||
transition: all ease 0.5s;
|
||||
}
|
||||
|
||||
.new-animal-ad .open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.new-animal-ad .closed {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.new-animal-ad legend {
|
||||
font-weight: bold;
|
||||
padding-right: 0.2em;
|
||||
color: var(--bulma-label-color);
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
.feedback-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.feedback-add-new {
|
||||
width: 40ch;
|
||||
min-height: 40ch;
|
||||
padding: 1.5em;
|
||||
background-color: var(--bulma-info-on-scheme);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.feedback-add-new.error {
|
||||
background-color: var(--bulma-danger-on-scheme);
|
||||
}
|
||||
|
||||
.feedback-add-new.success {
|
||||
background-color: var(--bulma-success-on-scheme);
|
||||
}
|
||||
|
@@ -1,27 +1,15 @@
|
||||
:root {
|
||||
--primary-light-one: #5daa68;
|
||||
--primary-light-two: #4a9455;
|
||||
--primary-dark-one: #17311b;
|
||||
--secondary-light-one: #faf1cf;
|
||||
--secondary-light-two: #e1d7b5;
|
||||
--background-one: var(--primary-light-one);
|
||||
--background-two: var(--primary-light-two);
|
||||
--background-three: var(--secondary-light-one);
|
||||
--background-four: var(--primary-dark-one);
|
||||
--highlight-one: var(--primary-dark-one);
|
||||
--highlight-one-text: var(--secondary-light-one);
|
||||
--text-one: var(--secondary-light-one);
|
||||
--shadow-one: var(--primary-dark-one);
|
||||
--text-two: var(--primary-dark-one);
|
||||
--text-three: var(--primary-light-one);
|
||||
--shadow-three: var(--primary-dark-one);
|
||||
--primary: #6CD4FF;
|
||||
--link: #292a2c;
|
||||
--grey-light: #c4c6ce;
|
||||
--grey-dark: #262728;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-one);
|
||||
color: var(--text-one);
|
||||
background-color: hsl(221, 14%, 100%)r;
|
||||
color: #000000;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -32,24 +20,22 @@ body {
|
||||
|
||||
|
||||
alert-box {
|
||||
color: var(--highlight-one);
|
||||
display: block;
|
||||
margin: 3rem 0;
|
||||
padding: 2rem 3rem;
|
||||
border: 1px solid var(--highlight-one);
|
||||
border-left-width: .5rem;
|
||||
border-radius: .4rem;
|
||||
background-color: var(--background-three);
|
||||
background-color: var(--primary);
|
||||
|
||||
a {
|
||||
color: var(--text-three);
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +51,7 @@ a {
|
||||
margin: 1rem;
|
||||
padding: 5px;
|
||||
border-radius: .4rem;
|
||||
background-color: var(--background-one);
|
||||
border: 3px solid var(--primary);
|
||||
}
|
||||
|
||||
.post-summary h1 {
|
||||
@@ -79,8 +65,7 @@ a {
|
||||
}
|
||||
|
||||
.navigation-sticky {
|
||||
background-color: var(--secondary-light-one);
|
||||
color: var(--primary-light-one);
|
||||
background-color: var(--primary);
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
border-bottom-right-radius: 8px;
|
||||
|
BIN
src/fellchensammlung/static/fellchensammlung/fonts/nunito.woff2
Normal file
BIN
src/fellchensammlung/static/fellchensammlung/fonts/nunito.woff2
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
Binary file not shown.
After Width: | Height: | Size: 546 B |
@@ -0,0 +1,423 @@
|
||||
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();
|
||||
});
|
||||
});
|
@@ -19,3 +19,6 @@ function geojson_to_searchable_string(location) {
|
||||
return ifdef(location.properties.name, "", ", ") + ifdef(location.properties.street, "", ifdef(location.properties.housenumber, " ",", ")) + ifdef(location.properties.city, "", ", ") + ifdef(location.properties.country, "", "")
|
||||
}
|
||||
|
||||
function truncate(str, n, url){
|
||||
return (str.length > n) ? str.slice(0, n-1) + '<a href="' + url + '">…</a>' : str;
|
||||
};
|
||||
|
2
src/fellchensammlung/static/fellchensammlung/js/jquery.min.js
vendored
Normal file
2
src/fellchensammlung/static/fellchensammlung/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
import PhotoSwipeLightbox from 'https://unpkg.com/photoswipe/dist/photoswipe-lightbox.esm.js';
|
||||
import PhotoSwipeLightbox from './photoswipe-lightbox.esm.js';
|
||||
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '#my-gallery',
|
||||
gallery: '.gallery',
|
||||
children: 'a',
|
||||
pswpModule: () => import('https://unpkg.com/photoswipe'),
|
||||
});
|
||||
|
@@ -29,4 +29,44 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
$notification.parentNode.removeChild($notification);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
(document.querySelectorAll('.message .delete') || []).forEach(($delete) => {
|
||||
$delete.addEventListener('click', () => {
|
||||
const message = $delete.closest('.message');
|
||||
if (message) {
|
||||
message.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
// DROPDOWNS
|
||||
const $clickableDropdowns = document.querySelectorAll(
|
||||
".dropdown:not(.is-hoverable)",
|
||||
);
|
||||
|
||||
if ($clickableDropdowns.length > 0) {
|
||||
$clickableDropdowns.forEach(($dropdown) => {
|
||||
if (!$dropdown.querySelector("button")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dropdown.querySelector("button").addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
$dropdown.classList.toggle("is-active");
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
closeDropdowns();
|
||||
});
|
||||
}
|
||||
|
||||
function closeDropdowns() {
|
||||
$clickableDropdowns.forEach(($el) => {
|
||||
$el.classList.remove("is-active");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@@ -48,6 +48,7 @@ def post_adoption_notice_save(pk):
|
||||
notify_search_subscribers(instance, only_if_active=True)
|
||||
notify_of_AN_to_be_checked(instance)
|
||||
|
||||
|
||||
@celery_app.task(name="tools.healthcheck")
|
||||
def task_healthcheck():
|
||||
healthcheck_ok()
|
||||
@@ -58,9 +59,10 @@ def task_healthcheck():
|
||||
def task_send_notification_email(notification_pk):
|
||||
send_notification_email(notification_pk)
|
||||
|
||||
|
||||
@celery_app.task(name="commit.post_rescue_org_save")
|
||||
def post_rescue_org_save(pk):
|
||||
instance = RescueOrganization.objects.get(pk=pk)
|
||||
Location.add_location_to_object(instance)
|
||||
set_timestamp("add_rescue_org_location")
|
||||
logging.info(f"Location was added to Rescue Organization {pk}")
|
||||
logging.info(f"Location was added to Rescue Organization {pk}")
|
||||
|
@@ -1,55 +1,38 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Über uns und Regeln" %}</title>{% endblock %}
|
||||
{% block title %}<title>{% translate "Über uns" %}</title>{% endblock %}
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{% translate "Über uns" %} - Notfellchen"/>
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="description" content="{% translate 'Erfahre mehr über das Notfellchen-Projekt. Im FAQ werden häufige Fragen beantwortet.' %}">
|
||||
{% endblock %}
|
||||
{% block og_description %}
|
||||
<meta name="og:description" content="{% translate 'Erfahre mehr über das Notfellchen-Projekt. Im FAQ werden häufige Fragen beantwortet.' %}">
|
||||
{% endblock %}
|
||||
{% block canonical_url %}{% host %}{% url 'about' %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% if about_us %}
|
||||
<div class="card">
|
||||
<h1>{{ about_us.title }}</h1>
|
||||
<p>
|
||||
<div class="block">
|
||||
<h1 class="title is-1">{{ about_us.title }}</h1>
|
||||
<div class="content">
|
||||
{{ about_us.content | render_markdown }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Regeln" %}</h2>
|
||||
{% include "fellchensammlung/lists/list-rules.html" %}
|
||||
|
||||
{% if faq %}
|
||||
<div class="card">
|
||||
<h2>{{ faq.title }}</h2>
|
||||
<p>
|
||||
<div class="card-header">
|
||||
<h2 class="card-header-title">{{ faq.title }}</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{{ faq.content | render_markdown }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if privacy_statement %}
|
||||
<div class="card">
|
||||
<h2>{{ privacy_statement.title }}</h2>
|
||||
<p>
|
||||
{{ privacy_statement.content | render_markdown }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if terms_of_service %}
|
||||
<div class="card">
|
||||
<h2>{{ terms_of_service.title }}</h2>
|
||||
<p>
|
||||
{{ terms_of_service.content | render_markdown }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if imprint %}
|
||||
<div class="card">
|
||||
<h2>{{ imprint.title }}</h2>
|
||||
<p>
|
||||
{{ imprint.content | render_markdown }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@@ -1,13 +1,55 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Tierschutzorganisationen" %}</title>{% endblock %}
|
||||
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{% translate "Tierschutzorganisationen" %} - Notfellchen"/>
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="description" content="{% translate 'Finde Tierschutzorganisationen in deiner Gegend.' %}">
|
||||
{% endblock %}
|
||||
{% block og_description %}
|
||||
<meta name="og:description" content="{% translate 'Finde Tierschutzorganisationen in deiner Gegend.' %}">
|
||||
{% endblock %}
|
||||
{% block canonical_url %}{% host %}{% url 'rescue-organizations' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-cards">
|
||||
<div class="card">
|
||||
<div class="block">
|
||||
<div style="height: 70vh">
|
||||
{% include "fellchensammlung/partials/partial-map.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "fellchensammlung/lists/list-animal-shelters.html" %}
|
||||
<div class="block">
|
||||
{% with rescue_organizations=rescue_organizations_to_list %}
|
||||
{% include "fellchensammlung/lists/list-animal-shelters.html" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<nav class="pagination" role="navigation" aria-label="{% trans 'Paginierung' %}">
|
||||
{% if rescue_organizations_to_list.has_previous %}
|
||||
<a class="pagination-previous"
|
||||
href="?page={{ rescue_organizations_to_list.previous_page_number }}">{% trans 'Vorherige' %}</a>
|
||||
{% endif %}
|
||||
{% if rescue_organizations_to_list.has_next %}
|
||||
<a class="pagination-next" href="?page={{ rescue_organizations_to_list.next_page_number }}">{% trans 'Nächste' %}</a>
|
||||
{% endif %}
|
||||
<ul class="pagination-list">
|
||||
{% for page in elided_page_range %}
|
||||
{% if page != "…" %}
|
||||
<li>
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination-link {% if page == rescue_organizations_to_list.number %} is-current{% endif %}"
|
||||
aria-label="{% blocktranslate %}Gehe zu Seite {{ page }}.{% endblocktranslate %}">
|
||||
{{ page }}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<span aria-hidden="true" class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
@@ -1,25 +1,31 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE%}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
{% block title %}{% endblock %}
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{% translate "Farbratten aus dem Tierschutz finden und adoptieren" %}">
|
||||
{% 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/bulma-styles.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fellchensammlung/css/bulma.min.css' %}">
|
||||
<link rel="stylesheet" href="https://unpkg.com/photoswipe@5.2.2/dist/photoswipe.css">
|
||||
<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">
|
||||
|
||||
<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' %}">
|
||||
@@ -29,15 +35,15 @@
|
||||
</head>
|
||||
<body>
|
||||
{% block header %}
|
||||
{% include "fellchensammlung/bulma-header.html" %}
|
||||
{% include "fellchensammlung/header.html" %}
|
||||
{% endblock %}
|
||||
<div class="content">
|
||||
<div class="main-content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
|
||||
{% block footer %}
|
||||
{% include "fellchensammlung/bulma-footer.html" %}
|
||||
{% include "fellchensammlung/footer.html" %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
@@ -1,37 +0,0 @@
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
{% block title %}{% endblock %}
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{% translate "Farbratten aus dem Tierschutz finden und adoptieren" %}">
|
||||
<!-- Add additional CSS in static file -->
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'fellchensammlung/css/styles.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">
|
||||
|
||||
<script src="{% static 'fellchensammlung/js/custom.js' %}"></script>
|
||||
|
||||
<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 %}
|
||||
</head>
|
||||
<body>
|
||||
{% block header %}
|
||||
{% include "fellchensammlung/header.html" %}
|
||||
{% endblock %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-10 content-box">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -1,27 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Über uns" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if about_us %}
|
||||
<div class="block">
|
||||
<h1 class="title is-1">{{ about_us.title }}</h1>
|
||||
<div class="content">
|
||||
{{ about_us.content | render_markdown }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if faq %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-header-title">{{ faq.title }}</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{{ faq.content | render_markdown }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@@ -1,43 +0,0 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="{% url 'index-bulma' %}">
|
||||
<img src="{% static 'fellchensammlung/img/logo_transparent.png' %}" alt="{% trans 'Notfellchen Logo' %}">
|
||||
<h1 class="title is-4">notfellchen.org</h1>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="{% url 'search-bulma' %}">
|
||||
<i class="fas fa-search"></i> {% translate 'Suchen' %}
|
||||
</a>
|
||||
|
||||
<a class="navbar-item" href="{% url "add-adoption-bulma" %}">
|
||||
<i class="fas fa-feather"></i> {% translate 'Vermittlung hinzufügen' %}
|
||||
</a>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary" href="{% url "django_registration_register" %}">
|
||||
<strong>{% translate "Registrieren" %}</strong>
|
||||
</a>
|
||||
<a class="button is-light" href="{% url "login" %}">
|
||||
<strong>{% translate "Login" %}</strong>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
@@ -1,34 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Notfellchen - Farbratten aus dem Tierschutz adoptieren" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% for announcement in announcements %}
|
||||
{% include "fellchensammlung/partials/bulma-partial-announcement.html" %}
|
||||
{% endfor %}
|
||||
{% if introduction %}
|
||||
<h1>{{ introduction.title }}</h1>
|
||||
{{ introduction.content | render_markdown }}
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Aktuelle Vermittlungen" %}</h2>
|
||||
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/lists/bulma-list-adoption-notices.html" %}
|
||||
<a class="button is-primary" href="{% url 'search' %}">{% translate "Mehr Vermittlungen" %}</a>
|
||||
</div>
|
||||
|
||||
<div class="block" style="height: 50vh">
|
||||
{% include "fellchensammlung/partials/bulma-partial-map.html" %}
|
||||
</div>
|
||||
|
||||
{% if how_to %}
|
||||
<div class="card">
|
||||
<h1>{{ how_to.title }}</h1>
|
||||
{{ how_to.content | render_markdown }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
@@ -1,9 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}<title>{% translate "Karte" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div style="height:70vh">
|
||||
{% include "fellchensammlung/partials/bulma-partial-map.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
@@ -1,97 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}<title>{% translate "Suche" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% get_current_language as LANGUAGE_CODE_CURRENT %}
|
||||
<div class="columns">
|
||||
<div class="column is-two-thirds">
|
||||
<div style="height: 50vh">
|
||||
{% include "fellchensammlung/partials/bulma-partial-map.html" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<form class="block" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="longitude" maxlength="200" id="longitude">
|
||||
<input type="hidden" name="latitude" maxlength="200" id="latitude">
|
||||
<input type="hidden" id="place_id" name="place_id">
|
||||
<!--- https://docs.djangoproject.com/en/5.2/topics/forms/#reusable-form-templates -->
|
||||
{{ search_form }}
|
||||
<ul id="results"></ul>
|
||||
<button class="button is-primary" type="submit" value="search" name="search">
|
||||
<i class="fas fa-search"></i> {% trans 'Suchen' %}
|
||||
</button>
|
||||
{% if searched %}
|
||||
{% if subscribed_search %}
|
||||
<button class="button" type="submit" value="{{ subscribed_search.pk }}"
|
||||
name="unsubscribe_to_search">
|
||||
<i class="fas fa-bell-slash"></i> {% trans 'Suche nicht mehr abonnieren' %}
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="button" type="submit" name="subscribe_to_search">
|
||||
<i class="fas fa-bell"></i> {% trans 'Suche abonnieren' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</form>
|
||||
<div class="block">
|
||||
{% if place_not_found %}
|
||||
<div class="block notification is-warning">
|
||||
<p>
|
||||
{% trans 'Ort nicht gefunden' %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
{% include "fellchensammlung/lists/bulma-list-adoption-notices.html" %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const locationInput = document.getElementById('id_location_string');
|
||||
const resultsList = document.getElementById('results');
|
||||
const placeIdInput = document.getElementById('place_id');
|
||||
|
||||
locationInput.addEventListener('input', async function () {
|
||||
const query = locationInput.value.trim();
|
||||
|
||||
if (query.length < 3) {
|
||||
resultsList.innerHTML = ''; // Don't search for or show results if input is less than 3 characters
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`{{ geocoding_api_url }}/?q=${encodeURIComponent(query)}&limit=5&lang={{ LANGUAGE_CODE_CURRENT }}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data && data.features) {
|
||||
resultsList.innerHTML = ''; // Clear previous results
|
||||
|
||||
const locations = data.features.slice(0, 5); // Show only the first 5 results
|
||||
|
||||
locations.forEach(location => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.classList.add('result-item');
|
||||
listItem.textContent = geojson_to_summary(location);
|
||||
|
||||
// Add event when user clicks on a result location
|
||||
listItem.addEventListener('click', () => {
|
||||
|
||||
locationInput.value = geojson_to_searchable_string(location); // Set input field to selected location
|
||||
resultsList.innerHTML = ''; // Clear the results after selecting a location
|
||||
});
|
||||
|
||||
resultsList.appendChild(listItem);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching location data:', error);
|
||||
resultsList.innerHTML = '<li class="result-item">Error fetching data. Please try again.</li>';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@@ -1,120 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% block title %}<title>{% translate "Styleguide für Bulma" %}</title>{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Hello World
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Notfellchen bald mit <strong>Bulma</strong>?
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<img
|
||||
src="{% static 'fellchensammlung/img/example_rat_single.png' %}"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img
|
||||
src="https://bulma.io/assets/images/placeholders/96x96.png"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">John Smith</p>
|
||||
<p class="subtitle is-6">@johnsmith</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
Süße Ratte sucht Zuhause
|
||||
<a href="#">#responsive</a>
|
||||
<br/>
|
||||
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<img
|
||||
src="{% static 'fellchensammlung/img/example_rat_single.png' %}"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img
|
||||
src="https://bulma.io/assets/images/placeholders/96x96.png"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">John Smith</p>
|
||||
<p class="subtitle is-6">@johnsmith</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
Süßeste Ratte sucht Zuhause
|
||||
<a href="#">#responsive</a>
|
||||
<br/>
|
||||
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<img
|
||||
src="{% static 'fellchensammlung/img/example_rat_single.png' %}"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img
|
||||
src="https://bulma.io/assets/images/placeholders/96x96.png"
|
||||
alt="Placeholder image"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">John Smith</p>
|
||||
<p class="subtitle is-6">@johnsmith</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
Süßere Ratte sucht Zuhause
|
||||
<a href="#">#responsive</a>
|
||||
<br/>
|
||||
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
13
src/fellchensammlung/templates/fellchensammlung/contacts.vcf
Normal file
13
src/fellchensammlung/templates/fellchensammlung/contacts.vcf
Normal file
@@ -0,0 +1,13 @@
|
||||
{% for contact in contacts %}
|
||||
BEGIN:VCARD
|
||||
VERSION:4.0
|
||||
N:{{contact.name|safe}};;;;
|
||||
FN:{{contact.name|safe}}
|
||||
{% if contact.location %}GEO:geo:{{contact.location.latitude}},{{contact.location.longitude}}
|
||||
{% endif %}TEL;TYPE=work:{{ contact.phone_number }}
|
||||
{% if contact.email %}EMAIL:{{ contact.email }}
|
||||
{% endif %}REV:{{ current_time|date:'Ymd' }}T{{ current_time|date:'His' }}Z
|
||||
CATEGORIES:{{ categories }}
|
||||
{% if contact.website %}URL:{{ contact.website }}
|
||||
{% endif %}END:VCARD
|
||||
{% endfor %}
|
@@ -1,113 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}<title>{{ adoption_notice.name }}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-header-title title is-2">{{ adoption_notice.name }}</h1>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<!--- General Information --->
|
||||
<div class="grid">
|
||||
|
||||
<div class="cell">
|
||||
<h2><strong>{% translate "Ort" %}</strong></h2>
|
||||
<p>{% if adoption_notice.location %}
|
||||
{{ adoption_notice.location }}
|
||||
{% else %}
|
||||
{{ adoption_notice.location_string }}
|
||||
{% endif %}</p>
|
||||
</div>
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/bulma-sex-overview.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<!--- Images --->
|
||||
<div class="column block">
|
||||
<div class="card">
|
||||
<div class="grid card-content">
|
||||
<div class="cell" id="my-gallery">
|
||||
{% for photo in adoption_notice.get_photos %}
|
||||
<a href="{{ MEDIA_URL }}/{{ photo.image }}"
|
||||
data-pswp-width="{{ photo.image.width }}"
|
||||
data-pswp-height="{{ photo.image.height }}"
|
||||
target="_blank">
|
||||
<img style="height: 12rem" src="{{ MEDIA_URL }}/{{ photo.image }}"
|
||||
alt="{ photo.alt_text }}"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--- Description --->
|
||||
<div class="column block">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-header-title title is-2">{% translate "Beschreibung" %}</h1>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p class="expandable">{% if adoption_notice.description %}
|
||||
{{ adoption_notice.description | render_markdown }}
|
||||
{% else %}
|
||||
{% translate "Keine Beschreibung angegeben" %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{% if has_edit_permission %}
|
||||
<div class="card-footer-item">
|
||||
<div class="column">
|
||||
<a class="button is-primary is-light"
|
||||
href="{% url 'adoption-notice-add-photo' adoption_notice_id=adoption_notice.pk %}">
|
||||
{% translate 'Foto hinzufügen' %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-footer-item">
|
||||
<a class="button is-primary"
|
||||
href="{% url 'adoption-notice-edit' adoption_notice_id=adoption_notice.pk %}">
|
||||
{% translate 'Bearbeiten' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
{% for animal in adoption_notice.animals %}
|
||||
<div class="column">
|
||||
{% include "fellchensammlung/partials/bulma-partial-animal-card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</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"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/partials/bulma-partial-comment-section.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,202 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}<title>{{ adoption_notice.name }}</title>{% endblock %}
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{{ adoption_notice.name }} - Notfellchen"/>
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="description" content="{{ adoption_notice.description }}">
|
||||
{% endblock %}
|
||||
{% block og_description %}
|
||||
<meta name="og:description" content="{{ adoption_notice.description }}">
|
||||
{% endblock %}
|
||||
{% block canonical_url %}{% host %}
|
||||
{% url 'adoption-notice-detail' adoption_notice_id=adoption_notice.id %}{% endblock %}
|
||||
|
||||
{% block og_image %}
|
||||
{% if adoption_notice.get_photos %}
|
||||
<meta property="og:image" content="{{ MEDIA_URL }}{{ adoption_notice.get_photos.0.image }}"/>
|
||||
{% else %}
|
||||
<meta property="og:image" content="{% static 'fellchensammlung/img/link_preview.png' %}"/>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<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 has_edit_permission %}
|
||||
|
||||
<a class="dropdown-item">
|
||||
<i class="fas fa-check"
|
||||
aria-hidden="true"></i> {% trans 'Als aktiv bestätigen' %}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
href="{% url 'adoption-notice-edit' adoption_notice_id=adoption_notice.pk %}">
|
||||
<i class="fas fa-pencil"
|
||||
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"
|
||||
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"
|
||||
aria-hidden="true"></i> {% trans 'Tier hinzufügen' %}
|
||||
</a>
|
||||
<a class="dropdown-item">
|
||||
<i class="fas fa-circle-xmark"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--- General Information --->
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<div class="grid">
|
||||
{% 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>
|
||||
{% endif %}
|
||||
|
||||
<div class="cell">
|
||||
<span>
|
||||
<i class="fa-solid fa-location-dot fa-fw" aria-label="{% trans 'Ort' %}"></i>
|
||||
{% if adoption_notice.location %}
|
||||
{{ adoption_notice.location }}
|
||||
{% else %}
|
||||
{{ adoption_notice.location_string }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/sex-overview.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- Images and Description --->
|
||||
<div class="columns">
|
||||
<!--- Images --->
|
||||
{% if adoption_notice.get_photos %}
|
||||
<div class="column block">
|
||||
<div class="card">
|
||||
<div class="grid card-content">
|
||||
<div class="gallery">
|
||||
{% with photo=adoption_notice.get_photos.0 %}
|
||||
<div class="main-photo">
|
||||
<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 %}
|
||||
|
||||
<div class="thumbnail-row">
|
||||
{% 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!--- Description --->
|
||||
<div class="column block">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-header-title title is-4">{% translate "Beschreibung" %}</h1>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p class="expandable">{% if adoption_notice.description %}
|
||||
{{ adoption_notice.description | render_markdown }}
|
||||
{% else %}
|
||||
{% translate "Keine Beschreibung angegeben" %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
{% for animal in adoption_notice.animals %}
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/partials/partial-animal-card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/partials/partial-comment-section.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
@@ -2,7 +2,7 @@
|
||||
{% load i18n %}
|
||||
<div class="detail-animal"></div>
|
||||
<div class="detail-animal-header">
|
||||
<h1>{{ animal.name }}</h1>
|
||||
<h1 class="title is-1">{{ animal.name }}</h1>
|
||||
<div class="tag">{{ animal.species }}</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -18,7 +18,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<p>{{ animal.description }}</p>
|
||||
<h2>{% translate "Bilder" %}</h2>
|
||||
<h2 class="title is-2">{% translate "Bilder" %}</h2>
|
||||
<div class="photos">
|
||||
{% for image in animal.get_photos %}
|
||||
<div class="card-photo">
|
||||
@@ -28,5 +28,4 @@
|
||||
{% if not animal.get_photos %}
|
||||
{% translate "Keine Bilder" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
@@ -1,9 +1,9 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% if form_complete %}
|
||||
<h1>{% translate "Erfolgreich gemeldet" %}</h1>
|
||||
<h1 class="title is-2">{% translate "Erfolgreich gemeldet" %}</h1>
|
||||
{% blocktranslate %}
|
||||
Wenn du sehen willst welche Moderationsentscheidungen getroffen werden, schau zu einem späteren Zeitpunkt
|
||||
wieder auf dieser Seite vorbei.
|
||||
@@ -11,8 +11,10 @@
|
||||
<a href="mailto:info@notfellchen.org">info@notfellchen.org</a> Einspruch einlegen.
|
||||
{% endblocktranslate %}
|
||||
{% endif %}
|
||||
<hr>
|
||||
{% include "fellchensammlung/partials/partial-report.html" %}
|
||||
<h2>{% translate "Moderationsverlauf" %}</h2>
|
||||
<hr>
|
||||
<h1 class="title is-1">{% translate "Moderationsverlauf" %}</h1>
|
||||
{% if report.get_moderation_actions %}
|
||||
{% include "fellchensammlung/lists/list-moderation-action.html" %}
|
||||
{% else %}
|
||||
|
@@ -1,62 +1,57 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
{% load admin_urls %}
|
||||
|
||||
{% block title %}<title>{{ org.name }}</title>{% endblock %}
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{{ org.name }} - Notfellchen"/>
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="description" content="{{ org.name }}: {% translate 'Kontaktdaten und Tiere zur Adoption' %}">
|
||||
{% endblock %}
|
||||
{% block og_description %}
|
||||
<meta property="og:description" content="{{ org.name }}: {% translate 'Kontaktdaten und Tiere zur Adoption' %}">
|
||||
{% endblock %}
|
||||
{% block canonical_url %}{% host %}{% url 'rescue-organization-detail' rescue_organization_id=org.id %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-cards">
|
||||
<div class="card half">
|
||||
<h1>{{ org.name }}</h1>
|
||||
|
||||
<b><i class="fa-solid fa-location-dot"></i></b>
|
||||
{% if org.location %}
|
||||
{{ org.location.str_hr }}
|
||||
{% else %}
|
||||
{{ org.location_string }}
|
||||
{% endif %}
|
||||
<p>{{ org.description | render_markdown }}</p>
|
||||
<table class="responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if org.website %}
|
||||
<td>{% translate "Website" %}</td>
|
||||
{% endif %}
|
||||
{% if org.phone_number %}
|
||||
<td>{% translate "Telefonnummer" %}</td>
|
||||
{% endif %}
|
||||
{% if org.email %}
|
||||
<td>{% translate "E-Mail" %}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
{% if org.website %}
|
||||
<td data-label="{% trans 'Website' %} ">
|
||||
{{ org.website }}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if org.phone_number %}
|
||||
<td data-label="{% trans 'Telefonnummer' %}">
|
||||
{{ org.phone_number }}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
{% if org.email %}
|
||||
<td data-label="{% trans 'E-Mail' %}">
|
||||
{{ org.email }}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
<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 %}
|
||||
<p>{{ org.description | render_markdown }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/partials/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 %}"><i class="fa-solid fa-tools fa-fw"></i> Admin interface</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card half">
|
||||
<div class="column">
|
||||
{% include "fellchensammlung/partials/partial-map.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>{% translate 'Vermittlungen der Organisation' %}</h2>
|
||||
<h2 class="title is-2">{% translate 'Vermittlungen der Organisation' %}</h2>
|
||||
<div class="container-cards">
|
||||
{% if org.adoption_notices %}
|
||||
{% for adoption_notice in org.adoption_notices %}
|
||||
|
@@ -1,83 +1,93 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}<title>{{ user.get_full_name }}</title>{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ user.get_full_name }}</h1>
|
||||
<div class="spaced">
|
||||
|
||||
<div class="container-cards">
|
||||
<h2>{% trans 'Daten' %}</h2>
|
||||
<div class="card">
|
||||
<p><strong>{% translate "Username" %}:</strong> {{ user.username }}</p>
|
||||
<p><strong>{% translate "E-Mail" %}:</strong> {{ user.email }}</p>
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<h1 class="title is-1"><i class="fas fa-user"></i> {{ user.get_full_name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container-cards">
|
||||
<h2>{% trans 'Profil verwalten' %}</h2>
|
||||
<div class="container-comment-form">
|
||||
<p>
|
||||
<a class="btn2" href="{% url 'password_change' %}">{% translate "Change password" %}</a>
|
||||
<a class="btn2" href="{% url 'user-me-export' %}">{% translate "Daten exportieren" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.id is request.user.id %}
|
||||
<div class="detail-animal-header"><h2>{% trans 'Einstellungen' %}</h2></div>
|
||||
<div class="container-cards">
|
||||
<form class="card" action="" method="POST">
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<form class="" action="{% url 'logout' %}" 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 %}
|
||||
<button class="button" type="submit">
|
||||
<i aria-hidden="true" class="fas fa-sign-out fa-fw"></i> Logout
|
||||
</button>
|
||||
</form>
|
||||
<div class="card">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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="btn" type="submit" name="delete_token"
|
||||
<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="btn" type="submit" name="create_token"
|
||||
<input class="button is-primary" type="submit" name="create_token"
|
||||
value={% translate "Create API token" %}>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<h2>{% translate 'Benachrichtigungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-notifications.html" %}
|
||||
</div>
|
||||
|
||||
<h2>{% translate 'Abonnierte Suchen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-search-subscriptions.html" %}
|
||||
<h2 class="title is-2">{% translate 'Benachrichtigungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-notifications.html" %}
|
||||
|
||||
<h2>{% translate 'Meine Vermittlungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
<h2 class="title is-2">{% translate 'Abonnierte Suchen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-search-subscriptions.html" %}
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
<h2 class="title is-2">{% translate 'Meine Vermittlungen' %}</h2>
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
@@ -1,133 +0,0 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}<title>{{ adoption_notice.name }}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="detail-adoption-notice-header">
|
||||
<div class="inline-container">
|
||||
<h1>{{ adoption_notice.name }}</h1>
|
||||
{% if not is_subscribed %}
|
||||
<div class="tooltip bottom">
|
||||
<form class="notification-card-mark-read" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="subscribe">
|
||||
<input type="hidden" name="adoption_notice_id" value="{{ adoption_notice.pk }}">
|
||||
<button class="btn2" type="submit" id="submit"><i class="fa-solid fa-bell"></i></button>
|
||||
</form>
|
||||
<span class="tooltiptext">
|
||||
{% translate 'Abonniere diese Vermittlung um bei Kommentaren oder Statusänderungen benachrichtigt zu werden' %}
|
||||
</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="tooltip bottom">
|
||||
<form class="notification-card-mark-read" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="unsubscribe">
|
||||
<input type="hidden" name="adoption_notice_id" value="{{ adoption_notice.pk }}">
|
||||
<button class="btn2" type="submit" id="submit"><i class="fa-solid fa-bell-slash"></i></button>
|
||||
</form>
|
||||
<span class="tooltiptext">
|
||||
{% translate 'Deabonnieren. Du bekommst keine Benachrichtigungen zu dieser Vermittlung mehr' %}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if adoption_notice.is_active %}
|
||||
<span id="submit" class="label active-adoption" style=>{% trans 'Aktive Vermittlung' %}</span>
|
||||
{% else %}
|
||||
<span id="submit" class="label inactive-adoption" style=>{% trans 'Vermittlung inaktiv' %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if has_edit_permission %}
|
||||
<a class="btn2"
|
||||
href="{% url 'adoption-notice-add-photo' adoption_notice_id=adoption_notice.pk %}">{% translate 'Foto hinzufügen' %}</a>
|
||||
<a class="btn2 detail-adoption-notice-header"
|
||||
href="{% url 'adoption-notice-edit' adoption_notice_id=adoption_notice.pk %}">{% translate 'Bearbeiten' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="table-adoption-notice-info">
|
||||
<table class="responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{% translate "Ort" %}</td>
|
||||
{% if adoption_notice.organization %}
|
||||
<td>{% translate "Organisation" %}</td>
|
||||
{% endif %}
|
||||
<td>{% translate "Suchen seit" %}</td>
|
||||
<td>{% translate "Zuletzt aktualisiert" %}</td>
|
||||
<td>{% translate "Weitere Informationen" %}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td data-label="{% trans 'Ort' %} ">
|
||||
{% if adoption_notice.location %}
|
||||
{{ adoption_notice.location }}
|
||||
{% else %}
|
||||
{{ adoption_notice.location_string }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if adoption_notice.organization %}
|
||||
<td data-label="{% trans 'Organisation' %}">
|
||||
<div>
|
||||
<a href="{{ adoption_notice.organization.get_absolute_url }}">{{ adoption_notice.organization }}</a>
|
||||
{% if adoption_notice.organization.trusted %}
|
||||
<div class="tooltip top">
|
||||
<div class="checkmark"><i class="fa-solid fa-check"></i></div>
|
||||
<span class="tooltiptext">
|
||||
{% translate 'Diese Organisation kennt sich mit Ratten aus und achtet auf gute Abgabebedingungen' %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<td data-label="{% trans 'Suchen seit' %}">{{ adoption_notice.searching_since }}</td>
|
||||
<td data-label="{% trans 'Zuletzt aktualisiert' %}">
|
||||
{{ adoption_notice.last_checked_hr }}
|
||||
</td>
|
||||
<td data-label="{% trans 'Weitere Informationen' %}">
|
||||
{% if adoption_notice.further_information %}
|
||||
<form method="get" action="{% url 'external-site' %}">
|
||||
<input type="hidden" name="url" value="{{ adoption_notice.further_information }}">
|
||||
<button class="btn" type="submit" id="submit">
|
||||
{{ adoption_notice.further_information | domain }} <i
|
||||
class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h1>{% translate "Bilder" %}</h1>
|
||||
{% for photo in adoption_notice.get_photos %}
|
||||
<img src="{{ MEDIA_URL }}/{{ photo.image }}" alt="{{ photo.alt_text }}">
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h1>{% translate "Beschreibung" %}</h1>
|
||||
<p>{% if adoption_notice.description %}
|
||||
{{ adoption_notice.description | render_markdown }}
|
||||
{% else %}
|
||||
{% translate "Keine Beschreibung angegeben" %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% for animal in adoption_notice.animals %}
|
||||
{% include "fellchensammlung/partials/partial-animal-card.html" %}
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% include "fellchensammlung/partials/partial-comment-section.html" %}
|
||||
|
||||
{% endblock %}
|
@@ -1,9 +0,0 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load custom_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}<title>{{ animal.name }}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "fellchensammlung/details/detail-animal-partial.html" %}
|
||||
{% endblock %}
|
@@ -1,11 +1,11 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "403 Forbidden" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>403 Forbidden</h1>
|
||||
<h1 class="title is-1">403 Forbidden</h1>
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Diese Aktion ist dir nicht erlaubt. Logge dich ein oder nutze einen anderen Account. Wenn du denkst, dass hier
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="content">
|
||||
{% if external_site_warning %}
|
||||
{{ external_site_warning.content | render_markdown }}
|
||||
{% else %}
|
||||
@@ -10,6 +10,6 @@
|
||||
<p>Achtung du verlässt notfellchen.org</p>
|
||||
{% endblocktranslate %}
|
||||
{% endif %}
|
||||
<a href="{{ url }}" class="btn button">{% translate "Weiter" %}</a>
|
||||
<a href="{{ url }}" class="button is-primary">{% translate "Weiter" %}</a>
|
||||
</div>
|
||||
{% endblock content %}
|
@@ -1,61 +1,62 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
|
||||
<footer class="footer">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="block">
|
||||
<h3 class="bd-footer-title title is-3 has-text-left">
|
||||
<h3 class="title is-3 has-text-left">
|
||||
Notfellchen
|
||||
</h3>
|
||||
|
||||
<!-- footer content -->
|
||||
<p class="bd-footer-link
|
||||
has-text-left">
|
||||
<p class="has-text-left">
|
||||
Für Menschen die Ratten aus dem Tierschutz ein liebendes Zuhause geben wollen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h3 class="bd-footer-title title is-5">
|
||||
<h3 class="title is-5">
|
||||
{% trans 'Sprache ändern' %}
|
||||
</h3>
|
||||
{% include "fellchensammlung/forms/change_language.html" %}
|
||||
{% include "fellchensammlung/forms/form_change_language.html" %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h4 class="bd-footer-title title is-4 has-text-justify">
|
||||
<h4 class="title is-4 has-text-justify">
|
||||
{% translate 'Über uns' %}
|
||||
</h4>
|
||||
|
||||
<a class="bd-footer-link" href="{% url "about-bulma" %}">
|
||||
<a href="{% url "about" %}">
|
||||
{% translate 'Das Notfellchen Projekt' %}
|
||||
</a>
|
||||
<br/>
|
||||
<a class="bd-footer-link" href="{% url "terms-of-service" %}">
|
||||
<a href="{% url "terms-of-service" %}">
|
||||
{% translate 'Nutzungsbedingungen' %}
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
<a class="bd-footer-link" href="{% url "privacy" %}">
|
||||
<a href="{% url "privacy" %}">
|
||||
{% translate 'Datenschutz' %}
|
||||
</a>
|
||||
<br/>
|
||||
<a class="bd-footer-link" href="{% url "imprint" %}">
|
||||
<a href="{% url "imprint" %}">
|
||||
{% translate 'Impressum' %}
|
||||
</a>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h4 class="bd-footer-title title is-4 has-text-justify">
|
||||
<h4 class="title is-4 has-text-justify">
|
||||
Technisches
|
||||
</h4>
|
||||
|
||||
<p class="bd-footer-link">
|
||||
<div>
|
||||
<a class="nav-link " href="{% url "rss" %}">
|
||||
<i class="fa-solid fa-rss"></i> {% translate 'RSS' %}
|
||||
<i class="fa-solid fa-rss fa-fw"></i> {% translate 'RSS' %}
|
||||
</a>
|
||||
<br/>
|
||||
|
||||
@@ -77,7 +78,28 @@
|
||||
<span>{% trans 'Code' %}</span>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4 class="title is-4 has-text-justify">
|
||||
{% trans 'Hilfreiche Links' %}
|
||||
</h4>
|
||||
<a class="nav-link " href="{% url "rescue-organizations" %}">
|
||||
{% translate 'Tierheime in der Nähe' %}
|
||||
</a>
|
||||
<br/>
|
||||
{% trust_level "MODERATOR" as coordinator_trust_level %}
|
||||
{% if request.user.trust_level >= coordinator_trust_level %}
|
||||
<a class="nav-link " href="{% url "modtools" %}">
|
||||
{% translate 'Moderationstools' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if request.user.is_superuser %}
|
||||
<a class="nav-link " href="{% url "admin:index" %}">
|
||||
<i class="fa-solid fa-screwdriver-wrench fa-fw"></i>{% translate 'Admin-Bereich' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
@@ -1,95 +0,0 @@
|
||||
{% extends "fellchensammlung/base_bulma.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}<title>{% translate "Vermittlung hinzufügen" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Vermitteln" %}</h1>
|
||||
<div class="notification">
|
||||
<button class="delete"></button>
|
||||
<p>
|
||||
{% url 'terms-of-service' as rules_url %}
|
||||
{% trans "Regeln" as rules_text %}
|
||||
{% blocktranslate with rules_link='<a href="'|add:rules_url|add:'">'|add:rules_text|add:'</a>'|safe %}
|
||||
Bitte mach dich zunächst mit unseren {{ rules_link }} vertraut. Dann trage hier die ersten Informationen
|
||||
ein.
|
||||
Fotos kannst du im nächsten Schritt hinzufügen.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-name">{{ form.name.label }}
|
||||
{% if form.name.field.required %}<span class="special_class">*</span>{% endif %}</label>
|
||||
{{ form.name|add_class:"input"|attr:"id:an-name" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-description">{% translate 'Beschreibung' %}</label>
|
||||
{{ form.description|add_class:"input textarea"|attr:"rows:3"|attr:"id:an-description" }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-location">{{ form.location_string.label }}</label>
|
||||
{{ form.location_string|add_class:"input"|attr:"id:an-location" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="checkbox" for="an-group-only">{{ form.group_only.label }}</label>
|
||||
{{ form.group_only|add_class:"checkbox"|attr:"id:an-group-only" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-searching-since">{{ form.searching_since.label }}</label>
|
||||
{{ form.searching_since|add_class:"input"|attr:"id:an-searching-since"|attr:"type:date" }}
|
||||
</div>
|
||||
|
||||
<div class="notification">
|
||||
<button class="delete"></button>
|
||||
<p>
|
||||
|
||||
{% blocktranslate %}
|
||||
Gibt hier schonmal erste Details zu den Tieren an.
|
||||
Wenn du Details und Fotos zu den Tieren hinzufügen willst oder ihr Geschlecht und Geburtsdatum
|
||||
anpassen
|
||||
willst,
|
||||
kannst du das im nächsten Schritt tun.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-species">{% translate 'Tierart' %}</label>
|
||||
<div class="select">
|
||||
{{ form.species|attr:"id:an-species" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-num-animals">{{ form.num_animals.label }}</label>
|
||||
{{ form.num_animals|add_class:"input"|attr:"id:an-num-animals" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-sex">{% translate 'Geschlecht' %}</label>
|
||||
<div class="select">
|
||||
{{ form.sex|attr:"id:an-sex" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-date-of-birth">{{ form.date_of_birth.label }}</label>
|
||||
{{ form.date_of_birth|add_class:"input"|attr:"id:an-date-of-birth"|attr:"type:date" }}
|
||||
</div>
|
||||
|
||||
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
</form>
|
||||
{% endblock %}
|
@@ -1,15 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
{% blocktrans %}
|
||||
Als {{ user }} kommentieren
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{% crispy comment_form %}
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,138 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load widget_tweaks %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}<title>{% translate "Vermittlung hinzufügen" %}</title>{% endblock %}
|
||||
|
||||
{% block additional_scrips %}
|
||||
<script src="{% static 'fellchensammlung/js/adoption-notice-form.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">{% translate "Vermitteln" %}</h1>
|
||||
<div class="notification is-info">
|
||||
<p>
|
||||
{% url 'terms-of-service' as rules_url %}
|
||||
{% trans "Regeln" as rules_text %}
|
||||
{% blocktranslate with rules_link='<a href="'|add:rules_url|add:'">'|add:rules_text|add:'</a>'|safe %}
|
||||
Bitte mach dich zunächst mit unseren {{ rules_link }} vertraut. Dann trage hier die ersten Informationen
|
||||
ein.
|
||||
Fotos kannst du im nächsten Schritt hinzufügen.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="add-adoption-notice-form">
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-name">{{ form.name.label }}
|
||||
{% if form.name.field.required %}<span class="special_class">*</span>{% endif %}</label>
|
||||
{{ form.name|add_class:"input"|attr:"id:an-name" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-description">
|
||||
{% translate 'Beschreibung' %}
|
||||
{% if form.description.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.description|add_class:"input textarea"|attr:"rows:3"|attr:"id:an-description" }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-location">
|
||||
{{ form.location_string.label }}
|
||||
{% if form.location_string.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.location_string|add_class:"input"|attr:"id:an-location" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="checkbox" for="an-group-only">
|
||||
{{ form.group_only.label }}
|
||||
{% if form.group_only.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.group_only|add_class:"checkbox"|attr:"id:an-group-only" }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-searching-since">
|
||||
{{ form.searching_since.label }}
|
||||
{% if form.searching_since.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.searching_since|add_class:"input"|attr:"id:an-searching-since"|attr:"type:date" }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-further-information">
|
||||
{{ form.further_information.label }}
|
||||
{% if form.further_information.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.further_information|add_class:"input"|attr:"id:an-further-information"|attr:"type:url" }}
|
||||
<div class="help">
|
||||
{{ form.further_information.help_text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-info">
|
||||
<p>
|
||||
|
||||
{% blocktranslate %}
|
||||
Gibt hier schonmal erste Details zu den Tieren an.
|
||||
Wenn du Details und Fotos zu den Tieren hinzufügen willst oder ihr Geschlecht und Geburtsdatum
|
||||
anpassen
|
||||
willst,
|
||||
kannst du das im nächsten Schritt tun.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-species">
|
||||
{% translate 'Tierart' %}
|
||||
{% if form.species.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
<div class="select">
|
||||
{{ form.species|attr:"id:an-species" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-num-animals">
|
||||
{{ form.num_animals.label }}
|
||||
{% if form.num_animals.field.required %}<span class="special_class">*</span>
|
||||
</label>
|
||||
{{ form.num_animals|add_class:"input"|attr:"id:an-num-animals" }}{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-sex">
|
||||
{% translate 'Geschlecht' %}
|
||||
{% if form.sex.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
<div class="select">
|
||||
{{ form.sex|attr:"id:an-sex" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="an-date-of-birth">
|
||||
{{ form.date_of_birth.label }}
|
||||
{% if form.date_of_birth.field.required %}<span class="special_class">*</span>{% endif %}
|
||||
</label>
|
||||
{{ form.date_of_birth|add_class:"input"|attr:"id:an-date-of-birth"|attr:"type:date" }}
|
||||
</div>
|
||||
|
||||
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,17 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">{% translate "Tiere hinzufügen" %}</h1>
|
||||
{% blocktranslate %}
|
||||
Hier kannst du jetzt einzelne Tiere zu deiner Vermittlung hinzufügen.
|
||||
{% endblocktranslate %}
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input class="button is-primary is-light" type="submit" value="{% translate "Speichern" %}">
|
||||
<input class="button is-primary" type="submit" name="save-and-add-another-animal" value="{% translate "Speichern und weiteres Tier hinzufügen" %}">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@@ -0,0 +1,13 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ adoption_notice }}</h1>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form }}
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
</form>
|
||||
{% endblock %}
|
@@ -1,8 +0,0 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Vermittlungsanzeige" %}</h1>
|
||||
{% crispy form %}
|
||||
{% endblock %}
|
@@ -0,0 +1,15 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">{{ animal }}</h1>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form }}
|
||||
<input class="button is-primary" type="submit" value="{% translate "Speichern" %}">
|
||||
|
||||
<a class="button is-danger" href="{% url 'animal-delete' animal_id=animal.pk %}">{% translate "Löschen" %}</a>
|
||||
</form>
|
||||
{% endblock %}
|
@@ -1,13 +1,19 @@
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
<div class="container-comment-form">
|
||||
<p>
|
||||
<b>
|
||||
{% blocktrans %}
|
||||
Als <i>{{ user }}</i> kommentieren
|
||||
{% endblocktrans %}
|
||||
</b>
|
||||
|
||||
{% crispy comment_form %}
|
||||
</p>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
{% blocktrans %}
|
||||
Als {{ user }} kommentieren
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
@@ -0,0 +1,17 @@
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block title %}
|
||||
<title>Löschen von {{ animal }} bestätigen</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title is-1">Löschen von {{ animal }} bestätigen</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<a class="button" href="{% url 'animal-edit' animal_id=animal.pk %}">{% translate "Zurück (nicht löschen)" %}</a>
|
||||
<input class="button is-danger" type="submit" name="delete" value={% translate "Löschen" %}>
|
||||
</form>
|
||||
{% endblock %}
|
@@ -1,17 +1,29 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
<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="btn2" href="https://www.dbsv.org/bildbeschreibung-4-regeln.html">{% translate 'Anleitung für Bildbeschreibungen' %}</a></p>
|
||||
</p>
|
||||
<div class="container-form">
|
||||
{% crispy form %}
|
||||
<p><a class="button" target="_blank"
|
||||
href="https://www.dbsv.org/bildbeschreibung-4-regeln.html">{% translate 'Anleitung für Bildbeschreibungen' %}</a>
|
||||
</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 %}
|
@@ -1,12 +1,12 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Melden" %}</h1>
|
||||
Wenn diese Vermittlung nicht unseren <a href='{% url "about" %}'>Regeln</a> entspricht, wähle bitte eine der folgenden Regeln aus und hinterlasse einen Kommentar der es detaillierter erklärt, insbesondere wenn der Regelverstoß nicht offensichtlich ist.
|
||||
<h1 class="title is-1">{% translate "Melden" %}</h1>
|
||||
Wenn dieser Inhalt nicht unseren <a href='{% url "about" %}'>Regeln</a> entspricht, wähle bitte eine der folgenden Regeln aus und hinterlasse einen Kommentar der es detaillierter erklärt, insbesondere wenn der Regelverstoß nicht offensichtlich ist.
|
||||
<form method = "post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button class="btn2" type="submit">{% translate "Melden" %}</button>
|
||||
{{ form }}
|
||||
<button class="button is-primary" type="submit">{% translate "Melden" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
@@ -1,17 +0,0 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Vermittlung hinzufügen" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Vermitteln" %}</h1>
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Bitte mach dich zunächst mit unseren Regeln vertraut. Dann trage hier die ersten Informationen ein.
|
||||
Wenn du Details und Fotos zu den Tieren hinzufügen willst oder ihr Geschlecht und Geburtsadatum anpassen willst,
|
||||
kannst du das im nächsten Schritt tun.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
{% crispy form %}
|
||||
{% endblock %}
|
@@ -1,13 +0,0 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Vermitteln" %}</h1>
|
||||
{% blocktranslate %}
|
||||
Hier kannst du jetzt einzelne Tiere zu deiner Vermittlung hinzufügen. Lad auch gerne Fotos hoch. Gruppenfotos
|
||||
kannst
|
||||
du im nächsten Schritt hochladen.
|
||||
{% endblocktranslate %}
|
||||
{% crispy form %}
|
||||
{% endblock %}
|
@@ -1,23 +1,35 @@
|
||||
<!--- See https://docs.djangoproject.com/en/5.2/topics/forms/#reusable-form-templates -->
|
||||
|
||||
{% load custom_tags %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% for field in form %}
|
||||
<div class="field">
|
||||
|
||||
<label class="label">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
<div class="control">
|
||||
{% if field|widget_type == 'TextInput' %}
|
||||
{{ field|add_class:"input" }}
|
||||
{% elif field|widget_type == 'Select' %}
|
||||
{% if field|widget_type == 'Select' %}
|
||||
<div class="select">
|
||||
{{ field }}
|
||||
</div>
|
||||
{% elif field|widget_type == 'selectmultiple' %}
|
||||
<div class="select is-multiple is-fullwidth">
|
||||
{{ field }}
|
||||
</div>
|
||||
{% elif field|widget_type == 'dateinput' %}
|
||||
{{ field|add_class:"input"|attr:"type:date" }}
|
||||
{% elif field|widget_type == 'textarea' %}
|
||||
{{ field|add_class:"input textarea"|attr:"rows:3" }}
|
||||
{% elif field|widget_type == 'checkboxinput' %}
|
||||
{{ field|add_class:"checkbox" }}
|
||||
{% else %}
|
||||
{{ field|add_class:"input" }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="help">
|
||||
{{ field.help_text }}
|
||||
</div>
|
||||
<div class="help is-danger">
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
|
@@ -1,59 +1,53 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<section class="header">
|
||||
<div>
|
||||
<a href="{% url "index" %}" class="logo">
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" tabindex="0" href="{% url 'index' %}">
|
||||
<img src="{% static 'fellchensammlung/img/logo_transparent.png' %}" alt="{% trans 'Notfellchen Logo' %}">
|
||||
<h1 class="title is-4">notfellchen.org</h1>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="{% trans 'Hauptmenü' %}" tabindex="0" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="profile-card">
|
||||
<div id="header-change-language">
|
||||
{% include "fellchensammlung/forms/change_language.html" %}
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="{% url 'search' %}">
|
||||
<i class="fas fa-search fa-fw"></i> {% translate 'Suchen' %}
|
||||
</a>
|
||||
|
||||
<a class="navbar-item" href="{% url "add-adoption" %}">
|
||||
<i class="fas fa-feather fa-fw"></i> {% translate 'Vermittlung hinzufügen' %}
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
{% if user.is_authenticated %}
|
||||
<div class="btn2 button_darken btn-notification">
|
||||
<a href="{{ user.get_notifications_url }}">
|
||||
<i class="fa fa-bell" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% if user.get_num_unread_notifications > 0 %}
|
||||
<span class="button__badge">{{ user.get_num_unread_notifications }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
{% if user.is_authenticated %}
|
||||
|
||||
<a class="btn2" href="{% url 'user-me' %}"><i aria-hidden="true" class="fas fa-user"></i></a>
|
||||
<form class="btn2 button_darken" id="header-sign-out" action="{% url 'logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="button" type="submit"><i aria-hidden="true" class="fas fa-sign-out"></i></button>
|
||||
</form>
|
||||
{% else %}
|
||||
<a class="btn2" href="{% url "django_registration_register" %}">{% translate "Registrieren" %}</a>
|
||||
<a class="btn2" href="{% url "login" %}"><i class="fa fa-sign-in" aria-label="Login"></i></a>
|
||||
{% endif %}
|
||||
<input id="menu-toggle" type="checkbox"/>
|
||||
<label class='menu-button-container' for="menu-toggle">
|
||||
<div class='menu-button'></div>
|
||||
</label>
|
||||
|
||||
<nav id="main-menu">
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<a class="nav-link " href="{% url "search" %}">
|
||||
<i class="fas fa-search"></i> {% translate 'Suchen' %}
|
||||
<div class="navbar-item">
|
||||
<a href="{% url 'user-me' %}">
|
||||
<i class="fas fa-user fa-fw"></i> {{ user }}
|
||||
</a>
|
||||
</li>
|
||||
<li><a class="nav-link " href="{% url "add-adoption" %}"><i
|
||||
class="fas fa-feather"></i> {% translate 'Vermittlung hinzufügen' %}</a></li>
|
||||
<li><a class="nav-link " href="{% url "about" %}"><i
|
||||
class="fas fa-info"></i> {% translate 'Über uns' %}
|
||||
</a>
|
||||
</li>
|
||||
<li><a class="nav-link " href="{% url "rss" %}"><i
|
||||
class="fa-solid fa-rss"></i> {% translate 'RSS' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary" href="{% url "django_registration_register" %}">
|
||||
<strong>{% translate "Registrieren" %}</strong>
|
||||
</a>
|
||||
<a class="button is-light" href="{% url "login" %}">
|
||||
<strong>{% translate "Login" %}</strong>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</nav>
|
@@ -1,8 +1,21 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}<title>{% translate "Notfellchen - Farbratten aus dem Tierschutz adoptieren" %}</title>{% endblock %}
|
||||
{% block og_title %}
|
||||
<meta property="og:title"
|
||||
content="{% translate "Notfellchen - Farbratten aus dem Tierschutz adoptieren" %}"/>{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="description"
|
||||
content="{% translate "notfellchen.org listet Farbratten in Tierheimen und Pflegestellen die ein Zuhause suchen. Adoptieren statt kaufen!" %}">{% endblock %}
|
||||
{% block og_description %}
|
||||
<meta name="og:description"
|
||||
content="{% translate "notfellchen.org listet Farbratten in Tierheimen und Pflegestellen die ein Zuhause suchen. Adoptieren statt kaufen!" %}">{% endblock %}
|
||||
{% block og_image %}
|
||||
<meta property="og:image" content="{% host %}{% static 'fellchensammlung/img/link_preview.png' %}"/>{% endblock %}
|
||||
{% block canonical_url %}{% host %}{% url 'index' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% for announcement in announcements %}
|
||||
@@ -13,24 +26,27 @@
|
||||
{{ introduction.content | render_markdown }}
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Aktuelle Vermittlungen" %}</h2>
|
||||
<h2 class="title is-2">{% translate "Aktuelle Vermittlungen" %}</h2>
|
||||
|
||||
<div class="container-cards">
|
||||
<div class="block">
|
||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||
<a class="btn2" href="{% url 'search' %}">{% translate "Mehr Vermittlungen" %}</a>
|
||||
<a class="button is-primary" href="{% url 'search' %}">{% translate "Mehr Vermittlungen" %}</a>
|
||||
</div>
|
||||
|
||||
<div class="map-in-content">
|
||||
<div class="card">
|
||||
<h1>{% translate 'Karte' %}</h1>
|
||||
{% include "fellchensammlung/partials/partial-map.html" %}
|
||||
</div>
|
||||
<div class="block" style="height: 50vh">
|
||||
{% include "fellchensammlung/partials/partial-map.html" %}
|
||||
</div>
|
||||
|
||||
{% if how_to %}
|
||||
<div class="card">
|
||||
<h1>{{ how_to.title }}</h1>
|
||||
{{ how_to.content | render_markdown }}
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
<h2 class="title is-1">{{ how_to.title }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{{ how_to.content | render_markdown }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@@ -1,12 +1,11 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% extends "fellchensammlung/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}<title>{% translate "Instanz-Check" %}</title> {% endblock %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h1>{% translate "Instanz-Check" %}</h1>
|
||||
{% if missing_texts|length > 0 %}
|
||||
<h2>{% trans "Fehlende Texte" %}</h2>
|
||||
<p>
|
||||
<h1 class="title is-1">{% translate "Instanz-Check" %}</h1>
|
||||
{% if missing_texts|length > 0 %}
|
||||
<h2 class="title is-2">{% trans "Fehlende Texte" %}</h2>
|
||||
<div class="block">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{% translate "Text Code" %}</th>
|
||||
@@ -19,14 +18,14 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{% translate "Texte scheinen vollständig" %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% translate "Texte scheinen vollständig" %}</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Zeitstempel" %}</h2>
|
||||
{% if timestamps|length > 0 %}
|
||||
<p>
|
||||
<h2 class="title is-2">{% trans "Zeitstempel" %}</h2>
|
||||
{% if timestamps|length > 0 %}
|
||||
<div class="block">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{% translate "Key" %}</th>
|
||||
@@ -41,81 +40,102 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{% translate "Keine Zeitstempel geloggt." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% translate "Keine Zeitstempel geloggt." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Nicht-lokalisierte Vermittlungen" %}</h2>
|
||||
{% if number_not_geocoded_adoption_notices > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_not_geocoded_adoption_notices }}/{{ number_of_adoption_notices }}</summary>
|
||||
<ul>
|
||||
{% for adoption_notice in none_geocoded_adoption_notices %}
|
||||
<li>
|
||||
<a href="{{ adoption_notice.get_absolute_url }}">{{ adoption_notice.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_not_geocoded_adoption_notices }}/{{ number_of_adoption_notices }}</p>
|
||||
{% endif %}
|
||||
<h2 class="title is-2">{% translate "Nicht-lokalisierte Vermittlungen" %}</h2>
|
||||
{% if number_not_geocoded_adoption_notices > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_not_geocoded_adoption_notices }}/{{ number_of_adoption_notices }}</summary>
|
||||
<ul>
|
||||
{% for adoption_notice in none_geocoded_adoption_notices %}
|
||||
<li>
|
||||
<a href="{{ adoption_notice.get_absolute_url }}">{{ adoption_notice.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_not_geocoded_adoption_notices }}/{{ number_of_adoption_notices }}</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Nicht-lokalisierte Tierschutzorganisationen" %}</h2>
|
||||
{% if number_not_geocoded_rescue_orgs > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_not_geocoded_rescue_orgs }}/{{ number_of_rescue_orgs }}</summary>
|
||||
<ul>
|
||||
{% for rescue_org in none_geocoded_rescue_orgs %}
|
||||
<li>
|
||||
<a href="{{ rescue_org.get_absolute_url }}">{{ rescue_org.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_not_geocoded_rescue_orgs }}/{{ number_of_rescue_orgs }}</p>
|
||||
{% endif %}
|
||||
<h2 class="title is-2">{% translate "Nicht-lokalisierte Tierschutzorganisationen" %}</h2>
|
||||
{% if number_not_geocoded_rescue_orgs > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_not_geocoded_rescue_orgs }}/{{ number_of_rescue_orgs }}</summary>
|
||||
<ul>
|
||||
{% for rescue_org in none_geocoded_rescue_orgs %}
|
||||
<li>
|
||||
<a href="{{ rescue_org.get_absolute_url }}">{{ rescue_org.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_not_geocoded_rescue_orgs }}/{{ number_of_rescue_orgs }}</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% translate "Nicht-geprüfte Vermittlungen" %}</h2>
|
||||
{% if number_unchecked_ans > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_unchecked_ans }}</summary>
|
||||
<ul>
|
||||
{% for unchecked_an in unchecked_ans %}
|
||||
<li>
|
||||
<a href="{{ unchecked_an.get_absolute_url }}">{{ unchecked_an.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_unchecked_ans }}</p>
|
||||
{% endif %}
|
||||
<h2 class="title is-2">{% translate "Nicht-geprüfte Vermittlungen" %}</h2>
|
||||
{% if number_unchecked_ans > 0 %}
|
||||
<details>
|
||||
<summary>{{ number_unchecked_ans }}</summary>
|
||||
<ul>
|
||||
{% for unchecked_an in unchecked_ans %}
|
||||
<li>
|
||||
<a href="{{ unchecked_an.get_absolute_url }}">{{ unchecked_an.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<p>{{ number_unchecked_ans }}</p>
|
||||
{% endif %}
|
||||
|
||||
<form class="notification-card-mark-read" method="post">
|
||||
<div class="grid">
|
||||
|
||||
<form class="cell" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="clean_locations">
|
||||
<button class="btn" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom"></i> {% translate "Erneut lokalisieren" %}
|
||||
<button class="button is-primary" type="submit" id="submit">
|
||||
<i class="fa-solid fa-location fa-fw"></i> {% translate "Erneut lokalisieren" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form class="notification-card-mark-read" method="post">
|
||||
<form class="cell" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="deactivate_unchecked_adoption_notices">
|
||||
<button class="btn" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom"></i> {% translate "Deaktiviere ungeprüfte Vermittlungen" %}
|
||||
<button class="button is-primary" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom fa-fw"></i> {% translate "Deaktiviere ungeprüfte Vermittlungen" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form class="notification-card-mark-read" method="post">
|
||||
<form class="cell" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="deactivate_404">
|
||||
<button class="btn" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom"></i> {% translate "Deaktiviere 404 Vermittlungen" %}
|
||||
<button class="button is-primary" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom fa-fw"></i> {% translate "Deaktiviere 404 Vermittlungen" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="block">
|
||||
<h2 class="title is-2">{% translate 'Testemail versenden' %}</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="send_test_email">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
{% trans 'E-Mail Addresse an die die Test E-Mail gesendet werden soll' as target_email %}
|
||||
<label hidden="hidden" aria-hidden="false" for="test_email_address">{{ target_email }}</label>
|
||||
<input class="input" id="test_email_address" name="test_email_address" type="text" placeholder="me@example.org">
|
||||
</p>
|
||||
<p class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
{% translate 'E-Mail senden testen' %}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% if adoption_notices %}
|
||||
<div class="grid">
|
||||
{% for adoption_notice in adoption_notices %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/bulma-partial-adoption-notice-minimal.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||
{% endif %}
|
@@ -1,5 +0,0 @@
|
||||
<div class="container-cards">
|
||||
{% for rule in rules %}
|
||||
{% include "fellchensammlung/partials/bulma-partial-rule.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
@@ -1,10 +1,12 @@
|
||||
{% load i18n %}
|
||||
<div class="container-cards">
|
||||
{% if adoption_notices %}
|
||||
{% if adoption_notices %}
|
||||
<div class="grid is-col-min-10">
|
||||
{% for adoption_notice in adoption_notices %}
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||
{% endif %}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
{% load i18n %}
|
||||
<div class="container-cards spaced">
|
||||
<div class="grid is-col-min-10">
|
||||
{% if rescue_organizations %}
|
||||
{% for rescue_organization in rescue_organizations %}
|
||||
{% include "fellchensammlung/partials/partial-rescue-organization.html" %}
|
||||
<div class="cell">
|
||||
{% include "fellchensammlung/partials/partial-rescue-organization.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>{% translate "Keine Tierschutzorganisationen gefunden." %}</p>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
<div class="container-cards">
|
||||
{% for moderation_action in moderation_actions %}
|
||||
<hr>
|
||||
{% include "fellchensammlung/partials/partial-moderation-action.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
@@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<div class="container-cards">
|
||||
<div class="block">
|
||||
{% if search_subscriptions %}
|
||||
{% for search_subscription in search_subscriptions %}
|
||||
{% include "fellchensammlung/partials/partial-search-subscription.html" %}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{{ site.name }}: {% trans "Account aktivieren" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
{% trans 'Hier ist dein Aktivierungs-Key. Mit diesem kannst du deinen Account freischalten.' %}
|
||||
</p>
|
||||
<code>
|
||||
{{ activation_key }}
|
||||
</code>
|
||||
|
||||
<p>
|
||||
{% trans "Öffne den folgenden link im Browser und gib den Aktivierungs-Key dort ein" %}
|
||||
</p>
|
||||
<a href="https://{{ site.domain }}{% url 'django_registration_activate' %}"
|
||||
class="cta-button">{% translate 'Jetzt aktivieren' %}</a>
|
||||
|
||||
<p>
|
||||
{% blocktrans %}Der Link ist für {{ expiration_days }} Tage gültig.{% endblocktrans %}
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f6f6f6;
|
||||
font-family: Nunito, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.email-wrapper {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #6CD4FF; /* $primary */
|
||||
color: #292a2c; /* $link */
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 30px 20px;
|
||||
color: #262728; /* $grey-dark */
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
padding: 12px 20px;
|
||||
background-color: hsl(133deg, 100%, 41%); /* $confirm */
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #c4c6ce; /* $grey-light */
|
||||
color: #292a2c;
|
||||
text-align: center;
|
||||
padding: 15px 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.content, .header, .footer {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-wrapper">
|
||||
|
||||
{% block header %}
|
||||
{% include "fellchensammlung/mail/header.html" %}
|
||||
{% endblock %}
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
{% include "fellchensammlung/mail/footer.html" %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,3 @@
|
||||
<div class="footer">
|
||||
🐀 notfellchen.org | Für Menschen die Ratten aus dem Tierschutz ein liebendes Zuhause geben wollen.
|
||||
</div>
|
@@ -0,0 +1,3 @@
|
||||
<div class="header">
|
||||
notfellchen.org
|
||||
</div>
|
@@ -0,0 +1,16 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung wurde deaktiviert' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
die Vermittlung {{ notification.adoption_notice }} wurde deaktiviert.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_full_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,15 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Neue Vermittlung gefunden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es wurde eine neue Vermittlung gefunden die deinen Kriterien entspricht: {{ notification.adoption_notice }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_full_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,15 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung muss überprüft werden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
die Vermittlung {{ notification.adoption_notice }} muss überprüft werden.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_full_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,19 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung muss überprüft werden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
folgender Kommentar wurde zur Vermittlung {{ notification.adoption_notice }} hinzugefügt:
|
||||
</p>
|
||||
<p><i>
|
||||
{{ notification.comment.text | render_markdown }}
|
||||
</i></p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_full_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,19 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Neuer User' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es wurde ein neuer Useraccount erstellt.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Details findest du hier
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.user_related.get_absolute_url }}" class="cta-button">{% translate 'User anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,25 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Neue Meldung' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es gibt eine neue Meldung. Folgende Nachricht wurde zur Meldung hinzugefügt:
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<i>
|
||||
{{ user_comment }}
|
||||
</i>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
Bitte bearbeite die Meldung möglichst bald.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ report_url }}" class="cta-button">{% translate 'Report bearbeiten' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,18 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Test-E-Mail' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
das ist eine Test-E-Mail.
|
||||
</p>
|
||||
<p>
|
||||
Hier ist ein total wichtiger Button. Klick den mal!
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://notfellchen.org" class="cta-button">{% translate 'Zur Website' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user