Compare commits
6 Commits
a139594863
...
10c08a2f5e
Author | SHA1 | Date | |
---|---|---|---|
10c08a2f5e | |||
50f60be10a | |||
8b32b56dd9 | |||
74d842a668 | |||
2ac39f719f | |||
9b6121e448 |
@ -8,6 +8,8 @@ from django.db import models
|
|||||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from lostplaces import widgets
|
||||||
from lostplaces.models import Place, PlaceImage, Voucher, Explorer
|
from lostplaces.models import Place, PlaceImage, Voucher, Explorer
|
||||||
|
|
||||||
class SignupVoucherForm(UserCreationForm):
|
class SignupVoucherForm(UserCreationForm):
|
||||||
@ -68,6 +70,15 @@ class PlaceForm(forms.ModelForm):
|
|||||||
model = Place
|
model = Place
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
exclude = ['submitted_by']
|
exclude = ['submitted_by']
|
||||||
|
widgets = {
|
||||||
|
'hero': widgets.SelectContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if 'instance' in kwargs:
|
||||||
|
self.fields['hero'].queryset = PlaceImage.objects.filter(place=kwargs['instance'])
|
||||||
|
self.fields['hero'].widget.attrs['item_template'] = 'partials/select_place_image_item.html'
|
||||||
|
|
||||||
latitude = forms.DecimalField(
|
latitude = forms.DecimalField(
|
||||||
widget=forms.NumberInput(attrs={'min':-90,'max': 90,'type': 'number', 'step': 'any'})
|
widget=forms.NumberInput(attrs={'min':-90,'max': 90,'type': 'number', 'step': 'any'})
|
||||||
@ -89,9 +100,24 @@ class PlaceImageForm(forms.ModelForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields['filename'].required = False
|
self.fields['filename'].required = False
|
||||||
|
|
||||||
|
class PlaceSetHeroForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Place
|
||||||
|
fields = ['hero']
|
||||||
|
widgets = {
|
||||||
|
'hero': widgets.SelectContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['hero'].queryset = PlaceImage.objects.filter(place=kwargs['instance'])
|
||||||
|
self.fields['hero'].widget.attrs['item_template'] = 'partials/select_place_image_item.html'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TagSubmitForm(forms.Form):
|
class TagSubmitForm(forms.Form):
|
||||||
tag_list = forms.CharField(
|
tag_list = forms.CharField(
|
||||||
max_length=500,
|
max_length=500,
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(attrs={'autocomplete':'off'})
|
widget=forms.TextInput(attrs={'autocomplete':'off'})
|
||||||
|
@ -11,6 +11,7 @@ from lostplaces.models.abstract_models import Submittable, Taggable, Mapable
|
|||||||
from easy_thumbnails.fields import ThumbnailerImageField
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
from easy_thumbnails.files import get_thumbnailer
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
|
|
||||||
|
|
||||||
class Place(Submittable, Taggable, Mapable):
|
class Place(Submittable, Taggable, Mapable):
|
||||||
"""
|
"""
|
||||||
Place defines a lost place (location, name, description etc.).
|
Place defines a lost place (location, name, description etc.).
|
||||||
@ -25,8 +26,31 @@ class Place(Submittable, Taggable, Mapable):
|
|||||||
verbose_name=_('Description'),
|
verbose_name=_('Description'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
hero = models.ForeignKey(
|
||||||
|
'PlaceImage',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='place_heros'
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_hero_image(self):
|
||||||
|
if self.hero:
|
||||||
|
return self.hero
|
||||||
|
elif len(self.placeimages.all()) > 0:
|
||||||
|
return self.placeimages.first()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('place_detail', kwargs={'pk': self.pk})
|
return reverse('place_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
def get_hero_index_in_queryset(self):
|
||||||
|
for i in range(0, len(self.placeimages.all())):
|
||||||
|
image = self.placeimages.all()[i]
|
||||||
|
if image == self.hero:
|
||||||
|
return i
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -89,7 +113,7 @@ class PlaceImage(PlaceAsset):
|
|||||||
upload_to=generate_place_image_filename,
|
upload_to=generate_place_image_filename,
|
||||||
resize_source=dict(size=(2560, 2560),
|
resize_source=dict(size=(2560, 2560),
|
||||||
sharpen=True),
|
sharpen=True),
|
||||||
verbose_name=_('Filename(s)'),
|
verbose_name=_('Images'),
|
||||||
help_text=_('Optional: One or more images to upload')
|
help_text=_('Optional: One or more images to upload')
|
||||||
)
|
)
|
||||||
place = models.ForeignKey(
|
place = models.ForeignKey(
|
||||||
@ -104,7 +128,7 @@ class PlaceImage(PlaceAsset):
|
|||||||
of this image as textual representation of this instance
|
of this image as textual representation of this instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return 'Image ' + str(self.pk)
|
return 'Image ' + str(self.place.name)
|
||||||
|
|
||||||
# These two auto-delete files from filesystem when they are unneeded:
|
# These two auto-delete files from filesystem when they are unneeded:
|
||||||
|
|
||||||
|
@ -858,6 +858,13 @@ body {
|
|||||||
.LP-Pagination .LP-Pagination__Item .LP-Link {
|
.LP-Pagination .LP-Pagination__Item .LP-Link {
|
||||||
padding: 8px 11px; } }
|
padding: 8px 11px; } }
|
||||||
|
|
||||||
|
.LP-Select {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer; }
|
||||||
|
.LP-Select:checked,
|
||||||
|
:checked + .LP-Select, .LP-Select--active {
|
||||||
|
box-shadow: 0 0 3px 3px #C09F80; }
|
||||||
|
|
||||||
.LP-Content {
|
.LP-Content {
|
||||||
padding: 35px; }
|
padding: 35px; }
|
||||||
|
|
||||||
@ -1060,19 +1067,13 @@ body {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
font-size: 25px; }
|
font-size: 25px; }
|
||||||
.LP-UserInfo__Meta {
|
.LP-UserInfo__Meta {
|
||||||
margin-top: 10px;
|
|
||||||
padding: 5px; }
|
padding: 5px; }
|
||||||
.LP-UserInfo__Meta * {
|
.LP-UserInfo__Meta * {
|
||||||
font-family: "Montserrat", Helvetica, sans-serif;
|
font-family: "Montserrat", Helvetica, sans-serif;
|
||||||
font-size: 18px; }
|
font-size: 18px; }
|
||||||
.LP-UserInfo__Meta .LP-UserInfo__Key {
|
.LP-UserInfo__Meta .LP-UserInfo__Key {
|
||||||
padding-right: 25px;
|
padding-right: 25px;
|
||||||
font-weight: bold;
|
font-weight: bold; }
|
||||||
white-space: nowrap; }
|
|
||||||
.LP-UserInfo__Meta .LP-UserInfo__Value {
|
|
||||||
white-space: nowrap; }
|
|
||||||
.LP-UserInfo__Edit {
|
|
||||||
margin-top: 10px; }
|
|
||||||
|
|
||||||
.LP-Header {
|
.LP-Header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1681,6 +1682,12 @@ body {
|
|||||||
object-position: botom; }
|
object-position: botom; }
|
||||||
.LP-ImageGrid__Item--center img {
|
.LP-ImageGrid__Item--center img {
|
||||||
object-position: center; }
|
object-position: center; }
|
||||||
|
.LP-ImageGrid__Item img {
|
||||||
|
height: unset;
|
||||||
|
width: unset; }
|
||||||
|
.LP-ImageGrid__Item .LP-Image {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%; }
|
||||||
.LP-ImageGrid__Item--add .LP-Link {
|
.LP-ImageGrid__Item--add .LP-Link {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -1734,12 +1741,17 @@ body {
|
|||||||
.LP-ImageGrid__LightBox:target {
|
.LP-ImageGrid__LightBox:target {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
grid-template-areas: 'picture picture' 'previous next';
|
grid-template-areas: 'picture picture' 'previous next';
|
||||||
grid-template-rows: 1fr 4rem;
|
grid-template-rows: 1fr 4rem;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
pointer-events: initial; }
|
pointer-events: initial; }
|
||||||
.LP-ImageGrid__FullSizeImage {
|
.LP-ImageGrid__FullSizeImage {
|
||||||
grid-area: picture; }
|
grid-area: picture;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
height: unset;
|
||||||
|
width: unset; }
|
||||||
.LP-ImageGrid__Previous {
|
.LP-ImageGrid__Previous {
|
||||||
grid-area: previous;
|
grid-area: previous;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
@ -1800,15 +1812,6 @@ body {
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
flex-basis: max-content; }
|
flex-basis: max-content; }
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
|
||||||
.LP-UserProfile {
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
gap: 35px; }
|
|
||||||
.LP-UserProfile__Info {
|
|
||||||
flex-basis: 100%; }
|
|
||||||
.LP-UserProfile__Info .LP-UserInfo {
|
|
||||||
max-width: unset; } }
|
|
||||||
|
|
||||||
.LP-MainContainer {
|
.LP-MainContainer {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 1280px; }
|
max-width: 1280px; }
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<article class="LP-PlaceTeaser">
|
<article class="LP-PlaceTeaser">
|
||||||
<div class="LP-PlaceTeaser__Image">
|
<div class="LP-PlaceTeaser__Image">
|
||||||
{% partial 'image' %}
|
{% partial 'image' %}
|
||||||
{% source_url source_url=place.placeimages.first.filename.thumbnail.url %}
|
{% set source_url place.placeimages.first.filename.thumbnail.url %}
|
||||||
{% endpartial %}
|
{% endpartial %}
|
||||||
</div>
|
</div>
|
||||||
<div class="LP-PlaceTeaser__Meta">
|
<div class="LP-PlaceTeaser__Meta">
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
<div class="LP-Input {% if classes%}{{classes}}{% endif %} {% if field.errors %} LP-Input--error {% endif %}">
|
<div class="LP-Input {% if classes%}{{classes}}{% endif %} {% if field.errors %} LP-Input--error {% endif %}">
|
||||||
<label for="{{field.id_for_label}}" class="LP-Input__Label">{{field.label}}</label>
|
<label for="{{field.id_for_label}}" class="LP-Input__Label">{{field.label}}</label>
|
||||||
{% with class="LP-Input__Field "%}
|
{% render_field field class+='LP-Input__Field' %}
|
||||||
{% render_field field class=class%}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<span class="LP-Input__Message">
|
<span class="LP-Input__Message">
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
<div class="LP-ImageGrid">
|
<div class="LP-ImageGrid">
|
||||||
<ul class="LP-ImageGrid__Container">
|
<ul class="LP-ImageGrid__Container">
|
||||||
{% for image in image_list %}
|
{% for image in image_list %}
|
||||||
<li id="thumbnail{{forloop.counter}}" class="LP-ImageGrid__Item">
|
<li id="thumbnail{{forloop.counter0}}" class="LP-ImageGrid__Item">
|
||||||
{{ "#image"|add:forloop.counter }}
|
{% include 'partials/image.html' with source_url=image.filename.thumbnail.url link_url="#image"|addstr:forloop.counter0 %}
|
||||||
{% include 'partials/image.html' with source_url=image.filename.thumbnail.url link_url="#image"|addstr:forloop.counter %}
|
|
||||||
{% if user.explorer == image.submitted_by%}
|
{% if user.explorer == image.submitted_by%}
|
||||||
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
|
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
|
||||||
<a href="{% url 'place_image_delete' pk=image.id %}" class="LP-Link">
|
<a href="{% url 'place_image_delete' pk=image.id %}" class="LP-Link">
|
||||||
@ -17,16 +16,16 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="image{{forloop.counter}}" class="LP-ImageGrid__LightBox">
|
<div id="image{{forloop.counter0}}" class="LP-ImageGrid__LightBox">
|
||||||
<img class="LP-ImageGrid__FullSizeImage" src="{{image.filename.large.url}}" loading="lazy"/>
|
<img class="LP-ImageGrid__FullSizeImage" src="{{image.filename.large.url}}" loading="lazy"/>
|
||||||
{% if forloop.counter < image_list|length %}
|
{% if forloop.counter0 < image_list|length|add:-1 %}
|
||||||
<a href="#image{{forloop.counter|add:1}}" class="LP-ImageGrid__Next">Next</a>
|
<a href="#image{{forloop.counter0|add:1}}" class="LP-ImageGrid__Next">Next</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if forloop.counter > 1 %}
|
{% if forloop.counter0 > 0 %}
|
||||||
<a href="#image{{forloop.counter|add:-1}}" class="LP-ImageGrid__Previous">Previous</a>
|
<a href="#image{{forloop.counter0|add:-1}}" class="LP-ImageGrid__Previous">Previous</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="LP-ImageGrid__Close LP-ImageGrid__DeleteItem" title="Schließen">
|
<span class="LP-ImageGrid__Close LP-ImageGrid__DeleteItem" title="Schließen">
|
||||||
<a href="#thumbnail{{forloop.counter}}" class="LP-Link">
|
<a href="#thumbnail{{forloop.counter0}}" class="LP-Link">
|
||||||
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}"/>
|
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}"/>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<article class="LP-PlaceTeaser {% if extended %} LP-PlaceTeaser--extended{% endif %}">
|
<article class="LP-PlaceTeaser {% if extended %} LP-PlaceTeaser--extended{% endif %}">
|
||||||
<div class="LP-PlaceTeaser__Image">
|
<div class="LP-PlaceTeaser__Image">
|
||||||
{% if place.placeimages.all|length > 0 %}
|
{% if place.get_hero_image %}
|
||||||
{% include 'partials/image.html' with source_url=place.placeimages.first.filename.thumbnail.url link_url=place.get_absolute_url%}
|
{% include 'partials/image.html' with source_url=place.get_hero_image.filename.thumbnail.url link_url=place.get_absolute_url%}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{place.get_absolute_url}}">
|
<a href="{{place.get_absolute_url}}">
|
||||||
<img class="LP-Image" src="{% static 'images/missing_image.png' %}" />
|
<img class="LP-Image" src="{% static 'images/missing_image.png' %}" />
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{%load lostplaces %}
|
||||||
|
|
||||||
|
{% include 'partials/image.html' with source_url=object.filename.thumbnail.url %}
|
@ -22,13 +22,15 @@
|
|||||||
|
|
||||||
{% block maincontent %}
|
{% block maincontent %}
|
||||||
<article class="LP-PlaceDetail">
|
<article class="LP-PlaceDetail">
|
||||||
|
|
||||||
<header class="LP-PlaceDetail__Header">
|
<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 }} {% include 'partials/icons/place_favorite.html' %} {% include 'partials/icons/place_visited.html' %}</h1>
|
||||||
{% if place.placeimages.first.filename.hero.url %}
|
{% if place.get_hero_image %}
|
||||||
<div class="LP-PlaceDetail__Image">
|
<div class="LP-PlaceDetail__Image">
|
||||||
{% partial image %}
|
{% partial image %}
|
||||||
{% set source_url place.placeimages.first.filename.hero.url %}
|
{% set source_url place.get_hero_image.filename.hero.url %}
|
||||||
|
{% set link_url %}
|
||||||
|
{{"#image"|addstr:place.get_hero_index_in_queryset}}
|
||||||
|
{% endset %}
|
||||||
{% endpartial %}
|
{% endpartial %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{% extends 'global.html'%}
|
{% extends 'global.html'%}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
# {% block title %}{% translate 'Edit place' %}{% endblock %}
|
# {% block title %}{% translate 'Edit place' %}{% endblock %}
|
||||||
|
|
||||||
@ -38,6 +39,15 @@
|
|||||||
{% include 'partials/form/inputField.html' with field=form.filename %}
|
{% include 'partials/form/inputField.html' with field=form.filename %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if object.placeimages.all|length > 0 %}
|
||||||
|
<legend class="LP-Form__Legend">{% translate 'Set Hero Image' %}</legend>
|
||||||
|
<div class="LP-Form__Composition">
|
||||||
|
<div class="LP-Form__Field">
|
||||||
|
{% render_field form.hero container_class='LP-ImageGrid__Container' item_class='LP-ImageGrid__Item LP-Select' current_selected_value=object.hero.id%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% translate 'Update' as action %}
|
{% translate 'Update' as action %}
|
||||||
<div class="LP-Form__Composition LP-Form__Composition--buttons">
|
<div class="LP-Form__Composition LP-Form__Composition--buttons">
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<ul class="{{widget.attrs.container_class}}">
|
||||||
|
{% for group_name, group_choices, group_index in widget.optgroups %}
|
||||||
|
{% for option in group_choices %}
|
||||||
|
{% if option.value %}
|
||||||
|
<li style="display: content;">
|
||||||
|
<input
|
||||||
|
style="display: none;"
|
||||||
|
name="{{option.name}}"
|
||||||
|
id="{{widget.attrs.id}}{{group_index}}"
|
||||||
|
type="radio"
|
||||||
|
value="{{option.value.instance.id}}"
|
||||||
|
{% if widget.attrs.current_selected_value|stringformat:'s' == option.value.instance.id|stringformat:'s' %}checked="checked"{% endif %}
|
||||||
|
/>
|
||||||
|
<label class="{{widget.attrs.item_class}}" for="{{widget.attrs.id}}{{group_index}}">
|
||||||
|
{% if widget.attrs.item_template %}
|
||||||
|
{% include widget.attrs.item_template with object=option.value.instance %}
|
||||||
|
{% endif %}
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
5
django_lostplaces/lostplaces/widgets.py
Normal file
5
django_lostplaces/lostplaces/widgets.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
class SelectContent(forms.Select):
|
||||||
|
template_name = 'widgets/select_content.html'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user