102 Commits

Author SHA1 Message Date
6b90ca39eb Converted indentation to spaces globally. 2020-08-17 15:38:52 +02:00
c1007fb7bc Added migrations directory to .gitignore. 2020-08-17 12:36:27 +02:00
aa4c77e675 Deleted old migrations. 2020-08-17 12:30:45 +02:00
7a378c26d0 Unified first/last - next/prev behaviour. Fixed previous button. 2020-08-17 12:03:30 +02:00
d58d118953 New Markup for Pagination 2020-08-17 08:45:12 +02:00
4ea8d16253 Fixed pagiatation 2020-08-16 16:29:04 +02:00
9f29ea603f Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-16 11:56:32 +02:00
60a9f61358 css 2020-08-16 11:56:30 +02:00
c2b9b494cc Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-16 11:56:18 +02:00
99fbe827c9 Changed pagination template to show disabled nav elements. 2020-08-16 11:56:13 +02:00
6f1598e978 Fresh css 2020-08-16 11:42:41 +02:00
7a432921d1 Sidebar animation is not triggered when loading page 2020-08-16 11:41:03 +02:00
97a5052ad8 Responsive Menu 2020-08-16 10:34:56 +02:00
42d1137dfb Fixed icon absolute path error. 2020-08-14 15:52:17 +02:00
e2152662cf Updated main css from frontend repo. Vertical pagination alignment. 2020-08-14 13:58:21 +02:00
9f33b994dd Renamed object instance to mark current page css class. 2020-08-14 10:58:31 +02:00
70f2137af5 Removed HTML entities, linked first/last css classes. 2020-08-14 10:13:44 +02:00
b5147ee59b Moved pagination to partials and included it in place_list. 2020-08-14 09:45:00 +02:00
df2aaf646a We need some ci for this sheet 2020-08-13 23:46:06 +02:00
62a53c98e6 Template and css tweaks 2020-08-13 23:42:43 +02:00
9fad8e8baa Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-13 22:52:45 +02:00
01ab8a304d CSs 2020-08-13 22:52:32 +02:00
86087c2b8d Fixed stupid syntax errors. 2020-08-13 20:30:43 +02:00
f616b713d4 Minor pagination themplate fixes. Needs unified decorations. 2020-08-13 12:54:59 +02:00
6482d622b2 CSS 2020-08-13 11:18:29 +02:00
faa4ed52ef Hackity hickity 2020-08-13 11:18:25 +02:00
9f788ab44c Added sweet navigation html entities. 2020-08-12 23:28:06 +02:00
f27fc71a01 Added pagination in PlaceList template. 2020-08-12 23:20:32 +02:00
2f1f356eb0 Fixed indentation. 2020-08-12 22:20:31 +02:00
adc0fd4d5e Changed PlaceList description to not cus description < threshold. 2020-08-12 22:15:44 +02:00
8889314d9e Changed sorting to newest first in PlaceGrid. 2020-08-12 22:00:45 +02:00
1a70c1437e Converted PlaceList into a ListView and added pagination. 2020-08-12 21:53:49 +02:00
c148cc5f10 New css 2020-08-12 21:07:24 +02:00
aa48af7c91 DeleteView success message 2020-08-12 21:07:13 +02:00
564d20c73e Added success message to deleteview. 2020-08-12 20:56:37 +02:00
0b3aff1d1d Simplified UpdatePlace success message using Mixin. 2020-08-12 20:52:17 +02:00
0cf482dc5c Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 20:44:13 +02:00
275bec6560 Success message for place update. 2020-08-12 20:44:08 +02:00
852def01d2 Added success and error messages to CreatePlaceView. 2020-08-12 20:35:49 +02:00
1eed5a8283 Throwing message when creating a user 2020-08-12 20:33:09 +02:00
4ebc8f93f7 Fancy messages 2020-08-12 20:24:42 +02:00
1130ee70d9 Added missing self to get method. 2020-08-12 20:11:25 +02:00
759c42279d Converted last function-based views into class-based-views. 2020-08-12 19:24:04 +02:00
87efccf6c9 Finally removed "hello_world". 2020-08-12 19:14:06 +02:00
a82ddaa44e Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 19:07:12 +02:00
78f087fb3c Moved check for submitter to own view, included it in update place. 2020-08-12 19:07:07 +02:00
1d62b20a3c More markup 2020-08-12 19:06:19 +02:00
9f3ed46b35 403 Autoredirect 2020-08-12 19:04:45 +02:00
b6b17f4caf Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 18:59:27 +02:00
c0191fc6c4 Added LoginRequiredMixin to check for authenticated user. 2020-08-12 18:59:21 +02:00
10d96c7c8f Using referrer in 403 2020-08-12 18:56:09 +02:00
0074c73562 Custom 403 2020-08-12 18:52:04 +02:00
1e082dcb25 Amind site link 2020-08-12 18:51:57 +02:00
d554355f9c User management forms 2020-08-12 18:32:14 +02:00
5c9a52b01c Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 18:32:11 +02:00
7bcb08a4ae More German removed. 2020-08-12 18:32:07 +02:00
5009b778f7 More buttons 2020-08-12 18:30:30 +02:00
f9fd9d68a7 More Form frickelei 2020-08-12 18:27:51 +02:00
cbcfda3726 Removed another German title string. 2020-08-12 18:16:32 +02:00
0620ac2325 Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 18:13:45 +02:00
c39bbef47c Removed german string, reodered comment. 2020-08-12 18:13:41 +02:00
bc49b305ff Delte Form hybsched 2020-08-12 18:12:12 +02:00
764d0f32be Fresh baked css 2020-08-12 18:11:57 +02:00
9dfd389a5d Merge branch 'master' of mowoe.com:reverend/lostplaces-backend 2020-08-12 17:51:24 +02:00
22e2657367 New css 2020-08-12 17:51:21 +02:00
aff5babb20 Added UserTestMixin to PlaceDeleteView. 2020-08-12 17:18:31 +02:00
5bc92a5841 Default login url 2020-08-12 16:52:03 +02:00
d5de4e0440 Rendering of messages, ugly, but its there 2020-08-12 16:51:55 +02:00
8f340f6567 Cancel buttons type="button". 2020-08-12 16:19:52 +02:00
118c48cd66 More markup errors. 2020-08-12 16:16:52 +02:00
70ee70b50f Markup Errro 2020-08-12 15:51:17 +02:00
bc484b9ff9 More Cancel-Buttons. (create_place, update_place). 2020-08-12 12:44:14 +02:00
313748f5ac Munch simpler solution using link-button. 2020-08-12 12:38:50 +02:00
8b01bbb2ce Added named cancel button that redirects to success_url. 2020-08-12 11:28:11 +02:00
147c0e8dfe Removed obsoleted checkbox form. 2020-08-11 10:34:39 +02:00
86d7212060 Adjusted templates 2020-08-10 20:10:22 +02:00
30e6c6fda6 static menu items 2020-08-10 20:01:03 +02:00
98a3c9f22c Unified template names 2020-08-10 20:00:54 +02:00
675cdc5721 Adjusted delete view 2020-08-10 19:54:17 +02:00
121b99730d Added Place edit/delete view 2020-08-10 19:36:42 +02:00
fb8e7a357b Updated icon path 2020-08-10 19:14:38 +02:00
f9cb21dc8e Adjusted template 2020-08-10 19:10:46 +02:00
06b7686264 Adjusted template 2020-08-10 19:06:45 +02:00
34df74e42a Current CSS 2020-08-09 23:56:32 +02:00
f77650b54d Hide hero image div if none present. 2020-08-09 13:48:45 +02:00
68db5fbc7a Fixed missing for scope 2020-08-09 11:40:23 +02:00
89060f4ce0 Removed debug output. 2020-08-07 12:27:54 +02:00
602a8bb1e6 Got Place object in get request. 2020-08-07 12:27:35 +02:00
fe97e02e41 Doesn't do shit. 2020-08-06 22:30:52 +02:00
15a74f1f31 Added basic checkbox form and delete template. 2020-08-06 22:29:03 +02:00
19847d0e1f Fixed typos and install / prod warning. 2020-08-06 20:34:46 +02:00
5a9e86d3c0 Transated to English. 2020-08-06 19:51:45 +02:00
172e462d1d Dyfunct, but non-breaking WIP commit. :P 2020-08-06 16:57:43 +02:00
565c58019b Limited length of name / location in placegrid. Added padding. 2020-08-05 20:44:31 +02:00
e63601955e Added max-height to hero image css. 2020-08-05 20:16:09 +02:00
c2d08f637f Removed obsoleted fonts.css. 2020-08-05 20:03:21 +02:00
805363eac5 Trying to move navigation into sidebar. 2020-08-05 19:54:13 +02:00
f9851071ed css 2020-08-05 19:52:00 +02:00
985ab2ed75 Unified indentation using 4 spacec according to PEP8. 2020-08-05 18:57:09 +02:00
2b3fa6f216 Updated readme, according to thumbnail plugin change. 2020-08-05 18:29:03 +02:00
5d500100e5 Formatting. 2020-08-05 18:23:11 +02:00
b7fd19e5ef Removed unused fonts. 2020-08-05 18:19:52 +02:00
61 changed files with 1049 additions and 790 deletions

