1 Commits

Author SHA1 Message Date
5035c6fe2f Implemented tag that checks if user is authorized. 2020-10-11 09:46:15 +02:00
47 changed files with 240 additions and 1408 deletions

View File

@@ -1,4 +1,3 @@
exclude
include LICENSE
include Readme.rst
include Pipfile

View File

@@ -6,9 +6,6 @@ The software is currently in early development status, neither scope, datamodel(
We value privacy as a whole, all resources the frontend requires will be shipped with lostplace's distribution. We also try to minimize the use of JavaScript as far as we can and try to offer JS-less alternatives where we can.
## Contact
If you run into any issues, have any questions or If you are interested in this project in general, feel free to get in touch with us via [reverend@reverend2048.de](mailto:reverend@reverend2048.de), we do speak English and German.
## Features
- Manage lost places with lots of useful information.
- OSM-Maps
@@ -25,7 +22,7 @@ Right now it depends on the following non-core Python 3 libraries. These can be
* [django-widget-tweaks](https://github.com/jazzband/django-widget-tweaks) Tweak the form field rendering in templates, not in python-level form definitions.
* [django-taggit](https://github.com/jazzband/django-taggit) A simpler approach to tagging with Django.
### Bundled Dependencies
### Bundled Dependencies
We also leverage some other great OpenSource projects' code. We bundle those in the distribution to obsolete the need to pull those files from monitored CDNs.
* [OpenLayers](https://openlayers.org/) [6.4.3] OpenLayers makes it easy to put a dynamic map in any web page. It can display map tiles, vector data and markers loaded from any source.
@@ -91,9 +88,7 @@ Run `django_lostplaces/managy.py collectstatic` and you should be ready to go.
If you haven't already setup a django instance, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/install/).
Afterwards, either
- download the desired release [from the releases page](https://git.mowoe.com/reverend/lostplaces-backend/releases) and install it using `pip install --user name-of-the-file.tar.gz`
- or install the latest release using `pip install django-lostplaces`
After that, download the desired release (probably the latest one) [from the releases page](https://git.mowoe.com/reverend/lostplaces-backend/releases) and install it using `pip install --user name-of-the-file.tar.gz`
*Note: You can run pip install without the --user flag, which will require root privileges and introduces potential security issues.*
@@ -148,4 +143,4 @@ Before making the django instance public, you should tweak the config `settings.
4. Set a new (random) SECRET_KEY in settings.py, e. g.: `base64 /dev/urandom | head -c50`
Run `django_lostplaces/manage.py collectstatic` you should be ready to go.
Run `django_lostplaces/manage.py collectstatic` you should be ready to go.

View File

@@ -9,6 +9,8 @@ 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):

View File

@@ -1,25 +0,0 @@
from django.shortcuts import redirect
def get_all_subclasses(cls):
'''
Gets all subclasses recursively, does not contain
abstract classes
'''
subclass_list = []
for subclass in cls.__subclasses__():
if not subclass._meta.abstract:
subclass_list.append(subclass)
subclass_list += get_all_subclasses(subclass)
return subclass_list
def redirect_referer_or(request, url='/'):
'''
Returns a django redirect to the requests referer,
if there is no referer the redirect will poin to the given url
Default url is /
'''
referer = request.META.get('HTTP_REFERER')
if referer is not None:
return redirect(referer)
else:
return redirect(url)

View File

@@ -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, Explorer
from lostplaces.models import Place, PlaceImage, Voucher
class SignupVoucherForm(UserCreationForm):
class ExplorerCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
fields = ('username', 'email')
voucher = forms.CharField(
max_length=30,
help_text=_('The Voucher you got from an administrator')
@@ -35,47 +35,16 @@ class SignupVoucherForm(UserCreationForm):
fetched_voucher.delete()
return True
class ExplorerUserChangeForm(UserChangeForm):
class ExplorerChangeForm(UserChangeForm):
class Meta:
model = User
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']
fields = ('username', 'email')
class PlaceForm(forms.ModelForm):
class Meta:
model = Place
fields = '__all__'
exclude = ['submitted_by', 'latitude', 'longitute']
latitude = forms.IntegerField(
widget=forms.NumberInput(attrs={'min':-90,'max': 90,'type': 'number'})
)
longitude = forms.IntegerField(
widget=forms.NumberInput(attrs={'min':-180,'max': 180,'type': 'number'})
)
exclude = ['submitted_by']
class PlaceImageForm(forms.ModelForm):
class Meta:

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-25 16:04+0100\n"
"POT-Creation-Date: 2020-10-11 07:48+0200\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 templates/explorer/profile.html:31
#: models/abstract_models.py:29
msgid "Name"
msgstr "Name"
@@ -50,22 +50,6 @@ msgstr "Längengrad"
msgid "Longitude in decimal format: e. g. 2.17403"
msgstr "Breitengrad in dezimaler Form: z. B. 7.4840155"
#: models/abstract_models.py:59
msgid "Submission date"
msgstr "Einreichungsdatum"
#: models/abstract_models.py:67
msgid "Submitter"
msgstr "Einreicher"
#: models/abstract_models.py:79
msgid "Creation date"
msgstr "Erstellungsdatum"
#: models/abstract_models.py:82
msgid "Expiration date"
msgstr "Ablaufdatum"
#: models/external_links.py:13
msgid "URL"
msgstr "Adresse (URL)"
@@ -74,22 +58,6 @@ 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"
@@ -126,93 +94,39 @@ 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"
msgstr "Abmelden"
#: templates/global.html:33
msgid "Profile"
msgstr "Profil"
#: templates/global.html:35
#: templates/global.html:34
msgid "Admin"
msgstr "Admin"
#: templates/global.html:40 templates/registration/login.html:4
#: templates/global.html:39 templates/registration/login.html:4
#: templates/registration/login.html:10 templates/registration/login.html:23
msgid "Login"
msgstr "Anmelden"
#: templates/global.html:41 templates/registration/login.html:29
#: templates/signup.html:6 templates/signup.html:12 templates/signup.html:49
msgid "Sign up"
#: templates/global.html:40 templates/registration/login.html:29
#: templates/signup.html:12 templates/signup.html:41
msgid "Sign Up"
msgstr "Registrieren"
#: templates/global.html:51 templates/home.html:10
#: templates/global.html:50 templates/home.html:10
msgid "Home"
msgstr "Startseite"
#: templates/global.html:52
#: templates/global.html:51
msgid "UrBex Codex"
msgstr "UrBex Codex"
#: templates/global.html:57 templates/place/place_create.html:5
#: templates/global.html:56 templates/place/place_create.html:5
#: templates/place/place_create.html:10
msgid "Create place"
msgid "Create Place"
msgstr "Place erstellen"
#: templates/global.html:58
msgid "All places"
#: templates/global.html:57
msgid "All Places"
msgstr "Alle Places"
#: templates/home.html:20 templates/home_unauth.html:20
@@ -264,14 +178,6 @@ 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"
@@ -341,8 +247,7 @@ msgid "Submit a photo album"
msgstr "Fotoalbum hinzufügen"
#: templates/photo_album/photo_album_create.html:8
#: templates/place/place_detail.html:18 templates/place/place_update.html:5
#: templates/place/place_update.html:10
#: templates/place/place_detail.html:18
msgid "Edit place"
msgstr "Place bearbeiten"
@@ -360,13 +265,12 @@ msgid "Create"
msgstr "Erstellen"
#: templates/place/place_delete.html:5
msgid "Delete lost place"
msgid "Lost Place Deletion"
msgstr "Lost Place löschen"
#: templates/place/place_delete.html:15
#, python-format
msgid "Are you sure you want to delete %(place_name)s?"
msgstr "Bist Du sicher, dass Du %(place_name)s löschen möchtest?"
msgid "Are you sure you want to delete"
msgstr "Willst Du den Ort wirklich löschen: "
#: templates/place/place_delete.html:19
msgid "Delete"
@@ -389,84 +293,85 @@ msgid "Images"
msgstr "Bilder"
#: templates/place/place_list.html:10
msgid "All Places"
msgid "Lost Places"
msgstr "Alle Places"
#: templates/place/place_list.html:16
msgid "Our lost places"
msgid "Listing our places"
msgstr "Unsere Lost Places"
#: templates/place/place_update.html:5 templates/place/place_update.html:10
msgid "Update place"
msgstr "Place bearbeiten"
#: 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"
#: templates/registration/login.html:29
msgid "No account, yet?"
msgid "Have No Account Yet?"
msgstr "Noch kein Konto?"
#: templates/signup.html:6
msgid "Sign up"
msgstr "Registrieren"
#: views/base_views.py:25
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"
#: views/place_image_views.py:41
msgid "Image deleted successfully"
msgstr "Bild erfolgreich gelöscht"
msgid "Image(s) deleted successfully"
msgstr "Bild(er) erfolgreich gelöscht"
#: views/place_image_views.py:42
msgid "You are not allowed to delete this image"
msgstr "Du darfst dieses Bild nicht löschen"
#: views/place_views.py:62
#, fuzzy
#| msgid "Successfully deleted place"
msgid "Successfully updated place"
msgstr "Place erfolgreich aktualisiert"
msgstr "Place erfolgreich gelöscht"
#: views/place_views.py:63
msgid "You are not allowed to edit this place"
msgstr "Du darfst diesen Place nicht bearbeiten"
msgid "You do no have permissions to alter this place"
msgstr "Du hast nicht die Berechtigung, diesen Place zu bearbeiten"
#: views/place_views.py:97
#, fuzzy
#| msgid "Successfully deleted place"
msgid "Successfully created place"
msgstr "Place erfolgreich erstellt"
msgstr "Place erfolgreich gelöscht"
#: views/place_views.py:112
msgid "Successfully deleted place"
msgstr "Place erfolgreich gelöscht"
#: views/place_views.py:114
msgid "You are not allowed to delete this place"
msgstr "Du darfst diesen Place nicht löschen"
msgid "You do no have permission to delete this place"
msgstr "Du hast nicht die Berechtigung, diesen Place zu löschen"
#: views/views.py:29
msgid "User created"
msgstr "Benutzer erstellt"
msgstr "User erstellt"
#: views/views.py:54
msgid "Photo album link submitted"
msgstr "Fotoalbum-Link hinzugefügt"
msgid "Photo Album submitted"
msgstr "Fotoalbum hinzugefügt"
#: views/views.py:59
msgid "Photo album link deleted"
msgstr "Fotoalbum-Link gelöscht"
msgid "Photo Album deleted"
msgstr "Fotoalbum 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)"
msgid "You do not have permissions to alter this photo album"
msgstr "Du hast nicht die Berechtigung, diesen Place zu bearbeiten"

View File

@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('submitted_when', models.DateTimeField(auto_now_add=True, null=True)),
('description', models.TextField(blank=True)),
('filename', easy_thumbnails.fields.ThumbnailerImageField(upload_to=lostplaces.models.place.generate_place_image_filename)),
('filename', easy_thumbnails.fields.ThumbnailerImageField(upload_to=lostplaces.models.place.generate_image_upload_path)),
('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='placeimages', to='lostplaces.place')),
('submitted_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='placeimages', to='lostplaces.explorer')),
],

View File

@@ -19,4 +19,4 @@ class Migration(migrations.Migration):
migrations.DeleteModel(
name='Expireable'
)
]
]

View File

@@ -22,4 +22,4 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
]
]

View File

@@ -1,118 +0,0 @@
# Generated by Django 3.1.4 on 2020-12-25 16:02
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import easy_thumbnails.fields
from lostplaces.models.models import generate_profile_image_filename
from lostplaces.models.place import generate_place_image_filename
class Migration(migrations.Migration):
dependencies = [
('lostplaces', '0003_voucher'),
]
operations = [
migrations.AddField(
model_name='explorer',
name='bio',
field=models.TextField(blank=True, help_text='Describe yourself, your preferences, etc. in a few sentences.', null=True, verbose_name='Biography / Description'),
),
# migrations.AddField(
# model_name='explorer',
# name='favorite_places',
# field=models.ManyToManyField(blank=True, related_name='explorer_favorites', to='lostplaces.Place', verbose_name='Explorers favorite places'),
# ),
migrations.AddField(
model_name='explorer',
name='profile_image',
field=easy_thumbnails.fields.ThumbnailerImageField(blank=True, help_text='Optional profile image for display in Explorer profile', null=True, upload_to=generate_profile_image_filename, verbose_name='Profile image'),
),
migrations.AlterField(
model_name='photoalbum',
name='label',
field=models.CharField(max_length=100, verbose_name='link text'),
),
migrations.AlterField(
model_name='photoalbum',
name='submitted_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='photoalbums', to='lostplaces.explorer', verbose_name='Submitter'),
),
migrations.AlterField(
model_name='photoalbum',
name='submitted_when',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Submission date'),
),
migrations.AlterField(
model_name='photoalbum',
name='url',
field=models.URLField(verbose_name='URL'),
),
migrations.AlterField(
model_name='place',
name='description',
field=models.TextField(help_text="Description of the place: e.g. how to get there, where to be careful, the place's history...", verbose_name='Description'),
),
migrations.AlterField(
model_name='place',
name='latitude',
field=models.FloatField(help_text='Latitude in decimal format: e. g. 41.40338', validators=[django.core.validators.MinValueValidator(-90), django.core.validators.MaxValueValidator(90)], verbose_name='Latitude'),
),
migrations.AlterField(
model_name='place',
name='location',
field=models.CharField(max_length=50, verbose_name='Location'),
),
migrations.AlterField(
model_name='place',
name='longitude',
field=models.FloatField(help_text='Longitude in decimal format: e. g. 2.17403', validators=[django.core.validators.MinValueValidator(-180), django.core.validators.MaxValueValidator(180)], verbose_name='Longitude'),
),
migrations.AlterField(
model_name='place',
name='name',
field=models.CharField(max_length=50, verbose_name='Name'),
),
migrations.AlterField(
model_name='place',
name='submitted_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='places', to='lostplaces.explorer', verbose_name='Submitter'),
),
migrations.AlterField(
model_name='place',
name='submitted_when',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Submission date'),
),
migrations.AlterField(
model_name='placeimage',
name='description',
field=models.TextField(blank=True, verbose_name='Description'),
),
migrations.AlterField(
model_name='placeimage',
name='filename',
field=easy_thumbnails.fields.ThumbnailerImageField(help_text='Optional: One or more images to upload', upload_to=generate_place_image_filename, verbose_name='Filename(s)'),
),
migrations.AlterField(
model_name='placeimage',
name='submitted_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='placeimages', to='lostplaces.explorer', verbose_name='Submitter'),
),
migrations.AlterField(
model_name='placeimage',
name='submitted_when',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Submission date'),
),
migrations.AlterField(
model_name='voucher',
name='created_when',
field=models.DateTimeField(auto_now_add=True, verbose_name='Creation date'),
),
migrations.AlterField(
model_name='voucher',
name='expires_when',
field=models.DateTimeField(verbose_name='Expiration date'),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.1.4 on 2020-12-25 18:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces', '0004_auto_20201225_1702'),
]
operations = [
migrations.AddField(
model_name='explorer',
name='visited_places',
field=models.ManyToManyField(blank=True, related_name='explorer_visits', to='lostplaces.Place', verbose_name='Explorers visited places'),
),
]

View File

@@ -53,18 +53,13 @@ class Submittable(models.Model):
class Meta:
abstract = True
submitted_when = models.DateTimeField(
auto_now_add=True,
null=True,
verbose_name=_('Submission date')
)
submitted_when = models.DateTimeField(auto_now_add=True, null=True)
submitted_by = models.ForeignKey(
'Explorer',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='%(class)ss',
verbose_name=_('Submitter')
related_name='%(class)ss'
)
class Expireable(models.Model):
@@ -74,13 +69,8 @@ class Expireable(models.Model):
class Meta:
abstract = True
created_when = models.DateTimeField(
auto_now_add=True,
verbose_name=_('Creation date')
)
expires_when = models.DateTimeField(
verbose_name=_('Expiration date')
)
created_when = models.DateTimeField(auto_now_add=True)
expires_when = models.DateTimeField()
@property
def is_expired(self):

View File

@@ -6,34 +6,19 @@
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, pre_save
from django.db.models.signals import post_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 Django user.
Profile that is linked to the a User.
Every user has a profile.
Provides additional attributes for user profile.
"""
user = models.OneToOneField(
@@ -41,37 +26,10 @@ 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='explorer_favorites',
verbose_name='Explorers favorite places',
blank=True
)
visited_places = models.ManyToManyField(
Place,
related_name='explorer_visits',
verbose_name='Explorers visited places',
blank=True
)
def __str__(self):
return self.user.username
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
@@ -81,34 +39,13 @@ 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 tokens to allow the registration of new users.
Vouchers are authorization to created_when = models.DateTimeField(auto_now_add=True)
expires_when = models.DateTimeField()kens 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)
@@ -119,3 +56,4 @@ class Voucher(Expireable):
def __str__(self):
return "Voucher " + str(self.code)

View File

@@ -49,10 +49,10 @@ class Place(Submittable, Taggable, Mapable):
return self.name
def generate_place_image_filename(instance, filename):
def generate_image_upload_path(instance, filename):
"""
Callback for generating filename for uploaded place images.
Returns filename as: place_pk-placename{-number}.jpg
Callback for generating path for uploaded images.
Returns filename as: place_pk-placename{-rnd_string}.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 (Submittable):
"""
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_place_image_filename,
upload_to=generate_image_upload_path,
resize_source=dict(size=(2560, 2560),
sharpen=True),
verbose_name=_('Filename(s)'),
@@ -104,6 +104,7 @@ class PlaceImage(PlaceAsset):
return 'Image ' + str(self.pk)
# These two auto-delete files from filesystem when they are unneeded:
@receiver(post_delete, sender=PlaceImage)
@@ -117,6 +118,7 @@ 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):
"""
@@ -135,4 +137,5 @@ 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:
old_file.delete(save=False)
if os.path.isfile(old_file.path):
os.remove(old_file.path)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 368.16 368.16" style="enable-background:new 0 0 368.16 368.16;" xml:space="preserve">
<g>
<g>
<g>
<path d="M184.08,0c-74.992,0-136,61.008-136,136c0,24.688,11.072,51.24,11.536,52.36c3.576,8.488,10.632,21.672,15.72,29.4
l93.248,141.288c3.816,5.792,9.464,9.112,15.496,9.112s11.68-3.32,15.496-9.104l93.256-141.296
c5.096-7.728,12.144-20.912,15.72-29.4c0.464-1.112,11.528-27.664,11.528-52.36C320.08,61.008,259.072,0,184.08,0z
M293.8,182.152c-3.192,7.608-9.76,19.872-14.328,26.8l-93.256,141.296c-1.84,2.792-2.424,2.792-4.264,0L88.696,208.952
c-4.568-6.928-11.136-19.2-14.328-26.808C74.232,181.816,64.08,157.376,64.08,136c0-66.168,53.832-120,120-120
c66.168,0,120,53.832,120,120C304.08,157.408,293.904,181.912,293.8,182.152z"/>
<path d="M184.08,64.008c-39.704,0-72,32.304-72,72c0,39.696,32.296,72,72,72c39.704,0,72-32.304,72-72
C256.08,96.312,223.784,64.008,184.08,64.008z M184.08,192.008c-30.872,0-56-25.12-56-56s25.128-56,56-56s56,25.12,56,56
S214.952,192.008,184.08,192.008z"/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,105 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 368.16 368.16"
style="enable-background:new 0 0 368.16 368.16;"
xml:space="preserve"
sodipodi:docname="pin_filled.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
id="metadata47"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs45" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="712"
id="namedview43"
showgrid="false"
inkscape:zoom="1.4803346"
inkscape:cx="184.08"
inkscape:cy="181.37791"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g10">
<g
id="g8">
<g
id="g6">
<path
d="M184.08,0c-74.992,0-136,61.008-136,136c0,24.688,11.072,51.24,11.536,52.36c3.576,8.488,10.632,21.672,15.72,29.4 l93.248,141.288c3.816,5.792,9.464,9.112,15.496,9.112s11.68-3.32,15.496-9.104l93.256-141.296 c5.096-7.728,12.144-20.912,15.72-29.4c0.464-1.112,11.528-27.664,11.528-52.36C320.08,61.008,259.072,0,184.08,0z M293.8,182.152c-3.192,7.608-9.76,19.872-14.328,26.8l-93.256,141.296c-1.84,2.792-2.424,2.792-4.264,0L88.696,208.952 c-4.568-6.928-11.136-19.2-14.328-26.808C74.232,181.816,64.08,157.376,64.08,136c0-66.168,53.832-120,120-120 c66.168,0,120,53.832,120,120C304.08,157.408,293.904,181.912,293.8,182.152z"
id="path2" />
<path
d="M184.08,64.008c-39.704,0-72,32.304-72,72c0,39.696,32.296,72,72,72c39.704,0,72-32.304,72-72 C256.08,96.312,223.784,64.008,184.08,64.008z M184.08,192.008c-30.872,0-56-25.12-56-56s25.128-56,56-56s56,25.12,56,56 S214.952,192.008,184.08,192.008z"
id="path4" />
</g>
</g>
</g>
<g
id="g12">
</g>
<g
id="g14">
</g>
<g
id="g16">
</g>
<g
id="g18">
</g>
<g
id="g20">
</g>
<g
id="g22">
</g>
<g
id="g24">
</g>
<g
id="g26">
</g>
<g
id="g28">
</g>
<g
id="g30">
</g>
<g
id="g32">
</g>
<g
id="g34">
</g>
<g
id="g36">
</g>
<g
id="g38">
</g>
<g
id="g40">
</g>
<path
style="fill:#d40000;stroke-width:0.675523"
d="M 183.2356,15.407728 C 148.115,15.680007 113.56046,32.165752 91.281357,59.335191 67.968201,87.080284 57.903075,125.97636 67.012946,161.30636 c 5.002813,21.56622 16.217838,40.96402 28.671518,59.02158 29.047926,44.21623 57.936976,88.55418 87.673836,132.30559 2.75528,0.71398 3.44582,-3.87416 5.23173,-5.36255 32.63958,-48.91094 65.16056,-97.91511 96.89098,-147.42174 14.94799,-26.09698 23.38967,-57.3347 16.79888,-87.25041 C 297.41258,88.732147 285.50707,66.159988 267.48079,49.622901 254.86863,36.299556 238.35251,27.47624 221.24564,21.334387 209.00763,17.320382 196.11719,15.285641 183.2356,15.407728 Z m 2.03448,49.555941 c 27.46439,-0.566602 54.10132,17.912787 64.54543,43.490751 4.10895,9.11958 5.7106,19.04018 5.10583,29.01713 0.83931,19.32103 -8.79389,38.15287 -22.74612,51.10133 -14.02478,12.97799 -33.27348,19.77398 -52.19585,18.33769 -29.18994,-0.56334 -56.21632,-22.71956 -64.31454,-50.57715 -3.46488,-10.42859 -2.56691,-21.58508 -1.94784,-32.37905 3.96475,-21.34187 17.606,-41.232363 37.2523,-51.078299 10.71386,-5.480174 22.38724,-8.587404 34.30079,-7.912402 z"
id="path91" /></svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -862,9 +862,10 @@ body {
margin: 0 3px; }
.LP-PlaceTeaser {
width: 100%; }
width: 280px; }
.LP-PlaceTeaser .LP-PlaceTeaser__Image {
width: 100%;
height: 165px;
width: 280px;
overflow: hidden; }
.LP-PlaceTeaser .LP-PlaceTeaser__Image .LP-Image {
max-width: unset;
@@ -872,8 +873,6 @@ body {
object-fit: cover; }
.LP-PlaceTeaser .LP-PlaceTeaser__Meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
justify-content: space-between;
padding: 5px;
@@ -884,30 +883,19 @@ body {
padding: 0;
margin: 0;
margin-top: 5px;
font-size: 1.3rem; }
font-size: 0.9rem; }
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Headline {
font-family: "Montserrat", Helvetica, sans-serif;
color: #565656;
font-size: 2rem;
font-size: 1rem;
padding: 0px;
margin: 0px; }
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Icon {
height: 2em;
width: 2em; }
.LP-PlaceTeaser .LP-PlaceTeaser__Description {
font-family: "Montserrat", Helvetica, sans-serif;
color: #565656;
order: 3;
flex-basis: 100%; }
.LP-PlaceTeaser .LP-PlaceTeaser__Description .LP-Paragraph {
font-size: 22px; }
display: none; }
@media (min-width: 650px) {
.LP-PlaceTeaser {
width: 280px; }
.LP-PlaceTeaser__Image {
width: 280px;
height: 165px; }
.LP-PlaceTeaser--extended {
display: flex;
flex-direction: row;
@@ -916,16 +904,12 @@ body {
height: 165px; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta {
margin: 0;
padding: 5px;
padding-left: 25px;
display: flex;
flex-wrap: nowrap;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
gap: unset; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Headline,
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Paragraph {
font-size: unset; }
align-items: flex-start; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Info .LP-Headline {
font-size: 28px; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons {
@@ -933,21 +917,10 @@ body {
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-PlaceTeaser__Icons ul {
padding: 0;
margin: 0; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Meta .LP-Icon {
height: 20px;
width: 20px; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Description {
display: block;
max-height: 55px;
overflow: hidden;
order: unset; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Description .LP-Paragraph {
font-size: unset;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis; }
overflow: hidden; }
.LP-PlaceTeaser--extended .LP-PlaceTeaser__Image {
height: 165px;
width: 280px;
@@ -1033,31 +1006,6 @@ body {
.LP-MessageList .LP-MessageList__Item {
margin: 5px 0; }
.LP-UserInfo {
display: flex;
flex-direction: column;
background-color: #D7CEC7;
max-width: 400px;
width: 100%;
min-width: 300px; }
.LP-UserInfo__UserName {
background-color: #C09F80; }
.LP-UserInfo__UserName .LP-Headline {
color: black;
padding: 0;
margin: 0;
text-align: center;
padding: 5px;
font-size: 25px; }
.LP-UserInfo__Meta {
padding: 5px; }
.LP-UserInfo__Meta * {
font-family: "Montserrat", Helvetica, sans-serif;
font-size: 18px; }
.LP-UserInfo__Meta .LP-UserInfo__Key {
padding-right: 25px;
font-weight: bold; }
.LP-Header {
display: flex;
align-items: center;
@@ -1121,9 +1069,7 @@ body {
box-shadow: 0 0 2px #565656; }
.LP-PlaceList .LP-PlaceList__List {
list-style-type: none;
padding: 0;
margin: 0; }
list-style-type: none; }
.LP-PlaceList .LP-PlaceList__List .LP-Link .LP-Place:hover {
color: #565656;
background-color: #f9f9f9;
@@ -1134,10 +1080,6 @@ body {
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
max-width: 900px;
margin: 18px 0; }
@media (max-width: 750px) {
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
width: 100%;
margin-bottom: 35px; } }
.LP-PlaceList .LP-Pagination {
margin-top: 50px; }
@@ -1701,11 +1643,6 @@ body {
.LP-Map {
margin-bottom: 25px; }
.LP-Map--wide {
height: 300px; }
.LP-Map--full {
height: 100%;
width: 100%; }
.LP-Map .ol-attribution {
font-family: "Montserrat", Helvetica, sans-serif;
color: #565656; }
@@ -1724,18 +1661,6 @@ body {
padding: .5em;
border-radius: 2px; }
.LP-UserProfile {
display: flex;
flex-direction: row; }
.LP-UserProfile__Bio {
flex-grow: 5;
flex-shrink: 10;
padding-right: 35px; }
.LP-UserProfile__Info {
flex-grow: 10;
flex-shrink: 1;
flex-basis: max-content; }
.LP-MainContainer {
margin: 0 auto;
max-width: 1280px; }
@@ -1748,9 +1673,8 @@ body {
width: 100%; } }
.LP-PlaceDetail .LP-PlaceDetail__Image {
max-width: 700px;
width: 700px;
max-height: 500px;
width: 50%;
box-shadow: 0 0 10px #565656;
box-shadow: 0 0 10px #565656;
object-fit: cover;
@@ -1758,16 +1682,15 @@ body {
margin: 0;
padding: 0;
float: right;
margin-left: 25px;
margin-bottom: 25px;
margin-right: 25px;
margin-left: 35px;
margin-bottom: 35px;
margin-right: 35px;
overflow: hidden; }
@media (max-width: 750px) {
@media (max-width: 1000px) {
.LP-PlaceDetail .LP-PlaceDetail__Header .LP-PlaceDetail__Image {
float: none;
width: 100%;
max-width: unset;
height: auto;
margin: 0;
padding: 0;

View File

@@ -1,173 +0,0 @@
{% extends 'global.html'%}
{% load static %}
{% load i18n %}
{% load svg_icon %}
{% 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>
</td>
<td class="LP-UserInfo__Value">
<span class="LP-Paragraph">{{explorer.user.date_joined|date:'d.m.Y'}}</span>
</td>
</tr>
<tr>
<td class="LP-UserInfo__Key">
<span class="LP-Paragraph">{% trans 'Places'%}</span>
</td>
<td class="LP-UserInfo__Value">
<span class="LP-Paragraph">{{place_count}}</span>
</td>
</tr>
<tr>
<td class="LP-UserInfo__Key">
<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 'Visited places' %}</h1>
<ul class="LP-PlaceList__List">
{% for place in explorer.visited_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>
<ul class="LP-PlaceList__List">
{% for place in place_list %}
<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">
<h1 class="LP-Headline">{% trans 'Images submitted by' %} {{explorer.user.username}}</h1>
<div class="LP-ImageGrid">
<ul class="LP-ImageGrid__Container">
{% for place_image in assets.placeimages.all %}
<li class="LP-ImageGrid__Item">
<a href="{{ place_image.filename.large.url }}" class="LP-Link">
<img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}">
</a>
{% if user.explorer == place_image.submitted_by%}
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
<a href="{% url 'place_image_delete' pk=place_image.id %}" class="LP-Link">
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}" />
</a>
</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</section>
<section class=" LP-Section">
<h1 class="LP-Headline">{% trans 'Photo albums submitted by' %} {{explorer.user.username}}</h1>
<div class="LP-LinkList">
<ul class="LP-LinkList__Container">
{% for photo_album in assets.photoalbums.all %}
<li class="LP-LinkList__Item">
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
<span class="LP-Text">{{photo_album.label}}</span>
</a>
{% if user.explorer == photo_album.submitted_by%}
<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">
{% icon 'trash' className="RV-Iconized__Icon" %}
</div>
</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</section>
{% endblock maincontent %}

View File

@@ -1,55 +0,0 @@
{% 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 %}

View File

@@ -29,16 +29,15 @@
<span class="LP-Paragraph">
{% if user.is_authenticated %}
Hi {{ user.username }}!
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a> |
<a class="LP-Link" href="{% url 'explorer_profile' explorer_id=user.pk%}"><span class="LP-Link__Text">{% trans 'Profile' %}</span></a>
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a>
{% if user.is_superuser %}
| <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a>
<a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a>
{% endif %}
{% else %}
You are not logged in.
<a class="LP-Link" href="{% url 'login' %}"><span class="LP-Link__Text">{% trans 'Login' %}</span></a> |
<a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">{% trans 'Sign up' %}</span></a>
<a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">{% trans 'Sign Up' %}</span></a>
{% endif %}
</span>
</div>
@@ -50,12 +49,12 @@
<ul class="LP-Menu__List">
<li class="LP-Menu__Item"><a href="{% url 'lostplaces_home' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Home' %}</span></a></li>
<li class="LP-Menu__Item"><a href="{% url 'flatpage' slug='codex' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'UrBex Codex' %}</span></a></li>
<li class="LP-Menu__Item"><a href="{% url 'osm' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Map' %}</span></a></li>
{% block additional_menu_items %}
{% endblock additional_menu_items %}
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_create'%}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Create place' %}</span></a></li>
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_list'%}" class="LP-Link"><span class="LP-Link__Text">{% trans 'All places' %}</span></a></li>
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_create'%}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Create Place' %}</span></a></li>
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_list'%}" class="LP-Link"><span class="LP-Link__Text">{% trans 'All Places' %}</span></a></li>
</ul>
</nav>
</aside>

View File

@@ -15,13 +15,36 @@
<article class="LP-TextSection">
</article>
{% include 'partials/osm_map.html' with config=mapping_config modifier='wide' %}
{% include 'partials/osm_map.html' with config=mapping_config %}
<div class="LP-PlaceGrid">
<h1 class="LP-Headline LP-Headline">{% trans 'Explore the latest places' %}</h1>
<ul class="LP-PlaceGrid__Grid">
{% for place in place_list %}
<li class="LP-PlaceGrid__Item">
{% include 'partials/place_teaser.html' with place=place%}
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<article class="LP-PlaceTeaser">
<div class="LP-PlaceTeaser__Image">
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url}}" />
</div>
<div class="LP-PlaceTeaser__Meta">
<div class="LP-PlaceTeaser__Info">
<span class="LP-PlaceTeaser__Title">
<h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
</span>
<span class="LP-PlaceTeaser__Detail">
<p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
</span>
</div>
<div class="LP-PlaceTeaser__Icons">
<ul class="LP-Icon__List">
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
</ul>
</div>
</div>
</article>
</a>
</li>
{% endfor %}
</ul>

View File

@@ -1,16 +0,0 @@
{% extends 'global.html'%}
{% load static %}
{% load i18n %}
{% block additional_head %}
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
<script src="{% static 'maps/ol.js' %}"></script>
{% endblock additional_head %}
# {% block title %}{% trans 'Map' %}{% endblock %}
{% block maincontent %}
{% include 'partials/osm_map.html' with config=mapping_config modifier='full' %}
{% endblock maincontent %}

View File

@@ -1,14 +0,0 @@
{%load static %}
{% load i18n %}
{% if request.user %}
{% if place in request.user.explorer.favorite_places.all %}
<a href="{% url 'place_unfavorite' place_id=place.id %}" class="LP-Link" title="{% trans 'Remove from favorites' %}">
<img class="LP-Icon" src="{% static '/icons/favorite_filled.svg' %}" />
</a>
{%else%}
<a href="{% url 'place_favorite' place_id=place.id %}" class="LP-Link" title="{% trans 'Save as favorite' %}">
<img class="LP-Icon" src="{% static '/icons/favorite.svg' %}" />
</a>
{% endif %}
{% endif %}

View File

@@ -1,14 +0,0 @@
{%load static %}
{% load i18n %}
{% if request.user %}
{% if place in request.user.explorer.visited_places.all %}
<a href="{% url 'place_visit_delete' place_id=place.id %}" class="LP-Link" title="{% trans 'Remove from visits' %}">
<img class="LP-Icon" src="{% static '/icons/pin_filled.svg' %}" />
</a>
{%else%}
<a href="{% url 'place_visit_create' place_id=place.id %}" class="LP-Link" title="{% trans 'Save as visited' %}">
<img class="LP-Icon" src="{% static '/icons/pin.svg' %}" />
</a>
{% endif %}
{% endif %}

View File

@@ -1,5 +1,5 @@
{% load static %}
<div tabindex="1" id="map" class="LP-Map {% if modifier %}LP-Map--{{modifier}}{% endif %} map"></div>
<div tabindex="1" id="map" class="LP-Map map" style="height: 300px"></div>
<div id="info" class="map-popup LP-Map__Popup"></div>
<script type="text/javascript">

View File

@@ -1,39 +0,0 @@
{%load static %}
<article class="LP-PlaceTeaser {% if extended %} LP-PlaceTeaser--extended{% endif %}">
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<div class="LP-PlaceTeaser__Image">
{% if place.placeimages.all|length > 0 %}
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url}}" />
{% else %}
<img class="LP-Image" src="{% static 'images/missing_image.png' %}" />
{% endif %}
</div>
</a>
<div class="LP-PlaceTeaser__Meta">
<div class="LP-PlaceTeaser__Info">
<span class="LP-PlaceTeaser__Title">
<h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
</span>
<span class="LP-PlaceTeaser__Detail">
<p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
</span>
</div>
<div class="LP-PlaceTeaser__Description">
<p class="LP-Paragraph">
{% if place.description|length > 210 %}
{{place.description|truncatechars:210|truncatewords:-1}}
{% else %}
{{place.description}}
{% endif %}
</p>
</div>
<div class="LP-PlaceTeaser__Icons">
<ul class="LP-Icon__List">
<li class="LP-Icon__Item">{% include 'partials/icons/place_favorite.html' with place=place%}</li>
<li class="LP-Icon__Item">{% include 'partials/icons/place_visited.html' with place=place%}</li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
</ul>
</div>
</div>
</article>

View File

@@ -2,12 +2,12 @@
{% load static %}
{% load i18n %}
# {% block title %}{% trans 'Create place' %}{% endblock %}
# {% block title %}{% trans 'Create Place' %}{% endblock %}
{% block maincontent %}
<form class="LP-Form" method="POST" enctype="multipart/form-data">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">{% trans 'Create place' %}</legend>
<legend class="LP-Form__Legend">{% trans 'Create Place' %}</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">

View File

@@ -2,7 +2,7 @@
{% load static %}
{% load i18n %}
{% block title %}{% trans 'Delete lost place' %}{% endblock %}
{% block title %}{% trans 'Lost Place Deletion' %}{% endblock %}
{% block maincontent %}
@@ -12,7 +12,7 @@
{% csrf_token %}
<div class="LP-Form__Composition">
<div class="LP-Form__Field LP-Form__InfoText">
<p class="LP-Paragraph">{% blocktrans with place_name=place.name%}Are you sure you want to delete {{place_name}}?{% endblocktrans %}</p>
<p class="LP-Paragraph">{% trans 'Are you sure you want to delete' %} "{{place.name}}"?</p>
</div>
</div>

View File

@@ -4,6 +4,7 @@
{% load thumbnail %}
{% load svg_icon %}
{% load lostplaces %}
{% block additional_head %}
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
@@ -23,7 +24,7 @@
<article class="LP-PlaceDetail">
<header class="LP-PlaceDetail__Header">
<h1 class="LP-Headline">{{ place.name }} {% include 'partials/icons/place_favorite.html' %} {% include 'partials/icons/place_visited.html' %}</h1>
<h1 class="LP-Headline">{{ place.name }}</h1>
{% if place.placeimages.first.filename.hero.url %}
<figure class="LP-PlaceDetail__Image">
<img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" />
@@ -36,15 +37,13 @@
</div>
<section class="LP-Section">
{% url 'place_tag_submit' place_id=place.id as tag_submit_url%}
{% include 'partials/tagging.html' with config=tagging_config %}
</section>
<section class="LP-Section">
<h1 class="LP-Headline">{% trans 'Map links' %}</h1>
{% include 'partials/osm_map.html' with config=mapping_config modifier='wide' %}
{% include 'partials/osm_map.html' with config=mapping_config%}
<div class="LP-LinkList">
<ul class="LP-LinkList__Container">
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude|safe}},{{place.longitude|safe}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>
@@ -63,7 +62,8 @@
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
<span class="LP-Text">{{photo_album.label}}</span>
</a>
{% if user.explorer == photo_album.submitted_by or user.explorer == place.submitted_by %}
{% can_modify_place_asset photo_album request as authorized %}
{% if authorized %}
<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">
{% icon 'trash' className="RV-Iconized__Icon" %}
@@ -98,7 +98,8 @@
<a href="{{ place_image.filename.large.url }}" class="LP-Link">
<img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}">
</a>
{% if user.explorer == place_image.submitted_by or user.explorer == place.submitted_by %}
{% can_modify_place_asset place_image request as authorized %}
{% if authorized %}
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
<a href="{% url 'place_image_delete' pk=place_image.id %}" class="LP-Link">
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}" />

View File

@@ -7,17 +7,49 @@
<script src="{% static 'maps/ol.js' %}"></script>
{% endblock additional_head %}
{% block title %}{% trans 'All Places' %}{% endblock %}
{% block title %}{% trans 'Lost Places' %}{% endblock %}
{% block maincontent %}
{% include 'partials/osm_map.html' with config=mapping_config modifier='wide' %}
{% include 'partials/osm_map.html' with config=mapping_config %}
<div class="LP-PlaceList">
<h1 class="LP-Headline">{% trans 'Our lost places' %}</h1>
<h1 class="LP-Headline">{% trans 'Listing our places' %}</h1>
<ul class="LP-PlaceList__List">
{% for place in place_list %}
<li class="LP-PlaceList__Item">
{% include 'partials/place_teaser.html' with place=place extended=True %}
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<article class="LP-PlaceTeaser LP-PlaceTeaser--extended">
<div class="LP-PlaceTeaser__Image">
<img class="LP-Image" src="{{ place.placeimages.first.filename.thumbnail.url }}" />
</div>
<div class="LP-PlaceTeaser__Meta">
<div class="LP-PlaceTeaser__Info">
<span class="LP-PlaceTeaser__Title">
<h2 class="LP-Headline LP-Headline--teaser">{{place.name}}</h2>
</span>
<span class="LP-PlaceTeaser__Detail">
<p class="LP-Paragraph">{{place.location}}</p>
</span>
</div>
<div class="LP-PlaceTeaser__Description">
<p class="LP-Paragraph">
{% if place.description|length > 210 %}
{{place.description|truncatechars:210|truncatewords:-1}}
{% else %}
{{place.description}}
{% endif %}
</p>
</div>
<div class="LP-PlaceTeaser__Icons">
<ul class="LP-Icon__List">
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
</ul>
</div>
</div>
</article>
</a>
</li>
{% endfor %}
</ul>

View File

@@ -2,12 +2,12 @@
{% load static %}
{% load i18n %}
# {% block title %}{% trans 'Edit place' %}{% endblock %}
# {% block title %}{% trans 'Update place' %}{% 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 place' %}</legend>
<legend class="LP-Form__Legend">{% trans 'Update place' %}</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">

View File

@@ -26,6 +26,6 @@
</fieldset>
</form>
<p class="LP-Headline">{% trans 'No account, yet?' %} <a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">{% trans 'Sign up' %}</span></a></p>
<p class="LP-Headline">{% trans 'Have No Account Yet?' %} <a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">{% trans 'Sign Up' %}</span></a></p>
{% endblock maincontent %}

View File

@@ -9,7 +9,7 @@
<form class="LP-Form" method="POST">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">{% trans 'Sign up' %}</legend>
<legend class="LP-Form__Legend">{% trans 'Sign Up' %}</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
@@ -19,14 +19,6 @@
{% 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">
@@ -46,7 +38,7 @@
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">{% trans 'Sign up' %}</button>
<button class="LP-Button">{% trans 'Sign Up' %}</button>
</div>
</div>
</fieldset>

View File

@@ -24,3 +24,13 @@ def proper_paginate(paginator, current_page, neighbors=2):
page_list = [f for f in range(start_index, end_index+1)]
return page_list[:(2*neighbors + 1)]
return paginator.page_range
@register.simple_tag
def can_modify_place_asset(place_asset, request, *args, **kwargs):
print(place_asset.place.submitted_by, place_asset.submitted_by, request.user)
if request.user.is_superuser:
return True
if request.user.explorer == place_asset.place.submitted_by or request.user.explorer == place_asset.submitted_by:
return True
else:
return False

View File

@@ -4,15 +4,15 @@ from django import forms
from django.utils import timezone
from lostplaces.tests.forms import FormTestCase
from lostplaces.forms import SignupVoucherForm
from lostplaces.forms import ExplorerCreationForm
from lostplaces.models.models import Voucher
class SignupVoucherFormTestCase(FormTestCase):
class ExplorerCreationFormTestCase(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 = SignupVoucherForm
form = ExplorerCreationForm
@classmethod
def setUpTestData(cls):
@@ -37,7 +37,7 @@ class SignupVoucherFormTestCase(FormTestCase):
)
def test_validation_valid(self):
form = SignupVoucherForm(self.post_data)
form = ExplorerCreationForm(self.post_data)
self.assertTrue(
form.is_valid(),
msg='Expecting the %s to validate' % (
@@ -49,7 +49,7 @@ class SignupVoucherFormTestCase(FormTestCase):
self.post_data = {
'voucher': 'Imanotacode123'
}
form = SignupVoucherForm(self.post_data)
form = ExplorerCreationForm(self.post_data)
self.assertFalse(
form.is_valid(),
msg='Expecting the %s to not validate' % (

View File

@@ -4,8 +4,6 @@
from django.urls import path
from lostplaces.views import (
HomeView,
FlatView,
OSMMapView,
PlaceDetailView,
PlaceListView,
PlaceCreateView,
@@ -13,41 +11,27 @@ from lostplaces.views import (
PlaceDeleteView,
PlaceTagDeleteView,
PlaceTagSubmitView,
PlaceFavoriteView,
PlaceUnfavoriteView,
PlaceVisitCreateView,
PlaceVisitDeleteView,
PhotoAlbumCreateView,
PhotoAlbumDeleteView,
PlaceImageCreateView,
PlaceImageDeleteView,
PhotoAlbumCreateView,
PhotoAlbumDeleteView,
ExplorerProfileView,
ExplorerProfileUpdateView
FlatView
)
urlpatterns = [
path('', HomeView.as_view(), name='lostplaces_home'),
path('flat/<slug:slug>/', FlatView, name='flatpage'),
path('osm/', OSMMapView.as_view(), name='osm'),
path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
path('explorer/update/', ExplorerProfileUpdateView.as_view(), name='explorer_profile_update'),
path('place/', PlaceListView.as_view(), name='place_list'),
path('place/<int:pk>/', PlaceDetailView.as_view(), name='place_detail'),
path('place/create/', PlaceCreateView.as_view(), name='place_create'),
path('photo_album/create/<int:place_id>', PhotoAlbumCreateView.as_view(), name='photo_album_create'),
path('photo_album/delete/<int:pk>', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'),
path('place/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'),
path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'),
path('place/tag/create/<int:tagged_id>/', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
path('place/tag/delete/<int:tagged_id>/<int:tag_id>/', PlaceTagDeleteView.as_view(), name='place_tag_delete'),
path('place/fav/create/<int:place_id>/', PlaceFavoriteView.as_view(), name='place_favorite'),
path('place/fav/delete/<int:place_id>/', PlaceUnfavoriteView.as_view(), name='place_unfavorite'),
path('place/visit/create/<int:place_id>/', PlaceVisitCreateView.as_view(), name='place_visit_create'),
path('place/visit/delete/<int:place_id>/', PlaceVisitDeleteView.as_view(), name='place_visit_delete'),
path('place/', PlaceListView.as_view(), name='place_list'),
path('place_image/create/<int:place_id>', PlaceImageCreateView.as_view(), name='place_image_create'),
path('place_image/delete/<int:pk>', PlaceImageDeleteView.as_view(), name='place_image_delete'),
path('flat/<slug:slug>/', FlatView, name='flatpage'),
path('place_image/create/<int:place_id>/', PlaceImageCreateView.as_view(), name='place_image_create'),
path('place_image/delete/<int:pk>/', PlaceImageDeleteView.as_view(), name='place_image_delete'),
path('photo_album/create/<int:place_id>/', PhotoAlbumCreateView.as_view(), name='photo_album_create'),
path('photo_album/delete/<int:pk>/', PhotoAlbumDeleteView.as_view(), name='photo_album_delete')
# POST-only URLs for tag submission
path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete')
]

View File

@@ -4,5 +4,4 @@
from lostplaces.views.base_views import *
from lostplaces.views.views import *
from lostplaces.views.place_views import *
from lostplaces.views.place_image_views import *
from lostplaces.views.explorer_views import *
from lostplaces.views.place_image_views import *

View File

@@ -10,11 +10,10 @@ from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from lostplaces.models import Place
from lostplaces.common import redirect_referer_or
class IsAuthenticatedMixin(LoginRequiredMixin, View):
'''
@@ -109,4 +108,4 @@ class PlaceAssetDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SingleOb
place_id = self.get_object().place.id
self.get_object().delete()
messages.success(self.request, self.success_message)
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place_id}))
return redirect(reverse_lazy('place_detail', kwargs={'pk': place_id}))

View File

@@ -1,87 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.views import View
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):
explorer = get_object_or_404(Explorer, pk=explorer_id)
place_list = explorer.places.all()
place_count = place_list.count()
context={
'explorer': explorer,
'place_count': place_count,
'place_list': place_list,
'assets': {}
}
asset_count = 0
for subclass in get_all_subclasses(PlaceAsset): # kinda ugly, but PlaceAsset cannot be abstract according to django ORM
objects = subclass.objects.filter(submitted_by=explorer)
context['assets'][subclass.__name__.lower()+'s'] = objects
asset_count += objects.count()
context['asset_count'] = asset_count
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'))

View File

@@ -38,5 +38,5 @@ class PlaceImageCreateView(MultiplePlaceImageUploadMixin, PlaceAssetCreateView):
class PlaceImageDeleteView(PlaceAssetDeleteView):
model = PlaceImage
success_message = _('Image deleted successfully')
success_message = _('Image(s) deleted successfully')
permission_denied_message = _('You are not allowed to delete this image')

View File

@@ -13,13 +13,12 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.urls import reverse_lazy
from lostplaces.models import Place, PlaceImage
from lostplaces.views.base_views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
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
@@ -61,7 +60,7 @@ class PlaceUpdateView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SuccessMessag
model = Place
form_class = PlaceForm
success_message = _('Successfully updated place')
place_submitter_error_message = _('You are not allowed to edit this place')
place_submitter_error_message = _('You do no have permissions to alter this place')
def get_success_url(self):
return reverse_lazy('place_detail', kwargs={'pk':self.get_object().pk})
@@ -103,23 +102,16 @@ class PlaceCreateView(MultiplePlaceImageUploadMixin, IsAuthenticatedMixin, View)
# Usually the browser should have checked the form before sending.
messages.error(
self.request,
_('Please fill in all required fields.')
)
return render(
request=request,
template_name='place/place_create.html',
context={
'place_form': place_form,
'place_image_form': PlaceImageForm()
}
'Please fill in all required fields.'
)
return render(request, 'place/place_create.html', context={'form': place_form})
class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
template_name = 'place/place_delete.html'
model = Place
success_message = _('Successfully deleted place')
success_url = reverse_lazy('place_list')
place_submitter_error_message = _('You are not allowed to delete this place')
place_submitter_error_message = _('You do no have permission to delete this place')
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
@@ -127,43 +119,3 @@ class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
def get_place(self):
return self.get_object()
class PlaceFavoriteView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
request.user.explorer.favorite_places.add(place)
request.user.explorer.save()
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:
request.user.explorer.favorite_places.remove(place)
request.user.explorer.save()
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
class PlaceVisitCreateView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
request.user.explorer.visited_places.add(place)
request.user.explorer.save()
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
class PlaceVisitDeleteView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
request.user.explorer.visited_places.remove(place)
request.user.explorer.save()
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))

View File

@@ -6,15 +6,14 @@ from django.views.generic.edit import CreateView
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib import messages
from django.urls import reverse_lazy, reverse
from django.urls import reverse_lazy
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 SignupVoucherForm, TagSubmitForm
from lostplaces.forms import ExplorerCreationForm, TagSubmitForm
from lostplaces.models import Place, PhotoAlbum
from lostplaces.views.base_views import IsAuthenticatedMixin
from lostplaces.common import redirect_referer_or
from lostplaces.views.base_views import (
PlaceAssetCreateView,
@@ -24,7 +23,7 @@ from lostplaces.views.base_views import (
from taggit.models import Tag
class SignUpView(SuccessMessageMixin, CreateView):
form_class = SignupVoucherForm
form_class = ExplorerCreationForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
success_message = _('User created')
@@ -52,13 +51,13 @@ class PhotoAlbumCreateView(PlaceAssetCreateView):
model = PhotoAlbum
fields = ['url', 'label']
template_name = 'photo_album/photo_album_create.html'
success_message = _('Photo album link submitted')
success_message = _('Photo Album submitted')
class PhotoAlbumDeleteView(PlaceAssetDeleteView):
model = PhotoAlbum
pk_url_kwarg = 'pk'
success_message = _('Photo album link deleted')
permission_denied_messsage = _('You are not allowed to edit this photo album link')
success_message = _('Photo Album deleted')
permission_denied_messsage = _('You do not have permissions to alter this photo album')
class PlaceTagSubmitView(IsAuthenticatedMixin, View):
def post(self, request, tagged_id, *args, **kwargs):
@@ -80,22 +79,10 @@ class PlaceTagDeleteView(IsAuthenticatedMixin, View):
place = get_object_or_404(Place, pk=tagged_id)
tag = get_object_or_404(Tag, pk=tag_id)
place.tags.remove(tag)
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': tagged_id}))
return redirect(reverse_lazy('place_detail', kwargs={'pk': tagged_id}))
def FlatView(request, slug):
if request.LANGUAGE_CODE == 'de':
return render(request, 'flat/' + slug + '-de' + '.html')
else:
return render(request, 'flat/' + slug + '.html')
class OSMMapView(IsAuthenticatedMixin, View):
def get(self, request):
place_list = Place.objects.all()
context = {
'mapping_config': {
'all_points': place_list,
'map_center': Place.average_latlon(place_list)
}
}
return render(request, 'osm_map_full.html', context)

View File

@@ -0,0 +1,8 @@
# Config options for coverage
# Docs: https://coverage.readthedocs.io/en/latest/config.html
[coverage:run]
source = .
[coverage:report]
show_missing = True

9
django_lostplaces/setup.py → setup.py Executable file → Normal file
View File

@@ -1,9 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages
with open('../Readme.md') as f:
with open('Readme.md') as f:
readme = f.read()
setup(
@@ -13,7 +13,7 @@ setup(
author='Reverend',
author_email='reverend@reverend2048.de',
url='https://git.mowoe.com/reverend/lostplaces-backend',
packages=find_packages(exclude=['django_lostplaces']),
packages=find_packages(exclude=['lostplaces']),
long_description=readme,
long_description_content_type='text/markdown',
classifiers=[
@@ -31,6 +31,5 @@ setup(
'django-widget-tweaks',
'django-taggit'
],
include_package_data=True,
license='MIT'
include_package_data=True
)