Compare commits
No commits in common. "bf54bc5d51a69017c975c39b75e8e78a761521bb" and "4e953c83eac1c9ec1c2fba8c522c58739fd05e0c" have entirely different histories.
bf54bc5d51
...
4e953c83ea
19
README.md
19
README.md
@ -44,16 +44,15 @@ nf query_location <query>
|
|||||||
There is a system for customizing texts in Notfellchen. Not every change of a tet should mean an update of the software. But this should also not become a CMS.
|
There is a system for customizing texts in Notfellchen. Not every change of a tet should mean an update of the software. But this should also not become a CMS.
|
||||||
Therefore, a solution is used where a number of predefined texts per site are supported. These markdown texts will then be included in the site, if defined.
|
Therefore, a solution is used where a number of predefined texts per site are supported. These markdown texts will then be included in the site, if defined.
|
||||||
|
|
||||||
| Textcode | Location |
|
| Textcode | Location |
|
||||||
|-------------------------|-----------------------|
|
|---------------------|----------|
|
||||||
| `how_to` | Index |
|
| `how_to` | Index |
|
||||||
| `introduction` | Index |
|
| `introduction` | Index |
|
||||||
| `privacy_statement` | About |
|
| `privacy_statement` | About |
|
||||||
| `terms_of_service` | About |
|
| `terms_of_service` | About |
|
||||||
| `imprint` | About |
|
| `imprint` | About |
|
||||||
| `about_us` | About |
|
| `about_us` | About |
|
||||||
| `external_site_warning` | External Site Warning |
|
| Any rule | About |
|
||||||
| Any rule | About |
|
|
||||||
|
|
||||||
# Developer Notes
|
# Developer Notes
|
||||||
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import csv
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.utils.html import format_html
|
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
|
from .models import User, Language, Text, ReportComment, ReportAdoptionNotice, Log, Timestamp
|
||||||
|
|
||||||
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
||||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions
|
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
|
|
||||||
class StatusInline(admin.StackedInline):
|
class StatusInline(admin.StackedInline):
|
||||||
@ -20,44 +15,14 @@ class StatusInline(admin.StackedInline):
|
|||||||
@admin.register(AdoptionNotice)
|
@admin.register(AdoptionNotice)
|
||||||
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
||||||
search_fields = ("name__icontains", "description__icontains")
|
search_fields = ("name__icontains", "description__icontains")
|
||||||
list_filter = ("owner",)
|
|
||||||
inlines = [
|
inlines = [
|
||||||
StatusInline,
|
StatusInline,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Re-register UserAdmin
|
# Re-register UserAdmin
|
||||||
@admin.register(User)
|
admin.site.register(User)
|
||||||
class UserAdmin(admin.ModelAdmin):
|
|
||||||
search_fields = ("usernamname__icontains", "first_name__icontains", "last_name__icontains", "email__icontains")
|
|
||||||
list_display = ("username", "email", "trust_level", "is_active", "view_adoption_notices")
|
|
||||||
list_filter = ("is_active", "trust_level",)
|
|
||||||
actions = ("export_as_csv",)
|
|
||||||
|
|
||||||
def view_adoption_notices(self, obj):
|
|
||||||
count = obj.adoption_notices.count()
|
|
||||||
url = (
|
|
||||||
reverse("admin:fellchensammlung_adoptionnotice_changelist")
|
|
||||||
+ "?"
|
|
||||||
+ urlencode({"owner__id": f"{obj.id}"})
|
|
||||||
)
|
|
||||||
return format_html('<a href="{}">{} Adoption Notices</a>', url, count)
|
|
||||||
|
|
||||||
def export_as_csv(self, request, queryset):
|
|
||||||
meta = self.model._meta
|
|
||||||
field_names = [field.name for field in meta.fields]
|
|
||||||
|
|
||||||
response = HttpResponse(content_type='text/csv')
|
|
||||||
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
|
|
||||||
writer = csv.writer(response)
|
|
||||||
|
|
||||||
writer.writerow(field_names)
|
|
||||||
for obj in queryset:
|
|
||||||
row = writer.writerow([getattr(obj, field) for field in field_names])
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
export_as_csv.short_description = _("Ausgewählte User exportieren")
|
|
||||||
|
|
||||||
def _reported_content_link(obj):
|
def _reported_content_link(obj):
|
||||||
reported_content = obj.reported_content
|
reported_content = obj.reported_content
|
||||||
@ -97,9 +62,6 @@ class RescueOrganizationAdmin(admin.ModelAdmin):
|
|||||||
class TextAdmin(admin.ModelAdmin):
|
class TextAdmin(admin.ModelAdmin):
|
||||||
search_fields = ("title__icontains", "text_code__icontains",)
|
search_fields = ("title__icontains", "text_code__icontains",)
|
||||||
|
|
||||||
@admin.register(Comment)
|
|
||||||
class CommentAdmin(admin.ModelAdmin):
|
|
||||||
list_filter = ("user",)
|
|
||||||
|
|
||||||
admin.site.register(Animal)
|
admin.site.register(Animal)
|
||||||
admin.site.register(Species)
|
admin.site.register(Species)
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
from venv import create
|
|
||||||
|
|
||||||
import django.conf.global_settings
|
import django.conf.global_settings
|
||||||
from django.db.models.signals import post_save
|
|
||||||
from django.dispatch import receiver
|
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.utils.translation import gettext
|
from django.utils.translation import gettext
|
||||||
@ -14,7 +10,6 @@ from notfellchen.settings import host
|
|||||||
|
|
||||||
NEWLINE = "\r\n"
|
NEWLINE = "\r\n"
|
||||||
|
|
||||||
|
|
||||||
def mail_admins_new_report(report):
|
def mail_admins_new_report(report):
|
||||||
subject = _("Neue Meldung")
|
subject = _("Neue Meldung")
|
||||||
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
||||||
@ -36,21 +31,3 @@ def mail_admins_new_report(report):
|
|||||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
||||||
print("Sending email to ", moderator.email)
|
print("Sending email to ", moderator.email)
|
||||||
message.send()
|
message.send()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
|
||||||
def mail_admins_new_member(sender, instance: User, created: bool, **kwargs):
|
|
||||||
if not created:
|
|
||||||
return
|
|
||||||
subject = _("Neuer User") + f": {instance.username}"
|
|
||||||
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
|
||||||
greeting = _("Moin,") + "{NEWLINE}"
|
|
||||||
new_report_text = _("es hat sich eine neue Person registriert.") + "{NEWLINE}"
|
|
||||||
user_detail_text = _("Username") + f": {instance.username}{NEWLINE}" + _(
|
|
||||||
"E-Mail") + f": {instance.email}{NEWLINE}"
|
|
||||||
user_url = "https://" + host + instance.get_absolute_url()
|
|
||||||
link_text = f"Um alle Details zu sehen, geh bitte auf: {user_url}"
|
|
||||||
body_text = greeting + new_report_text + user_detail_text + link_text
|
|
||||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
|
||||||
print("Sending email to ", moderator.email)
|
|
||||||
message.send()
|
|
||||||
|
@ -74,10 +74,6 @@ class User(AbstractUser):
|
|||||||
def get_num_unread_notifications(self):
|
def get_num_unread_notifications(self):
|
||||||
return BaseNotification.objects.filter(user=self, read=False).count()
|
return BaseNotification.objects.filter(user=self, read=False).count()
|
||||||
|
|
||||||
@property
|
|
||||||
def adoption_notices(self):
|
|
||||||
return AdoptionNotice.objects.filter(owner=self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def owner(self):
|
def owner(self):
|
||||||
return self
|
return self
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
{% extends "fellchensammlung/base_generic.html" %}
|
{% extends "fellchensammlung/base_generic.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load custom_tags %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
{% if external_site_warning %}
|
{% blocktranslate %}
|
||||||
{{ external_site_warning.content | render_markdown }}
|
<p>Achtung du verlässt notfellchen.org</p>
|
||||||
{% else %}
|
{% endblocktranslate %}
|
||||||
{% blocktranslate %}
|
<a href="{{ url }}" class="btn button" >{% translate "Weiter" %}</a>
|
||||||
<p>Achtung du verlässt notfellchen.org</p>
|
|
||||||
{% endblocktranslate %}
|
|
||||||
{% endif %}
|
|
||||||
<a href="{{ url }}" class="btn button">{% translate "Weiter" %}</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -537,7 +537,6 @@ def external_site_warning(request):
|
|||||||
context = {"url": url}
|
context = {"url": url}
|
||||||
language_code = translation.get_language()
|
language_code = translation.get_language()
|
||||||
lang = Language.objects.get(languagecode=language_code)
|
lang = Language.objects.get(languagecode=language_code)
|
||||||
texts = Text.get_texts(["external_site_warning", "good_adoption_practices"], language=lang)
|
Text.get_texts(["external_site_warning", "good_adoption_practices"], language=lang)
|
||||||
context.update(texts)
|
|
||||||
|
|
||||||
return render(request, 'fellchensammlung/external_site_warning.html', context=context)
|
return render(request, 'fellchensammlung/external_site_warning.html', context=context)
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
from fellchensammlung.forms import AdoptionNoticeFormWithDateWidgetAutoAnimal
|
|
||||||
from fellchensammlung.models import Species
|
|
||||||
from model_bakery import baker
|
|
||||||
|
|
||||||
|
|
||||||
class TestAdoptionNoticeFormWithDateWidgetAutoAnimal(TestCase):
|
|
||||||
@classmethod
|
|
||||||
def setUpTestData(cls):
|
|
||||||
rat = baker.make(Species, name="Farbratte")
|
|
||||||
|
|
||||||
def test_forms(self):
|
|
||||||
form_data = {"name": "TestAdoption3",
|
|
||||||
"species": Species.objects.first(),
|
|
||||||
"num_animals": "2",
|
|
||||||
"date_of_birth": "2024-11-04",
|
|
||||||
"sex": "M",
|
|
||||||
"group_only": "on",
|
|
||||||
"searching_since": "2024-11-10",
|
|
||||||
"location_string": "Mannheim",
|
|
||||||
"description": "Blaaaa",
|
|
||||||
"further_information": "https://notfellchen.org",
|
|
||||||
"save-and-add-another-animal": "Speichern"}
|
|
||||||
form = AdoptionNoticeFormWithDateWidgetAutoAnimal(data=form_data)
|
|
||||||
self.assertTrue(form.is_valid())
|
|
@ -4,15 +4,7 @@ from django.utils import timezone
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
from fellchensammlung.models import Announcement, Language, User
|
from fellchensammlung.models import Announcement, Language
|
||||||
|
|
||||||
|
|
||||||
class UserTest(TestCase):
|
|
||||||
def test_creating_user(self):
|
|
||||||
test_user_1 = User.objects.create(username="Testuser1", password="SUPERSECRET", email="test@example.org")
|
|
||||||
|
|
||||||
self.assertTrue(test_user_1.trust_level == 1)
|
|
||||||
self.assertTrue(test_user_1.trust_level == User.TRUST_LEVEL[User.MEMBER])
|
|
||||||
|
|
||||||
|
|
||||||
class AnnouncementTest(TestCase):
|
class AnnouncementTest(TestCase):
|
||||||
@ -77,3 +69,4 @@ class AnnouncementTest(TestCase):
|
|||||||
self.assertTrue(self.announcement2 not in active_announcements)
|
self.assertTrue(self.announcement2 not in active_announcements)
|
||||||
self.assertTrue(self.announcement4 not in active_announcements)
|
self.assertTrue(self.announcement4 not in active_announcements)
|
||||||
self.assertTrue(self.announcement5 in active_announcements)
|
self.assertTrue(self.announcement5 in active_announcements)
|
||||||
|
|
||||||
|
@ -20,8 +20,7 @@ class AnimalAndAdoptionTest(TestCase):
|
|||||||
first_name="Max",
|
first_name="Max",
|
||||||
last_name="Müller",
|
last_name="Müller",
|
||||||
password='12345')
|
password='12345')
|
||||||
test_user0.trust_level = User.TRUST_LEVEL[User.ADMIN]
|
test_user1.save()
|
||||||
test_user0.save()
|
|
||||||
|
|
||||||
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
||||||
rat = baker.make(Species, name="Farbratte")
|
rat = baker.make(Species, name="Farbratte")
|
||||||
@ -51,28 +50,6 @@ class AnimalAndAdoptionTest(TestCase):
|
|||||||
self.assertContains(response, "TestAdoption1")
|
self.assertContains(response, "TestAdoption1")
|
||||||
self.assertContains(response, "Rat1")
|
self.assertContains(response, "Rat1")
|
||||||
|
|
||||||
def test_creating_AN_as_admin(self):
|
|
||||||
self.client.login(username='testuser0', password='12345')
|
|
||||||
|
|
||||||
form_data = {"name": "TestAdoption4",
|
|
||||||
"species": Species.objects.first().pk,
|
|
||||||
"num_animals": "2",
|
|
||||||
"date_of_birth": "2024-11-04",
|
|
||||||
"sex": "M",
|
|
||||||
"group_only": "on",
|
|
||||||
"searching_since": "2024-11-10",
|
|
||||||
"location_string": "Mannheim",
|
|
||||||
"description": "Blaaaa",
|
|
||||||
"further_information": "https://notfellchen.org",
|
|
||||||
"save-and-add-another-animal": "Speichern"}
|
|
||||||
|
|
||||||
response = self.client.post(reverse('add-adoption'), data=form_data)
|
|
||||||
print(response.content)
|
|
||||||
|
|
||||||
self.assertTrue(response.status_code < 400)
|
|
||||||
self.assertTrue(AdoptionNotice.objects.get(name="TestAdoption4").is_active)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SearchTest(TestCase):
|
class SearchTest(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -192,4 +169,3 @@ class UpdateQueueTest(TestCase):
|
|||||||
self.assertNotContains(response, "TestAdoption3")
|
self.assertNotContains(response, "TestAdoption3")
|
||||||
self.assertFalse(self.adoption3.is_active)
|
self.assertFalse(self.adoption3.is_active)
|
||||||
self.assertEqual(self.adoption3.adoptionnoticestatus.major_status, AdoptionNoticeStatus.CLOSED)
|
self.assertEqual(self.adoption3.adoptionnoticestatus.major_status, AdoptionNoticeStatus.CLOSED)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user