4
.gitignore vendored
View File

@@ -66,6 +66,10 @@ coverage.xml
*.mo
# Django stuff:
# exclude migrations from repository. These should be created locally, matching local DB requirements.
# lostplaces/manage.py makemigrations && lostplaces/manage.py migrate
lostplaces/lostplaces_app/migrations/
# pyenv
.python-version

View File

@@ -11,6 +11,7 @@ django = "*"
easy-thumbnails = "*"
image = "*"
django-widget-tweaks = "*"
# Commented out to not explicitly specify Python3 subversion.
# Commented out to not explicitly specify Python 3 subversion.
# [requires]
# python_version = "3.8"

View File

@@ -1,14 +1,14 @@
# lostplaces-backend
lostplaces-backend is a django based Webproject. It once wants to become a software which allows a group of urban explorers to manage, document and share the locations of lost places while not exposing too much / any information to the public.
lostplaces-backend is a django (3.x) based webproject. It once wants to become a software which allows a group of urban explorers to manage, document and share the locations of lost places while not exposing too much / any information to the public.
The software ist currently in early development status, neither scope, datalmodel(s) nor features are finalized yet.
The software is currently in early development status, neither scope, datalmodel(s) nor features are finalized yet. Therefore we would not recommend to download or install this piece of software anywhere - except your local django dev server.
## Dependencies
Right now it depends on the following non-core Python 3 libraries. These can be installed using the package manager of your distribution or into the venv locally.
* [django](https://www.djangoproject.com/) django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
* [django-thumbs-v2](https://github.com/rrmerugu/django-thumbs-v2) Create thumbnails for your images with django.
* [easy-thumbnails](https://github.com/SmileyChris/easy-thumbnails) A powerful, yet easy to implement thumbnailing application for Django 1.11+
### Setting up a (pipenv) virtual environment for development

View File

@@ -14,6 +14,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
from django.urls import reverse_lazy
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -42,7 +43,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'easy_thumbnails',
'widget_tweaks',
'widget_tweaks',
]
MIDDLEWARE = [
@@ -138,10 +139,12 @@ AUTH_USER_MODEL = 'lostplaces_app.Explorer'
LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'home'
LOGIN_URL = reverse_lazy('login')
THUMBNAIL_ALIASES = {
'': {
'thumbnail': {'size': (300, 300), 'crop': False},
'hero': {'size': (700, 700), 'crop': False},
'large': {'size': (1920, 1920), 'crop': False},
'hero': {'size': (700, 700), 'crop': False},
'large': {'size': (1920, 1920), 'crop': False},
},
}

View File

@@ -8,43 +8,43 @@ from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import Explorer, Place, PlaceImage, Voucher
class ExplorerCreationForm(UserCreationForm):
class Meta:
model = Explorer
fields = ('username', 'email')
voucher = forms.CharField(max_length=10, help_text='The Voucher you got from an administrator')
class Meta:
model = Explorer
fields = ('username', 'email')
voucher = forms.CharField(max_length=10, help_text='The Voucher you got from an administrator')
def is_valid(self):
super().is_valid()
sumitted_voucher = self.cleaned_data.get('voucher')
try:
fetched_voucher = Voucher.objects.get(code=sumitted_voucher)
except Voucher.DoesNotExist:
self.add_error('voucher', 'Invalid voucher')
return False
def is_valid(self):
super().is_valid()
sumitted_voucher = self.cleaned_data.get('voucher')
try:
fetched_voucher = Voucher.objects.get(code=sumitted_voucher)
except Voucher.DoesNotExist:
self.add_error('voucher', 'Invalid voucher')
return False
fetched_voucher.delete()
return True
fetched_voucher.delete()
return True
class ExplorerChangeForm(UserChangeForm):
class Meta:
model = Explorer
fields = ('username', 'email')
class Meta:
model = Explorer
fields = ('username', 'email')
class PlaceForm(forms.ModelForm):
class Meta:
model = Place
fields = '__all__'
exclude = ['submitted_by']
class Meta:
model = Place
fields = '__all__'
exclude = ['submitted_by']
class PlaceImageCreateForm(forms.ModelForm):
class Meta:
model = PlaceImage
fields = ['filename']
widgets = {
'filename': forms.ClearableFileInput(attrs={'multiple': True})
}
class Meta:
model = PlaceImage
fields = ['filename']
widgets = {
'filename': forms.ClearableFileInput(attrs={'multiple': True})
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['filename'].required = False
self.fields['filename'].required = False

View File

@@ -1,67 +0,0 @@
# Generated by Django 3.0.8 on 2020-07-28 19:00
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import django_thumbs.fields
import lostplaces_app.models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
name='Place',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('location', models.CharField(max_length=50)),
('latitude', models.FloatField()),
('longitude', models.FloatField()),
('description', models.TextField()),
],
),
migrations.CreateModel(
name='PlaceImage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('filename', django_thumbs.fields.ImageThumbsField(max_length=50, sizes=({'code': 'thumbnail', 'wxh': '390x390'}, {'code': 'hero', 'wxh': '700x700'}, {'code': 'large', 'wxh': '1920x1920'}), upload_to=lostplaces_app.models.generate_image_upload_path)),
('description', models.TextField(blank=True)),
('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='lostplaces_app.Place')),
],
),
migrations.CreateModel(
name='Explorer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@@ -1,20 +0,0 @@
# Generated by Django 3.0.8 on 2020-07-29 16:29
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='place',
name='submitted_by',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -1,20 +0,0 @@
# Generated by Django 3.0.8 on 2020-07-29 18:22
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0002_place_submitted_by'),
]
operations = [
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=settings.AUTH_USER_MODEL),
),
]

View File

@@ -1,20 +0,0 @@
# Generated by Django 3.0.8 on 2020-07-30 09:18
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0003_auto_20200729_1822'),
]
operations = [
migrations.AddField(
model_name='placeimage',
name='submitted_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='images', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -1,22 +0,0 @@
# Generated by Django 3.0.8 on 2020-08-01 10:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0004_placeimage_submitted_by'),
]
operations = [
migrations.CreateModel(
name='Voucher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=10)),
('created', models.DateField(auto_now_add=True)),
('expires', models.DateField()),
],
),
]

View File

