Merge remote-tracking branch 'origin/develop' into feature/50-Linktypes
This commit is contained in:
commit
4dbd8ae82d
1
Pipfile
1
Pipfile
@ -13,6 +13,7 @@ twine = "*"
|
||||
pandoc = "*"
|
||||
pylint-django = "*"
|
||||
setuptools = "*"
|
||||
django-nose = "*"
|
||||
|
||||
[packages]
|
||||
django = "*"
|
||||
|
@ -49,7 +49,15 @@ INSTALLED_APPS = [
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles'
|
||||
'django.contrib.staticfiles',
|
||||
'django_nose'
|
||||
]
|
||||
|
||||
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||
|
||||
NOSE_ARGS = [
|
||||
'--with-coverage',
|
||||
'--cover-package=lostplaces',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -5,7 +5,7 @@ from django.conf import settings
|
||||
|
||||
settings.THUMBNAIL_ALIASES = {
|
||||
'': {
|
||||
'thumbnail': {'size': (300, 200), 'sharpen': True, 'crop': True},
|
||||
'thumbnail': {'size': (300, 200), 'sharpen': True, 'crop': True, 'upscale': True},
|
||||
'hero': {'size': (700, 466), 'sharpen': True, 'crop': True},
|
||||
'large': {'size': (1920, 1920), 'sharpen': True, 'crop': False},
|
||||
},
|
||||
|
@ -4,4 +4,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class LostplacesAppConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.AutoField'
|
||||
name = 'lostplaces'
|
||||
|
@ -82,6 +82,18 @@ class Explorer(models.Model):
|
||||
choices=EXPLORER_LEVELS
|
||||
)
|
||||
|
||||
def get_places_eligible_to_see(self):
|
||||
if self.user.is_superuser:
|
||||
return Place.objects.all()
|
||||
return Place.objects.all().filter(level__lte=self.level) | self.places.all()
|
||||
|
||||
def is_eligible_to_see(self, place):
|
||||
return (
|
||||
self.user.is_superuser or
|
||||
place.submitted_by == self or
|
||||
place in self.get_places_eligible_to_see()
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
|
@ -37,9 +37,36 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
|
||||
def setUpTestData(cls):
|
||||
user = User.objects.create_user(
|
||||
username='testpeter',
|
||||
password='Develop123',
|
||||
)
|
||||
user.explorer.level = 3
|
||||
user.explorer.save()
|
||||
|
||||
# default level should be 1, not setting required
|
||||
other_user = User.objects.create_user(
|
||||
username='blubberbernd',
|
||||
password='Develop123'
|
||||
)
|
||||
|
||||
superuser = User.objects.create_user(
|
||||
username='toor',
|
||||
password='Develop123'
|
||||
)
|
||||
|
||||
superuser.is_superuser = True
|
||||
superuser.save()
|
||||
|
||||
Place.objects.create(
|
||||
name='Im a own place',
|
||||
submitted_when=timezone.now(),
|
||||
submitted_by=other_user.explorer,
|
||||
location='Test %d town' % 5,
|
||||
latitude=50.5 + 5/10,
|
||||
longitude=7.0 - 5/10,
|
||||
description='This is just a test, do not worry %d' % 5,
|
||||
level=3
|
||||
)
|
||||
|
||||
for i in range(12):
|
||||
place = Place.objects.create(
|
||||
name='Im a place %d' % i,
|
||||
@ -48,7 +75,8 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
|
||||
location='Test %d town' % i,
|
||||
latitude=50.5 + i/10,
|
||||
longitude=7.0 - i/10,
|
||||
description='This is just a test, do not worry %d' % i
|
||||
description='This is just a test, do not worry %d' % i,
|
||||
level=3
|
||||
)
|
||||
place.tags.add('I a tag', 'testlocation')
|
||||
place.save()
|
||||
@ -90,6 +118,42 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
|
||||
),
|
||||
msg='Expecting the place list to be paginated like [first] [previous] [item] at least 2 times [next] [last]'
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level(self):
|
||||
self.client.login(username='blubberbernd', password='Develop123')
|
||||
response = self.client.get(reverse('place_list'))
|
||||
|
||||
self.assertFalse(
|
||||
'Im a place' in response.content.decode(),
|
||||
msg='Expecting the user to not see any places'
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level_superuser(self):
|
||||
self.client.login(username='toor', password='Develop123')
|
||||
response = self.client.get(reverse('place_list'))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a place' in response.content.decode(),
|
||||
msg='Expecting the superuser to see all places'
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level_own_place(self):
|
||||
self.client.login(username='blubberbernd', password='Develop123')
|
||||
response = self.client.get(reverse('place_list'))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a own place' in response.content.decode(),
|
||||
msg='Expecting the user to see it\'s own places'
|
||||
)
|
||||
|
||||
def test_eligible_to_see(self):
|
||||
self.client.login(username='testpeter', password='Develop123')
|
||||
response = self.client.get(reverse('place_list'))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a own place' in response.content.decode(),
|
||||
msg='Expecting the user to see places where their level is high enough'
|
||||
)
|
||||
|
||||
class TestPlaceCreateView(ViewTestCase):
|
||||
view = PlaceCreateView
|
||||
@ -308,6 +372,8 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
|
||||
username='testpeter',
|
||||
password='Develop123'
|
||||
)
|
||||
user.explorer.level = 3
|
||||
user.explorer.save()
|
||||
|
||||
place = Place.objects.create(
|
||||
name='Im a place',
|
||||
@ -316,11 +382,72 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
|
||||
location='Testtown',
|
||||
latitude=50.5,
|
||||
longitude=7.0,
|
||||
description='This is just a test, do not worry'
|
||||
description='This is just a test, do not worry',
|
||||
level=3
|
||||
)
|
||||
place.tags.add('I a tag', 'testlocation')
|
||||
place.save()
|
||||
|
||||
other_user = User.objects.create_user(
|
||||
username='blubberbernd',
|
||||
password='Develop123'
|
||||
)
|
||||
|
||||
superuser = User.objects.create_user(
|
||||
username='toor',
|
||||
password='Develop123'
|
||||
)
|
||||
|
||||
superuser.is_superuser = True
|
||||
superuser.save()
|
||||
|
||||
Place.objects.create(
|
||||
name='Im a own place',
|
||||
submitted_when=timezone.now(),
|
||||
submitted_by=other_user.explorer,
|
||||
location='Test %d town' % 5,
|
||||
latitude=50.5 + 5/10,
|
||||
longitude=7.0 - 5/10,
|
||||
description='This is just a test, do not worry %d' % 5,
|
||||
level=3
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level(self):
|
||||
self.client.login(username='blubberbernd', password='Develop123')
|
||||
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
|
||||
|
||||
self.assertFalse(
|
||||
'Im a place' in response.content.decode(),
|
||||
msg='Expecting the user to not see the places'
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level_superuser(self):
|
||||
self.client.login(username='toor', password='Develop123')
|
||||
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a place' in response.content.decode(),
|
||||
msg='Expecting the superuser to see all places'
|
||||
)
|
||||
|
||||
def test_not_eligible_to_see_because_of_low_level_own_place(self):
|
||||
self.client.login(username='blubberbernd', password='Develop123')
|
||||
response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a own place' in response.content.decode(),
|
||||
msg='Expecting the user to see it\'s own places'
|
||||
)
|
||||
|
||||
def test_eligible_to_see(self):
|
||||
self.client.login(username='testpeter', password='Develop123')
|
||||
response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
|
||||
|
||||
self.assertTrue(
|
||||
'Im a own place' in response.content.decode(),
|
||||
msg='Expecting the user to see places where their level is high enough'
|
||||
)
|
||||
|
||||
def test_not_authenticated(self):
|
||||
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
|
||||
self.assertHttpRedirect(response)
|
||||
|
@ -4,6 +4,7 @@
|
||||
from django.views import View
|
||||
from django.views.generic.edit import CreateView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic import ListView
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
|
||||
@ -26,7 +27,8 @@ class IsAuthenticatedMixin(LoginRequiredMixin, View):
|
||||
permission_denied_message = _('Please login to proceed')
|
||||
|
||||
def handle_no_permission(self):
|
||||
messages.error(self.request, self.permission_denied_message)
|
||||
if not self.request.user.is_authenticated:
|
||||
messages.error(self.request, self.permission_denied_message)
|
||||
return super().handle_no_permission()
|
||||
|
||||
class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
|
||||
@ -60,6 +62,23 @@ class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
|
||||
messages.error(self.request, self.place_submitter_error_message)
|
||||
return False
|
||||
|
||||
class IsEligibleToSeePlaceMixin(UserPassesTestMixin):
|
||||
not_eligible_to_see_message = None
|
||||
|
||||
def get_place(self):
|
||||
pass
|
||||
|
||||
def test_func(self):
|
||||
if not hasattr(self.request, 'user'):
|
||||
return False
|
||||
|
||||
if self.request.user.explorer.is_eligible_to_see(self.get_place()):
|
||||
return True
|
||||
|
||||
if self.not_eligible_to_see_message:
|
||||
messages.error(self.request, self.not_eligible_to_see_message)
|
||||
return False
|
||||
|
||||
class PlaceAssetCreateView(IsAuthenticatedMixin, SuccessMessageMixin, CreateView):
|
||||
"""
|
||||
Abstract View for creating a place asset (i.e. PlaceImage)
|
||||
@ -113,3 +132,10 @@ class PlaceAssetDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SingleOb
|
||||
self.get_object().delete()
|
||||
messages.success(self.request, self.success_message)
|
||||
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place_id}))
|
||||
|
||||
|
||||
class LevelCapPlaceListView(ListView):
|
||||
model = Place
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.user.explorer.get_places_eligible_to_see()
|
@ -18,7 +18,10 @@ class MultiplePlaceImageUploadMixin:
|
||||
submitted_by=submitted_by
|
||||
)
|
||||
place_image.save()
|
||||
|
||||
if place.hero is None:
|
||||
place.hero = place.placeimages.all()[0]
|
||||
place.save()
|
||||
|
||||
class PlaceImageCreateView(MultiplePlaceImageUploadMixin, PlaceAssetCreateView):
|
||||
model = PlaceImage
|
||||
form_class = PlaceImageForm
|
||||
|
@ -6,7 +6,6 @@ from django.db.models.functions import Lower
|
||||
from django.views import View
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic import ListView
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
@ -16,16 +15,20 @@ from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.urls import reverse_lazy, reverse
|
||||
|
||||
from lostplaces.models import Place, PlaceImage
|
||||
from lostplaces.views.base_views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
|
||||
from lostplaces.views.base_views import (
|
||||
IsAuthenticatedMixin,
|
||||
IsPlaceSubmitterMixin,
|
||||
LevelCapPlaceListView,
|
||||
IsEligibleToSeePlaceMixin
|
||||
)
|
||||
from lostplaces.views.place_image_views import MultiplePlaceImageUploadMixin
|
||||
from lostplaces.forms import PlaceForm, PlaceImageForm, TagSubmitForm
|
||||
from lostplaces.common import redirect_referer_or
|
||||
|
||||
from taggit.models import Tag
|
||||
|
||||
class PlaceListView(IsAuthenticatedMixin, ListView):
|
||||
class PlaceListView(IsAuthenticatedMixin, LevelCapPlaceListView):
|
||||
paginate_by = 5
|
||||
model = Place
|
||||
template_name = 'place/place_list.html'
|
||||
ordering = [Lower('name')]
|
||||
|
||||
@ -37,9 +40,15 @@ class PlaceListView(IsAuthenticatedMixin, ListView):
|
||||
}
|
||||
return context
|
||||
|
||||
class PlaceDetailView(IsAuthenticatedMixin, View):
|
||||
def get(self, request, pk):
|
||||
place = get_object_or_404(Place, pk=pk)
|
||||
class PlaceDetailView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
|
||||
not_eligible_to_see_message = _('You\'r not allowed to see this place')
|
||||
|
||||
def get_place(self):
|
||||
return get_object_or_404(Place, pk=self.kwargs['pk'])
|
||||
|
||||
def get(self, request, pk):
|
||||
place = self.get_place()
|
||||
|
||||
context = {
|
||||
'place': place,
|
||||
'mapping_config': {
|
||||
@ -129,10 +138,14 @@ class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
|
||||
def get_place(self):
|
||||
return self.get_object()
|
||||
|
||||
class PlaceFavoriteView(IsAuthenticatedMixin, View):
|
||||
|
||||
class PlaceFavoriteView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
|
||||
not_eligible_to_see_message = _('You\'r not allowed to favorite this place')
|
||||
|
||||
def get_place(self):
|
||||
return get_object_or_404(Place, pk=self.kwargs['place_id'])
|
||||
|
||||
def get(self, request, place_id):
|
||||
place = get_object_or_404(Place, id=place_id)
|
||||
place = self.get_place()
|
||||
if request.user is not None:
|
||||
request.user.explorer.favorite_places.add(place)
|
||||
request.user.explorer.save()
|
||||
@ -140,7 +153,7 @@ class PlaceFavoriteView(IsAuthenticatedMixin, View):
|
||||
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
|
||||
|
||||
class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
|
||||
|
||||
|
||||
def get(self, request, place_id):
|
||||
place = get_object_or_404(Place, id=place_id)
|
||||
if request.user is not None:
|
||||
@ -149,10 +162,14 @@ class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
|
||||
|
||||
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
|
||||
|
||||
class PlaceVisitCreateView(IsAuthenticatedMixin, View):
|
||||
|
||||
class PlaceVisitCreateView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
|
||||
not_eligible_to_see_message = _('You\'r not allowed to visit this place :P (Now please stop trying out URL\'s)')
|
||||
|
||||
def get_place(self):
|
||||
return get_object_or_404(Place, pk=self.kwargs['place_id'])
|
||||
|
||||
def get(self, request, place_id):
|
||||
place = get_object_or_404(Place, id=place_id)
|
||||
place = self.get_place()
|
||||
if request.user is not None:
|
||||
request.user.explorer.visited_places.add(place)
|
||||
request.user.explorer.save()
|
||||
|
@ -31,7 +31,7 @@ class SignUpView(SuccessMessageMixin, CreateView):
|
||||
|
||||
class HomeView(IsAuthenticatedMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
place_list = Place.objects.all().order_by('-submitted_when')[:10]
|
||||
place_list = request.user.explorer.get_places_eligible_to_see()
|
||||
context = {
|
||||
'place_list': place_list,
|
||||
'mapping_config': {
|
||||
|
Loading…
Reference in New Issue
Block a user