Skip to content

Commit

Permalink
✨ Add organizer with popup in form - ref #3587
Browse files Browse the repository at this point in the history
  • Loading branch information
TheoLechemia authored and submarcos committed Feb 29, 2024
1 parent 84eab09 commit cc5e070
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG

2.102.1+dev (XXXX-XX-XX)
------------------------
- Add popup button to add organizer in touristic event form

**New features**

Expand Down
15 changes: 14 additions & 1 deletion geotrek/tourism/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from geotrek.tourism.widgets import AutoLocateMapWidget

from crispy_forms.layout import Div, HTML, Fieldset
from mapentity.widgets import SelectMultipleWithPop


from .models import (TouristicContent, TouristicEvent, TouristicEventParticipantCount,
TouristicEventParticipantCategory)
TouristicEventParticipantCategory, TouristicEventOrganizer)
from geotrek.common.forms import CommonForm


Expand Down Expand Up @@ -142,6 +144,11 @@ def __init__(self, *args, **kwargs):
self.fields['end_date'].widget.attrs['placeholder'] = _('dd/mm/yyyy')
self.fields['start_time'].widget.attrs['placeholder'] = _('HH:MM')
self.fields['end_time'].widget.attrs['placeholder'] = _('HH:MM')
if self.user.has_perm("tourism.add_touristiceventorganizer"):
self.fields['organizer'].widget = SelectMultipleWithPop(
choices=self.fields['organizer'].choices,
add_url=TouristicEventOrganizer.get_add_url()
)
# Since we use chosen() in trek_form.html, we don't need the default help text
for f in ['themes', 'source']:
self.fields[f].help_text = ''
Expand Down Expand Up @@ -189,3 +196,9 @@ def _save_m2m(self):
TouristicEventParticipantCount.objects.update_or_create(event=self.instance, category=category, defaults={'count': count})
else:
TouristicEventParticipantCount.objects.filter(event=self.instance, category=category).delete()


class TouristicEventOrganizerFormPopup(CommonForm):
class Meta:
model = TouristicEventOrganizer
fields = ['label']
7 changes: 6 additions & 1 deletion geotrek/tourism/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.dispatch import receiver
from django.utils.formats import date_format
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from easy_thumbnails.alias import aliases
from easy_thumbnails.exceptions import InvalidImageFormatError
from easy_thumbnails.files import get_thumbnailer
Expand Down Expand Up @@ -387,7 +388,7 @@ def __str__(self):


class TouristicEventOrganizer(TimeStampedModelMixin):
label = models.CharField(verbose_name=_("Label"), max_length=256)
label = models.CharField(verbose_name=_("OrganizerLabel"), max_length=256)

class Meta:
verbose_name = _("Organizer")
Expand All @@ -397,6 +398,10 @@ class Meta:
def __str__(self):
return self.label

@classmethod
def get_add_url(cls):
return reverse('tourism:organizer_add')


class TouristicEvent(ZoningPropertiesMixin, AddPropertyMixin, PublishableMixin, GeotrekMapEntityMixin,
StructureRelated, PicturesMixin, TimeStampedModelMixin, NoDeleteMixin):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends "mapentity/base_site.html" %}
{% load static i18n crispy_forms_tags %}

{% block title %}{% trans "Add Organizer" %}{% endblock title %}

{% block navbar %}{% endblock navbar %}

{% block content %}
<h3>{% trans "Add Organizer"%}</h3>
{% crispy form %}
{% endblock content %}
25 changes: 25 additions & 0 deletions geotrek/tourism/tests/test_forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@


from django.forms.widgets import Select
from django.test import TestCase
from django.contrib.auth.models import Permission

from mapentity.widgets import SelectMultipleWithPop

from geotrek.authent.tests.factories import UserFactory
from geotrek.tourism.forms import TouristicEventForm
Expand Down Expand Up @@ -85,3 +90,23 @@ def test_begin_end_date(self):
)
self.assertFalse(form1.is_valid())
self.assertIn("Start date is after end date", str(form1.errors))

def test_organizers_widget_select(self):
# if user has 'add_touristiceventorganizer' permission the widget must be a SelectMultipleWithPop
# otherwith a Select
form = TouristicEventForm(
user=UserFactory(),
data={}
)
assert type(form.fields['organizer'].widget) is Select

def test_organizers_widget_select_multiple_with_pop(self):
# if user has 'add_touristiceventorganizer' permission the widget must be a SelectMultipleWithPop
# otherwith a Select
user = UserFactory()
user.user_permissions.add(Permission.objects.get(codename='add_touristiceventorganizer'))
form = TouristicEventForm(
user=user,
data={}
)
assert type(form.fields['organizer'].widget) is SelectMultipleWithPop
6 changes: 5 additions & 1 deletion geotrek/tourism/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from mapentity.middleware import clear_internal_user_cache