@@ -1,22 +0,0 @@
# Generated by Django 3.0.8 on 2020-08-01 10:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0005_voucher'),
]
operations = [
migrations.RemoveField(
model_name='voucher',
name='id',
),
migrations.AlterField(
model_name='voucher',
name='code',
field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.0.8 on 2020-08-01 10:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0006_auto_20200801_1037'),
]
operations = [
migrations.AlterField(
model_name='voucher',
name='created',
field=models.DateTimeField(auto_now_add=True),
),
]

View File

@@ -1,24 +0,0 @@
# Generated by Django 3.0.8 on 2020-08-01 10:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0007_auto_20200801_1039'),
]
operations = [
migrations.AddField(
model_name='voucher',
name='id',
field=models.AutoField(auto_created=True, default=0, primary_key=True, serialize=False, verbose_name='ID'),
preserve_default=False,
),
migrations.AlterField(
model_name='voucher',
name='code',
field=models.CharField(max_length=10, unique=True),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.0.9 on 2020-08-03 16:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0008_auto_20200801_1044'),
]
operations = [
migrations.AddField(
model_name='place',
name='submitted_when',
field=models.DateTimeField(auto_now_add=True, null=True),
),
]

View File

@@ -1,20 +0,0 @@
# Generated by Django 3.0.9 on 2020-08-03 17:07
from django.db import migrations
import easy_thumbnails.fields
import lostplaces_app.models
class Migration(migrations.Migration):
dependencies = [
('lostplaces_app', '0009_place_submitted_when'),
]
operations = [
migrations.AlterField(
model_name='placeimage',
name='filename',
field=easy_thumbnails.fields.ThumbnailerImageField(upload_to=lostplaces_app.models.generate_image_upload_path),
),
]

View File

@@ -14,115 +14,115 @@ from easy_thumbnails.fields import ThumbnailerImageField
# Create your models here.
class Explorer(AbstractUser):
"""
Custom user model
Addtional fields wbd
"""
"""
Custom user model
Addtional fields wbd
"""
def __str__(self):
return self.username
def __str__(self):
return self.username
class Voucher(models.Model):
"""
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.
"""
"""
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.
"""
code = models.CharField(unique=True, max_length=10)
created = models.DateTimeField(auto_now_add=True)
expires = models.DateField()
code = models.CharField(unique=True, max_length=10)
created = models.DateTimeField(auto_now_add=True)
expires = models.DateField()
def __str__(self):
return "Voucher " + str(self.pk)
def __str__(self):
return "Voucher " + str(self.pk)
class Place (models.Model):
"""
Place defines a lost place (location, name, description etc.).
"""
"""
Place defines a lost place (location, name, description etc.).
"""
name = models.CharField(max_length=50)
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='places'
)
location = models.CharField(max_length=50)
latitude = models.FloatField()
longitude = models.FloatField()
description = models.TextField()
name = models.CharField(max_length=50)
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='places'
)
location = models.CharField(max_length=50)
latitude = models.FloatField()
longitude = models.FloatField()
description = models.TextField()
def __str__(self):
return self.name
def __str__(self):
return self.name
def generate_image_upload_path(instance, filename):
"""
Callback for generating path for uploaded images.
"""
"""
Callback for generating path for uploaded images.
"""
return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1]
return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1]
class PlaceImage (models.Model):
"""
PlaceImage defines an image file object that points to a file in uploads/.
Intermediate image sizes are generated as defined in SIZES.
PlaceImage references a Place to which it belongs.
"""
description = models.TextField(blank=True)
filename = ThumbnailerImageField(upload_to=generate_image_upload_path)
place = models.ForeignKey(
Place,
on_delete=models.CASCADE,
related_name='images'
)
submitted_by = models.ForeignKey(
Explorer,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='images'
)
"""
PlaceImage defines an image file object that points to a file in uploads/.
Intermediate image sizes are generated as defined in SIZES.
PlaceImage references a Place to which it belongs.
"""
description = models.TextField(blank=True)
filename = ThumbnailerImageField(upload_to=generate_image_upload_path)
place = models.ForeignKey(
Place,
on_delete=models.CASCADE,
related_name='images'
)
submitted_by = models.ForeignKey(
Explorer,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='images'
)
def __str__(self):
"""
Returning the name of the corresponding place + id
of this image as textual represntation of this instance
"""
def __str__(self):
"""
Returning the name of the corresponding place + id
of this image as textual represntation of this instance
"""
return ' '.join([self.place.name, str(self.pk)])
return ' '.join([self.place.name, str(self.pk)])
# These two auto-delete files from filesystem when they are unneeded:
@receiver(models.signals.post_delete, sender=PlaceImage)
def auto_delete_file_on_delete(sender, instance, **kwargs):
"""
Deletes file from filesystem
when corresponding `PlaceImage` object is deleted.
"""
if instance.filename:
if os.path.isfile(instance.filename.path):
os.remove(instance.filename.path)
"""
Deletes file from filesystem
when corresponding `PlaceImage` object is deleted.
"""
if instance.filename:
if os.path.isfile(instance.filename.path):
os.remove(instance.filename.path)
@receiver(models.signals.pre_save, sender=PlaceImage)
def auto_delete_file_on_change(sender, instance, **kwargs):
"""
Deletes old file from filesystem
when corresponding `PlaceImage` object is updated
with new file.
"""
if not instance.pk:
return False
"""
Deletes old file from filesystem
when corresponding `PlaceImage` object is updated
with new file.
"""
if not instance.pk:
return False
try:
old_file = PlaceImage.objects.get(pk=instance.pk).filename
except PlaceImage.DoesNotExist:
return False
try:
old_file = PlaceImage.objects.get(pk=instance.pk).filename
except PlaceImage.DoesNotExist:
return False
new_file = instance.filename
if not old_file == new_file:
if os.path.isfile(old_file.path):
os.remove(old_file.path)
new_file = instance.filename
if not old_file == new_file:
if os.path.isfile(old_file.path):
os.remove(old_file.path)

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="30px" height="30px"><path d="M 3 7 A 1.0001 1.0001 0 1 0 3 9 L 27 9 A 1.0001 1.0001 0 1 0 27 7 L 3 7 z M 3 14 A 1.0001 1.0001 0 1 0 3 16 L 27 16 A 1.0001 1.0001 0 1 0 27 14 L 3 14 z M 3 21 A 1.0001 1.0001 0 1 0 3 23 L 27 23 A 1.0001 1.0001 0 1 0 27 21 L 3 21 z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@@ -0,0 +1 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 25 2 C 12.309295 2 2 12.309295 2 25 C 2 37.690705 12.309295 48 25 48 C 37.690705 48 48 37.690705 48 25 C 48 12.309295 37.690705 2 25 2 z M 25 4 C 36.609824 4 46 13.390176 46 25 C 46 36.609824 36.609824 46 25 46 C 13.390176 46 4 36.609824 4 25 C 4 13.390176 13.390176 4 25 4 z M 25 11 A 3 3 0 0 0 22 14 A 3 3 0 0 0 25 17 A 3 3 0 0 0 28 14 A 3 3 0 0 0 25 11 z M 21 21 L 21 23 L 22 23 L 23 23 L 23 36 L 22 36 L 21 36 L 21 38 L 22 38 L 23 38 L 27 38 L 28 38 L 29 38 L 29 36 L 28 36 L 27 36 L 27 21 L 26 21 L 22 21 L 21 21 z"/></svg>

After

