Compare commits
No commits in common. "3eb7dbe9843dfa6fecf653477d59464e8e557936" and "bf54bc5d51a69017c975c39b75e8e78a761521bb" have entirely different histories.
3eb7dbe984
...
bf54bc5d51
@ -35,7 +35,7 @@ dependencies = [
|
|||||||
"markdown",
|
"markdown",
|
||||||
"Pillow",
|
"Pillow",
|
||||||
"django-registration",
|
"django-registration",
|
||||||
"psycopg2-binary",
|
"psycopg2",
|
||||||
"django-crispy-forms",
|
"django-crispy-forms",
|
||||||
"crispy-bootstrap4",
|
"crispy-bootstrap4",
|
||||||
"djangorestframework",
|
"djangorestframework",
|
||||||
|
@ -15,4 +15,3 @@ class FellchensammlungConfig(AppConfig):
|
|||||||
except Permission.DoesNotExist:
|
except Permission.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
post_migrate.connect(ensure_languages, sender=self)
|
post_migrate.connect(ensure_languages, sender=self)
|
||||||
import fellchensammlung.receivers
|
|
||||||
|
@ -9,14 +9,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from notfellchen.settings import MEDIA_URL
|
from notfellchen.settings import MEDIA_URL
|
||||||
|
|
||||||
|
|
||||||
def animal_validator(value: str):
|
|
||||||
value = value.lower()
|
|
||||||
animal_list = ["ratte", "farbratte", "katze", "hund", "kaninchen", "hase", "kuh", "fuchs", "cow", "rat", "cat",
|
|
||||||
"dog", "rabbit", "fox", "fancy rat"]
|
|
||||||
if value not in animal_list:
|
|
||||||
raise forms.ValidationError(_("Dieses Tier kenne ich nicht. Probier ein anderes"))
|
|
||||||
|
|
||||||
|
|
||||||
class DateInput(forms.DateInput):
|
class DateInput(forms.DateInput):
|
||||||
input_type = 'date'
|
input_type = 'date'
|
||||||
|
|
||||||
@ -51,7 +43,6 @@ class AdoptionNoticeForm(forms.ModelForm):
|
|||||||
'group_only',
|
'group_only',
|
||||||
'searching_since',
|
'searching_since',
|
||||||
'location_string',
|
'location_string',
|
||||||
'organization',
|
|
||||||
'description',
|
'description',
|
||||||
'further_information',
|
'further_information',
|
||||||
),
|
),
|
||||||
@ -59,19 +50,19 @@ class AdoptionNoticeForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AdoptionNotice
|
model = AdoptionNotice
|
||||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string",
|
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string"]
|
||||||
"organization"]
|
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeFormWithDateWidget(AdoptionNoticeForm):
|
class AdoptionNoticeFormWithDateWidget(AdoptionNoticeForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AdoptionNotice
|
model = AdoptionNotice
|
||||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string", "organization"]
|
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string"]
|
||||||
widgets = {
|
widgets = {
|
||||||
'searching_since': DateInput(),
|
'searching_since': DateInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AnimalForm(forms.ModelForm):
|
class AnimalForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if 'in_adoption_notice_creation_flow' in kwargs:
|
if 'in_adoption_notice_creation_flow' in kwargs:
|
||||||
@ -100,7 +91,6 @@ class AnimalFormWithDateWidget(AnimalForm):
|
|||||||
'date_of_birth': DateInput(),
|
'date_of_birth': DateInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeFormWithDateWidgetAutoAnimal(AdoptionNoticeFormWithDateWidget):
|
class AdoptionNoticeFormWithDateWidgetAutoAnimal(AdoptionNoticeFormWithDateWidget):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(AdoptionNoticeFormWithDateWidgetAutoAnimal, self).__init__(*args, **kwargs)
|
super(AdoptionNoticeFormWithDateWidgetAutoAnimal, self).__init__(*args, **kwargs)
|
||||||
@ -169,8 +159,6 @@ class CustomRegistrationForm(RegistrationForm):
|
|||||||
class Meta(RegistrationForm.Meta):
|
class Meta(RegistrationForm.Meta):
|
||||||
model = User
|
model = User
|
||||||
|
|
||||||
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):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
@ -181,7 +169,7 @@ class CustomRegistrationForm(RegistrationForm):
|
|||||||
|
|
||||||
|
|
||||||
def _get_distances():
|
def _get_distances():
|
||||||
return {i: i for i in [20, 50, 100, 200, 500]}
|
return {i: i for i in [10, 20, 50, 100, 200, 500]}
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeSearchForm(forms.Form):
|
class AdoptionNoticeSearchForm(forms.Form):
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
|
from venv import create
|
||||||
|
|
||||||
|
import django.conf.global_settings
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
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.conf import settings
|
from django.conf import settings
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from fellchensammlung.models import User, CommentNotification, BaseNotification, TrustLevel
|
from django.db.models import Q, Min
|
||||||
|
from fellchensammlung.models import User
|
||||||
from notfellchen.settings import host
|
from notfellchen.settings import host
|
||||||
|
|
||||||
NEWLINE = "\r\n"
|
NEWLINE = "\r\n"
|
||||||
@ -12,7 +17,7 @@ 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=TrustLevel.MODERATOR):
|
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
||||||
greeting = _("Moin,") + "{NEWLINE}"
|
greeting = _("Moin,") + "{NEWLINE}"
|
||||||
new_report_text = _("es wurde ein Regelverstoß gemeldet.") + "{NEWLINE}"
|
new_report_text = _("es wurde ein Regelverstoß gemeldet.") + "{NEWLINE}"
|
||||||
if len(report.reported_broken_rules.all()) > 0:
|
if len(report.reported_broken_rules.all()) > 0:
|
||||||
@ -29,15 +34,23 @@ def mail_admins_new_report(report):
|
|||||||
link_text = f"Um alle Details zu sehen, geh bitte auf: {report_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
|
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 = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [moderator.email])
|
||||||
|
print("Sending email to ", moderator.email)
|
||||||
message.send()
|
message.send()
|
||||||
|
|
||||||
|
|
||||||
def send_notification_email(notification_pk):
|
@receiver(post_save, sender=User)
|
||||||
try:
|
def mail_admins_new_member(sender, instance: User, created: bool, **kwargs):
|
||||||
notification = CommentNotification.objects.get(pk=notification_pk)
|
if not created:
|
||||||
except CommentNotification.DoesNotExist:
|
return
|
||||||
notification = BaseNotification.objects.get(pk=notification_pk)
|
subject = _("Neuer User") + f": {instance.username}"
|
||||||
subject = f"🔔 {notification.title}"
|
for moderator in User.objects.filter(trust_level__gt=User.TRUST_LEVEL[User.MODERATOR]):
|
||||||
body_text = notification.text
|
greeting = _("Moin,") + "{NEWLINE}"
|
||||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [notification.user.email])
|
new_report_text = _("es hat sich eine neue Person registriert.") + "{NEWLINE}"
|
||||||
message.send()
|
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()
|
||||||
|
@ -7,7 +7,7 @@ from fellchensammlung import baker_recipes
|
|||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
from fellchensammlung.models import AdoptionNotice, Species, Animal, Image, ModerationAction, User, Rule, \
|
from fellchensammlung.models import AdoptionNotice, Species, Animal, Image, ModerationAction, User, Rule, \
|
||||||
Report, Comment, ReportAdoptionNotice, TrustLevel
|
Report, Comment, ReportAdoptionNotice
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@ -101,10 +101,10 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
User.objects.create_user('test', password='foobar')
|
User.objects.create_user('test', password='foobar')
|
||||||
admin1 = User.objects.create_superuser(username="admin", password="admin", email="admin1@example.org",
|
admin1 = User.objects.create_superuser(username="admin", password="admin", email="admin1@example.org",
|
||||||
trust_level=TrustLevel.ADMIN)
|
trust_level=User.TRUST_LEVEL[User.ADMIN])
|
||||||
|
|
||||||
mod1 = User.objects.create_user(username="mod1", password="mod", email="mod1@example.org",
|
mod1 = User.objects.create_user(username="mod1", password="mod", email="mod1@example.org",
|
||||||
trust_level=TrustLevel.MODERATOR)
|
trust_level=User.TRUST_LEVEL[User.MODERATOR])
|
||||||
|
|
||||||
comment1 = baker.make(Comment, user=admin1, text="This is a comment", adoption_notice=adoption1)
|
comment1 = baker.make(Comment, user=admin1, text="This is a comment", adoption_notice=adoption1)
|
||||||
comment2 = baker.make(Comment,
|
comment2 = baker.make(Comment,
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-13 15:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0015_rescueorganization_comment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='rescueorganization',
|
|
||||||
name='phone_number',
|
|
||||||
field=models.CharField(blank=True, max_length=15, null=True, verbose_name='Telefonnummer'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-14 06:42
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0016_rescueorganization_phone_number'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='user',
|
|
||||||
name='organization_affiliation',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='fellchensammlung.rescueorganization', verbose_name='Organisation'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-14 17:58
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0017_user_organization_affiliation'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='rescueorganization',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True, null=True, verbose_name='Beschreibung'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-14 18:30
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0018_rescueorganization_description'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='rescueorganization',
|
|
||||||
old_name='comment',
|
|
||||||
new_name='internal_comment',
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-14 18:31
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0019_rename_comment_rescueorganization_internal_comment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='rescueorganization',
|
|
||||||
name='internal_comment',
|
|
||||||
field=models.TextField(blank=True, null=True, verbose_name='Interner Kommentar'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-14 20:15
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0020_alter_rescueorganization_internal_comment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='user',
|
|
||||||
name='reason_for_signup',
|
|
||||||
field=models.TextField(default='-', verbose_name='Grund für die Registrierung'),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-20 18:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0021_user_reason_for_signup'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='reason_for_signup',
|
|
||||||
field=models.TextField(help_text="Wir würden gerne wissen warum du dich registriertst, ob du dich z.B. Tiere eines bestimmten Tierheim einstellen willst 'nur mal gucken' willst. Beides ist toll! Wenn du für ein Tierheim/eine Pflegestelle arbeitest kontaktieren wir dich ggf. um dir erweiterte Rechte zu geben.", verbose_name='Grund für die Registrierung'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='trust_level',
|
|
||||||
field=models.IntegerField(choices=[(1, 'Member'), (2, 'Coordinator'), (3, 'Moderator'), (4, 'Admin')], default=1),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.1.1 on 2024-11-20 19:25
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('fellchensammlung', '0022_alter_user_reason_for_signup_alter_user_trust_level'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='user',
|
|
||||||
name='email_notifications',
|
|
||||||
field=models.BooleanField(default=True, verbose_name='Benachrichtigung per E-Mail'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -34,6 +34,86 @@ class Language(models.Model):
|
|||||||
verbose_name_plural = _('Sprachen')
|
verbose_name_plural = _('Sprachen')
|
||||||
|
|
||||||
|
|
||||||
|
class User(AbstractUser):
|
||||||
|
"""
|
||||||
|
Model that holds a user's profile, including the django user model
|
||||||
|
|
||||||
|
The trust levels act as permission system and can be displayed as a badge for the user
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Admins can perform all actions and have the highest trust associated with them
|
||||||
|
# Moderators can make moderation decisions regarding the deletion of content
|
||||||
|
# Coordinators can create adoption notices without them being checked
|
||||||
|
# Members can create adoption notices that must be activated
|
||||||
|
ADMIN = "admin"
|
||||||
|
MODERATOR = "Moderator"
|
||||||
|
COORDINATOR = "Koordinator*in"
|
||||||
|
MEMBER = "Mitglied"
|
||||||
|
TRUST_LEVEL = {
|
||||||
|
ADMIN: 4,
|
||||||
|
MODERATOR: 3,
|
||||||
|
COORDINATOR: 2,
|
||||||
|
MEMBER: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
preferred_language = models.ForeignKey(Language, on_delete=models.PROTECT, null=True, blank=True,
|
||||||
|
verbose_name=_('Bevorzugte Sprache'))
|
||||||
|
trust_level = models.IntegerField(choices=TRUST_LEVEL, default=TRUST_LEVEL[MEMBER])
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Nutzer*in')
|
||||||
|
verbose_name_plural = _('Nutzer*innen')
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("user-detail", args=[str(self.pk)])
|
||||||
|
|
||||||
|
def get_notifications_url(self):
|
||||||
|
return self.get_absolute_url()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class Image(models.Model):
|
||||||
|
image = models.ImageField(upload_to='images')
|
||||||
|
alt_text = models.TextField(max_length=2000)
|
||||||
|
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.alt_text
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_html(self):
|
||||||
|
return f'<img src="{MEDIA_URL}/{self.image}" alt="{self.alt_text}">'
|
||||||
|
|
||||||
|
|
||||||
|
class Species(models.Model):
|
||||||
|
"""Model representing a species of animal."""
|
||||||
|
name = models.CharField(max_length=200, help_text=_('Name der Tierart'),
|
||||||
|
verbose_name=_('Name'))
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""String for representing the Model object."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Tierart')
|
||||||
|
verbose_name_plural = _('Tierarten')
|
||||||
|
|
||||||
|
|
||||||
class Location(models.Model):
|
class Location(models.Model):
|
||||||
place_id = models.IntegerField()
|
place_id = models.IntegerField()
|
||||||
latitude = models.FloatField()
|
latitude = models.FloatField()
|
||||||
@ -106,104 +186,10 @@ class RescueOrganization(models.Model):
|
|||||||
facebook = models.URLField(null=True, blank=True, verbose_name=_('Facebook Profil'))
|
facebook = models.URLField(null=True, blank=True, verbose_name=_('Facebook Profil'))
|
||||||
fediverse_profile = models.URLField(null=True, blank=True, verbose_name=_('Fediverse Profil'))
|
fediverse_profile = models.URLField(null=True, blank=True, verbose_name=_('Fediverse Profil'))
|
||||||
email = models.EmailField(null=True, blank=True, verbose_name=_('E-Mail'))
|
email = models.EmailField(null=True, blank=True, verbose_name=_('E-Mail'))
|
||||||
phone_number = models.CharField(max_length=15, null=True, blank=True, verbose_name=_('Telefonnummer'))
|
|
||||||
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
|
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
internal_comment = models.TextField(verbose_name=_("Interner Kommentar"), null=True, blank=True, )
|
comment = models.TextField(verbose_name=_("Kommentar"), null=True, blank=True,)
|
||||||
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung')) # Markdown allowed
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse("rescue-organization-detail", args=[str(self.pk)])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def adoption_notices(self):
|
|
||||||
return AdoptionNotice.objects.filter(organization=self)
|
|
||||||
|
|
||||||
|
|
||||||
# Admins can perform all actions and have the highest trust associated with them
|
|
||||||
# Moderators can make moderation decisions regarding the deletion of content
|
|
||||||
# Coordinators can create adoption notices without them being checked
|
|
||||||
# Members can create adoption notices that must be activated
|
|
||||||
class TrustLevel(models.IntegerChoices):
|
|
||||||
MEMBER = 1, 'Member'
|
|
||||||
COORDINATOR = 2, 'Coordinator'
|
|
||||||
MODERATOR = 3, 'Moderator'
|
|
||||||
ADMIN = 4, 'Admin'
|
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
|
||||||
"""
|
|
||||||
Model that holds a user's profile, including the django user model
|
|
||||||
|
|
||||||
The trust levels act as permission system and can be displayed as a badge for the user
|
|
||||||
"""
|
|
||||||
|
|
||||||
trust_level = models.IntegerField(
|
|
||||||
choices=TrustLevel.choices,
|
|
||||||
default=TrustLevel.MEMBER, # Default to the lowest trust level
|
|
||||||
)
|
|
||||||
preferred_language = models.ForeignKey(Language, on_delete=models.PROTECT, null=True, blank=True,
|
|
||||||
verbose_name=_('Bevorzugte Sprache'))
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
|
||||||
organization_affiliation = models.ForeignKey(RescueOrganization, on_delete=models.PROTECT, null=True, blank=True,
|
|
||||||
verbose_name=_('Organisation'))
|
|
||||||
reason_for_signup = models.TextField(verbose_name=_("Grund für die Registrierung"), help_text=_(
|
|
||||||
"Wir würden gerne wissen warum du dich registriertst, ob du dich z.B. Tiere eines bestimmten Tierheim einstellen willst 'nur mal gucken' willst. Beides ist toll! Wenn du für ein Tierheim/eine Pflegestelle arbeitest kontaktieren wir dich ggf. um dir erweiterte Rechte zu geben."))
|
|
||||||
email_notifications = models.BooleanField(verbose_name=_("Benachrichtigung per E-Mail"), default=True)
|
|
||||||
REQUIRED_FIELDS = ["reason_for_signup", "email"]
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Nutzer*in')
|
|
||||||
verbose_name_plural = _('Nutzer*innen')
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse("user-detail", args=[str(self.pk)])
|
|
||||||
|
|
||||||
def get_notifications_url(self):
|
|
||||||
return self.get_absolute_url()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class Image(models.Model):
|
|
||||||
image = models.ImageField(upload_to='images')
|
|
||||||
alt_text = models.TextField(max_length=2000)
|
|
||||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.alt_text
|
|
||||||
|
|
||||||
@property
|
|
||||||
def as_html(self):
|
|
||||||
return f'<img src="{MEDIA_URL}/{self.image}" alt="{self.alt_text}">'
|
|
||||||
|
|
||||||
|
|
||||||
class Species(models.Model):
|
|
||||||
"""Model representing a species of animal."""
|
|
||||||
name = models.CharField(max_length=200, help_text=_('Name der Tierart'),
|
|
||||||
verbose_name=_('Name'))
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""String for representing the Model object."""
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Tierart')
|
|
||||||
verbose_name_plural = _('Tierarten')
|
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNotice(models.Model):
|
class AdoptionNotice(models.Model):
|
||||||
@ -253,6 +239,7 @@ class AdoptionNotice(models.Model):
|
|||||||
else:
|
else:
|
||||||
return "mixed"
|
return "mixed"
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comments(self):
|
def comments(self):
|
||||||
return Comment.objects.filter(adoption_notice=self)
|
return Comment.objects.filter(adoption_notice=self)
|
||||||
@ -728,6 +715,7 @@ class CommentNotification(BaseNotification):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
print(f"URL: self.comment.get_absolute_url()")
|
||||||
return self.comment.get_absolute_url
|
return self.comment.get_absolute_url
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
from django.db.models.signals import post_save
|
|
||||||
from django.dispatch import receiver
|
|
||||||
from fellchensammlung.models import BaseNotification, CommentNotification, User, TrustLevel
|
|
||||||
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:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
task_send_notification_email.delay(instance.pk)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
|
||||||
def notification_new_user(sender, instance: User, created: bool, **kwargs):
|
|
||||||
NEWLINE = "\r\n"
|
|
||||||
if not created:
|
|
||||||
return
|
|
||||||
# Create Notification text
|
|
||||||
subject = _("Neuer User") + f": {instance.username}"
|
|
||||||
new_user_text = _("Es hat sich eine neue Person registriert.") + f"{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 = 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.save()
|
|
@ -95,7 +95,6 @@ textarea {
|
|||||||
.container-cards {
|
.container-cards {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@ -453,11 +452,13 @@ select, .button {
|
|||||||
.card h1 {
|
.card h1 {
|
||||||
color: var(--text-three);
|
color: var(--text-three);
|
||||||
text-shadow: 1px 1px var(--shadow-three);
|
text-shadow: 1px 1px var(--shadow-three);
|
||||||
|
width: 85%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card h2 {
|
.card h2 {
|
||||||
color: var(--text-three);
|
color: var(--text-three);
|
||||||
text-shadow: 1px 1px var(--shadow-three);
|
text-shadow: 1px 1px var(--shadow-three);
|
||||||
|
width: 85%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card img {
|
.card img {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
from celery.app import shared_task
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from notfellchen.celery import app as celery_app
|
from notfellchen.celery import app as celery_app
|
||||||
from .mail import send_notification_email
|
|
||||||
from .tools.admin import clean_locations, deactivate_unchecked_adoption_notices, deactivate_404_adoption_notices
|
from .tools.admin import clean_locations, deactivate_unchecked_adoption_notices, deactivate_404_adoption_notices
|
||||||
from .tools.misc import healthcheck_ok
|
from .tools.misc import healthcheck_ok
|
||||||
from .models import Location, AdoptionNotice, Timestamp
|
from .models import Location, AdoptionNotice, Timestamp
|
||||||
@ -45,8 +43,3 @@ def add_adoption_notice_location(pk):
|
|||||||
def task_healthcheck():
|
def task_healthcheck():
|
||||||
healthcheck_ok()
|
healthcheck_ok()
|
||||||
set_timestamp("task_healthcheck")
|
set_timestamp("task_healthcheck")
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def task_send_notification_email(notification_pk):
|
|
||||||
send_notification_email(notification_pk)
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
{% extends "fellchensammlung/base_generic.html" %}
|
|
||||||
{% load custom_tags %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}<title>{{ org.name }}</title>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="card">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
<h2>{% translate 'Vermittlungen der Organisation' %}</h2>
|
|
||||||
<div class="container-cards">
|
|
||||||
{% if org.adoption_notices %}
|
|
||||||
{% for adoption_notice in org.adoption_notices %}
|
|
||||||
{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -13,38 +13,12 @@
|
|||||||
<p>{% translate "Keine bevorzugte Sprache gesetzt." %}</p>
|
<p>{% translate "Keine bevorzugte Sprache gesetzt." %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="container-cards">
|
{% if user.id is request.user.id %}
|
||||||
{% if user.id is request.user.id %}
|
|
||||||
<div class="card">
|
|
||||||
{% 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"
|
|
||||||
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"
|
|
||||||
value={% translate "Create API token" %}>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div><p>
|
|
||||||
<div class="container-comment-form">
|
|
||||||
<h2>{% trans 'Profil verwalten' %}</h2>
|
|
||||||
<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>
|
|
||||||
</p>
|
|
||||||
<h2>{% translate 'Benachrichtigungen' %}</h2>
|
|
||||||
{% include "fellchensammlung/lists/list-notifications.html" %}
|
|
||||||
<h2>{% translate 'Meine Vermittlungen' %}</h2>
|
|
||||||
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
|
||||||
|
|
||||||
{% endif %}
|
<h2>{% translate 'Benachrichtigungen' %}</h2>
|
||||||
|
{% include "fellchensammlung/lists/list-notifications.html" %}
|
||||||
|
<h2>{% translate 'Meine Vermittlungen' %}</h2>
|
||||||
|
{% include "fellchensammlung/lists/list-adoption-notices.html" %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -2,7 +2,7 @@
|
|||||||
{% load custom_tags %}
|
{% load custom_tags %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}<title>{{ adoption_notice.name }}</title>{% endblock %}
|
{% block title %}<title>{{adoption_notice.name }}</title>{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="detail-adoption-notice-header">
|
<div class="detail-adoption-notice-header">
|
||||||
@ -34,9 +34,6 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% translate "Ort" %}</th>
|
<th>{% translate "Ort" %}</th>
|
||||||
{% if adoption_notice.organization %}
|
|
||||||
<th>{% translate "Organisation" %}</th>
|
|
||||||
{% endif %}
|
|
||||||
<th>{% translate "Suchen seit" %}</th>
|
<th>{% translate "Suchen seit" %}</th>
|
||||||
<th>{% translate "Zuletzt aktualisiert" %}</th>
|
<th>{% translate "Zuletzt aktualisiert" %}</th>
|
||||||
<th>{% translate "Weitere Informationen" %}</th>
|
<th>{% translate "Weitere Informationen" %}</th>
|
||||||
@ -49,9 +46,6 @@
|
|||||||
{{ adoption_notice.location_string }}
|
{{ adoption_notice.location_string }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% if adoption_notice.organization %}
|
|
||||||
<td><a href="{{adoption_notice.organization.get_absolute_url }}">{{ adoption_notice.organization }}</a></td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<td>{{ adoption_notice.searching_since }}</td>
|
<td>{{ adoption_notice.searching_since }}</td>
|
||||||
<td>{{ adoption_notice.last_checked | date:'d. F Y' }}</td>
|
<td>{{ adoption_notice.last_checked | date:'d. F Y' }}</td>
|
||||||
@ -60,8 +54,7 @@
|
|||||||
<form method="get" action="{% url 'external-site' %}">
|
<form method="get" action="{% url 'external-site' %}">
|
||||||
<input type="hidden" name="url" value="{{ adoption_notice.further_information }}">
|
<input type="hidden" name="url" value="{{ adoption_notice.further_information }}">
|
||||||
<button class="btn" type="submit" id="submit">
|
<button class="btn" type="submit" id="submit">
|
||||||
{{ adoption_notice.further_information | domain }} <i
|
{{ adoption_notice.further_information | domain }} <i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||||
class="fa-solid fa-arrow-up-right-from-square"></i>
|
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
{% extends "fellchensammlung/base_generic.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load custom_tags %}
|
|
||||||
|
|
||||||
{% block title %}<title>{% translate "403 Forbidden" %}</title>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>403 Forbidden</h1>
|
|
||||||
<p>
|
|
||||||
{% blocktranslate %}
|
|
||||||
Diese Aktion ist dir nicht erlaubt. Logge dich ein oder nutze einen anderen Account. Wenn du denkst, dass hier
|
|
||||||
ein Fehler vorliegt, kontaktiere das Team!
|
|
||||||
{% endblocktranslate %}
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
@ -8,6 +8,9 @@
|
|||||||
<option value="{{ language.0 }}" {% if language.0 == LANGUAGE_CODE_CURRENT %} selected{% endif %}>
|
<option value="{{ language.0 }}" {% if language.0 == LANGUAGE_CODE_CURRENT %} selected{% endif %}>
|
||||||
{{ language.0|language_name_local }}
|
{{ language.0|language_name_local }}
|
||||||
</option>
|
</option>
|
||||||
|
<!--<option value="{{ language.0 }}" {% if language.0 == LANGUAGE_CODE %} selected{% endif %}>
|
||||||
|
{{ language.0|language_name_local }} ({{ language.0 }})
|
||||||
|
</option>-->
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<!--<input type="submit" value={% translate "change" %}>-->
|
<!--<input type="submit" value={% translate "change" %}>-->
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="btn2" href="{% url 'user-me' %}"><i aria-hidden="true" class="fas fa-user"></i></a>
|
<a class="btn2" href="{{ user.get_absolute_url }}"><i aria-hidden="true" class="fas fa-user"></i></a>
|
||||||
<form class="btn2 button_darken" action="{% url 'logout' %}" method="post">
|
<form class="btn2 button_darken" action="{% url 'logout' %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button class="button" type="submit"><i aria-hidden="true" class="fas fa-sign-out"></i></button>
|
<button class="button" type="submit"><i aria-hidden="true" class="fas fa-sign-out"></i></button>
|
||||||
|
@ -24,8 +24,6 @@ urlpatterns = [
|
|||||||
path("vermittlung/<int:adoption_notice_id>/add-photo", views.add_photo_to_adoption_notice, name="adoption-notice-add-photo"),
|
path("vermittlung/<int:adoption_notice_id>/add-photo", views.add_photo_to_adoption_notice, name="adoption-notice-add-photo"),
|
||||||
# ex: /adoption_notice/2/add-animal
|
# ex: /adoption_notice/2/add-animal
|
||||||
path("vermittlung/<int:adoption_notice_id>/add-animal", views.adoption_notice_add_animal, name="adoption-notice-add-animal"),
|
path("vermittlung/<int:adoption_notice_id>/add-animal", views.adoption_notice_add_animal, name="adoption-notice-add-animal"),
|
||||||
path("organisation/<int:rescue_organization_id>/", views.detail_view_rescue_organization,
|
|
||||||
name="rescue-organization-detail"),
|
|
||||||
|
|
||||||
# ex: /search/
|
# ex: /search/
|
||||||
path("suchen/", views.search, name="search"),
|
path("suchen/", views.search, name="search"),
|
||||||
@ -52,9 +50,7 @@ urlpatterns = [
|
|||||||
## USERS ##
|
## USERS ##
|
||||||
###########
|
###########
|
||||||
# ex: user/1
|
# ex: user/1
|
||||||
path("user/<int:user_id>/", views.user_by_id, name="user-detail"),
|
path("user/<int:user_id>/", views.user_detail, name="user-detail"),
|
||||||
path("user/me/", views.my_profile, name="user-me"),
|
|
||||||
path('user/me/export/', views.export_own_profile, name='user-me-export'),
|
|
||||||
|
|
||||||
path('accounts/register/',
|
path('accounts/register/',
|
||||||
RegistrationView.as_view(
|
RegistrationView.as_view(
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse
|
from django.http import HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
from django.core.serializers import serialize
|
|
||||||
import json
|
|
||||||
|
|
||||||
from .mail import mail_admins_new_report
|
from .mail import mail_admins_new_report
|
||||||
from notfellchen import settings
|
from notfellchen import settings
|
||||||
@ -16,7 +14,7 @@ from notfellchen import settings
|
|||||||
from fellchensammlung import logger
|
from fellchensammlung import logger
|
||||||
from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
|
from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
|
||||||
User, Location, AdoptionNoticeStatus, Subscriptions, CommentNotification, BaseNotification, RescueOrganization, \
|
User, Location, AdoptionNoticeStatus, Subscriptions, CommentNotification, BaseNotification, RescueOrganization, \
|
||||||
Species, Log, Timestamp, TrustLevel
|
Species, Log, Timestamp
|
||||||
from .forms import AdoptionNoticeForm, AdoptionNoticeFormWithDateWidget, ImageForm, ReportAdoptionNoticeForm, \
|
from .forms import AdoptionNoticeForm, AdoptionNoticeFormWithDateWidget, ImageForm, ReportAdoptionNoticeForm, \
|
||||||
CommentForm, ReportCommentForm, AnimalForm, \
|
CommentForm, ReportCommentForm, AnimalForm, \
|
||||||
AdoptionNoticeSearchForm, AnimalFormWithDateWidget, AdoptionNoticeFormWithDateWidgetAutoAnimal
|
AdoptionNoticeSearchForm, AnimalFormWithDateWidget, AdoptionNoticeFormWithDateWidgetAutoAnimal
|
||||||
@ -25,19 +23,18 @@ from .tools.geo import GeoAPI
|
|||||||
from .tools.metrics import gather_metrics_data
|
from .tools.metrics import gather_metrics_data
|
||||||
from .tools.admin import clean_locations, get_unchecked_adoption_notices, deactivate_unchecked_adoption_notices
|
from .tools.admin import clean_locations, get_unchecked_adoption_notices, deactivate_unchecked_adoption_notices
|
||||||
from .tasks import add_adoption_notice_location
|
from .tasks import add_adoption_notice_location
|
||||||
from rest_framework.authtoken.models import Token
|
|
||||||
|
|
||||||
|
|
||||||
def user_is_trust_level_or_above(user, trust_level=TrustLevel.MODERATOR):
|
def user_is_trust_level_or_above(user, trust_level=User.MODERATOR):
|
||||||
return user.is_authenticated and user.trust_level >= trust_level
|
return user.is_authenticated and user.trust_level >= User.TRUST_LEVEL[trust_level]
|
||||||
|
|
||||||
|
|
||||||
def user_is_owner_or_trust_level(user, django_object, trust_level=TrustLevel.MODERATOR):
|
def user_is_owner_or_trust_level(user, django_object, trust_level=User.MODERATOR):
|
||||||
return user.is_authenticated and (
|
return user.is_authenticated and (
|
||||||
user.trust_level == trust_level or django_object.owner == user)
|
user.trust_level == User.TRUST_LEVEL[trust_level] or django_object.owner == user)
|
||||||
|
|
||||||
|
|
||||||
def fail_if_user_not_owner_or_trust_level(user, django_object, trust_level=TrustLevel.MODERATOR):
|
def fail_if_user_not_owner_or_trust_level(user, django_object, trust_level=User.MODERATOR):
|
||||||
if not user_is_owner_or_trust_level(user, django_object, trust_level):
|
if not user_is_owner_or_trust_level(user, django_object, trust_level):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
@ -72,9 +69,7 @@ def change_language(request):
|
|||||||
translation.activate(language_code)
|
translation.activate(language_code)
|
||||||
response = HttpResponseRedirect(redirect_path)
|
response = HttpResponseRedirect(redirect_path)
|
||||||
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language_code)
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language_code)
|
||||||
return response
|
return response
|
||||||
else:
|
|
||||||
return render(request, 'fellchensammlung/index.html')
|
|
||||||
|
|
||||||
|
|
||||||
def adoption_notice_detail(request, adoption_notice_id):
|
def adoption_notice_detail(request, adoption_notice_id):
|
||||||
@ -153,8 +148,7 @@ def adoption_notice_edit(request, adoption_notice_id):
|
|||||||
adoption_notice_instance.save()
|
adoption_notice_instance.save()
|
||||||
|
|
||||||
"""Log"""
|
"""Log"""
|
||||||
Log.objects.create(user=request.user, action="adoption_notice_edit",
|
Log.objects.create(user=request.user, action="adoption_notice_edit", text=f"{request.user} hat Vermittlung {adoption_notice.pk} geändert")
|
||||||
text=f"{request.user} hat Vermittlung {adoption_notice.pk} geändert")
|
|
||||||
return redirect(reverse("adoption-notice-detail", args=[adoption_notice_instance.pk], ))
|
return redirect(reverse("adoption-notice-detail", args=[adoption_notice_instance.pk], ))
|
||||||
else:
|
else:
|
||||||
form = AdoptionNoticeForm(instance=adoption_notice)
|
form = AdoptionNoticeForm(instance=adoption_notice)
|
||||||
@ -210,7 +204,7 @@ def add_adoption_notice(request):
|
|||||||
add_adoption_notice_location.delay_on_commit(instance.pk)
|
add_adoption_notice_location.delay_on_commit(instance.pk)
|
||||||
|
|
||||||
# Set correct status
|
# Set correct status
|
||||||
if request.user.trust_level >= TrustLevel.MODERATOR:
|
if request.user.trust_level >= User.TRUST_LEVEL[User.COORDINATOR]:
|
||||||
instance.set_active()
|
instance.set_active()
|
||||||
else:
|
else:
|
||||||
instance.set_unchecked()
|
instance.set_unchecked()
|
||||||
@ -420,41 +414,16 @@ def report_detail_success(request, report_id):
|
|||||||
return report_detail(request, report_id, form_complete=True)
|
return report_detail(request, report_id, form_complete=True)
|
||||||
|
|
||||||
|
|
||||||
def user_detail(request, user, token=None):
|
|
||||||
context = {"user": user,
|
|
||||||
"adoption_notices": AdoptionNotice.objects.filter(owner=user),
|
|
||||||
"notifications": BaseNotification.objects.filter(user=user, read=False)}
|
|
||||||
if token is not None:
|
|
||||||
context["token"] = token
|
|
||||||
return render(request, 'fellchensammlung/details/detail-user.html', context=context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def user_by_id(request, user_id):
|
def user_detail(request, user_id):
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
# Only users that are mods or owners of the user are allowed to view
|
# Only users that are mods or owners of the user are allowed to view
|
||||||
fail_if_user_not_owner_or_trust_level(request.user, user)
|
fail_if_user_not_owner_or_trust_level(request.user, user)
|
||||||
if user == request.user:
|
if request.method == "POST":
|
||||||
return my_profile(request)
|
|
||||||
else:
|
|
||||||
return user_detail(request, user)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required()
|
|
||||||
def my_profile(request):
|
|
||||||
if request.method == 'POST':
|
|
||||||
if "create_token" in request.POST:
|
|
||||||
Token.objects.create(user=request.user)
|
|
||||||
elif "delete_token" in request.POST:
|
|
||||||
Token.objects.get(user=request.user).delete()
|
|
||||||
|
|
||||||
action = request.POST.get("action")
|
action = request.POST.get("action")
|
||||||
if action == "notification_mark_read":
|
if action == "notification_mark_read":
|
||||||
notification_id = request.POST.get("notification_id")
|
notification_id = request.POST.get("notification_id")
|
||||||
try:
|
notification = CommentNotification.objects.get(pk=notification_id)
|
||||||
notification = CommentNotification.objects.get(pk=notification_id)
|
|
||||||
except CommentNotification.DoesNotExist:
|
|
||||||
notification = BaseNotification.objects.get(pk=notification_id)
|
|
||||||
notification.read = True
|
notification.read = True
|
||||||
notification.save()
|
notification.save()
|
||||||
elif action == "notification_mark_all_read":
|
elif action == "notification_mark_all_read":
|
||||||
@ -462,11 +431,11 @@ def my_profile(request):
|
|||||||
for notification in notifications:
|
for notification in notifications:
|
||||||
notification.read = True
|
notification.read = True
|
||||||
notification.save()
|
notification.save()
|
||||||
try:
|
|
||||||
token = Token.objects.get(user=request.user)
|
context = {"user": user,
|
||||||
except Token.DoesNotExist:
|
"adoption_notices": AdoptionNotice.objects.filter(owner=user),
|
||||||
token = None
|
"notifications": CommentNotification.objects.filter(user=user, read=False)}
|
||||||
return user_detail(request, request.user, token)
|
return render(request, 'fellchensammlung/details/detail-user.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(user_is_trust_level_or_above)
|
@user_passes_test(user_is_trust_level_or_above)
|
||||||
@ -478,19 +447,16 @@ def modqueue(request):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def updatequeue(request):
|
def updatequeue(request):
|
||||||
|
#TODO: Make sure update can only be done for instances with permission
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
adoption_notice = AdoptionNotice.objects.get(id=request.POST.get("adoption_notice_id"))
|
adoption_notice = AdoptionNotice.objects.get(id=request.POST.get("adoption_notice_id"))
|
||||||
edit_permission = request.user == adoption_notice.owner or user_is_trust_level_or_above(request.user,
|
|
||||||
TrustLevel.MODERATOR)
|
|
||||||
if not edit_permission:
|
|
||||||
return render(request, "fellchensammlung/errors/403.html", status=403)
|
|
||||||
action = request.POST.get("action")
|
action = request.POST.get("action")
|
||||||
if action == "checked_inactive":
|
if action == "checked_inactive":
|
||||||
adoption_notice.set_closed()
|
adoption_notice.set_closed()
|
||||||
if action == "checked_active":
|
if action == "checked_active":
|
||||||
adoption_notice.set_active()
|
adoption_notice.set_active()
|
||||||
|
|
||||||
if user_is_trust_level_or_above(request.user, TrustLevel.MODERATOR):
|
if user_is_trust_level_or_above(request.user, User.MODERATOR):
|
||||||
last_checked_adoption_list = AdoptionNotice.objects.order_by("last_checked")
|
last_checked_adoption_list = AdoptionNotice.objects.order_by("last_checked")
|
||||||
else:
|
else:
|
||||||
last_checked_adoption_list = AdoptionNotice.objects.filter(owner=request.user).order_by("last_checked")
|
last_checked_adoption_list = AdoptionNotice.objects.filter(owner=request.user).order_by("last_checked")
|
||||||
@ -575,20 +541,3 @@ def external_site_warning(request):
|
|||||||
context.update(texts)
|
context.update(texts)
|
||||||
|
|
||||||
return render(request, 'fellchensammlung/external_site_warning.html', context=context)
|
return render(request, 'fellchensammlung/external_site_warning.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
def detail_view_rescue_organization(request, rescue_organization_id):
|
|
||||||
org = RescueOrganization.objects.get(pk=rescue_organization_id)
|
|
||||||
return render(request, 'fellchensammlung/details/detail-rescue-organization.html', context={"org": org})
|
|
||||||
|
|
||||||
|
|
||||||
def export_own_profile(request):
|
|
||||||
user = request.user
|
|
||||||
ANs = AdoptionNotice.objects.filter(owner=user)
|
|
||||||
user_as_json = serialize('json', [user])
|
|
||||||
user_editable = json.loads(user_as_json)
|
|
||||||
user_editable[0]["fields"]["password"] = "Password hash redacted for security reasons"
|
|
||||||
user_as_json = json.dumps(user_editable)
|
|
||||||
ANs_as_json = serialize('json', ANs)
|
|
||||||
full_json = f"{user_as_json}, {ANs_as_json}"
|
|
||||||
return HttpResponse(full_json, content_type="application/json")
|
|
||||||
|
@ -169,7 +169,6 @@ INSTALLED_APPS = [
|
|||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
"crispy_bootstrap4",
|
"crispy_bootstrap4",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
'rest_framework.authtoken'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -25,9 +25,9 @@ urlpatterns = [
|
|||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += i18n_patterns(
|
urlpatterns += i18n_patterns (
|
||||||
path("", include("fellchensammlung.urls")),
|
path("", include("fellchensammlung.urls")),
|
||||||
prefix_default_language=False
|
prefix_default_language = False
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{{ site.name }}: {% trans "Account aktivieren" %}
|
{% trans "Account aktivieren" %} {{ site.name }}:
|
||||||
|
|
||||||
{% trans 'Hier ist dein Aktivierungs-Key. Mit diesem kannst du deinen Account freischalten.' %}
|
<a href="{{ site.domain }}{% url 'django_registration_activate' activation_key%}">{% trans "Activate by clicking this link" %}</a>
|
||||||
{{ activation_key }}
|
{% trans "oder öffne den folgenden link im Browser" %}:
|
||||||
|
{{ site.domain }}{% url 'django_registration_activate' activation_key%}
|
||||||
|
|
||||||
{% trans "Öffne den folgenden link im Browser und gib den Aktivierungs-Key dort ein" %}:
|
{% blocktrans %}Der link ist gültig für {{ expiration_days }} tage.{% endblocktrans %}
|
||||||
https://{{ site.domain }}{% url 'django_registration_activate' %}
|
|
||||||
|
|
||||||
{% blocktrans %}Der Link ist für {{ expiration_days }} Tage gültig.{% endblocktrans %}
|
|
@ -1 +1 @@
|
|||||||
{% load i18n %}{{ site.name }}: {% translate "Account aktivieren" %}
|
{% load i18n %}{% translate "Account aktivieren" %} {{ site.name }}
|
@ -1,15 +0,0 @@
|
|||||||
{% extends "fellchensammlung/base_generic.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% if not user.is_authenticated %}
|
|
||||||
<form action="" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_p }}
|
|
||||||
<input type="submit" class="btn2" value={% translate 'Absenden' %}>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<p>{% translate "Du bist bereits eingeloggt." %}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
@ -2,5 +2,5 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>{% translate "Du bist nun registriert. Du hast eine E-Mail mit einem Link zur Aktivierung deines Kontos bekommen." %}</p>
|
<p>{% translate "Du bist nun registriert. Du hast eine E-Mail mit einem Link zur aktivierung bekommen." %}</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -4,7 +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, TrustLevel
|
from fellchensammlung.models import Announcement, Language, User
|
||||||
|
|
||||||
|
|
||||||
class UserTest(TestCase):
|
class UserTest(TestCase):
|
||||||
@ -12,7 +12,7 @@ class UserTest(TestCase):
|
|||||||
test_user_1 = User.objects.create(username="Testuser1", password="SUPERSECRET", email="test@example.org")
|
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 == 1)
|
||||||
self.assertTrue(test_user_1.trust_level == TrustLevel.MEMBER)
|
self.assertTrue(test_user_1.trust_level == User.TRUST_LEVEL[User.MEMBER])
|
||||||
|
|
||||||
|
|
||||||
class AnnouncementTest(TestCase):
|
class AnnouncementTest(TestCase):
|
||||||
|
@ -4,7 +4,7 @@ from django.urls import reverse
|
|||||||
|
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus, TrustLevel
|
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus
|
||||||
from fellchensammlung.views import add_adoption_notice
|
from fellchensammlung.views import add_adoption_notice
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +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 = TrustLevel.ADMIN
|
test_user0.trust_level = User.TRUST_LEVEL[User.ADMIN]
|
||||||
test_user0.save()
|
test_user0.save()
|
||||||
|
|
||||||
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
||||||
@ -133,7 +133,7 @@ class UpdateQueueTest(TestCase):
|
|||||||
first_name="Admin",
|
first_name="Admin",
|
||||||
last_name="BOFH",
|
last_name="BOFH",
|
||||||
password='12345',
|
password='12345',
|
||||||
trust_level=TrustLevel.MODERATOR)
|
trust_level=User.TRUST_LEVEL[User.MODERATOR])
|
||||||
test_user0.is_superuser = True
|
test_user0.is_superuser = True
|
||||||
test_user0.save()
|
test_user0.save()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user