Compare commits
10 Commits
4e953c83ea
...
bf54bc5d51
Author | SHA1 | Date | |
---|---|---|---|
bf54bc5d51 | |||
93ae172431 | |||
03d40a5092 | |||
993f8f9cd2 | |||
8efc0aad21 | |||
3a6e7f5344 | |||
dac9661d51 | |||
b9bfa8e359 | |||
d07589464c | |||
1880da5151 |
19
README.md
19
README.md
@ -44,15 +44,16 @@ 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.
|
||||
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 |
|
||||
|---------------------|----------|
|
||||
| `how_to` | Index |
|
||||
| `introduction` | Index |
|
||||
| `privacy_statement` | About |
|
||||
| `terms_of_service` | About |
|
||||
| `imprint` | About |
|
||||
| `about_us` | About |
|
||||
| Any rule | About |
|
||||
| Textcode | Location |
|
||||
|-------------------------|-----------------------|
|
||||
| `how_to` | Index |
|
||||
| `introduction` | Index |
|
||||
| `privacy_statement` | About |
|
||||
| `terms_of_service` | About |
|
||||
| `imprint` | About |
|
||||
| `about_us` | About |
|
||||
| `external_site_warning` | External Site Warning |
|
||||
| Any rule | About |
|
||||
|
||||
# Developer Notes
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
import csv
|
||||
|
||||
from django.contrib import admin
|
||||
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
|
||||
|
||||
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class StatusInline(admin.StackedInline):
|
||||
@ -15,14 +20,44 @@ class StatusInline(admin.StackedInline):
|
||||
@admin.register(AdoptionNotice)
|
||||
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
||||
search_fields = ("name__icontains", "description__icontains")
|
||||
list_filter = ("owner",)
|
||||
inlines = [
|
||||
StatusInline,
|
||||
]
|
||||
|
||||
|
||||
# Re-register UserAdmin
|
||||
admin.site.register(User)
|
||||
@admin.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):
|
||||
reported_content = obj.reported_content
|
||||
@ -62,6 +97,9 @@ class RescueOrganizationAdmin(admin.ModelAdmin):
|
||||
class TextAdmin(admin.ModelAdmin):
|
||||
search_fields = ("title__icontains", "text_code__icontains",)
|
||||
|
||||
@admin.register(Comment)
|
||||
class CommentAdmin(admin.ModelAdmin):
|
||||
list_filter = ("user",)
|
||||
|
||||
admin.site.register(Animal)
|
||||
admin.site.register(Species)
|
||||
|
@ -1,4 +1,8 @@
|
||||
from venv import create
|
||||
|
||||
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
|
||||
@ -10,6 +14,7 @@ from notfellchen.settings import host
|
||||
|
||||
NEWLINE = "\r\n"
|
||||
|
||||
|
||||
def mail_admins_new_report(report):
|
||||
subject = _("Neue Meldung")
|
||||
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
||||
@ -31,3 +36,21 @@ def mail_admins_new_report(report):
|
||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
||||
print("Sending email to ", moderator.email)
|
||||
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,6 +74,10 @@ class User(AbstractUser):
|
||||
def get_num_unread_notifications(self):
|
||||
return BaseNotification.objects.filter(user=self, read=False).count()
|
||||
|
||||
@property
|
||||
def adoption_notices(self):
|
||||
return AdoptionNotice.objects.filter(owner=self)
|
||||
|
||||
@property
|
||||
def owner(self):
|
||||
return self
|
||||
|
@ -1,10 +1,15 @@
|
||||
{% extends "fellchensammlung/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
{% blocktranslate %}
|
||||
<p>Achtung du verlässt notfellchen.org</p>
|
||||
{% endblocktranslate %}
|
||||
<a href="{{ url }}" class="btn button" >{% translate "Weiter" %}</a>
|
||||
{% if external_site_warning %}
|
||||
{{ external_site_warning.content | render_markdown }}
|
||||
{% else %}
|
||||
{% blocktranslate %}
|
||||
<p>Achtung du verlässt notfellchen.org</p>
|
||||
{% endblocktranslate %}
|
||||
{% endif %}
|
||||
<a href="{{ url }}" class="btn button">{% translate "Weiter" %}</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
@ -537,6 +537,7 @@ def external_site_warning(request):
|
||||
context = {"url": url}
|
||||
language_code = translation.get_language()
|
||||
lang = Language.objects.get(languagecode=language_code)
|
||||
Text.get_texts(["external_site_warning", "good_adoption_practices"], language=lang)
|
||||
texts = Text.get_texts(["external_site_warning", "good_adoption_practices"], language=lang)
|
||||
context.update(texts)
|
||||
|
||||
return render(request, 'fellchensammlung/external_site_warning.html', context=context)
|
||||
|
25
src/tests/test_forms.py
Normal file
25
src/tests/test_forms.py
Normal file
@ -0,0 +1,25 @@
|
||||
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,7 +4,15 @@ from django.utils import timezone
|
||||
from django.test import TestCase
|
||||
from model_bakery import baker
|
||||
|
||||
from fellchensammlung.models import Announcement, Language
|
||||
from fellchensammlung.models import Announcement, Language, User
|
||||
|
||||
|
||||
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):
|
||||
@ -69,4 +77,3 @@ class AnnouncementTest(TestCase):
|
||||
self.assertTrue(self.announcement2 not in active_announcements)
|
||||
self.assertTrue(self.announcement4 not in active_announcements)
|
||||
self.assertTrue(self.announcement5 in active_announcements)
|
||||
|
||||
|
@ -20,7 +20,8 @@ class AnimalAndAdoptionTest(TestCase):
|
||||
first_name="Max",
|
||||
last_name="Müller",
|
||||
password='12345')
|
||||
test_user1.save()
|
||||
test_user0.trust_level = User.TRUST_LEVEL[User.ADMIN]
|
||||
test_user0.save()
|
||||
|
||||
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
||||
rat = baker.make(Species, name="Farbratte")
|
||||
@ -50,6 +51,28 @@ class AnimalAndAdoptionTest(TestCase):
|
||||
self.assertContains(response, "TestAdoption1")
|
||||
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):
|
||||
@classmethod
|
||||
@ -169,3 +192,4 @@ class UpdateQueueTest(TestCase):
|
||||
self.assertNotContains(response, "TestAdoption3")
|
||||
self.assertFalse(self.adoption3.is_active)
|
||||
self.assertEqual(self.adoption3.adoptionnoticestatus.major_status, AdoptionNoticeStatus.CLOSED)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user