Compare commits

...

2 Commits

13 changed files with 66 additions and 44 deletions

View File

@ -134,9 +134,6 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'static_files')
MEDIA_URL = '/uploads/' MEDIA_URL = '/uploads/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
# Use custom user model
AUTH_USER_MODEL = 'lostplaces_app.Explorer'
# Templates to use for authentication # Templates to use for authentication
LOGIN_URL = reverse_lazy('login') LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('lostplaces_home') LOGIN_REDIRECT_URL = reverse_lazy('lostplaces_home')

View File

@ -12,17 +12,11 @@ from .forms import ExplorerCreationForm, ExplorerChangeForm
# Register your models here. # Register your models here.
class ExplorerAdmin(UserAdmin):
add_form = ExplorerCreationForm
form = ExplorerChangeForm
model = Explorer
list_display = ['email', 'username',]
class VoucherAdmin(admin.ModelAdmin): class VoucherAdmin(admin.ModelAdmin):
fields = ['code', 'expires', 'created'] fields = ['code', 'expires', 'created']
readonly_fields = ['created'] readonly_fields = ['created']
admin.site.register(Explorer, ExplorerAdmin) admin.site.register(Explorer)
admin.site.register(Voucher, VoucherAdmin) admin.site.register(Voucher, VoucherAdmin)
admin.site.register(Place) admin.site.register(Place)
admin.site.register(PlaceImage) admin.site.register(PlaceImage)

View File