from geotrek.core.tests import factories as core_factories
from geotrek.tourism.models import TouristicContentType
from geotrek.tourism.models import TouristicContentType, TouristicEventOrganizer
from geotrek.tourism.tests import factories as tourism_factories
from geotrek.tourism.tests.factories import (InformationDeskFactory,
InformationDeskTypeFactory,
Expand Down Expand Up @@ -149,6 +149,10 @@ def test_str(self):
organizer = tourism_factories.TouristicEventOrganizerFactory(label="foo bar")
self.assertEqual('foo bar', str(organizer))

def test_get_add_url(self):
url = TouristicEventOrganizer.get_add_url()
self.assertEqual(url, "/popup/add/organizer/")


class TouristicEventModelTest(TestCase):
def test_dates_display_no_end_date(self):
Expand Down
18 changes: 17 additions & 1 deletion geotrek/tourism/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from unittest import mock

from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.auth.models import Group, Permission

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.test.utils import override_settings
Expand Down Expand Up @@ -486,6 +487,21 @@ def test_not_published_document_pdf(self):
self.assertEqual(response.status_code, 403)


class TouristicEventOrganizerCreatePopupTest(TestCase):
def test_cannot_create_organizer(self):
url = '/popup/add/organizer/'
response = self.client.get(url)
self.assertRedirects(response, "/login/?next=/popup/add/organizer/")

def test_can_create_organizer(self):
user = UserFactory()
user.user_permissions.add(Permission.objects.get(codename='add_touristiceventorganizer'))
self.client.force_login(user=user)
url = '/popup/add/organizer/'
response = self.client.get(url)
self.assertEqual(response.status_code, 200)


class TouristicEventCustomViewTests(TrekkingManagerTest):
@mock.patch('mapentity.helpers.requests.get')
def test_public_document_pdf(self, mocked):
Expand Down
2 changes: 2 additions & 0 deletions geotrek/tourism/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
path('api/<lang:lang>/touristiccategories.json', tourism_views.TouristicCategoryView.as_view(), name="touristic_categories_json"),
path('api/<lang:lang>/touristiccontents/<int:pk>/meta.html', tourism_views.TouristicContentMeta.as_view(), name="touristiccontent_meta"),
path('api/<lang:lang>/touristicevents/<int:pk>/meta.html', tourism_views.TouristicEventMeta.as_view(), name="touristicevent_meta"),
path('popup/add/organizer/', tourism_views.TouristicEventOrganizerCreatePopup.as_view(), name='organizer_add'),

]


Expand Down
29 changes: 25 additions & 4 deletions geotrek/tourism/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
import os

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.gis.db.models.functions import Transform
from django.core.exceptions import PermissionDenied
from django.db.models import Q, Sum
from django.http import Http404
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import DetailView
from django.utils.html import escape
from django.views.generic import DetailView, CreateView
from django_filters.rest_framework import DjangoFilterBackend
from mapentity.views import (MapEntityCreate, MapEntityUpdate, MapEntityList, MapEntityDetail,
MapEntityDelete, MapEntityFormat, MapEntityDocument)
Expand All @@ -25,8 +29,8 @@
from geotrek.common.viewsets import GeotrekMapentityViewSet
from geotrek.trekking.models import Trek
from .filters import TouristicContentFilterSet, TouristicEventFilterSet, TouristicEventApiFilterSet
from .forms import TouristicContentForm, TouristicEventForm
from .models import (TouristicContent, TouristicEvent, TouristicContentCategory, InformationDesk)
from .forms import TouristicContentForm, TouristicEventForm, TouristicEventOrganizerFormPopup
from .models import (TouristicContent, TouristicEvent, TouristicContentCategory, TouristicEventOrganizer, InformationDesk)
from .serializers import (TouristicContentSerializer, TouristicEventSerializer,
TouristicContentAPIGeojsonSerializer, TouristicEventAPIGeojsonSerializer,
InformationDeskGeojsonSerializer, TouristicContentAPISerializer, TouristicEventAPISerializer,
Expand Down Expand Up @@ -275,6 +279,23 @@ def get_context_data(self, **kwargs):
return context


class TouristicEventOrganizerCreatePopup(CreateView):
model = TouristicEventOrganizer
form_class = TouristicEventOrganizerFormPopup

@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm("tourism.add_touristiceventorganizer"):
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)

def form_valid(self, form):
self.object = form.save()
return HttpResponse("""
<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>
""" % (escape(form.instance._get_pk_val()), escape(form.instance)))


class TouristicEventDocumentPublic(TouristicEventDocumentPublicMixin, DocumentPublic):
pass

Expand Down

0 comments on commit cc5e070

Please sign in to comment.