Compare commits
	
		
			44 Commits
		
	
	
		
			27-Favorit
			...
			feature/30
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c5f6355f19 | |||
| dac63e99ba | |||
| 9f369456d5 | |||
| 011a58f6b3 | |||
| 67ce6cb50b | |||
| 86cc7f23fe | |||
| 3eaa186b66 | |||
| cc59254ba4 | |||
| 67a6517716 | |||
| 0f5474c2d3 | |||
| 17e71b71d4 | |||
| b1aa4473e9 | |||
| 871acd1dce | |||
| 14ca45d111 | |||
| c71fe4cf2d | |||
| 7359bf5fab | |||
| 4f0182fc3e | |||
| 30f259fb4d | |||
| 53f89caef5 | |||
| 3855fb28d7 | |||
| 3a20a60f05 | |||
| 648264c9fc | |||
| a7c8848fd6 | |||
| 07ea2f164c | |||
| b0396f5223 | |||
| 88bffbef8e | |||
| 2022a924c4 | |||
| d5827b2006 | |||
| a9013d9673 | |||
| 0c38ca9a15 | |||
| 734d09df90 | |||
| e601b9bf6b | |||
| 6f6bd6376d | |||
| dc2a703c39 | |||
| 063777ece4 | |||
| 4675e5814f | |||
| 35e0f912fe | |||
| 07fe1bc3ca | |||
| d04e986419 | |||
| cbbda88850 | |||
| 981c440ce3 | |||
| c7368f5c44 | |||
| 20dd880a11 | |||
| 946c3091c0 | 
@@ -9,8 +9,6 @@ from django.contrib.auth.admin import UserAdmin
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from lostplaces.models import *
 | 
			
		||||
 | 
			
		||||
from lostplaces.forms import ExplorerCreationForm, ExplorerChangeForm
 | 
			
		||||
 | 
			
		||||
# Register your models here.
 | 
			
		||||
 | 
			
		||||
class VoucherAdmin(admin.ModelAdmin):
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,12 @@ from django.db import models
 | 
			
		||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from lostplaces.models import Place, PlaceImage, Voucher
 | 
			
		||||
from lostplaces.models import Place, PlaceImage, Voucher, Explorer
 | 
			
		||||
 | 
			
		||||
class ExplorerCreationForm(UserCreationForm):
 | 
			
		||||