@ -5,11 +5,12 @@
from django import forms from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import Explorer, Place, PlaceImage, Voucher from django.contrib.auth.models import User
from lostplaces_app.models import Place, PlaceImage, Voucher
class ExplorerCreationForm(UserCreationForm): class ExplorerCreationForm(UserCreationForm):
class Meta: class Meta:
model = Explorer model = User
fields = ('username', 'email') fields = ('username', 'email')
voucher = forms.CharField( voucher = forms.CharField(
max_length=30, max_length=30,
@ -30,7 +31,7 @@ class ExplorerCreationForm(UserCreationForm):
class ExplorerChangeForm(UserChangeForm): class ExplorerChangeForm(UserChangeForm):
class Meta: class Meta:
model = Explorer model = User
fields = ('username', 'email') fields = ('username', 'email')
class PlaceForm(forms.ModelForm): class PlaceForm(forms.ModelForm):

View File

@ -10,22 +10,40 @@ import os
import uuid import uuid
from django.db import models from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.auth.models import AbstractUser
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
# Create your models here. # Create your models here.
class Explorer(AbstractUser):
class Explorer(models.Model):
""" """
Custom user model Profile that is linked to the a User.
Addtional fields wbd Every user has a profile.
""" """
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name='explorer'
)
def __str__(self): def __str__(self):
return self.username return self.user.name
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Explorer.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.explorer.save()
class Voucher(models.Model): class Voucher(models.Model):
""" """
@ -42,6 +60,7 @@ class Voucher(models.Model):
def __str__(self): def __str__(self):
return "Voucher " + str(self.pk) return "Voucher " + str(self.pk)
class Place (models.Model): class Place (models.Model):
""" """
Place defines a lost place (location, name, description etc.). Place defines a lost place (location, name, description etc.).
@ -53,7 +72,7 @@ class Place (models.Model):
Explorer, Explorer,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
blank=True, blank=True,
related_name='places' related_name='places'
) )
location = models.CharField(max_length=50) location = models.CharField(max_length=50)
@ -79,7 +98,7 @@ class Place (models.Model):
# Init fill values to prevent None # Init fill values to prevent None
longitude = 0 longitude = 0
latitude = 0 latitude = 0
if amount > 0: if amount > 0:
for place in place_list: for place in place_list:
longitude += place.longitude longitude += place.longitude
@ -91,6 +110,7 @@ class Place (models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
def generate_image_upload_path(instance, filename): def generate_image_upload_path(instance, filename):
""" """
Callback for generating path for uploaded images. Callback for generating path for uploaded images.
@ -98,13 +118,14 @@ def generate_image_upload_path(instance, filename):
return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1] return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1]
class PlaceImage (models.Model): class PlaceImage (models.Model):
""" """
PlaceImage defines an image file object that points to a file in uploads/. PlaceImage defines an image file object that points to a file in uploads/.
Intermediate image sizes are generated as defined in SIZES. Intermediate image sizes are generated as defined in SIZES.
PlaceImage references a Place to which it belongs. PlaceImage references a Place to which it belongs.
""" """
description = models.TextField(blank=True) description = models.TextField(blank=True)
filename = ThumbnailerImageField(upload_to=generate_image_upload_path) filename = ThumbnailerImageField(upload_to=generate_image_upload_path)
place = models.ForeignKey( place = models.ForeignKey(
@ -117,7 +138,7 @@ class PlaceImage (models.Model):
Explorer, Explorer,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
blank=True, blank=True,
related_name='images' related_name='images'
) )
@ -130,6 +151,8 @@ class PlaceImage (models.Model):
return ' '.join([self.place.name, str(self.pk)]) return ' '.join([self.place.name, str(self.pk)])
# These two auto-delete files from filesystem when they are unneeded: # These two auto-delete files from filesystem when they are unneeded:
@receiver(models.signals.post_delete, sender=PlaceImage) @receiver(models.signals.post_delete, sender=PlaceImage)
def auto_delete_file_on_delete(sender, instance, **kwargs): def auto_delete_file_on_delete(sender, instance, **kwargs):
""" """
@ -140,6 +163,7 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
if os.path.isfile(instance.filename.path): if os.path.isfile(instance.filename.path):
os.remove(instance.filename.path) os.remove(instance.filename.path)
@receiver(models.signals.pre_save, sender=PlaceImage) @receiver(models.signals.pre_save, sender=PlaceImage)
def auto_delete_file_on_change(sender, instance, **kwargs): def auto_delete_file_on_change(sender, instance, **kwargs):
""" """
@ -160,6 +184,7 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
if os.path.isfile(old_file.path): if os.path.isfile(old_file.path):
os.remove(old_file.path) os.remove(old_file.path)
class ExternalLink(models.Model): class ExternalLink(models.Model):
url = models.URLField(max_length=200) url = models.URLField(max_length=200)
label = models.CharField(max_length=100) label = models.CharField(max_length=100)
@ -167,11 +192,12 @@ class ExternalLink(models.Model):
Explorer, Explorer,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
blank=True, blank=True,
related_name='external_links' related_name='external_links'
) )
submitted_when = models.DateTimeField(auto_now_add=True, null=True) submitted_when = models.DateTimeField(auto_now_add=True, null=True)
class PhotoAlbum(ExternalLink): class PhotoAlbum(ExternalLink):
place = models.ForeignKey( place = models.ForeignKey(
Place, Place,

View File

@ -6,7 +6,7 @@
<a href="#" class="LP-Link"> <a href="#" class="LP-Link">
<span class="LP-Link__Text">{{tag}}</span> <span class="LP-Link__Text">{{tag}}</span>
</a> </a>
{% if request.user and request.user == config.tagged_item.submitted_by %} {% if request.user and request.user.explorer == config.tagged_item.submitted_by %}
<a href="{% url config.delete_url_name tagged_id=config.tagged_item.id tag_id=tag.id %}" class="LP-Link"> <a href="{% url config.delete_url_name tagged_id=config.tagged_item.id tag_id=tag.id %}" class="LP-Link">
<span class="LP-Tag__Remove RV-Iconized__Container RV-Iconized__Container--extraSmall"> <span class="LP-Tag__Remove RV-Iconized__Container RV-Iconized__Container--extraSmall">
<svg class="RV-Iconized__Icon" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <svg class="RV-Iconized__Icon" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
@ -28,7 +28,7 @@
<legend class="LP-Form__Legend">Tags hinzufügen</legend> <legend class="LP-Form__Legend">Tags hinzufügen</legend>
{% csrf_token %} {% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable"> <div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field LP-Form__Button LP-Input LP-Input--tagging"> <div class="LP-Form__Field LP-Form__Button LP-Input LP-Input--tagging">
<button id="id_tag_submit_button" class="LP-Button"> Tags hinzufügen</button> <button id="id_tag_submit_button" class="LP-Button"> Tags hinzufügen</button>
</div> </div>
<div class="LP-Form__Field"> <div class="LP-Form__Field">
@ -46,11 +46,15 @@
submit_form.onsubmit = () => false submit_form.onsubmit = () => false
const tagify = new Tagify(input, { const tagify = new Tagify(input, {
'whitelist': [ 'whitelist': [{
{% for tag in all_tags %} %
'{{tag}}', for tag in all_tags %
{% endfor %} }
] '{{tag}}',
{
% endfor %
}
]
}) })
const on_form_submit = function() { const on_form_submit = function() {

View File

@ -62,7 +62,7 @@
<a target="_blank" href="{{photo_album.url}}" class="LP-Link"> <a target="_blank" href="{{photo_album.url}}" class="LP-Link">
<span class="LP-Text">{{photo_album.label}}</span> <span class="LP-Text">{{photo_album.label}}</span>
</a> </a>
{% if user == photo_album.submitted_by or user == place.submitted_by %} {% if user.explorer == photo_album.submitted_by or user.explorer == place.submitted_by %}
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album"> <a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
<div class="RV-Iconized__Container RV-Iconized__Container--small"> <div class="RV-Iconized__Container RV-Iconized__Container--small">
{% icon 'trash' className="RV-Iconized__Icon" %} {% icon 'trash' className="RV-Iconized__Icon" %}

View File

@ -1,11 +1,11 @@
from django.db import models as django_models from django.db import models as django_models
from lostplaces_app.models import Explorer from django.contrib.auth.models import User
def mock_user(): def mock_user():
explorer_list = Explorer.objects.all() explorer_list = User.objects.all()
if len(explorer_list) <= 0: if len(explorer_list) <= 0:
return Explorer.objects.create_user( return User.objects.create_user(
username='testpeter', username='testpeter',
password='Develop123' password='Develop123'
) )

View File

@ -19,7 +19,7 @@ def mock_place_image():
filename=mock.MagicMock(spec=File, name='FileMock'), filename=mock.MagicMock(spec=File, name='FileMock'),
place=mock_place(), place=mock_place(),
submitted_when=datetime.datetime.now(), submitted_when=datetime.datetime.now(),
submitted_by=mock_user() submitted_by=mock_user().explorer
) )
class TestPlaceImage(TestSubmittable, TestCase): class TestPlaceImage(TestSubmittable, TestCase):

View File

@ -14,7 +14,7 @@ def mock_place():
place = Place.objects.create( place = Place.objects.create(
name='Im a place', name='Im a place',
submitted_when=datetime.datetime.now(), submitted_when=datetime.datetime.now(),
submitted_by=mock_user(), submitted_by=mock_user().explorer,
location='Testtown', location='Testtown',
latitude=50.5, latitude=50.5,
longitude=7.0, longitude=7.0,

View File

@ -3,7 +3,7 @@ from django.urls import reverse_lazy
from lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.models import Explorer from django.contrib.auth.models import User
from lostplaces_app.tests.models.test_place_model import mock_place from lostplaces_app.tests.models.test_place_model import mock_place
from lostplaces_app.tests import mock_user from lostplaces_app.tests import mock_user
@ -23,7 +23,7 @@ class TestIsAuthenticated(TestCase):
response = self.client.get(url, follow=True) response = self.client.get(url, follow=True)
self.assertRedirects( self.assertRedirects(
response=response, response=response,
expected_url='?'.join([str(reverse_lazy('login')), 'redirect_to=/place/1/']), expected_url='?'.join([str(reverse_lazy('login')), 'next=/place/1/']),
status_code=302, status_code=302,
target_status_code=200, target_status_code=200,
msg_prefix='''Accesing an IsAuthenticated view while not logged should msg_prefix='''Accesing an IsAuthenticated view while not logged should
@ -47,7 +47,7 @@ class TestIsPlaceSubmitter(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_is_no_submitter(self): def test_is_no_submitter(self):
Explorer.objects.create_user( User.objects.create_user(
username='manfred', username='manfred',
password='Develop123' password='Develop123'
) )

View File

@ -23,7 +23,7 @@ class TestPlaceCreateView(TestCase):
response = self.client.get(url) response = self.client.get(url)
self.assertRedirects( self.assertRedirects(
response=response, response=response,
expected_url='?'.join([str(reverse_lazy('login')), 'redirect_to=/place/1/']), expected_url='?'.join([str(reverse_lazy('login')), 'next=/place/1/']),
status_code=302, status_code=302,
target_status_code=200, target_status_code=200,
msg_prefix='''Accesing PlaceDetailView while not logged should msg_prefix='''Accesing PlaceDetailView while not logged should

View File

@ -48,7 +48,7 @@ class IsPlaceSubmitter(UserPassesTestMixin, View):
# Check if currently logged in user was the submitter # Check if currently logged in user was the submitter
place_obj = self.get_place() place_obj = self.get_place()
if place_obj and hasattr(place_obj, 'submitted_by') and self.request.user == place_obj.submitted_by: if place_obj and hasattr(place_obj, 'submitted_by') and self.request.user.explorer == place_obj.submitted_by:
return True return True
if self.place_submitter_error_message: if self.place_submitter_error_message:
@ -69,7 +69,7 @@ class PlaceAssetCreateView(IsAuthenticated, SuccessMessageMixin, CreateView):
self.place = Place.objects.get(pk=place_id) self.place = Place.objects.get(pk=place_id)
response = super().post(request, *args, **kwargs) response = super().post(request, *args, **kwargs)
self.object.place = self.place self.object.place = self.place
self.object.submitted_by = request.user self.object.submitted_by = request.user.explorer
self.object.save() self.object.save()
return response return response
@ -95,7 +95,7 @@ class PlaceAssetDeleteView(IsAuthenticated, IsPlaceSubmitter, SingleObjectMixin,
if can_edit_place: if can_edit_place:
return True return True
if self.get_object().submitted_by == self.request.user: if self.get_object().submitted_by == self.request.user.explorer:
return True return True
messages.error(self.request, self.permission_denied_message) messages.error(self.request, self.permission_denied_message)

View File

@ -72,7 +72,7 @@ class PlaceCreateView(IsAuthenticated, View):
place_form = PlaceForm(request.POST) place_form = PlaceForm(request.POST)
if place_form.is_valid(): if place_form.is_valid():
submitter = request.user submitter = request.user.explorer
place = place_form.save(commit=False) place = place_form.save(commit=False)
# Save logged in user as "submitted_by" # Save logged in user as "submitted_by"
place.submitted_by = submitter place.submitted_by = submitter