diff --git a/core/admin/__init__.py b/core/admin/__init__.py index 31ddfd0d6..dbc809b06 100644 --- a/core/admin/__init__.py +++ b/core/admin/__init__.py @@ -5,8 +5,10 @@ from core.admin.event_page_content import EventPageContentAdmin from core.admin.event_page_menu import EventPageMenuAdmin from core.admin.flat_page import MyFlatPageAdmin +from core.admin.organizerissue import OrganizerIssueAdmin from core.admin.user import UserAdmin from core.models import Event, EventPageContent, EventPageMenu, User +from core.models.organizerissue import OrganizerIssue admin.site.unregister(FlatPage) admin.site.register(FlatPage, MyFlatPageAdmin) @@ -14,3 +16,4 @@ admin.site.register(EventPageContent, EventPageContentAdmin) admin.site.register(EventPageMenu, EventPageMenuAdmin) admin.site.register(User, UserAdmin) +admin.site.register(OrganizerIssue, OrganizerIssueAdmin) diff --git a/core/admin/forms/organizer.py b/core/admin/forms/organizerissue.py similarity index 82% rename from core/admin/forms/organizer.py rename to core/admin/forms/organizerissue.py index 2d5fe2d9d..2a442a48e 100644 --- a/core/admin/forms/organizer.py +++ b/core/admin/forms/organizerissue.py @@ -1,7 +1,8 @@ from django import forms -from models.event import Event -from models.organizer import OrganizerIssue -from models.user import User + +from core.models.event import Event +from core.models.organizerissue import OrganizerIssue +from core.models.user import User class OrganizerIssueForm(forms.ModelForm): @@ -21,5 +22,4 @@ class Meta: "issue_handled_by", "findings", "comments", - "last_updated", ) diff --git a/core/admin/organizer.py b/core/admin/organizer.py deleted file mode 100644 index 8d733e164..000000000 --- a/core/admin/organizer.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.contrib import admin, messages -from django.shortcuts import get_object_or_404 -from django.urls import path -from django.utils.translation import gettext_lazy as _ -from models.organizer import OrganizerIssue - - -@admin.register(OrganizerIssue) -class OrganizerIssueAdmin(admin.ModelAdmin): - list_display = ( - "organizer", - "event", - "date_reported", - "reported_by", - "issue_handled", - "issue_handled_by", - "last_updated", - ) - list_filter = ( - "organizer", - "event", - "reported_by", - ) - search_fields = ( - "organizer", - "event", - "reported_by", - ) - - def get_urls(self): - urls = super().get_urls() - - my_urls = [ - path( - "/blacklist_organizer/", - self.admin_site.admin_view(self.blacklist_organizer), - name="core_organizer_blacklist_organizer", - ), - ] - - return my_urls + urls - - def blacklist_organizer(self, request, organizerissue_id): - organizer = get_object_or_404(OrganizerIssue, id=organizerissue_id) - organizer.blacklist_organizer() - messages.success( - request, - _("Organizer %(organizer)s, of %(event)s has been blackliste.") - % {"organizer": f"{organizer.organizer.get_full_name()}", "event": organizer.event}, - ) diff --git a/core/admin/organizerissue.py b/core/admin/organizerissue.py new file mode 100644 index 000000000..3d07370d1 --- /dev/null +++ b/core/admin/organizerissue.py @@ -0,0 +1,68 @@ +from django.contrib import admin, messages +from django.shortcuts import get_object_or_404, redirect +from django.urls import path +from django.utils.translation import gettext_lazy as _ + +from core.models.organizerissue import OrganizerIssue + +from .forms.organizerissue import OrganizerIssueForm + + +class OrganizerIssueAdmin(admin.ModelAdmin): + form = OrganizerIssueForm + list_display = ( + "organizer", + "event", + "date_reported", + "reported_by", + "issue_handled", + "issue_handled_by", + "last_updated", + ) + list_filter = ( + "organizer", + "event", + "reported_by", + ) + search_fields = ( + "organizer", + "event", + "reported_by", + ) + + def get_urls(self): + urls = super().get_urls() + + my_urls = [ + path( + "/triage/blacklist/", + self.admin_site.admin_view(self.blacklist), + name="core_organizerissue_blacklist", + ), + path( + "/triage/reverse_blacklist/", + self.admin_site.admin_view(self.reverse_blacklist), + name="core_organizerissue_reverse_blacklist", + ), + ] + return my_urls + urls + + def blacklist(self, request, organizerissue_id): + organizer = get_object_or_404(OrganizerIssue, id=organizerissue_id) + organizer.blacklist_organizer() + messages.success( + request, + _("Organizer %(organizer)s, of %(event)s has been blacklisted.") + % {"organizer": f"{organizer.organizer.get_full_name()}", "event": organizer.event}, + ) + return redirect("admin:core_organizerissue_changelist") + + def reverse_blacklist(self, request, organizerissue_id): + organizer = get_object_or_404(OrganizerIssue, id=organizerissue_id) + organizer.reverse_blacklist_organizer() + messages.success( + request, + _("Blacklisting for organizer %(organizer)s, of %(event)s has been reversed.") + % {"organizer": f"{organizer.organizer.get_full_name()}", "event": organizer.event}, + ) + return redirect("admin:core_organizerissue_changelist") diff --git a/core/migrations/0006_auto_20240130_1644.py b/core/migrations/0006_auto_20240130_1644.py new file mode 100644 index 000000000..1535ca007 --- /dev/null +++ b/core/migrations/0006_auto_20240130_1644.py @@ -0,0 +1,69 @@ +# Generated by Django 3.2.20 on 2024-01-30 16:44 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.db.models.manager + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0005_auto_20220422_1321"), + ] + + operations = [ + migrations.AlterModelManagers( + name="event", + managers=[ + ("all_objects", django.db.models.manager.Manager()), + ], + ), + migrations.AddField( + model_name="user", + name="is_blacklisted", + field=models.BooleanField(default=False), + ), + migrations.CreateModel( + name="OrganizerIssue", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("date_reported", models.DateField()), + ("reported_by", models.CharField(max_length=100)), + ("reporter_email", models.EmailField(max_length=100)), + ("issue", models.TextField()), + ("issue_handled", models.BooleanField()), + ("findings", models.TextField(blank=True, null=True)), + ("comments", models.TextField(blank=True, null=True)), + ("last_updated", models.DateTimeField(auto_now=True)), + ( + "event", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="event", + to="core.event", + ), + ), + ( + "issue_handled_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="staff_responsible", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "organizer", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="oganizer", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/core/models.py b/core/models.py index 45ae5c762..e69de29bb 100644 --- a/core/models.py +++ b/core/models.py @@ -1,28 +0,0 @@ -from django.db import models -from models.event import Event -from models.user import User - - -class OrganizerIssue(models.Model): - created_at = models.DateTimeField(auto_now_add=True) - organizer = models.ForeignKey(User, related_name="oganizer", on_delete=models.deletion.CASCADE) - event = models.ForeignKey(to=Event, null=True, blank=True, related_name="event", on_delete=models.deletion.SET_NULL) - date_reported = models.DateField() - reported_by = models.CharField(max_length=100) - reporter_email = models.EmailField(max_length=100) - issue = models.TextField() - issue_handled = models.BooleanField() - issue_handled_by = models.ForeignKey( - to=User, null=True, blank=True, related_name="staff_responsible", on_delete=models.deletion.SET_NULL - ) - findings = models.TextChoices(blank=True, null=True) - comments = models.TextField(blank=True, null=True) - last_updated = models.DateTimeField(auto_now=True) - - def __str__(self): - return f"{self.organizer.get_full_name()} - {self.event}" - - def blacklist_organizer(self): - user = User.objects.get(id=self.organizer) - user.is_blacklisted = True - user.save() diff --git a/core/models/managers/organizer.py b/core/models/managers/organizer.py new file mode 100644 index 000000000..0b19164b7 --- /dev/null +++ b/core/models/managers/organizer.py @@ -0,0 +1,5 @@ +from django.db import models + + +class OrganizerManager(models.Manager): + pass diff --git a/core/models/organizer.py b/core/models/organizerissue.py similarity index 75% rename from core/models/organizer.py rename to core/models/organizerissue.py index d942d954e..7c93671d9 100644 --- a/core/models/organizer.py +++ b/core/models/organizerissue.py @@ -1,7 +1,7 @@ from django.db import models -from .event import Event -from .user import User +from core.models.event import Event +from core.models.user import User class OrganizerIssue(models.Model): @@ -16,7 +16,7 @@ class OrganizerIssue(models.Model): issue_handled_by = models.ForeignKey( to=User, null=True, blank=True, related_name="staff_responsible", on_delete=models.deletion.SET_NULL ) - findings = models.TextChoices(blank=True, null=True) + findings = models.TextField(blank=True, null=True) comments = models.TextField(blank=True, null=True) last_updated = models.DateTimeField(auto_now=True) @@ -24,6 +24,11 @@ def __str__(self): return f"{self.organizer.get_full_name()} - {self.event}" def blacklist_organizer(self): - user = User.objects.get(id=self.organizer) + user = User.objects.get(id=self.organizer.id) user.is_blacklisted = True user.save() + + def reverse_blacklist_organizer(self): + user = User.objects.get(id=self.organizer.id) + user.is_blacklisted = False + user.save() diff --git a/core/models/user.py b/core/models/user.py index d0d8dbdbd..b7ca6cc4b 100644 --- a/core/models/user.py +++ b/core/models/user.py @@ -42,9 +42,11 @@ def add_to_organizers_group(self): self.groups.add(group) def __str__(self): + status = "- (Organizer is Blacklisted)" if self.is_blacklisted else "" + if not self.first_name and not self.last_name: - return f"{self.email}" - return f"{self.get_full_name()} ({self.email})" + return f"{self.email} {status}" + return f"{self.get_full_name()} ({self.email}) {status}" def get_short_name(self): return self.first_name diff --git a/organize/admin.py b/organize/admin.py index 7e5e528b1..8f970bb39 100644 --- a/organize/admin.py +++ b/organize/admin.py @@ -50,6 +50,7 @@ class EventApplicationAdmin(admin.ModelAdmin): "status", "created_at", "status_changed_at", + "organizer_blacklisted", "about_you", "why", "involvement", @@ -126,7 +127,12 @@ class EventApplicationAdmin(admin.ModelAdmin): ( _("Main organizer"), { - "fields": ["main_organizer_email", "main_organizer_first_name", "main_organizer_last_name"], + "fields": [ + "main_organizer_email", + "main_organizer_first_name", + "main_organizer_last_name", + "organizer_blacklisted", + ], "classes": ( "suit-tab", "suit-tab-organizers", diff --git a/organize/migrations/0010_auto_20240131_1204.py b/organize/migrations/0010_auto_20240131_1204.py new file mode 100644 index 000000000..fa9e67974 --- /dev/null +++ b/organize/migrations/0010_auto_20240131_1204.py @@ -0,0 +1,283 @@ +# Generated by Django 3.2.20 on 2024-01-31 12:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("organize", "0009_eventapplication_confirm_covid_19_protocols"), + ] + + operations = [ + migrations.AddField( + model_name="eventapplication", + name="organizer_blacklisted", + field=models.BooleanField(default=False, verbose_name="Main organizer or co-organizer(s) blacklisted"), + ), + migrations.AlterField( + model_name="eventapplication", + name="confirm_covid_19_protocols", + field=models.BooleanField( + default=False, + verbose_name="Confirmation that you will postpone or have a remote event if your governmentregulations for Covid-19 change.", + ), + ), + migrations.AlterField( + model_name="eventapplication", + name="country", + field=models.CharField( + choices=[ + ("AF", "Afghanistan"), + ("AX", "Åland Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BQ", "Bonaire, Sint Eustatius and Saba"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("CV", "Cabo Verde"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo (the Democratic Republic of the)"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "Côte d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CW", "Curaçao"), + ("CY", "Cyprus"), + ("CZ", "Czechia"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("SZ", "Eswatini"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Laos"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia (Federated States of)"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("KP", "North Korea"), + ("MK", "North Macedonia"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestine, State of"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "Réunion"), + ("RO", "Romania"), + ("RU", "Russia"), + ("RW", "Rwanda"), + ("BL", "Saint Barthélemy"), + ("SH", "Saint Helena, Ascension and Tristan da Cunha"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin (French part)"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SX", "Sint Maarten (Dutch part)"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("KR", "South Korea"), + ("SS", "South Sudan"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syria"), + ("TW", "Taiwan"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Türkiye"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("GB", "United Kingdom"), + ("UM", "United States Minor Outlying Islands"), + ("US", "United States of America"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Vietnam"), + ("VG", "Virgin Islands (British)"), + ("VI", "Virgin Islands (U.S.)"), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), + ], + max_length=200, + ), + ), + ] diff --git a/organize/models.py b/organize/models.py index 80f7ff670..798f00cb5 100644 --- a/organize/models.py +++ b/organize/models.py @@ -10,7 +10,7 @@ from core import gmail_accounts from core.deploy_event import copy_event -from core.models import Event +from core.models import Event, User from core.utils import get_coordinates_for_city from core.validators import validate_approximatedate @@ -21,8 +21,16 @@ class EventApplicationManager(models.Manager): def create(self, **data_dict): + main_organizer_email = data_dict["main_organizer_email"] + try: + main_organizer = User.objects.get(email=main_organizer_email) + if main_organizer.is_blacklisted: + data_dict["organizer_blacklisted"] = True + except User.DoesNotExist: + pass + previous_application = ( - EventApplication.objects.filter(main_organizer_email=data_dict["main_organizer_email"], status=NEW) + EventApplication.objects.filter(main_organizer_email=main_organizer_email, status=NEW) .order_by("-created_at") .first() ) @@ -43,7 +51,7 @@ def create(self, **data_dict): ) previous_event = ( - EventApplication.objects.filter(main_organizer_email=data_dict["main_organizer_email"], status=DEPLOYED) + EventApplication.objects.filter(main_organizer_email=main_organizer_email, status=DEPLOYED) .order_by("-date") .first() ) @@ -90,6 +98,9 @@ class EventApplication(models.Model): main_organizer_email = models.EmailField() main_organizer_first_name = models.CharField(max_length=30) main_organizer_last_name = models.CharField(max_length=30, blank=True, default="") + organizer_blacklisted = models.BooleanField( + default=False, verbose_name="Main organizer or co-organizer(s) blacklisted" + ) created_at = models.DateTimeField(auto_now_add=True) # application fields diff --git a/organize/views.py b/organize/views.py index ce9386a05..bb2bd71c6 100644 --- a/organize/views.py +++ b/organize/views.py @@ -3,6 +3,8 @@ from django.shortcuts import redirect, render from formtools.wizard.views import NamedUrlSessionWizardView +from core.models import User + from .emails import send_application_confirmation, send_application_notification from .forms import ( ApplicationForm, @@ -50,6 +52,13 @@ def done(self, form_list, **kwargs): application = EventApplication.object.create(**data_dict) for organizer in organizers_data: application.coorganizers.create(**organizer) + try: + user = User.objects.get(email=organizer["email"]) + if user.is_blacklisted: + application.organizer_blacklisted = True + application.save() + except User.DoesNotExist: + pass send_application_confirmation(application) send_application_notification(application) except ValidationError as error: diff --git a/templates/admin/core/organizerissue/change_form.html b/templates/admin/core/organizerissue/change_form.html new file mode 100644 index 000000000..a109f0440 --- /dev/null +++ b/templates/admin/core/organizerissue/change_form.html @@ -0,0 +1,24 @@ +{% extends "admin/change_form.html" %} +{% load i18n %} + +{% block extrastyle %} +{{ block.super }} + +{% endblock %} + +{% block object-tools %} +{% if original %} + +{% endif %} +{{ block.super }} +{% endblock %} \ No newline at end of file