class SignupVoucherForm(UserCreationForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = User
 | 
			
		||||
        fields = ('username', 'email')
 | 
			
		||||
        fields = ('username', 'email', 'first_name', 'last_name')
 | 
			
		||||
    voucher = forms.CharField(
 | 
			
		||||
        max_length=30,
 | 
			
		||||
        help_text=_('The Voucher you got from an administrator')
 | 
			
		||||
@@ -35,10 +35,33 @@ class ExplorerCreationForm(UserCreationForm):
 | 
			
		||||
        fetched_voucher.delete()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
class ExplorerChangeForm(UserChangeForm):
 | 
			
		||||
class ExplorerUserChangeForm(UserChangeForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = User
 | 
			
		||||
        fields = ('username', 'email')
 | 
			
		||||
        fields = [ 'username', 'first_name', 'last_name', 'email' ]
 | 
			
		||||
    ''' Hide password hint.'''
 | 
			
		||||
    password = None
 | 
			
		||||
 | 
			
		||||
    ''' Display username, but display it non-editable and not required. '''
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.fields['username'].required = False
 | 
			
		||||
        self.fields['username'].help_text = None
 | 
			
		||||
        self.fields['username'].widget.attrs['disabled'] = 'disabled'
 | 
			
		||||
    
 | 
			
		||||
    def clean_username(self):
 | 
			
		||||
        # As shown in the above answer.
 | 
			
		||||
        instance = getattr(self, 'instance', None)
 | 
			
		||||
        if instance:
 | 
			
		||||
            return instance.username
 | 
			
		||||
        else:
 | 
			
		||||
            return self.cleaned_data.get('username', None)
 | 
			
		||||
 | 
			
		||||
class ExplorerChangeForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Explorer
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
        exclude = ['user', 'favorite_places']
 | 
			
		||||
 | 
			
		||||
class PlaceForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: \n"
 | 
			
		||||
"POT-Creation-Date: 2020-10-11 21:53+0200\n"
 | 
			
		||||
"POT-Creation-Date: 2020-12-25 16:04+0100\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: Commander1024 <commander@commander1024.de>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
@@ -30,7 +30,7 @@ msgstr "Ungültiger Voucher"
 | 
			
		||||
msgid "Expired voucher"
 | 
			
		||||
msgstr "Abgelaufener Voucher"
 | 
			
		||||
 | 
			
		||||
#: models/abstract_models.py:29
 | 
			
		||||
#: models/abstract_models.py:29 templates/explorer/profile.html:31
 | 
			
		||||
msgid "Name"
 | 
			
		||||
msgstr "Name"
 | 
			
		||||
 | 
			
		||||
@@ -74,6 +74,22 @@ msgstr "Adresse (URL)"
 | 
			
		||||
msgid "link text"
 | 
			
		||||
msgstr "Linktext"
 | 
			
		||||
 | 
			
		||||
#: models/models.py:47
 | 
			
		||||
msgid "Biography"
 | 
			
		||||
msgstr "Beschreibung"
 | 
			
		||||
 | 
			
		||||
#: models/models.py:48
 | 
			
		||||
msgid "Describe yourself, your preferences, etc. in a few sentences."
 | 
			
		||||
msgstr "Beschreibe Dich selbst, Deine Vorlieben, usw. in ein paar Sätzen."
 | 
			
		||||
 | 
			
		||||
#: models/models.py:56
 | 
			
		||||
msgid "Profile image"
 | 
			
		||||
msgstr "Profilbild"
 | 
			
		||||
 | 
			
		||||
#: models/models.py:57
 | 
			
		||||
msgid "Optional profile image for display in Explorer profile"
 | 
			
		||||
msgstr "Optionales Profilbild zur Anzeige im Explorerprofil"
 | 
			
		||||
 | 
			
		||||
#: models/place.py:21
 | 
			
		||||
msgid "Location"
 | 
			
		||||
msgstr "Ort"
 | 
			
		||||
@@ -110,38 +126,92 @@ msgstr "Du wirst in 5 Sekunden weitergeleitet"
 | 
			
		||||
msgid "Go Back"
 | 
			
		||||
msgstr "Zurück"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:41
 | 
			
		||||
msgid "E-mail"
 | 
			
		||||
msgstr "E-Mail"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:52
 | 
			
		||||
msgid "Joined"
 | 
			
		||||
msgstr "Beigetreten"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:60
 | 
			
		||||
#, fuzzy
 | 
			
		||||
#| msgid "All Places"
 | 
			
		||||
msgid "Places"
 | 
			
		||||
msgstr "Alle Places"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:68
 | 
			
		||||
msgid "Place assets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:76
 | 
			
		||||
#, fuzzy
 | 
			
		||||
#| msgid "Edit Explorer profile"
 | 
			
		||||
msgid "Edit Profile"
 | 
			
		||||
msgstr "Explorerprofil bearbeiten"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:87
 | 
			
		||||
msgid "Favorite places"
 | 
			
		||||
msgstr "Favoriten"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:103
 | 
			
		||||
msgid "Places submitted by"
 | 
			
		||||
msgstr "Places hinzugefügt von"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:118
 | 
			
		||||
msgid "Images submitted by"
 | 
			
		||||
msgstr "Bilder hinzugefügt von"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile.html:140
 | 
			
		||||
msgid "Photo albums submitted by"
 | 
			
		||||
msgstr "Fotoalben hinzugefügt von"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile_update.html:6
 | 
			
		||||
#: templates/explorer/profile_update.html:12
 | 
			
		||||
msgid "Edit Explorer profile"
 | 
			
		||||
msgstr "Explorerprofil bearbeiten"
 | 
			
		||||
 | 
			
		||||
#: templates/explorer/profile_update.html:48
 | 
			
		||||
#: templates/place/place_update.html:42
 | 
			
		||||
msgid "Update"
 | 
			
		||||
msgstr "Aktualisieren"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:32
 | 
			
		||||
msgid "Logout"
 | 
			
		||||
msgstr "Ausloggen"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:34
 | 
			
		||||
#: templates/global.html:33
 | 
			
		||||
msgid "Profile"
 | 
			
		||||
msgstr "Profil"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:35
 | 
			
		||||
msgid "Admin"
 | 
			
		||||
msgstr "Admin"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:39 templates/registration/login.html:4
 | 
			
		||||
#: templates/global.html:40 templates/registration/login.html:4
 | 
			
		||||
#: templates/registration/login.html:10 templates/registration/login.html:23
 | 
			
		||||
msgid "Login"
 | 
			
		||||
msgstr "Anmelden"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:40 templates/registration/login.html:29
 | 
			
		||||
#: templates/signup.html:6 templates/signup.html:12 templates/signup.html:41
 | 
			
		||||
#: templates/global.html:41 templates/registration/login.html:29
 | 
			
		||||
#: templates/signup.html:6 templates/signup.html:12 templates/signup.html:49
 | 
			
		||||
msgid "Sign up"
 | 
			
		||||
msgstr "Registrieren"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:50 templates/home.html:10
 | 
			
		||||
#: templates/global.html:51 templates/home.html:10
 | 
			
		||||
msgid "Home"
 | 
			
		||||
msgstr "Startseite"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:51
 | 
			
		||||
#: templates/global.html:52
 | 
			
		||||
msgid "UrBex Codex"
 | 
			
		||||
msgstr "UrBex Codex"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:56 templates/place/place_create.html:5
 | 
			
		||||
#: templates/global.html:57 templates/place/place_create.html:5
 | 
			
		||||
#: templates/place/place_create.html:10
 | 
			
		||||
msgid "Create place"
 | 
			
		||||
msgstr "Place erstellen"
 | 
			
		||||
 | 
			
		||||
#: templates/global.html:57
 | 
			
		||||
#: templates/global.html:58
 | 
			
		||||
msgid "All places"
 | 
			
		||||
msgstr "Alle Places"
 | 
			
		||||
 | 
			
		||||
@@ -194,6 +264,14 @@ msgstr "Abschicken"
 | 
			
		||||
msgid "Cancel"
 | 
			
		||||
msgstr "Abbrechen"
 | 
			
		||||
 | 
			
		||||
#: templates/partials/icons/place_favorite.html:6
 | 
			
		||||
msgid "Remove from favorites"
 | 
			
		||||
msgstr "Aus den Favoriten entfernen"
 | 
			
		||||
 | 
			
		||||
#: templates/partials/icons/place_favorite.html:10
 | 
			
		||||
msgid "Save as favorite"
 | 
			
		||||
msgstr "In den Favoriten speichern"
 | 
			
		||||
 | 
			
		||||
#: templates/partials/nav/footer.html:64
 | 
			
		||||
msgid "Made by"
 | 
			
		||||
msgstr "Erstellt von"
 | 
			
		||||
@@ -318,10 +396,6 @@ msgstr "Alle Places"
 | 
			
		||||
msgid "Our lost places"
 | 
			
		||||
msgstr "Unsere Lost Places"
 | 
			
		||||
 | 
			
		||||
#: templates/place/place_update.html:42
 | 
			
		||||
msgid "Update"
 | 
			
		||||
msgstr "Aktualisieren"
 | 
			
		||||
 | 
			
		||||
#: templates/place_image/place_image_create.html:7
 | 
			
		||||
msgid "Submit images to a place"
 | 
			
		||||
msgstr "Bilder zu einem Place hinzufügen"
 | 
			
		||||
@@ -334,6 +408,16 @@ msgstr "Noch kein Konto?"
 | 
			
		||||
msgid "Please login to proceed"
 | 
			
		||||
msgstr "Bitte log Dich ein um fortzufahren"
 | 
			
		||||
 | 
			
		||||
#: views/explorer_views.py:78
 | 
			
		||||
#, fuzzy
 | 
			
		||||
#| msgid "Successfully updated place"
 | 
			
		||||
msgid "Successfully updated Explorer profile"
 | 
			
		||||
msgstr "Place erfolgreich aktualisiert"
 | 
			
		||||
 | 
			
		||||
#: views/explorer_views.py:84 views/place_views.py:105
 | 
			
		||||
msgid "Please fill in all required fields."
 | 
			
		||||
msgstr "Bitte füll alle benötigten Felder aus."
 | 
			
		||||
 | 
			
		||||
#: views/place_image_views.py:26
 | 
			
		||||
msgid "Image(s) submitted successfully"
 | 
			
		||||
msgstr "Bild(er) erfolgreich hinzugefügt"
 | 
			
		||||
@@ -358,10 +442,6 @@ msgstr "Du darfst diesen Place nicht bearbeiten"
 | 
			
		||||
msgid "Successfully created place"
 | 
			
		||||
msgstr "Place erfolgreich erstellt"
 | 
			
		||||
 | 
			
		||||
#: views/place_views.py:105
 | 
			
		||||
msgid "Please fill in all required fields."
 | 
			
		||||
msgstr "Bitte füll alle benötigten Felder aus."
 | 
			
		||||
 | 
			
		||||
#: views/place_views.py:112
 | 
			
		||||
msgid "Successfully deleted place"
 | 
			
		||||
msgstr "Place erfolgreich gelöscht"
 | 
			
		||||
@@ -385,3 +465,8 @@ msgstr "Fotoalbum-Link gelöscht"
 | 
			
		||||
#: views/views.py:60
 | 
			
		||||
msgid "You are not allowed to edit this photo album link"
 | 
			
		||||
msgstr "Du darfst diesen Fotoalbum-Link nicht bearbeiten"
 | 
			
		||||
 | 
			
		||||
#, fuzzy
 | 
			
		||||
#~| msgid "Filename(s)"
 | 
			
		||||
#~ msgid "Filename"
 | 
			
		||||
#~ msgstr "Dateiname(n)"
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
# Generated by Django 3.1.1 on 2020-10-04 19:37
 | 
			
		||||
# Edited by reverend
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.utils.timezone
 | 
			
		||||
from django.utils.timezone import utc
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('lostplaces', '0001_initial'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.DeleteModel(
 | 
			
		||||
            name='Voucher'
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.DeleteModel(
 | 
			
		||||
            name='Expireable'
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
@@ -1,25 +1,25 @@
 | 
			
		||||
    # Generated by Django 3.1.1 on 2020-10-04 19:52
 | 
			
		||||
# Generated by Django 3.1.1 on 2020-10-04 19:52
 | 
			
		||||
 | 
			
		||||
    from django.db import migrations, models
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Migration(migrations.Migration):
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
        dependencies = [
 | 
			
		||||
            ('lostplaces', '0002_reomve_vouchers'),
 | 
			
		||||
        ]
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('lostplaces', '0002_remove_vouchers'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
        operations = [
 | 
			
		||||
            migrations.CreateModel(
 | 
			
		||||
                name='Voucher',
 | 
			
		||||
                fields=[
 | 
			
		||||
                    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                    ('created_when', models.DateTimeField(auto_now_add=True)),
 | 
			
		||||
                    ('expires_when', models.DateTimeField()),
 | 
			
		||||
                    ('code', models.CharField(max_length=30, unique=True)),
 | 
			
		||||
                ],
 | 
			
		||||
                options={
 | 
			
		||||
                    'abstract': False,
 | 
			
		||||
                },
 | 
			
		||||
            ),
 | 
			
		||||
        ]
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Voucher',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('created_when', models.DateTimeField(auto_now_add=True)),
 | 
			
		||||
                ('expires_when', models.DateTimeField()),
 | 
			
		||||
                ('code', models.CharField(max_length=30, unique=True)),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                'abstract': False,
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -6,20 +6,34 @@
 | 
			
		||||
database.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db.models.signals import post_save
 | 
			
		||||
from django.db.models.signals import post_save, pre_save
 | 
			
		||||
from django.dispatch import receiver
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from lostplaces.models.abstract_models import Expireable
 | 
			
		||||
from lostplaces.models.place import Place
 | 
			
		||||
 | 
			
		||||
from easy_thumbnails.fields import ThumbnailerImageField
 | 
			
		||||
from easy_thumbnails.files import get_thumbnailer
 | 
			
		||||
 | 
			
		||||
def generate_profile_image_filename(instance, filename):
 | 
			
		||||
    """
 | 
			
		||||
    Callback for generating filename for uploaded explorer profile images.
 | 
			
		||||
    Returns filename as: explorer_pk-username.jpg
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return 'explorers/' + str(instance.user.pk) + '-' + str(instance.user.username) + '.' + filename.split('.')[-1]
 | 
			
		||||
 | 
			
		||||
class Explorer(models.Model):
 | 
			
		||||
    """
 | 
			
		||||
    Profile that is linked to the a User.
 | 
			
		||||
    Profile that is linked to the Django user.
 | 
			
		||||
    Every user has a profile.
 | 
			
		||||
    Provides additional attributes for user profile.
 | 
			
		||||
    """
 | 
			
		||||
    
 | 
			
		||||
    user = models.OneToOneField(
 | 
			
		||||
@@ -27,7 +41,21 @@ class Explorer(models.Model):
 | 
			
		||||
        on_delete=models.CASCADE,
 | 
			
		||||
        related_name='explorer'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    bio = models.TextField(
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        verbose_name=_('Biography / Description'),
 | 
			
		||||
        help_text=_('Describe yourself, your preferences, etc. in a few sentences.')
 | 
			
		||||
    )
 | 
			
		||||
    profile_image = ThumbnailerImageField(
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        upload_to=generate_profile_image_filename, 
 | 
			
		||||
        resize_source=dict(size=(400, 400), 
 | 
			
		||||
        sharpen=True),
 | 
			
		||||
        verbose_name=_('Profile image'),
 | 
			
		||||
        help_text=_('Optional profile image for display in Explorer profile')
 | 
			
		||||
    )
 | 
			
		||||
    favorite_places = models.ManyToManyField(
 | 
			
		||||
        Place,
 | 
			
		||||
        related_name='favorite_places',
 | 
			
		||||
@@ -37,7 +65,7 @@ class Explorer(models.Model):
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.user.username
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
@receiver(post_save, sender=User)
 | 
			
		||||
def create_user_profile(sender, instance, created, **kwargs):
 | 
			
		||||
    if created:
 | 
			
		||||
@@ -47,13 +75,34 @@ def create_user_profile(sender, instance, created, **kwargs):
 | 
			
		||||
def save_user_profile(sender, instance, **kwargs):
 | 
			
		||||
    instance.explorer.save()
 | 
			
		||||
 | 
			
		||||
@receiver(pre_save, sender=Explorer)
 | 
			
		||||
def auto_delete_file_on_change(sender, instance, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Deletes old file from filesystem
 | 
			
		||||
    when corresponding `Explorer` object is updated
 | 
			
		||||
    with new file.
 | 
			
		||||
    """
 | 
			
		||||
    if not instance.pk:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        old_file = Explorer.objects.get(pk=instance.pk).profile_image
 | 
			
		||||
    except Explorer.DoesNotExist:
 | 
			
		||||
        return False
 | 
			
		||||
    print("Deleting:", old_file)
 | 
			
		||||
    new_file = instance.profile_image
 | 
			
		||||
    if not old_file == new_file:
 | 
			
		||||
        old_file.delete(save=False)
 | 
			
		||||
 | 
			
		||||
class Voucher(Expireable):
 | 
			
		||||
    """
 | 
			
		||||
    Vouchers are authorization to    created_when = models.DateTimeField(auto_now_add=True)
 | 
			
		||||
    expires_when = models.DateTimeField()kens to allow the registration of new users.
 | 
			
		||||
    Vouchers are authorization tokens to allow the registration of new users.
 | 
			
		||||
    A voucher has a code, a creation and a deletion date, which are all 
 | 
			
		||||
    positional. Creation date is being set automatically during voucher 
 | 
			
		||||
    creation. 
 | 
			
		||||
 | 
			
		||||
    created_when = models.DateTimeField(auto_now_add=True)
 | 
			
		||||
    expires_when = models.DateTimeField()
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    code = models.CharField(unique=True, max_length=30)
 | 
			
		||||
@@ -64,4 +113,3 @@ class Voucher(Expireable):
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "Voucher " + str(self.code)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,10 +49,10 @@ class Place(Submittable, Taggable, Mapable):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_image_upload_path(instance, filename):
 | 
			
		||||
def generate_place_image_filename(instance, filename):
 | 
			
		||||
    """
 | 
			
		||||
    Callback for generating path for uploaded images.
 | 
			
		||||
    Returns filename as: place_pk-placename{-rnd_string}.jpg
 | 
			
		||||
    Callback for generating filename for uploaded place images.
 | 
			
		||||
    Returns filename as: place_pk-placename{-number}.jpg
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return 'places/' + str(instance.place.pk) + '-' + str(instance.place.name) + '.' + filename.split('.')[-1]
 | 
			
		||||
@@ -72,7 +72,7 @@ class PlaceAsset(Submittable):
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
class PlaceImage (PlaceAsset):
 | 
			
		||||
class PlaceImage(PlaceAsset):
 | 
			
		||||
    """
 | 
			
		||||
    PlaceImage defines an image file object that points to a file in uploads/.
 | 
			
		||||
    Intermediate image sizes are generated as defined in THUMBNAIL_ALIASES.
 | 
			
		||||
@@ -84,7 +84,7 @@ class PlaceImage (PlaceAsset):
 | 
			
		||||
        verbose_name=_('Description'),
 | 
			
		||||
    )
 | 
			
		||||
    filename = ThumbnailerImageField(
 | 
			
		||||
        upload_to=generate_image_upload_path, 
 | 
			
		||||
        upload_to=generate_place_image_filename, 
 | 
			
		||||
        resize_source=dict(size=(2560, 2560), 
 | 
			
		||||
        sharpen=True),
 | 
			
		||||
        verbose_name=_('Filename(s)'),
 | 
			
		||||
@@ -104,7 +104,6 @@ class PlaceImage (PlaceAsset):
 | 
			
		||||
 | 
			
		||||
        return 'Image ' + str(self.pk)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# These two auto-delete files from filesystem when they are unneeded:
 | 
			
		||||
 | 
			
		||||
@receiver(post_delete, sender=PlaceImage)
 | 
			
		||||
@@ -118,7 +117,6 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
 | 
			
		||||
        thumbmanager = get_thumbnailer(instance.filename)
 | 
			
		||||
        thumbmanager.delete(save=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(pre_save, sender=PlaceImage)
 | 
			
		||||
def auto_delete_file_on_change(sender, instance, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
@@ -137,5 +135,4 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
 | 
			
		||||
    # No need to delete thumbnails, as they will be overwritten on regeneration.
 | 
			
		||||
    new_file = instance.filename
 | 
			
		||||
    if not old_file == new_file:
 | 
			
		||||
        if os.path.isfile(old_file.path):
 | 
			
		||||
            os.remove(old_file.path)
 | 
			
		||||
        old_file.delete(save=False)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,46 @@
 | 
			
		||||
{% block maincontent %}
 | 
			
		||||
 | 
			
		||||
<div class="LP-UserProfile">
 | 
			
		||||
	<div class="LP-UserProfile__Bio">
 | 
			
		||||
        <h2 class="LP-Headline">Bio</h2>
 | 
			
		||||
        <p class="LP-Paragraph">{{explorer.bio}}</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="LP-UserProfile__Info">
 | 
			
		||||
        <div class="LP-UserInfo">
 | 
			
		||||
            <div class="LP-UserInfo__UserName">
 | 
			
		||||
                <h1 class="LP-Headline">{{explorer.user.username}}</h1>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="LP-UserInfo__Meta">
 | 
			
		||||
                <table>
 | 
			
		||||
					<tr>
 | 
			
		||||
						{% if explorer.profile_image %}
 | 
			
		||||
						<figure class="LP-UserInfo__Image">
 | 
			
		||||
							<img src="{{ explorer.profile_image.url }}" class="LP-Image" />
 | 
			
		||||
						</figure>
 | 
			
		||||
						{% endif %}
 | 
			
		||||
                    <tr>
 | 
			
		||||
						{% if explorer.user.first_name %}
 | 
			
		||||
                        <td class="LP-UserInfo__Key">
 | 
			
		||||
                            <span class="LP-Paragraph">{% trans 'Name' %}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td class="LP-UserInfo__Value">
 | 
			
		||||
                            <span class="LP-Paragraph">{{explorer.user.first_name}} {{explorer.user.last_name}}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
						{% endif %}
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        {% if explorer.user.email %}
 | 
			
		||||
						<td class="LP-UserInfo__Key">
 | 
			
		||||
                            <span class="LP-Paragraph">{% trans 'E-mail' %}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td class="LP-UserInfo__Value">
 | 
			
		||||
							<span class="LP-Paragraph">
 | 
			
		||||
                            	<a href="{{explorer.user.email}}" class="LP-Link">{{explorer.user.email}}</a>
 | 
			
		||||
							</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
						{% endif %}
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <td class="LP-UserInfo__Key">
 | 
			
		||||
                            <span class="LP-Paragraph">{% trans 'Joined' %}</span>
 | 
			
		||||
@@ -32,18 +65,39 @@
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <td class="LP-UserInfo__Key">
 | 
			
		||||
                            <span class="LP-Paragraph">{% trans 'Place Assets'%}</span>
 | 
			
		||||
                            <span class="LP-Paragraph">{% trans 'Place assets'%}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td class="LP-UserInfo__Value">
 | 
			
		||||
                            <span class="LP-Paragraph">{{asset_count}}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <a href="/explorer/update/"><button class="LP-Button LP-Button">{% trans 'Edit Profile' %}</button></a>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<section class="LP-Section">
 | 
			
		||||
    <div class="LP-PlaceList">
 | 
			
		||||
        <h1 class="LP-Headline">{% trans 'Favorite places' %}</h1>
 | 
			
		||||
        <ul class="LP-PlaceList__List">
 | 
			
		||||
            {% for place in explorer.favorite_places.all %}
 | 
			
		||||
            <li class="LP-PlaceList__Item">
 | 
			
		||||
                {% include 'partials/place_teaser.html' with place=place extended=True %}
 | 
			
		||||
            </li>
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        {% include 'partials/nav/pagination.html' %}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<section class="LP-Section">
 | 
			
		||||
    <div class="LP-PlaceList">
 | 
			
		||||
        <h1 class="LP-Headline">{% trans 'Places submitted by' %} {{explorer.user.username}}</h1>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
{% extends 'global.html'%}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load thumbnail %}
 | 
			
		||||
{% load widget_tweaks %}
 | 
			
		||||
 | 
			
		||||
# {% block title %}{% trans 'Edit Explorer profile' %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block maincontent %}
 | 
			
		||||
 | 
			
		||||
<form class="LP-Form" method="POST" enctype="multipart/form-data">
 | 
			
		||||
    <fieldset class="LP-Form__Fieldset">
 | 
			
		||||
        <legend class="LP-Form__Legend">{% trans 'Edit Explorer profile' %}</legend>
 | 
			
		||||
        {% csrf_token %}
 | 
			
		||||
        <div class="LP-Form__Composition LP-Form__Composition--breakable">
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_user_change_form.username %}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_user_change_form.email %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="LP-Form__Composition LP-Form__Composition--breakable">
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_user_change_form.first_name %}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_user_change_form.last_name %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="LP-Form__Composition">
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_change_form.bio %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="LP-Form__Composition">
 | 
			
		||||
            {% if explorer_image %}
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                <img src="{% thumbnail explorer_image 200x200 %}"/>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=explorer_change_form.profile_image %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        {% trans 'Update' as action %}
 | 
			
		||||
        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
			
		||||
            {% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER action=action %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </fieldset>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
{% endblock maincontent %}
 | 
			
		||||
@@ -19,6 +19,14 @@
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=form.email %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="LP-Form__Composition LP-Form__Composition--breakable">
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=form.first_name %}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
                {% include 'partials/form/inputField.html' with field=form.last_name %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="LP-Form__Composition">
 | 
			
		||||
            <div class="LP-Form__Field">
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,15 @@ from django import forms
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
 | 
			
		||||
from lostplaces.tests.forms import FormTestCase
 | 
			
		||||
from lostplaces.forms import ExplorerCreationForm
 | 
			
		||||
from lostplaces.forms import SignupVoucherForm
 | 
			
		||||
from lostplaces.models.models import Voucher
 | 
			
		||||
 | 
			
		||||
class ExplorerCreationFormTestCase(FormTestCase):
 | 
			
		||||
class SignupVoucherFormTestCase(FormTestCase):
 | 
			
		||||
    """
 | 
			
		||||
    This test case only tests for the voucher since all other aspects don't realy matter
 | 
			
		||||
    to this project and are already tested by django
 | 
			
		||||
    """
 | 
			
		||||
    form = ExplorerCreationForm
 | 
			
		||||
    form = SignupVoucherForm
 | 
			
		||||
    
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpTestData(cls):
 | 
			
		||||
@@ -37,7 +37,7 @@ class ExplorerCreationFormTestCase(FormTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        
 | 
			
		||||
    def test_validation_valid(self):
 | 
			
		||||
        form = ExplorerCreationForm(self.post_data)
 | 
			
		||||
        form = SignupVoucherForm(self.post_data)
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            form.is_valid(),
 | 
			
		||||
            msg='Expecting the %s to validate' % (
 | 
			
		||||
@@ -49,7 +49,7 @@ class ExplorerCreationFormTestCase(FormTestCase):
 | 
			
		||||
        self.post_data = {
 | 
			
		||||
            'voucher': 'Imanotacode123'
 | 
			
		||||
        }
 | 
			
		||||
        form = ExplorerCreationForm(self.post_data)
 | 
			
		||||
        form = SignupVoucherForm(self.post_data)
 | 
			
		||||
        self.assertFalse(
 | 
			
		||||
            form.is_valid(),
 | 
			
		||||
            msg='Expecting the %s to not validate' % (
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,8 @@ from lostplaces.views import (
 | 
			
		||||
    PlaceImageCreateView,
 | 
			
		||||
    PlaceImageDeleteView,
 | 
			
		||||
    FlatView,
 | 
			
		||||
    ExplorerProfileView
 | 
			
		||||
    ExplorerProfileView,
 | 
			
		||||
    ExplorerProfileUpdateView
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
@@ -39,9 +40,8 @@ urlpatterns = [
 | 
			
		||||
    path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete'),
 | 
			
		||||
    
 | 
			
		||||
    path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
 | 
			
		||||
    path('explorer/update/', ExplorerProfileUpdateView.as_view(), name='explorer_profile_update'),
 | 
			
		||||
    
 | 
			
		||||
    path('explorer/favorite/<int:place_id>/', PlaceFavoriteView.as_view(), name='place_favorite'),
 | 
			
		||||
    path('explorer/unfavorite/<int:place_id>/', PlaceUnfavoriteView.as_view(), name='place_unfavorite')
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,13 @@ from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from django.shortcuts import render, redirect, get_object_or_404
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
 | 
			
		||||
from lostplaces.common import get_all_subclasses
 | 
			
		||||
from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
			
		||||
from lostplaces.models.models import Explorer
 | 
			
		||||
from lostplaces.models.place import Place, PlaceAsset
 | 
			
		||||
from lostplaces.forms import ExplorerChangeForm, ExplorerUserChangeForm
 | 
			
		||||
 | 
			
		||||
class ExplorerProfileView(IsAuthenticatedMixin, View):
 | 
			
		||||
    def get(self, request, explorer_id):
 | 
			
		||||
@@ -33,13 +35,53 @@ class ExplorerProfileView(IsAuthenticatedMixin, View):
 | 
			
		||||
            asset_count += objects.count()
 | 
			
		||||
        
 | 
			
		||||
        context['asset_count'] = asset_count
 | 
			
		||||
        
 | 
			
		||||
        print(context['assets'])
 | 
			
		||||
        
 | 
			
		||||
                
 | 
			
		||||
        return render(
 | 
			
		||||
            request=request,
 | 
			
		||||
            template_name='explorer/profile.html',
 | 
			
		||||
            context=context
 | 
			
		||||
        )
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
class ExplorerProfileUpdateView(IsAuthenticatedMixin, View):
 | 
			
		||||
    success_message = ''
 | 
			
		||||
    permission_denied_message = ''
 | 
			
		||||
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        context = {
 | 
			
		||||
            'explorer_user_change_form': ExplorerUserChangeForm(instance=request.user),
 | 
			
		||||
            'explorer_change_form': ExplorerChangeForm(instance=request.user.explorer)
 | 
			
		||||
        }
 | 
			
		||||
        if request.user.explorer.profile_image:
 | 
			
		||||
            context['explorer_image'] = request.user.explorer.profile_image
 | 
			
		||||
        return render(request, 'explorer/profile_update.html', context)
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        print(request.POST)
 | 
			
		||||
        explorer_user_change_form = ExplorerUserChangeForm(
 | 
			
		||||
            request.POST,
 | 
			
		||||
            instance=request.user
 | 
			
		||||
            )
 | 
			
		||||
        explorer_change_form = ExplorerChangeForm(
 | 
			
		||||
            request.POST,
 | 
			
		||||
            request.FILES,
 | 
			
		||||
            instance=request.user.explorer
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if explorer_change_form.is_valid() and explorer_user_change_form.is_valid():
 | 
			
		||||
            explorer_user_change_form.save()
 | 
			
		||||
            explorer_change_form.save()
 | 
			
		||||
 | 
			
		||||
            print(explorer_change_form.cleaned_data)
 | 
			
		||||
 | 
			
		||||
            messages.success(
 | 
			
		||||
                self.request,
 | 
			
		||||
                _('Successfully updated Explorer profile')
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            # Usually the browser should have checked the form before sending.
 | 
			
		||||
            messages.error(
 | 
			
		||||
                self.request,
 | 
			
		||||
                _('Please fill in all required fields.')
 | 
			
		||||
            )
 | 
			
		||||
        return redirect(reverse_lazy('explorer_profile_update'))
 | 
			
		||||
    
 | 
			
		||||
@@ -150,4 +150,5 @@ class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
 | 
			
		||||
        else:
 | 
			
		||||
            return redirect(
 | 
			
		||||
                reverse_lazy('place_detail', kwargs={'pk': place.pk})
 | 
			
		||||
            )            
 | 
			
		||||
            )       
 | 
			
		||||
     
 | 
			
		||||
@@ -11,7 +11,7 @@ from django.shortcuts import render, redirect, get_object_or_404
 | 
			
		||||
from django.http import HttpResponseForbidden
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from lostplaces.forms import ExplorerCreationForm, TagSubmitForm
 | 
			
		||||
from lostplaces.forms import SignupVoucherForm, TagSubmitForm
 | 
			
		||||
from lostplaces.models import Place, PhotoAlbum
 | 
			
		||||
from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +23,7 @@ from lostplaces.views.base_views import (
 | 
			
		||||
from taggit.models import Tag
 | 
			
		||||
 | 
			
		||||
class SignUpView(SuccessMessageMixin, CreateView):
 | 
			
		||||
    form_class = ExplorerCreationForm
 | 
			
		||||
    form_class = SignupVoucherForm
 | 
			
		||||
    success_url = reverse_lazy('login')
 | 
			
		||||
    template_name = 'signup.html'
 | 
			
		||||
    success_message = _('User created')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user