Width:  |  Height:  |  Size: 641 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><defs><style>.cls-1{fill:#231f20;}</style></defs><title>Wondicon - UI (Free)</title><path class="cls-1" d="M125,177.86a5,5,0,0,1-4.74-3.42L96,101.76,27.64,77.57a5,5,0,0,1,.2-9.49L169.22,24.53a5,5,0,0,1,6.23,6.3L129.76,174.37a5,5,0,0,1-4.73,3.49ZM45.23,73.19l56.44,20a5,5,0,0,1,3.07,3.13l20.15,60.44L163,36.9Z"/><path class="cls-1" d="M100,102.84a4.94,4.94,0,0,1-3.59-1.52,5,5,0,0,1,.11-7.07L131.86,60a5,5,0,0,1,7,7.18l-35.35,34.26A5,5,0,0,1,100,102.84Z"/><path class="cls-1" d="M75,175.69a5,5,0,0,1-3.19-8.85l25-20.69a5,5,0,0,1,6.38,7.7l-25,20.69A5,5,0,0,1,75,175.69Z"/><path class="cls-1" d="M29.31,175.69a5,5,0,0,1-3.53-8.54l45.68-45.69a5,5,0,0,1,7.08,7.08L32.85,174.22A5,5,0,0,1,29.31,175.69Z"/><path class="cls-1" d="M29.31,130a5,5,0,0,1-3.85-8.19l20.69-25a5,5,0,0,1,7.7,6.38l-20.69,25A5,5,0,0,1,29.31,130Z"/></svg>

After

Width:  |  Height:  |  Size: 914 B

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" ?><svg style="enable-background:new 0 0 24 24;" version="1.1" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style type="text/css">
.st0{display:none;}
</style><g class="st0" id="grid"/><g id="icon"><path d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M12,23C5.935,23,1,18.065,1,12S5.935,1,12,1 s11,4.935,11,11S18.065,23,12,23z"/><path d="M16.801,8.403l-6.132,6.133L7.753,11.62c-0.195-0.195-0.512-0.195-0.707,0s-0.195,0.512,0,0.707l3.27,3.27 c0.094,0.094,0.221,0.146,0.354,0.146s0.26-0.053,0.354-0.146l6.485-6.486c0.195-0.195,0.195-0.512,0-0.707 S16.997,8.208,16.801,8.403z"/></g></svg>

After

Width:  |  Height:  |  Size: 714 B

View File

@@ -13,7 +13,85 @@ html {
body {
height: 100%;
margin: 0; }
margin: 0;
padding: 0; }
.LP-Wrapper__Site {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr;
grid-template-areas: "header header" "sidebar content";
margin: 0;
padding: 0;
min-height: 100vh; }
.LP-Main__Content {
min-height: 1px;
padding: 25px;
grid-area: content; }
.LP-Main__Sidebar {
grid-area: sidebar;
background-color: #f9f9f9; }
.LP-Section {
clear: both;
padding: 25px 0px;
padding-left: 25px; }
.LP-Section .LP-Headline {
margin-left: -25px; }
.LP-Menu__Trigger {
display: none; }
@media (max-width: 1000px) {
.LP-Wrapper__Site {
grid-template-columns: 187.5px 1fr; } }
@media (max-width: 650px) {
.LP-Section {
padding-left: 0;
padding-right: 0; }
.LP-Section .LP-Headline {
margin: 0; } }
@media (max-width: 450px) {
.LP-Main__Sidebar {
max-width: 100vw; } }
@media (max-width: 650px) {
.LP-Wrapper__Site {
grid-template-columns: 0 1fr; }
.LP-Main__Sidebar {
grid-area: unset;
width: 250px;
z-index: 15;
position: fixed;
left: -251px;
height: 100vh;
top: 60px;
border-right: 1px solid #C09F80;
transition: left 0.3s; }
.LP-Main__Sidebar--hidden {
visibility: hidden; }
#toggle_sidebar:checked ~ .LP-Main__Sidebar {
left: 0; }
#toggle_sidebar ~ .LP-Main__Sidebar {
left: -251px; }
.LP-Menu__TriggerLabel {
z-index: 20;
height: 60px;
width: 60px;
background-image: url("icons/hamburger_menu.svg");
background-repeat: no-repeat;
background-clip: content-box;
background-position: center;
position: fixed; }
.LP-Menu__Trigger:checked ~ .LP-Menu__TriggerLabel {
background-color: darkgray;
filter: invert(1); }
.LP-Main__Content {
margin-top: 60px; } }
.LP-Link {
color: #565656;
@@ -36,7 +114,7 @@ body {
padding-top: 0px;
margin-top: 0px;
padding-bottom: 0px;
margin-bottom: 0px; }
margin-bottom: 25px; }
.LP-Headline--main {
position: relative;
top: 2rem;
@@ -47,7 +125,9 @@ body {
.LP-Paragraph {
color: black;
font-family: Crimson, Times, serif;
font-size: 1.2rem; }
font-size: 1.2rem;
padding: 0;
margin: 0; }
.LP-Icon {
height: 20px;
@@ -68,12 +148,17 @@ body {
border: none;
padding: 8px 14px;
border-radius: 2px;
font-weight: bold; }
font-weight: bold;
cursor: pointer; }
.LP-Button:active {
background-color: #76323F;
color: #f9f9f9; }
.LP-Button--cancel {
background-color: #f9f9f9; }
background-color: #565656;
color: #f9f9f9; }
.LP-Button--cancel:active {
color: #565656;
background-color: #f9f9f9; }
.LP-Form .LP-Form__Checkbox {
display: none; }
@@ -101,6 +186,17 @@ body {
background-color: #f9f9f9;
border-radius: 3px 3px 0 0;
box-shadow: none; }
.LP-Input .LP-Input__Field[type=submit] {
background-color: #C09F80;
color: #565656;
border: none;
padding: 8px 14px;
border-radius: 2px;
font-weight: bold;
cursor: pointer; }
.LP-Input .LP-Input__Field[type=submit]:active {
background-color: #76323F;
color: #f9f9f9; }
.LP-Input .LP-Input__Label {
font-family: Montserrat, Helvetica, sans-serif;
font-size: 16px; }
@@ -135,7 +231,9 @@ body {
object-fit: cover;
width: 100%;
height: auto;
vertical-align: top; }
vertical-align: top;
margin: 0;
padding: 0; }
.LP-Logo {
max-width: 100%;
@@ -155,6 +253,103 @@ body {
font-size: 1em;
display: inline; }
.LP-Message {
display: flex;
flex-direction: row;
justify-content: center;
align-items: stretch;
background-color: #f9f9f9;
font-family: Montserrat, Helvetica, sans-serif;
border-radius: 3px;
font-weight: bold;
box-shadow: 0 0 2px #565656;
overflow: hidden; }
.LP-Message--error .LP-Message__Icon {
background-color: #02979e;
background-image: url("icons/error.png"); }
.LP-Message--warning .LP-Message__Icon {
background-color: #0047e7;
background-image: url("icons/error.png"); }
.LP-Message--info .LP-Message__Icon {
background-color: #522719;
background-image: url("icons/information.svg"); }
.LP-Message--success .LP-Message__Icon {
background-color: #6937ff;
background-image: url("icons/success.svg"); }
.LP-Message--debug .LP-Message__Icon {
background-color: #046a2f;
background-image: url("icons/debug.png"); }
.LP-Message .LP-Message__Icon {
background-size: 40px 40px;
background-repeat: no-repeat;
background-position: center;
height: 100%;
min-height: 50px;
width: 50px;
filter: invert(1);
flex-shrink: 0;
flex-grow: 0; }
.LP-Message .LP-Message__Text {
padding: 0 15px;
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: 8px; }
.LP-Pagination {
font-family: Montserrat, Helvetica, sans-serif;
font-weight: bold;
display: flex;
flex-direction: row;
justify-content: center; }
.LP-Pagination .LP-Pagination__List {
list-style-type: none;
display: flex;
flex-direction: row;
padding-left: 0;
padding-right: 0; }
.LP-Pagination .LP-Pagination__Item {
margin: 0 4px; }
.LP-Pagination .LP-Pagination__Item--disabled {
color: #b6b6b6; }
.LP-Pagination .LP-Pagination__Item--disabled .LP-Link {
color: #b6b6b6;
cursor: default; }
.LP-Pagination .LP-Pagination__Item--disabled .LP-Link:hover {
background-color: unset;
color: unset; }
.LP-Pagination .LP-Pagination__Item--current .LP-Link {
background-color: #D7CEC7; }
.LP-Pagination .LP-Pagination__Item--current .LP-Link:hover {
background-color: #D7CEC7;
color: #565656; }
.LP-Pagination .LP-Link {
padding: 15px 20px;
vertical-align: sub;
border-radius: 2px; }
.LP-Pagination .LP-Link:active, .LP-Pagination .LP-Link:hover {
background-color: #D7CEC7;
color: #565656; }
.LP-Pagination .LP-Icon {
font-size: larger; }
@media (max-width: 1000px) {
.LP-Pagination .LP-Link {
padding: 10px 15px; }
.LP-Pagination .LP-Pagination__Item--other .LP-Text {
display: none; } }
@media (max-width: 650px) {
.LP-Pagination .LP-Pagination__Item {
margin: 0 1px; }
.LP-Pagination .LP-Pagination__Item .LP-Link {
padding: 13px 16px; } }
@media (max-width: 450px) {
.LP-Pagination .LP-Pagination__Item .LP-Link {
padding: 8px 11px; } }
.LP-Content {
padding: 35px; }
@@ -179,7 +374,9 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 10px; }
padding: 5px;
padding-bottom: 10px;
padding-top: 10px; }
.LP-PlaceTeaser .LP-PlaceTeaser__Meta .LP-Paragraph {
font-family: Montserrat, Helvetica, sans-serif;
padding: 0;
@@ -265,7 +462,6 @@ body {
border-left: none;
min-width: 60px;
background-color: #f9f9f9;
height: 100%;
padding-top: 25px; }
.LP-Menu--sidebar .LP-Menu__List {
flex-direction: column; }
@@ -273,6 +469,8 @@ body {
text-align: left;
margin-bottom: 10px;
padding-left: 25px; }
.LP-Menu--sidebar .LP-Menu__List .LP-Menu__Item--additional {
background-color: #ccc; }
.LP-Menu--sidebar .LP-Menu__List .LP-Menu__Item .LP-Link {
line-height: 1em; }
.LP-Menu--sidebar .LP-Menu__List .LP-Menu__Item:last-child {
@@ -280,7 +478,8 @@ body {
.LP-Menu--sidebar .LP-Menu__List .LP-Menu__Item:hover {
border-right: 2px solid #C09F80;
position: relative;
background-color: #D7CEC7; }
background-color: #D7CEC7;
color: #76323F; }
@media (max-width: 750px) {
.LP-Menu:not(.LP-Menu--sidebar) .LP-Menu__List {
@@ -294,17 +493,31 @@ body {
.LP-Menu .LP-Menu__List {
justify-content: space-between; } }
.LP-MessageList {
padding: 25px; }
.LP-MessageList .LP-MessageList__List {
padding: 0;
margin: 0;
list-style-type: none;
display: flex;
flex-direction: column;
justify-content: space-between; }
.LP-MessageList .LP-MessageList__Item {
margin: 5px 0; }
.LP-Header {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
box-shadow: 0 0 2px #C09F80; }
box-shadow: 0 0 2px #C09F80;
grid-area: header;
background-color: white;
padding-left: 25px; }
.LP-Header__Navigation {
flex-grow: 2; }
.LP-Header__Logo {
height: 45px;
margin: 25px;
object-fit: cover;
max-height: 100%;
width: 225px;
@@ -335,6 +548,13 @@ body {
.LP-Header__Navigation {
width: 100%; } }
@media (max-width: 650px) {
.LP-Header {
padding-left: 60px;
width: calc(100% - 60px);
position: fixed;
z-index: 10; } }
.LP-PlaceGrid .LP-PlaceGrid__Grid {
margin: 0;
padding: 0;
@@ -358,9 +578,11 @@ body {
border-left: 2px #565656 solid; }
.LP-PlaceList .LP-PlaceList__List .LP-PlaceList__Item {
max-width: 900px;
min-width: 450px;
margin: 18px 0; }
.LP-PlaceList .LP-Pagination {
margin-top: 50px; }
.LP-LinkList__List {
list-style-type: none;
display: grid;
@@ -369,7 +591,7 @@ body {
padding: 0; }
.LP-LinkList__List .LP-LinkList__Item {
border-left: 1px solid #C09F80;
width: 100%;
width: calc(100% - 1px);
margin-top: 12px; }
.LP-LinkList__List .LP-LinkList__Item .LP-Link {
padding: 1em 0 1em 1em;
@@ -421,28 +643,49 @@ body {
.LP-Footer .LP-LinkList__List .LP-LinkList__Item .LP-Link:hover {
background-color: inherit; }
.LP-Form .LP-Form__Fieldset {
border: none; }
.LP-Form .LP-Form__Fieldset .LP-Form__Legend {
margin: 0;
padding: 0;
font-family: Montserrat, Helvetica, sans-serif;
font-size: 21px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition {
display: flex;
flex-direction: row;
justify-content: space-between; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field {
flex: 3 1 100px;
padding: 6px 15px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--wider {
flex: 5 1; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--wide {
flex: 4 1; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--narrow {
flex: 2 0; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--narrower {
flex: 1 0; }
.LP-Form {
display: flex;
flex-direction: column;
align-items: center; }
.LP-Form .LP-Form__Fieldset {
border: none;
max-width: 1200px;
min-width: 750px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Legend {
margin: 0;
padding: 0;
font-family: Montserrat, Helvetica, sans-serif;
font-size: 21px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition {
display: flex;
flex-direction: row;
justify-content: space-between; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field {
flex: 3 2 100px;
padding: 6px 15px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--wider {
flex: 5 2; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--wide {
flex: 4 2; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--narrow {
flex: 2 1; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field--narrower {
flex: 1 2; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition--buttons {
justify-content: flex-end; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Button {
flex-grow: 0;
padding-left: 0;
min-width: 130px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Button .LP-Link {
display: contents; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__InfoText .LP-Paragraph {
font-family: Montserrat, Helvetica, sans-serif;
color: #565656; }
@media (max-width: 750px) {
.LP-Form .LP-Form__Fieldset {
min-width: unset; } }
@media (max-width: 650px) {
.LP-Form .LP-Form__Fieldset .LP-Form__Composition--breakable {
@@ -455,9 +698,43 @@ body {
display: flex;
flex-direction: column;
justify-content: space-between; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field {
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Field:not(.LP-Form__Button) {
flex: 3 1 100px;
padding: 12px 15px; } }
padding: 12px 15px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition .LP-Form__Button {
padding: 0 15px; }
.LP-Form .LP-Form__Fieldset .LP-Form__Composition--buttons {
justify-content: flex-end; } }
.LP-ImageGrid .LP-ImageGrid__List {
list-style-type: none;
display: grid;
grid-template-columns: repeat(auto-fit, 300px);
align-content: space-around;
justify-content: center;
margin: 0px;
padding: 0px; }
.LP-ImageGrid .LP-ImageGrid__Item {
margin-top: 10px; }
.LP-ImageGrid .LP-Link {
overflow: hidden; }
.LP-ImageGrid .LP-Image {
box-shadow: 0 0 5px #565656;
height: 200px;
width: 290px;
object-fit: cover; }
@media (max-width: 650px) {
.LP-ImageGrid .LP-ImageGrid__List {
grid-template-columns: 1fr; }
.LP-ImageGrid .LP-ImageGrid__List .LP-Image {
box-shadow: 0 0 5px #565656;
height: auto;
width: 100%;
object-fit: cover; } }
.LP-MainContainer {
margin: 0 auto;
@@ -470,61 +747,25 @@ body {
.LP-MainContainer {
width: 100%; } }
.LP-PlaceOverview .LP-PlaceOverview__Info .LP-PlaceOveriew__Image {
.LP-PlaceDetail .LP-PlaceDetail__Image {
width: 700px;
max-height: 500px;
box-shadow: 0 0 10px #565656;
object-fit: cover;
object-position: 0 0;
margin: 0;
padding: 0;
float: right;
margin-left: 35px;
margin-bottom: 35px;
margin-right: 35px;
overflow: hidden; }
.LP-PlaceOverview .LP-PlaceOverview__Info .LP-PlaceOverView__Description {
padding: 0px;
position: relative;
top: -15px; }
.LP-PlaceOverview .LP-PlaceOverview__Info .LP-PlaceOverView__Description .LP-Headline {
position: relative;
top: 15px;
margin-bottom: 30px; }
.LP-PlaceOverview .LP-PlaceOverView__ImageList {
list-style-type: none;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
margin: 0px;
padding: 0px; }
.LP-PlaceOverview .LP-PlaceOverView__ImageList .LP-PlaceOverView__ImageItem {
margin-top: 10px; }
.LP-PlaceOverview .LP-PlaceOverView__ImageList .LP-PlaceOverView__ImageItem .LP-Link {
overflow: hidden; }
.LP-PlaceOverview .LP-PlaceOverView__ImageList .LP-PlaceOverView__ImageItem .LP-Image {
box-shadow: 0 0 5px #565656;
height: 200px;
width: 290px;
object-fit: cover; }
@media (max-width: 1000px) {
.LP-PlaceOverview .LP-PlaceOverview__Info .LP-TextSection {
margin-top: 30px; }
.LP-PlaceOverview .LP-PlaceOverview__Info .LP-PlaceOveriew__Image {
.LP-PlaceDetail .LP-PlaceDetail__Header .LP-PlaceDetail__Image {
float: none;
width: 100%;
height: auto;
margin: 0;
padding: 0; } }
.LP-Main {
display: flex;
flex-direction: row-reverse;
height: calc(100% - 61px); }
.LP-Main .LP-Main__Content {
flex-grow: 1;
width: calc(100% -300px);
min-height: 1px; }
.LP-Main .LP-Main__Sidebar {
flex-shrink: 0;
width: 250px;
flex-grow: 0;
height: 100%; }
padding: 0;
margin-bottom: 25px; } }

View File

@@ -1,48 +0,0 @@
{% extends 'global.html'%}
{% load static %}
# {% block title %}Place erstellen{% endblock %}
{% block maincontent %}
<form class="LP-Form" method="POST" enctype="multipart/form-data">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Place erstellen</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.name %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.location %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.latitude %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.longitude %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.description %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_image_form.filename %}
</div>
</div>
<div class="LP-Form__Composition">
<input type="submit" class="LP-Button" value="Abschicken"/>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -5,50 +5,92 @@
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{% static 'main.css' %}">
<link rel="stylesheet" href="{% static 'fonts.css' %}">
<link rel="icon" type="image/png" href="{% static 'favicon.ico' %}">
<title>
{% block title %}Urban Exploration{% endblock %}
</title>
{% block additional_head %}
{% endblock additional_head %}
<script>
document.addEventListener("DOMContentLoaded", function(){
Array.from(document.getElementsByClassName('LP-Main__Sidebar')).forEach(function(element){
element.classList.add('LP-Main__Sidebar--hidden')
})
setTimeout(function(){
Array.from(document.getElementsByClassName('LP-Main__Sidebar')).forEach(function(element){
element.classList.remove('LP-Main__Sidebar--hidden')
})
}, 500)
})
</script>
</head>
<body>
<header class="LP-Header">
<div class="LP-Header__Logo">
<a class="LP-Link" href="/">
<img src="{% static 'logo.png' %}" class="LP-Image" />
</a>
</div>
<div class="LP-Header__UserInformation">
<span class="LP-Paragraph">
{% if user.is_authenticated %}
Hi {{ user.username }}!
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">logout</span></a>
{% else %}
Du bist nicht eingeloggt.
<a class="LP-Link" href="{% url 'login' %}"><span class="LP-Link__Text">login</span></a> |
<a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">signup</span></a>
{% endif %}
</span>
</div>
<div class="LP-Header__Navigation">
<nav class="LP-Menu LP-Menu">
<div class="LP-Wrapper__Site">
<header class="LP-Header">
<div class="LP-Header__Logo">
<a class="LP-Link" href="/">
<img src="{% static 'logo.png' %}" class="LP-Image" />
</a>
</div>
<div class="LP-Header__UserInformation">
<span class="LP-Paragraph">
{% if user.is_authenticated %}
Hi {{ user.username }}!
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">logout</span></a>
{% if user.is_superuser %}
| <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">admin</span></a>
{% endif %}
{% else %}
You are not logged in.
<a class="LP-Link" href="{% url 'login' %}"><span class="LP-Link__Text">login</span></a> |
<a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">signup</span></a>
{% endif %}
</span>
</div>
</header>
<input id="toggle_sidebar" class="LP-Menu__Trigger" type="checkbox"/>
<label id="toggle_sidebar_label" for="toggle_sidebar" class="LP-Menu__TriggerLabel"></label>
<aside class="LP-Main__Sidebar">
<nav class="LP-Menu LP-Menu--sidebar">
<ul class="LP-Menu__List">
<li class="LP-Menu__Item"><a href="/" class="LP-Link">
<span class="LP-Link__Text">Home</span></a></li>
<li class="LP-Menu__Item"><a href="" class="LP-Link">
<span class="LP-Link__Text">About</span></a></li>
<li class="LP-Menu__Item"><a href="" class="LP-Link">
<span class="LP-Link__Text">Contact</span></a></li>
<li class="LP-Menu__Item"><a href="/" class="LP-Link"><span class="LP-Link__Text">Home</span></a></li>
<li class="LP-Menu__Item"><a href="" class="LP-Link"><span class="LP-Link__Text">About</span></a></li>
<li class="LP-Menu__Item"><a href="" class="LP-Link"><span class="LP-Link__Text">Contact</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">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">See all places</span></a></li>
</ul>
</nav>
</div>
</header>
<article class="LP-MainContainer">
{% block maincontent %}
{% endblock maincontent %}
</article>
</aside>
<main class="LP-Main__Content">
{% if messages %}
<div class="LP-MessageList">
<ul class="LP-MessageList__List">
{% for message in messages %}
<li class="LP-MessageList__Item">
<div class="LP-Message {% if message.tags %}LP-Message--{{ message.tags }}{% endif %}">
<div>
<div class="LP-Message__Icon">
</div>
</div>
<div class="LP-Message__Text">
{{ message }}
</div>
</div>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% block maincontent %}
{% endblock maincontent %}
</main>
</div>
</body>
</html>

View File

@@ -8,7 +8,7 @@
<div class="LP-PlaceGrid">
<h1 class="LP-Headline LP-Headline">Explore the latest locations</h1>
<ul class="LP-PlaceGrid__Grid">
{% for place in place_list %}
{% for place in place_list %}
<li class="LP-PlaceGrid__Item">
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<article class="LP-PlaceTeaser">
@@ -18,15 +18,12 @@
<div class="LP-PlaceTeaser__Meta">
<div class="LP-PlaceTeaser__Info">
<span class="LP-PlaceTeaser__Title">
<h1 class="LP-Headline LP-Headline--teaser">{{place.name}}</h1>
<h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
</span>
<span class="LP-PlaceTeaser__Detail">
<p class="LP-Paragraph">{{place.location}}</p>
<p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
</span>
</div>
<div class="LP-PlaceTeaser__Description">
<p class="LP-Paragraph">{{place.description}}</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>
@@ -35,10 +32,10 @@
</ul>
</div>
</div>
</article>
</a>
</li>
{% endfor %}
</article>
</a>
</li>
{% endfor %}
</ul>
</div>

View File

@@ -1,16 +1,16 @@
{% load widget_tweaks %}
<div class="LP-Input {% if field.errors %} LP-Input--error {% endif %}">
<label for="{{field.id_for_label}}" class="LP-Input__Label">{{field.label}}</label>
{% render_field field class="LP-Input__Field"%}
<label for="{{field.id_for_label}}" class="LP-Input__Label">{{field.label}}</label>
{% render_field field class="LP-Input__Field"%}
<span class="LP-Input__Message">
{% if field.errors %}
{% for error in field.errors%}
{{error}}
{% endfor %}
{% elif field.help_text%}
{{ field.help_text }}
{% endif %}
</span>
<span class="LP-Input__Message">
{% if field.errors %}
{% for error in field.errors%}
{{error}}
{% endfor %}
{% elif field.help_text%}
{{ field.help_text }}
{% endif %}
</span>
</div>

View File

@@ -0,0 +1,74 @@
{% load lostplaces %}
{% if is_paginated %}
<div class="LP-Pagination">
<ul class="LP-Pagination__List">
{% if page_obj.has_previous %}
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--first">
<a href="?page=1" class="LP-Link">
<span class="LP-Icon"></span>
<span class="LP-Text">First</span>
</a>
</li>
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--previous">
<a href="?page={{ page_obj.previous_page_number }}" class="LP-Link">
<span class="LP-Icon"></span>
<span class="LP-Text">Previous</span>
</a>
</li>
{% else %}
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--first LP-Pagination__Item--disabled">
<a href="#" class="LP-Link">
<span class="LP-Icon"></span>
<span class="LP-Text">First</span>
</a>
</li>
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--previous LP-Pagination__Item--disabled">
<a href="#" class="LP-Link">
<span class="LP-Icon"></span>
<span class="LP-Text">Previous</span>
</a>
</li>
{% endif %}
{% for i in page_obj.paginator|proper_paginate:page_obj.number %}
{% if i == page_obj.number %}
<li class="LP-Pagination__Item LP-Pagination__Item--current">
{% else %}
<li class="LP-Pagination__Item LP-Pagination__Item--neighbor">
{% endif %}
<a href="?page={{i}}" class="LP-Link">
<span class="LP-Text">{{i}}</span>
</a>
</li>
{% endfor %}
{% if page_obj.has_next %}
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--next">
<a href="?page={{ page_obj.next_page_number }}" class="LP-Link">
<span class="LP-Text">Next</span>
<span class="LP-Icon"></span>
</a>
</li>
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--last">
<a href="?page={{ page_obj.paginator.num_pages }}" class="LP-Link">
<span class="LP-Text">Last</span>
<span class="LP-Icon"></span>
</a>
</li>
{% else %}
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--next LP-Pagination__Item--disabled">
<a href="#" class="LP-Link">
<span class="LP-Text">Next</span>
<span class="LP-Icon"></span>
</a>
<li class="LP-Pagination__Item LP-Pagination__Item--other LP-Pagination__Item--last LP-Pagination__Item--disabled">
<a href="#" class="LP-Link">
<span class="LP-Text">Last</span>
<span class="LP-Icon"></span>
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}

View File

@@ -0,0 +1,55 @@
{% extends 'global.html'%}
{% load static %}
# {% block title %}Place erstellen{% endblock %}
{% block maincontent %}
<form class="LP-Form" method="POST" enctype="multipart/form-data">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Create Place</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.name %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.location %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.latitude %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.longitude %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_form.description %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=place_image_form.filename %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">Create</button>
</div>
<div class="LP-Form__Field LP-Form__Button LP-Input">
<a class="LP-Link" href="{% url 'place_list' %}">
<button type="button" class="LP-Button LP-Button--cancel">Cancel</button>
</a>
</div>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -0,0 +1,30 @@
{% extends 'global.html'%}
{% load static %}
{% block title %}Lost Place Deletion{% endblock %}
{% block maincontent %}
<form class="LP-Form" method="POST">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Delete place</legend>
{% csrf_token %}
<div class="LP-Form__Composition">
<div class="LP-Form__Field LP-Form__InfoText">
<p class="LP-Paragraph">Are you sure you want to delete "{{place.name}}"? </p>
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">Delete</button>
</div>
<div class="LP-Form__Field LP-Form__Button LP-Input">
<a class="LP-Link" href="{% url 'place_detail' pk=place.pk %}">
<button type="button" class="LP-Button LP-Button--cancel">Cancel</button>
</a>
</div>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -0,0 +1,53 @@
{% extends 'global.html'%}
{% load static %}
{% load thumbnail %}
{% block title %}{{place.name}}{% endblock %}
{% block additional_menu_items %}
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_edit' pk=place.pk %}" class="LP-Link"><span class="LP-Link__Text">Edit place</span></a></li>
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_delete' pk=place.pk %}" class="LP-Link"><span class="LP-Link__Text">Delete place</span></a></li>
{% endblock additional_menu_items %}
{% block maincontent %}
<article class="LP-PlaceDetail">
<header class="LP-PlaceDetail__Header">
<h1 class="LP-Headline">{{ place.name }}</h1>
{% if place.images.first.filename.hero.url %}
<figure class="LP-PlaceDetail__Image">
<img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
</figure>
{% endif %}
</header>
<div class="LP-PlaceDetail__Description">
<p class="LP-Paragraph">{{ place.description }}</p>
</div>
<section class="LP-Section">
<h1 class="LP-Headline">Map-Links</h1>
<div class="LP-LinkList">
<ul class="LP-LinkList__List">
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.tim-online.nrw.de/tim-online2/?center={{place.latitude}},{{place.longitude}}&icon=true&bg=dop" class="LP-Link"><span class="LP-Text">TIM Online</span></a></li>
<li class="LP-LinkList__Item"><a target="_blank" href="http://www.openstreetmap.org/?mlat={{place.latitude}}&mlon={{place.longitude}}&zoom=16" class="LP-Link"><span class="LP-Text">OSM</span></a></li>
</ul>
</div>
</section>
<section class="LP-Section">
<h1 class="LP-Headline">Bilder</h1>
<div class="LP-ImageGrid">
<ul class="LP-ImageGrid__List">
{% for place_image in place.images.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>
</li>
{% endfor %}
</ul>
</div>
</section>
</article>
{% endblock maincontent %}

View File

@@ -0,0 +1,52 @@
{% extends 'global.html'%}
{% load static %}
{% block title %}Lost Places{% endblock %}
{% block maincontent %}
<div class="LP-PlaceList">
<h1 class="LP-Headline">Listing our places</h1>
<ul class="LP-PlaceList__List">
{% for place in place_list %}
<li class="LP-PlaceList__Item">
<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.images.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>
{% include 'partials/nav/pagination.html' %}
</div>
{% endblock maincontent %}

View File

@@ -0,0 +1,55 @@
{% extends 'global.html'%}
{% load static %}
# {% block title %}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">Update place</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.name %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.location %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.latitude %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.longitude %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.description %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.filename %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">Update</button>
</div>
<div class="LP-Form__Field LP-Form__Button LP-Input">
<a class="LP-Link" href="{% url 'place_detail' pk=place.pk %}">
<button type="button" class="LP-Button LP-Button--cancel">Cancel</button>
</a>
</div>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -1,35 +0,0 @@
{% extends 'global.html'%}
{% load static %}
{% block title %}Lost Places{% endblock %}
{% block maincontent %}
<ul class="LP-Place__List">
{% for place in place_list %}
<li class="LP-Place__Item">
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<article class="LP-Place">
<div class="LP-Place__ImageContainer">
<img class="LP-Place__Image" src="{{ place.images.first.filename.thumbnail.url }}" />
</div>
<div class="LP-Place__Assets">
<div class="LP-Place__Info">
<h3 class="LP-Place__Title">{{place.name}}</h3>
<p class="LP-Place__Detail">{{place.location}}</p>
</div>
<p class="LP-TextSection LP-Place__Description">
{{place.description|truncatechars:210|truncatewords:-1}}
</p>
<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>
</article>
</a>
</li>
{% endfor %}
</ul>
{% endblock maincontent %}

View File

@@ -1,81 +0,0 @@
{% extends 'global.html'%}
{% load static %}
{% load thumbnail %}
{% block title %}{{place.name}}{% endblock %}
{% block maincontent %}
<article class="LP-PlaceOverview">
<div class="LP-PlaceOverview__Info">
<div class="LP-PlaceOveriew__Image">
<img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
</div>
<article class="LP-PlaceOverView__Description">
<div class="LP-TextSection">
<h1 class="LP-Headline LP-Headline">{{place.name}}</h1>
<p class="LP-Paragraph LP-Paragraph">{{place.description}}</p>
</div>
</article>
</div>
<article class="LP-Section">
<h1 class="LP-Headline LP-Headline">Sicherheitsmaßnahmen</h1>
<div class="LP-Content__Wrapper">
<div class="LP-Content">
<div class="LP-TagList">
<ul class="LP-TagList__List">
<li class="LP-TagList__Item">
<div class="LP-Tag">
<p class="LP-Paragraph LP-Paragraph">Kamera</p>
</div>
</li>
<li class="LP-TagList__Item">
<div class="LP-Tag">
<p class="LP-Paragraph LP-Paragraph">Wachhund</p>
</div>
</li>
<li class="LP-TagList__Item">
<div class="LP-Tag">
<p class="LP-Paragraph LP-Paragraph">Zaun</p>
</div>
</li>
<li class="LP-TagList__Item">
<div class="LP-Tag">
<p class="LP-Paragraph LP-Paragraph">Security</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</article>
<article class="LP-Section">
<h1 class="LP-Headline LP-Headline">Links</h1>
<div class="LP-Content__Wrapper">
<div class="LP-Content">
<ul class="LP-LinkList__List">
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.tim-online.nrw.de/tim-online2/?center={{place.latitude}},{{place.longitude}}&icon=true&bg=dop" class="LP-Link"><span class="LP-Text">TIM Online</span></a></li>
<li class="LP-LinkList__Item"><a target="_blank" href="http://www.openstreetmap.org/?mlat={{place.latitude}}&mlon={{place.longitude}}&zoom=16" class="LP-Link"><span class="LP-Text">OSM</span></a></li>
</ul>
</div>
</div>
</article>
<article class="LP-Section">
<h1 class="LP-Headline LP-Headline">Bilder</h1>
<div class="LP-Content__Wrapper">
<div class=" LP-Content">
<ul class="LP-PlaceOverView__ImageList">
{% for place_image in place.images.all %}
<li class="LP-PlaceOverView__ImageItem">
<a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}"></a>
</li>
{% endfor %}
</ul>
</div>
</div>
</article>
</article>
{% endblock maincontent %}

View File

@@ -1,48 +0,0 @@
{% extends 'global.html'%}
{% load static %}
# {% block title %}Place aktualisieren{% endblock %}
{% block maincontent %}
<form class="LP-Form" method="POST" enctype="multipart/form-data">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Place aktualisieren</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.name %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.location %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.latitude %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.longitude %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.description %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.filename %}
</div>
</div>
<div class="LP-Form__Composition">
<input type="submit" class="LP-Button" value="Abschicken"/>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -0,0 +1,22 @@
from django import template
register = template.Library()
@register.filter(name='proper_paginate')
def proper_paginate(paginator, current_page, neighbors=2):
if paginator.num_pages > 2*neighbors:
start_index = max(1, current_page-neighbors)
end_index = min(paginator.num_pages, current_page + neighbors)
if end_index < start_index + 2*neighbors:
end_index = start_index + 2*neighbors
elif start_index > end_index - 2*neighbors:
start_index = end_index - 2*neighbors
if start_index < 1:
end_index -= start_index
start_index = 1
elif end_index > paginator.num_pages:
start_index -= (end_index-paginator.num_pages)
end_index = paginator.num_pages
page_list = [f for f in range(start_index, end_index+1)]
return page_list[:(2*neighbors + 1)]
return paginator.page_range

View File

@@ -1,20 +1,20 @@
from django.urls import path
from .views import (
hello_world,
HomeView,
place_detail_view,
place_list_view,
HomeView,
PlaceDetailView,
PlaceListView,
SignUpView,
PlaceCreateView,
PlaceUpdateView
PlaceUpdateView,
PlaceDeleteView
)
urlpatterns = [
path('hello_world/', hello_world), # You know what this is :P
path('', HomeView.as_view(), name='home'),
path('', HomeView.as_view(), name='home'),
path('signup/', SignUpView.as_view(), name='signup'),
path('place/<int:pk>/', place_detail_view, name='place_detail'),
path('place/<int:pk>/', PlaceDetailView.as_view(), name='place_detail'),
path('place/create/', PlaceCreateView.as_view(), name='place_create'),
path('place/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'),
path('place/', place_list_view, name='place_list')
path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'),
path('place/', PlaceListView.as_view(), name='place_list')
]

View File

@@ -4,47 +4,81 @@
''' Django views. '''
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic import ListView
from django.views import View
from django.http import Http404
from django.views.generic.edit import UpdateView
from django.contrib import messages
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from .forms import ExplorerCreationForm, PlaceForm, PlaceImageCreateForm
from django.contrib.messages.views import SuccessMessageMixin
from .forms import (
ExplorerCreationForm,
PlaceForm,
PlaceImageCreateForm
)
from .models import Place, PlaceImage, Voucher
# Create your views here.
class SignUpView(CreateView):
# BaseView that checks if user is logged in.
class IsAuthenticated(LoginRequiredMixin, View):
redirect_field_name = 'redirect_to'
# BaseView that checks if logged in user is submitter of place.
class IsSubmitter(UserPassesTestMixin, View):
def test_func(self):
""" Check if user is eligible to modify place. """
if self.request.user.is_superuser:
return True
# Check if currently logged in user was the submitter
place_obj = self.get_object()
if self.request.user == place_obj.submitted_by:
return True
messages.error(
self.request, 'You do not have permission to do this.')
return False
class SignUpView(SuccessMessageMixin, CreateView):
form_class = ExplorerCreationForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
success_message = 'User created.'
def place_list_view(request,):
return render(request, 'placeList.html', {'place_list':Place.objects.all()})
class PlaceListView(IsAuthenticated, ListView):
paginate_by = 2
model = Place
template_name = 'place/place_list.html'
def place_detail_view(request, pk):
return render(request, 'placeOverview.html', {'place':Place.objects.get(pk=pk)})
def hello_world(request):
return render(request, 'hello_world.html', {'text':'Hello World!'})
class PlaceDetailView(IsAuthenticated, View):
def get(self, request, pk):
context = {
'place': Place.objects.get(pk=pk)
}
return render(request, 'place/place_detail.html', context)
class HomeView(View):
def get(self, request, *args, **kwargs):
place_list = Place.objects.all().order_by('submitted_when')[:10]
context = {
'place_list': place_list
}
return render(request, 'home.html', context)
def get(self, request, *args, **kwargs):
place_list = Place.objects.all().order_by('-submitted_when')[:10]
context = {
'place_list': place_list
}
return render(request, 'home.html', context)
class PlaceUpdateView(UpdateView):
template_name = 'update_place.html'
class PlaceUpdateView(IsAuthenticated, IsSubmitter, SuccessMessageMixin, UpdateView):
template_name = 'place/place_update.html'
model = Place
form_class = PlaceForm
success_message = 'Successfully updated place.'
def get_success_url(self):
return reverse_lazy('place_detail', kwargs={'pk':self.get_object().pk})
class PlaceCreateView(View):
class PlaceCreateView(IsAuthenticated, View):
def get(self, request, *args, **kwargs):
place_image_form = PlaceImageCreateForm()
@@ -54,7 +88,7 @@ class PlaceCreateView(View):
'place_form': place_form,
'place_image_form': place_image_form
}
return render(request, 'create_place.html', context)
return render(request, 'place/place_create.html', context)
def post(self, request, *args, **kwargs):
place_form = PlaceForm(request.POST)
@@ -76,17 +110,37 @@ class PlaceCreateView(View):
kwargs_to_pass = {
'pk': place.pk
}
messages.success(
self.request, 'Successfully created place.')
return redirect(reverse_lazy('place_detail', kwargs=kwargs_to_pass))
else:
context = {
'form': form_place
}
return render(request, 'create_place.html', context)
# Usually the browser should have checked the form before sending.
messages.error(
self.request, 'Please fill in all required fields.')
return render(request, 'place/place_create.html', context)
def _apply_multipart_image_upload(self, files, place, submitter):
for image in files:
place_image = PlaceImage.objects.create(
filename=image,
place=place,
submitted_by=submitter
)
place_image.save()
class PlaceDeleteView(IsAuthenticated, IsSubmitter, DeleteView):
template_name = 'place/place_delete.html'
model = Place
success_message = 'Successfully deleted place.'
success_url = reverse_lazy('place_list')
success_message = 'Place deleted'
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

View File

@@ -0,0 +1,15 @@
{% extends 'global.html'%}
{% block title %}Forbidden{% endblock %}
{% block additional_head %}
{% if request.META.HTTP_REFERER %}
<meta http-equiv="refresh" content="5;url={{ request.META.HTTP_REFERER }}" />
{% endif %}
{% endblock additional_head %}
{% block maincontent %}
{% if request.META.HTTP_REFERER %}
<p class="LP-Headline">You will be redirected in 5 seconds</p><p class="LP-Headline"><a href="{{ request.META.HTTP_REFERER }}" class="LP-Link">Go Back</a></p>
{% endif %}
{% endblock maincontent %}

View File

@@ -5,11 +5,27 @@
{% block maincontent %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
<form class="LP-Form" method="POST" enctype="multipart/form-data">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Login</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.username %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.password %}
</div>
</div>
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">Login</button>
</div>
</div>
</div>
</fieldset>
</form>
{% endblock maincontent %}

View File

@@ -7,38 +7,40 @@
{% block maincontent %}
<form class="LP-Form" method="POST">
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Registrierung</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.username %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.email %}
</div>
</div>
<fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Registration</legend>
{% csrf_token %}
<div class="LP-Form__Composition LP-Form__Composition--breakable">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.username %}
</div>
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.email %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.password1 %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.password2 %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.voucher %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.password1 %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.password2 %}
</div>
</div>
<div class="LP-Form__Composition">
<div class="LP-Form__Field">
{% include 'partials/form/inputField.html' with field=form.voucher %}
</div>
</div>
<div class="LP-Form__Composition">
<input type="submit" class="LP-Button" value="Registrieren"/>
</div>
</fieldset>
<div class="LP-Form__Composition LP-Form__Composition--buttons">
<div class="LP-Form__Field LP-Form__Button LP-Input">
<button class="LP-Button">Sign up</button>
</div>
</div>
</fieldset>
</form>