Compare commits
18 Commits
843832d978
...
8ab0e1b177
Author | SHA1 | Date | |
---|---|---|---|
8ab0e1b177 | |||
47718ce17b | |||
6d89bca033 | |||
69f0f4ccfd | |||
75f8ac4a95 | |||
c3eede548c | |||
0eea31a0af | |||
2509c669f9 | |||
e00c9318fa | |||
6ed6c2c990 | |||
328e6899a6 | |||
e4cd8bb301 | |||
66581a9d2d | |||
490a0e9f3e | |||
fc39b46c52 | |||
36744c65fb | |||
8198c43451 | |||
78f0e80136 |
2
Pipfile
2
Pipfile
@ -11,7 +11,7 @@ django = "*"
|
||||
easy-thumbnails = "*"
|
||||
image = "*"
|
||||
django-widget-tweaks = "*"
|
||||
django-svg-icons = "*"
|
||||
django-taggit = "*"
|
||||
|
||||
# Commented out to not explicitly specify Python 3 subversion.
|
||||
# [requires]
|
||||
|
24
Readme.md
24
Readme.md
@ -11,12 +11,13 @@ Right now it depends on the following non-core Python 3 libraries. These can be
|
||||
* [easy-thumbnails](https://github.com/SmileyChris/easy-thumbnails) A powerful, yet easy to implement thumbnailing application for Django 1.11+
|
||||
* [image](https://github.com/francescortiz/image) Image cropping for django
|
||||
* [django-widget-tweaks](https://github.com/jazzband/django-widget-tweaks) Tweak the form field rendering in templates, not in python-level form definitions.
|
||||
* [django-taggit](https://github.com/jazzband/django-taggit) A simpler approach to tagging with Django.
|
||||
|
||||
|
||||
## Development
|
||||
### Setting up a (pipenv) virtual environment for development
|
||||
|
||||
After having obtained the repository contents (either via .zip download or git clone), you can easily setup a pipenv virtual environment. The repo provides a Pipfile for easy dependency management that does not mess with your system.
|
||||
After having obtained the repository contents (either via .zip download or git clone), you can easily setup a [pipenv](https://docs.pipenv.org/) virtual environment. The repo provides a Pipfile for easy dependency management that does not mess with your system.
|
||||
|
||||
```
|
||||
$ cd lostplaces-backend
|
||||
@ -25,14 +26,17 @@ $ pipenv shell
|
||||
(lostplaces-backend) $ lostplaces/manage.py makemigrations
|
||||
(lostplaces-backend) $ lostplaces/manage.py migrate
|
||||
(lostplaces-backend) $ lostplaces/manage.py createsuperuser
|
||||
(lostplaces-backend) $ lostplaces/manage.py runserver
|
||||
(lostplaces-backend) $ lostplaces/manage.py runserver --ipv6
|
||||
```
|
||||
|
||||
### Returning to the venv
|
||||
```
|
||||
$ cd lostplaces-backend
|
||||
$ pipenv shell
|
||||
(lostplaces-backend) $ lostplaces/manage.py runserver
|
||||
(lostplaces-backend) $ pipenv update # If dependencies changed, or updates available
|
||||
(lostplaces-backend) $ lostplaces/manage.py makemigrations # If datamodels changed
|
||||
(lostplaces-backend) $ lostplaces/manage.py migrate # If datamodels changed
|
||||
(lostplaces-backend) $ lostplaces/manage.py runserver --ipv6
|
||||
```
|
||||
|
||||
Visit: [admin](http://localhost:8000/admin) for administrative backend or
|
||||
@ -41,16 +45,15 @@ Visit: [admin](http://localhost:8000/admin) for administrative backend or
|
||||
## Installing lostplaces
|
||||
|
||||
### Install dependencies
|
||||
Python3, Django3, easy-thumbnails, image, django-widget-tweaks
|
||||
Python3, Django3, easy-thumbnails, image, django-widget-tweaks, django-taggit
|
||||
```
|
||||
pip install --user django easy-thumbnails image django-widget-tweaks
|
||||
pip install --user django easy-thumbnails image django-widget-tweaks django-taggit
|
||||
```
|
||||
Or, if you use pipenv
|
||||
```
|
||||
pipenv install
|
||||
pipenv install / update
|
||||
```
|
||||
|
||||
|
||||
### Add 'lostplaces_app' to your INSTALLED_APPS setting like this
|
||||
|
||||
```
|
||||
@ -59,6 +62,7 @@ INSTALLED_APPS = [
|
||||
'lostplaces_app',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks',
|
||||
'django_taggit'
|
||||
]
|
||||
```
|
||||
|
||||
@ -92,11 +96,11 @@ urlpatterns = [
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
```
|
||||
|
||||
Run ``python manage.py migrate`` to create the lost places models.
|
||||
Run ``./manage.py migrate`` to create the lost places models.
|
||||
|
||||
Start the development server and visit http://127.0.0.1:8000/admin/
|
||||
Start the development server and visit http://localhost:8000/admin/
|
||||
|
||||
Visit http://127.0.0.1:8000/lostplaces/ to CRUD lost places.
|
||||
Visit http://localhost:8000/lostplaces/ to CRUD lost places.
|
||||
|
||||
|
||||
Happy developing ;-)
|
||||
|
@ -29,7 +29,7 @@ SECRET_KEY = 'n$(bx8(^)*wz1ygn@-ekt7rl^1km*!_c+fwwjiua8m@-x_rpl0'
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ['localhost', '192.168.178.49']
|
||||
|
||||
|
||||
# Application definition
|
||||
@ -43,7 +43,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks'
|
||||
'widget_tweaks',
|
||||
'taggit'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -48,3 +48,11 @@ class PlaceImageCreateForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['filename'].required = False
|
||||
|
||||
|
||||
class TagSubmitForm(forms.Form):
|
||||
tag_list = forms.CharField(
|
||||
max_length=500,
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'autocomplete':'off'})
|
||||
)
|
@ -10,6 +10,7 @@ from django.db import models
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from easy_thumbnails.fields import ThumbnailerImageField
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
# Create your models here.
|
||||
|
||||
@ -55,6 +56,7 @@ class Place (models.Model):
|
||||
longitude = models.FloatField()
|
||||
description = models.TextField()
|
||||
|
||||
tags = TaggableManager(blank=True)
|
||||
# Get center position of LP-geocoordinates.
|
||||
|
||||
def average_latlon(place_list):
|
||||
@ -145,21 +147,21 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
|
||||
|
||||
|
||||
class ExternalLink(models.Model):
|
||||
url = models.URLField(max_length=200)
|
||||
label = models.CharField(max_length=100)
|
||||
submitted_by = models.ForeignKey(
|
||||
url = models.URLField(max_length=200)
|
||||
label = models.CharField(max_length=100)
|
||||
submitted_by = models.ForeignKey(
|
||||
Explorer,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='external_links'
|
||||
)
|
||||
submitted_when = models.DateTimeField(auto_now_add=True, null=True)
|
||||
submitted_when = models.DateTimeField(auto_now_add=True, null=True)
|
||||
|
||||
class PhotoAlbum(ExternalLink):
|
||||
place = models.ForeignKey(
|
||||
place = models.ForeignKey(
|
||||
Place,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='photo_albums',
|
||||
null=True
|
||||
null=True
|
||||
)
|
@ -225,17 +225,13 @@
|
||||
|
||||
.RV-BoxShadow {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 40%;
|
||||
margin: 2em 10px 4em;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset; }
|
||||
float: left; }
|
||||
.RV-BoxShadow:before, .RV-BoxShadow:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: -2; }
|
||||
.RV-BoxShadow--simple {
|
||||
box-shadow: 0 0 20px #6b5690; }
|
||||
box-shadow: 0 0 10px #6b5690; }
|
||||
.RV-BoxShadow--raised:after {
|
||||
box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset; }
|
||||
.RV-BoxShadow--liftedCorners:before, .RV-BoxShadow--liftedCorners:after {
|
||||
@ -578,7 +574,8 @@ body {
|
||||
padding: 8px 14px;
|
||||
border-radius: 2px;
|
||||
font-weight: bold;
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
white-space: nowrap; }
|
||||
.LP-Button:active {
|
||||
background-color: #76323F;
|
||||
color: #f9f9f9; }
|
||||
@ -604,19 +601,28 @@ body {
|
||||
flex-direction: column;
|
||||
margin-bottom: -30px;
|
||||
padding: 10px 0; }
|
||||
.LP-Input .LP-Input__Field {
|
||||
.LP-Input--tagging .LP-Button {
|
||||
height: 53px; }
|
||||
.LP-Input--tagging .LP-Input__Field, .LP-Input--tagging .tagify {
|
||||
min-height: 36px;
|
||||
height: max-content;
|
||||
font-family: Montserrat, Helvetica, sans-serif;
|
||||
font-size: 1em;
|
||||
padding: 0;
|
||||
padding-left: 8px; }
|
||||
.LP-Input .LP-Input__Field, .LP-Input .tagify {
|
||||
border: none;
|
||||
border-bottom: 1px solid #565656;
|
||||
padding: 8px 0;
|
||||
margin-bottom: 30px;
|
||||
width: 100%; }
|
||||
.LP-Input .LP-Input__Field:focus, .LP-Input .LP-Input__Field:active, .LP-Input .LP-Input__Field:invalid {
|
||||
.LP-Input .LP-Input__Field:focus, .LP-Input .tagify:focus, .LP-Input .LP-Input__Field:active, .LP-Input .tagify:active, .LP-Input .LP-Input__Field:invalid, .LP-Input .tagify:invalid, .LP-Input .LP-Input__Field--active, .LP-Input .tagify--focus {
|
||||
margin-bottom: 29px;
|
||||
border-bottom: 2px solid #76323F;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 3px 3px 0 0;
|
||||
box-shadow: none; }
|
||||
.LP-Input .LP-Input__Field[type=submit] {
|
||||
.LP-Input .LP-Input__Field[type=submit], .LP-Input .tagify[type=submit] {
|
||||
background-color: #C09F80;
|
||||
color: #565656;
|
||||
border: none;
|
||||
@ -624,7 +630,7 @@ body {
|
||||
border-radius: 2px;
|
||||
font-weight: bold;
|
||||
cursor: pointer; }
|
||||
.LP-Input .LP-Input__Field[type=submit]:active {
|
||||
.LP-Input .LP-Input__Field[type=submit]:active, .LP-Input .tagify[type=submit]:active {
|
||||
background-color: #76323F;
|
||||
color: #f9f9f9; }
|
||||
.LP-Input .LP-Input__Label {
|
||||
@ -640,22 +646,30 @@ body {
|
||||
position: relative;
|
||||
top: -30px;
|
||||
overflow: hidden; }
|
||||
.LP-Input--error .LP-Input__Field {
|
||||
.LP-Input--error .LP-Input__Field, .LP-Input--error .tagify {
|
||||
margin-bottom: 25px;
|
||||
border-bottom: 2px solid #76323F;
|
||||
margin-bottom: 29px; }
|
||||
.LP-Input--error .LP-Input__Message {
|
||||
color: #76323F; }
|
||||
.LP-Input--disabled .LP-Input__Field, .LP-Input--disabled .LP-Input__Field:disabled {
|
||||
.LP-Input--disabled .LP-Input__Field, .LP-Input--disabled .tagify,
|
||||
.LP-Input--disabled .LP-Input__Field:disabled,
|
||||
.LP-Input--disabled .tagify:disabled {
|
||||
background-color: transparent;
|
||||
border-bottom: 1px dashed #565656;
|
||||
cursor: not-allowed; }
|
||||
label + .LP-Input--disabled .LP-Input__Field, label + .LP-Input--disabled .LP-Input__Field:disabled {
|
||||
label + .LP-Input--disabled .LP-Input__Field, label + .LP-Input--disabled .tagify, label + .LP-Input--disabled .LP-Input__Field:disabled, label + .LP-Input--disabled .tagify:disabled {
|
||||
color: red; }
|
||||
.LP-Input--disabled .LP-Input__Field:focus, .LP-Input--disabled .LP-Input__Field:active, .LP-Input--disabled .LP-Input__Field:disabled:focus, .LP-Input--disabled .LP-Input__Field:disabled:active {
|
||||
.LP-Input--disabled .LP-Input__Field:focus, .LP-Input--disabled .tagify:focus, .LP-Input--disabled .LP-Input__Field:active, .LP-Input--disabled .tagify:active,
|
||||
.LP-Input--disabled .LP-Input__Field:disabled:focus,
|
||||
.LP-Input--disabled .tagify:disabled:focus,
|
||||
.LP-Input--disabled .LP-Input__Field:disabled:active,
|
||||
.LP-Input--disabled .tagify:disabled:active {
|
||||
margin-bottom: 30px;
|
||||
border-radius: 0; }
|
||||
.LP-Input--disabled .LP-Input__Field ~ .LP-Input__Message, .LP-Input--disabled .LP-Input__Field:disabled ~ .LP-Input__Message {
|
||||
.LP-Input--disabled .LP-Input__Field ~ .LP-Input__Message, .LP-Input--disabled .tagify ~ .LP-Input__Message,
|
||||
.LP-Input--disabled .LP-Input__Field:disabled ~ .LP-Input__Message,
|
||||
.LP-Input--disabled .tagify:disabled ~ .LP-Input__Message {
|
||||
visibility: hidden; }
|
||||
.LP-Input--disabled .LP-Input__Label {
|
||||
color: #565656; }
|
||||
@ -674,12 +688,14 @@ body {
|
||||
width: auto;
|
||||
object-fit: contain; }
|
||||
|
||||
.LP-Tag {
|
||||
.LP-Tag, .tagify__tag {
|
||||
padding: 8px 14px;
|
||||
background-color: #D7CEC7;
|
||||
border-radius: 2px;
|
||||
width: max-content; }
|
||||
.LP-Tag .LP-Paragraph {
|
||||
.LP-Tag:hover, .tagify__tag:hover {
|
||||
background-color: #bdbdbd; }
|
||||
.LP-Tag .LP-Paragraph, .tagify__tag .LP-Paragraph {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Montserrat, Helvetica, sans-serif;
|
||||
@ -865,8 +881,8 @@ body {
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.LP-TagList .LP-TagList__List .LP-TagList__Item {
|
||||
margin: 6px; }
|
||||
.LP-TagList .LP-TagList__List .LP-TagList__Item, .LP-TagList .LP-TagList__List .tagify__tag {
|
||||
margin: 3px; }
|
||||
|
||||
.LP-Menu {
|
||||
border-left: 1px solid #C09F80; }
|
||||
@ -1193,7 +1209,13 @@ body {
|
||||
.LP-Footer .LP-LinkList__List .LP-LinkList__Item .LP-Link:hover {
|
||||
background-color: inherit; }
|
||||
|
||||
.LP-Form--inline .LP-Form__Legend, .LP-Form--inline .LP-Input__Label {
|
||||
.LP-Form--tagging {
|
||||
margin-top: 25px; }
|
||||
.LP-Form--tagging div.LP-Form__Composition {
|
||||
gap: 25px; }
|
||||
|
||||
.LP-Form--inline .LP-Form__Legend,
|
||||
.LP-Form--inline .LP-Input__Label {
|
||||
display: none; }
|
||||
|
||||
.LP-Form--inline .LP-Form__Button {
|
||||
@ -1202,6 +1224,12 @@ body {
|
||||
width: min-content;
|
||||
flex-basis: max-content; }
|
||||
|
||||
.LP-Form--inline fieldset.LP-Form__Fieldset {
|
||||
max-width: unset; }
|
||||
|
||||
.LP-Form--inline div.LP-Form__Composition {
|
||||
padding: 0; }
|
||||
|
||||
@media (max-width: 450px) {
|
||||
.LP-Form:not(.LP-Form--inline) .LP-Form__Composition {
|
||||
flex-wrap: wrap; } }
|
||||
@ -1396,6 +1424,7 @@ body {
|
||||
width: 700px;
|
||||
max-height: 500px;
|
||||
box-shadow: 0 0 10px #565656;
|
||||
box-shadow: 0 0 10px #565656;
|
||||
object-fit: cover;
|
||||
object-position: 0 0;
|
||||
margin: 0;
|
||||
@ -1414,3 +1443,116 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-bottom: 25px; } }
|
||||
|
||||
.tagify {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap; }
|
||||
.tagify + input,
|
||||
.tagify + textarea {
|
||||
display: none; }
|
||||
|
||||
.tagify__tag {
|
||||
background-color: #bdbdbd;
|
||||
display: inline-flex;
|
||||
cursor: default;
|
||||
transition: .13s ease-out;
|
||||
height: max-content;
|
||||
align-items: center;
|
||||
gap: 3px; }
|
||||
.tagify__tag:hover {
|
||||
background-color: #e9e9e9; }
|
||||
|
||||
.tagify__input {
|
||||
flex-grow: 1;
|
||||
display: inline-block;
|
||||
min-width: 110px;
|
||||
margin: 5px;
|
||||
line-height: inherit;
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
margin-left: 15px;
|
||||
height: 1em; }
|
||||
|
||||
.tagify__tag__removeBtn {
|
||||
order: 5;
|
||||
cursor: pointer;
|
||||
font: 1em/1 Arial;
|
||||
transition: .2s ease-out;
|
||||
color: #76323F; }
|
||||
|
||||
.tagify__tag__removeBtn::after {
|
||||
content: "\00D7"; }
|
||||
|
||||
.tagify__tag__removeBtn:hover {
|
||||
color: #565656; }
|
||||
|
||||
.tagify__tag__removeBtn:hover + div > span {
|
||||
opacity: .5; }
|
||||
|
||||
.tagify__tag__removeBtn:hover + div::before {
|
||||
box-shadow: 0 0 0 1.1em rgba(211, 148, 148, 0.3) inset !important;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg) inset !important;
|
||||
transition: .2s; }
|
||||
|
||||
.tagify__tag--loading .tagify__tag__removeBtn {
|
||||
display: none; }
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix) .tagify__tag__removeBtn {
|
||||
display: none; }
|
||||
|
||||
.tagify__dropdown {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
transform: translateY(1px);
|
||||
overflow: hidden; }
|
||||
|
||||
.tagify__dropdown[placement=top] {
|
||||
margin-top: 0;
|
||||
transform: translateY(-100%); }
|
||||
|
||||
.tagify__dropdown[placement=top] .tagify__dropdown__wrapper {
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 0; }
|
||||
|
||||
.tagify__dropdown[position=text] {
|
||||
box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1);
|
||||
font-size: .9em; }
|
||||
|
||||
.tagify__dropdown[position=text] .tagify__dropdown__wrapper {
|
||||
border-width: 1px; }
|
||||
|
||||
.tagify__dropdown__wrapper {
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.2);
|
||||
transition: 0.25s cubic-bezier(0, 1, 0.5, 1); }
|
||||
|
||||
.tagify__dropdown__wrapper:hover {
|
||||
overflow: auto; }
|
||||
|
||||
.tagify__dropdown--initial .tagify__dropdown__wrapper {
|
||||
max-height: 20px;
|
||||
transform: translateY(-1em); }
|
||||
|
||||
.tagify__dropdown--initial[placement=top] .tagify__dropdown__wrapper {
|
||||
transform: translateY(2em); }
|
||||
|
||||
.tagify__dropdown__item {
|
||||
box-sizing: inherit;
|
||||
padding: .3em .5em;
|
||||
margin: 1px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
outline: 0;
|
||||
font-family: Montserrat, Helvetica, sans-serif; }
|
||||
|
||||
.tagify__dropdown__item--active {
|
||||
color: #f9f9f9;
|
||||
background-color: gray; }
|
||||
|
||||
.tagify__dropdown__item:active {
|
||||
filter: brightness(105%); }
|
||||
|
548
lostplaces/lostplaces_app/static/tagify.css
Normal file
548
lostplaces/lostplaces_app/static/tagify.css
Normal file
@ -0,0 +1,548 @@
|
||||
:root {
|
||||
--tagify-dd-color-primary: rgb(53, 149, 246);
|
||||
--tagify-dd-bg-color: white
|
||||
}
|
||||
|
||||
.tagify {
|
||||
--tags-border-color: #DDD;
|
||||
--tags-hover-border-color: #CCC;
|
||||
--tags-focus-border-color: #3595f6;
|
||||
--tag-bg: #E5E5E5;
|
||||
--tag-hover: #D3E2E2;
|
||||
--tag-text-color: black;
|
||||
--tag-text-color--edit: black;
|
||||
--tag-pad: 0.3em 0.5em;
|
||||
--tag-inset-shadow-size: 1.1em;
|
||||
--tag-invalid-color: #D39494;
|
||||
--tag-invalid-bg: rgba(211, 148, 148, 0.5);
|
||||
--tag-remove-bg: rgba(211, 148, 148, 0.3);
|
||||
--tag-remove-btn-color: black;
|
||||
--tag-remove-btn-bg: none;
|
||||
--tag-remove-btn-bg--hover: #c77777;
|
||||
--input-color: inherit;
|
||||
--tag--min-width: 1ch;
|
||||
--tag--max-width: auto;
|
||||
--tag-hide-transition: .3s;
|
||||
--placeholder-color: rgba(0, 0, 0, 0.4);
|
||||
--placeholder-color-focus: rgba(0, 0, 0, 0.25);
|
||||
--loader-size: .8em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--tags-border-color);
|
||||
padding: 0;
|
||||
line-height: 1.1;
|
||||
cursor: text;
|
||||
outline: 0;
|
||||
position: relative;
|
||||
transition: .1s
|
||||
}
|
||||
|
||||
@keyframes tags--bump {
|
||||
30% {
|
||||
transform: scale(1.2)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateLoader {
|
||||
to {
|
||||
transform: rotate(1turn)
|
||||
}
|
||||
}
|
||||
|
||||
.tagify:hover {
|
||||
border-color: #ccc;
|
||||
border-color: var(--tags-hover-border-color)
|
||||
}
|
||||
|
||||
.tagify.tagify--focus {
|
||||
transition: 0s;
|
||||
border-color: #3595f6;
|
||||
border-color: var(--tags-focus-border-color)
|
||||
}
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix) {
|
||||
cursor: default
|
||||
}
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix)>.tagify__input {
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
margin: 5px 0
|
||||
}
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix) .tagify__tag__removeBtn {
|
||||
display: none
|
||||
}
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix) .tagify__tag>div {
|
||||
padding: .3em .5em;
|
||||
padding: var(--tag-pad)
|
||||
}
|
||||
|
||||
.tagify[readonly]:not(.tagify--mix) .tagify__tag>div::before {
|
||||
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
|
||||
box-shadow: none;
|
||||
filter: brightness(.95)
|
||||
}
|
||||
|
||||
.tagify--loading .tagify__input::before {
|
||||
content: none
|
||||
}
|
||||
|
||||
.tagify--loading .tagify__input::after {
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
opacity: 1;
|
||||
width: .7em;
|
||||
height: .7em;
|
||||
width: var(--loader-size);
|
||||
height: var(--loader-size);
|
||||
border: 3px solid;
|
||||
border-color: #eee #bbb #888 transparent;
|
||||
border-radius: 50%;
|
||||
animation: rotateLoader .4s infinite linear;
|
||||
margin: -2px 0 -2px .5em
|
||||
}
|
||||
|
||||
.tagify--loading .tagify__input:empty::after {
|
||||
margin-left: 0
|
||||
}
|
||||
|
||||
.tagify+input,
|
||||
.tagify+textarea {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
.tagify__tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 5px 0 5px 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
outline: 0;
|
||||
cursor: default;
|
||||
transition: .13s ease-out
|
||||
}
|
||||
|
||||
.tagify__tag>div {
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
padding: .3em .5em;
|
||||
padding: var(--tag-pad);
|
||||
color: #000;
|
||||
color: var(--tag-text-color);
|
||||
line-height: inherit;
|
||||
border-radius: 3px;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
transition: .13s ease-out
|
||||
}
|
||||
|
||||
.tagify__tag>div>* {
|
||||
white-space: pre-wrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
min-width: var(--tag--min-width);
|
||||
max-width: var(--tag--max-width);
|
||||
transition: .8s ease, .1s color
|
||||
}
|
||||
|
||||
.tagify__tag>div>[contenteditable] {
|
||||
outline: 0;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
margin: -2px;
|
||||
padding: 2px;
|
||||
max-width: 350px
|
||||
}
|
||||
|
||||
.tagify__tag>div::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-radius: inherit;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
transition: 120ms ease;
|
||||
animation: tags--bump .3s ease-out 1;
|
||||
box-shadow: 0 0 0 1.1em #e5e5e5 inset;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset
|
||||
}
|
||||
|
||||
.tagify__tag:hover:not([readonly]) div::before {
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
left: -2px;
|
||||
box-shadow: 0 0 0 1.1em #d3e2e2 inset;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-hover) inset
|
||||
}
|
||||
|
||||
.tagify__tag--loading {
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.tagify__tag--loading .tagify__tag__removeBtn {
|
||||
display: none
|
||||
}
|
||||
|
||||
.tagify__tag--loading::after {
|
||||
--loader-size: .4em;
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
opacity: 1;
|
||||
width: .7em;
|
||||
height: .7em;
|
||||
width: var(--loader-size);
|
||||
height: var(--loader-size);
|
||||
border: 3px solid;
|
||||
border-color: #eee #bbb #888 transparent;
|
||||
border-radius: 50%;
|
||||
animation: rotateLoader .4s infinite linear;
|
||||
margin: 0 .5em 0 -.1em
|
||||
}
|
||||
|
||||
.tagify__tag--flash div::before {
|
||||
animation: none
|
||||
}
|
||||
|
||||
.tagify__tag--hide {
|
||||
width: 0 !important;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: .3s;
|
||||
transition: var(--tag-hide-transition);
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.tagify__tag.tagify--noAnim>div::before {
|
||||
animation: none
|
||||
}
|
||||
|
||||
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div>span {
|
||||
opacity: .5
|
||||
}
|
||||
|
||||
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div::before {
|
||||
box-shadow: 0 0 0 1.1em rgba(211, 148, 148, .5) inset !important;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-invalid-bg) inset !important;
|
||||
transition: .2s
|
||||
}
|
||||
|
||||
.tagify__tag[readonly] .tagify__tag__removeBtn {
|
||||
display: none
|
||||
}
|
||||
|
||||
.tagify__tag[readonly]>div::before {
|
||||
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
|
||||
box-shadow: none;
|
||||
filter: brightness(.95)
|
||||
}
|
||||
|
||||
.tagify__tag--editable>div {
|
||||
color: #000;
|
||||
color: var(--tag-text-color--edit)
|
||||
}
|
||||
|
||||
.tagify__tag--editable>div::before {
|
||||
box-shadow: 0 0 0 2px #d3e2e2 inset !important;
|
||||
box-shadow: 0 0 0 2px var(--tag-hover) inset !important
|
||||
}
|
||||
|
||||
.tagify__tag--editable.tagify--invalid>div::before {
|
||||
box-shadow: 0 0 0 2px #d39494 inset !important;
|
||||
box-shadow: 0 0 0 2px var(--tag-invalid-color) inset !important
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn {
|
||||
order: 5;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font: 14px/1 Arial;
|
||||
background: 0 0;
|
||||
background: var(--tag-remove-btn-bg);
|
||||
color: #000;
|
||||
color: var(--tag-remove-btn-color);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 4.66667px;
|
||||
margin-left: -4.66667px;
|
||||
transition: .2s ease-out
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn::after {
|
||||
content: "\00D7"
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn:hover {
|
||||
color: #fff;
|
||||
background: #c77777;
|
||||
background: var(--tag-remove-btn-bg--hover)
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn:hover+div>span {
|
||||
opacity: .5
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn:hover+div::before {
|
||||
box-shadow: 0 0 0 1.1em rgba(211, 148, 148, .3) inset !important;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg) inset !important;
|
||||
transition: .2s
|
||||
}
|
||||
|
||||
.tagify:not(.tagify--mix) .tagify__input br {
|
||||
display: none
|
||||
}
|
||||
|
||||
.tagify:not(.tagify--mix) .tagify__input * {
|
||||
display: inline;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.tagify__input {
|
||||
flex-grow: 1;
|
||||
display: inline-block;
|
||||
min-width: 110px;
|
||||
margin: 5px;
|
||||
padding: .3em .5em;
|
||||
padding: var(--tag-pad, .3em .5em);
|
||||
line-height: inherit;
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
color: inherit;
|
||||
color: var(--input-color)
|
||||
}
|
||||
|
||||
.tagify__input:empty::before {
|
||||
transition: .2s ease-out;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
display: inline-block;
|
||||
width: auto
|
||||
}
|
||||
|
||||
.tagify--mix .tagify__input:empty::before {
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.tagify__input:focus {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
.tagify__input:focus::before {
|
||||
transition: .2s ease-out;
|
||||
opacity: 0;
|
||||
transform: translatex(6px)
|
||||
}
|
||||
|
||||
@media all and (-ms-high-contrast:none),
|
||||
(-ms-high-contrast:active) {
|
||||
.tagify__input:focus::before {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-ms-ime-align:auto) {
|
||||
.tagify__input:focus::before {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
.tagify__input:focus:empty::before {
|
||||
transition: .2s ease-out;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
color: rgba(0, 0, 0, .25);
|
||||
color: var(--placeholder-color-focus)
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.tagify__input:focus:empty::after {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
.tagify__input::before {
|
||||
content: attr(data-placeholder);
|
||||
height: 1em;
|
||||
line-height: 1em;
|
||||
margin: auto 0;
|
||||
z-index: 1;
|
||||
color: rgba(0, 0, 0, .4);
|
||||
color: var(--placeholder-color);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.tagify--mix .tagify__input::before {
|
||||
display: none;
|
||||
position: static;
|
||||
line-height: inherit
|
||||
}
|
||||
|
||||
.tagify__input::after {
|
||||
content: attr(data-suggest);
|
||||
display: inline-block;
|
||||
white-space: pre;
|
||||
color: #000;
|
||||
opacity: .3;
|
||||
pointer-events: none;
|
||||
max-width: 100px
|
||||
}
|
||||
|
||||
.tagify__input .tagify__tag {
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.tagify__input .tagify__tag>div {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0
|
||||
}
|
||||
|
||||
.tagify--mix {
|
||||
display: block
|
||||
}
|
||||
|
||||
.tagify--mix .tagify__input {
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 1.5
|
||||
}
|
||||
|
||||
.tagify--mix .tagify__input::before {
|
||||
height: auto
|
||||
}
|
||||
|
||||
.tagify--mix .tagify__input::after {
|
||||
content: none
|
||||
}
|
||||
|
||||
.tagify--select::after {
|
||||
content: '>';
|
||||
opacity: .5;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font: 16px monospace;
|
||||
line-height: 8px;
|
||||
height: 8px;
|
||||
pointer-events: none;
|
||||
transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg);
|
||||
transition: .2s ease-in-out
|
||||
}
|
||||
|
||||
.tagify--select[aria-expanded=true]::after {
|
||||
transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2)
|
||||
}
|
||||
|
||||
.tagify--select .tagify__tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1.8em;
|
||||
bottom: 0
|
||||
}
|
||||
|
||||
.tagify--select .tagify__tag div {
|
||||
display: none
|
||||
}
|
||||
|
||||
.tagify--select .tagify__input {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.tagify--invalid {
|
||||
--tags-border-color: #D39494
|
||||
}
|
||||
|
||||
.tagify__dropdown {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
transform: translateY(1px);
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.tagify__dropdown[placement=top] {
|
||||
margin-top: 0;
|
||||
transform: translateY(-100%)
|
||||
}
|
||||
|
||||
.tagify__dropdown[placement=top] .tagify__dropdown__wrapper {
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 0
|
||||
}
|
||||
|
||||
.tagify__dropdown[position=text] {
|
||||
box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), .1);
|
||||
font-size: .9em
|
||||
}
|
||||
|
||||
.tagify__dropdown[position=text] .tagify__dropdown__wrapper {
|
||||
border-width: 1px
|
||||
}
|
||||
|
||||
.tagify__dropdown__wrapper {
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
background: var(--tagify-dd-bg-color);
|
||||
border: 1px solid #3595f6;
|
||||
border-color: var(--tagify-dd-color-primary);
|
||||
border-top-width: 0;
|
||||
box-shadow: 0 2px 4px -2px rgba(0, 0, 0, .2);
|
||||
transition: .25s cubic-bezier(0, 1, .5, 1)
|
||||
}
|
||||
|
||||
.tagify__dropdown__wrapper:hover {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.tagify__dropdown--initial .tagify__dropdown__wrapper {
|
||||
max-height: 20px;
|
||||
transform: translateY(-1em)
|
||||
}
|
||||
|
||||
.tagify__dropdown--initial[placement=top] .tagify__dropdown__wrapper {
|
||||
transform: translateY(2em)
|
||||
}
|
||||
|
||||
.tagify__dropdown__item {
|
||||
box-sizing: inherit;
|
||||
padding: .3em .5em;
|
||||
margin: 1px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
outline: 0
|
||||
}
|
||||
|
||||
.tagify__dropdown__item--active {
|
||||
background: #3595f6;
|
||||
background: var(--tagify-dd-color-primary);
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.tagify__dropdown__item:active {
|
||||
filter: brightness(105%)
|
||||
}
|
1
lostplaces/lostplaces_app/static/tagify.min.js
vendored
Normal file
1
lostplaces/lostplaces_app/static/tagify.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -4,6 +4,9 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% block additional_head %}
|
||||
{% endblock additional_head %}
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static 'main.css' %}">
|
||||
@ -12,9 +15,6 @@
|
||||
{% block title %}Urban Exploration{% endblock %}
|
||||
</title>
|
||||
|
||||
{% block additional_head %}
|
||||
{% endblock additional_head %}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,16 +1,18 @@
|
||||
{% load widget_tweaks %}
|
||||
|
||||
<div class="LP-Input {% 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>
|
||||
{% render_field field class="LP-Input__Field"%}
|
||||
{% with class="LP-Input__Field "%}
|
||||
{% render_field field class=class%}
|
||||
{% endwith %}
|
||||
|
||||
<span class="LP-Input__Message">
|
||||
{% if field.errors %}
|
||||
{% for error in field.errors%}
|
||||
{{error}}
|
||||
{% endfor %}
|
||||
{% if field.errors %}
|
||||
{% for error in field.errors%}
|
||||
{{error}}
|
||||
{% endfor %}
|
||||
{% elif field.help_text%}
|
||||
{{ field.help_text }}
|
||||
{{ field.help_text }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
66
lostplaces/lostplaces_app/templates/partials/tagging.html
Normal file
66
lostplaces/lostplaces_app/templates/partials/tagging.html
Normal file
@ -0,0 +1,66 @@
|
||||
<div class="LP-TagList">
|
||||
<ul class="LP-TagList__List">
|
||||
{% for tag in tag_list %}
|
||||
<li class="LP-TagList__Item">
|
||||
<div class="LP-Tag">
|
||||
<a href="#" class="LP-Link">
|
||||
<span class="LP-Link__Text">{{tag}}</span>
|
||||
</a>
|
||||
{% if request.user and request.user == config.tagged_item.submitted_by %}
|
||||
<a href="{% url config.delete_url_name tagged_id=config.tagged_item.id tag_id=tag.id %}" class="LP-Link">
|
||||
<span class="LP-Tag__Remove RV-Iconized__Container RV-Iconized__Container--extraSmall">
|
||||
<svg class="RV-Iconized__Icon" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M19 6.4L17.6 5 12 10.6 6.4 5 5 6.4 10.6 12 5 17.6 6.4 19 12 13.4 17.6 19 19 17.6 13.4 12z">
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form id="id_tag_submit_form" class="LP-Form LP-Form--inline LP-Form--tagging" method="POST" action="{{config.submit_url}}">
|
||||
<fieldset class="LP-Form__Fieldset">
|
||||
<legend class="LP-Form__Legend">Tags hinzufügen</legend>
|
||||
{% csrf_token %}
|
||||
<div class="LP-Form__Composition LP-Form__Composition--breakable">
|
||||
<div class="LP-Form__Field LP-Form__Button LP-Input LP-Input--tagging">
|
||||
<button id="id_tag_submit_button" class="LP-Button"> Tags hinzufügen</button>
|
||||
</div>
|
||||
<div class="LP-Form__Field">
|
||||
{% include 'partials/form/inputField.html' with field=config.submit_form.tag_list classes="LP-Input--tagging" %}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const input = document.getElementById('{{config.submit_form.tag_list.auto_id}}')
|
||||
const submit_form = document.getElementById('id_tag_submit_form')
|
||||
const submit_button = document.getElementById('id_tag_submit_button')
|
||||
|
||||
submit_form.onsubmit = () => false
|
||||
|
||||
const tagify = new Tagify(input, {
|
||||
'whitelist': [
|
||||
{% for tag in all_tags %}
|
||||
'{{tag}}',
|
||||
{% endfor %}
|
||||
]
|
||||
})
|
||||
|
||||
const on_form_submit = function() {
|
||||
concat_value = ''
|
||||
console.log(tagify)
|
||||
concat_value = tagify.value.map(value => value.value).join(',')
|
||||
console.log(concat_value)
|
||||
input.value = concat_value
|
||||
submit_form.submit()
|
||||
}
|
||||
|
||||
submit_button.onclick = on_form_submit
|
||||
</script>
|
@ -6,102 +6,103 @@
|
||||
|
||||
{% block additional_head %}
|
||||
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
|
||||
|
||||
|
||||
<script src="{% static 'maps/ol.js' %}"></script>
|
||||
|
||||
<script src="{% static 'tagify.min.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'minimal.css' %}" type="text/css">
|
||||
{% endblock additional_head %}
|
||||
|
||||
{% 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>
|
||||
<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>
|
||||
<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>
|
||||
<div class="LP-PlaceDetail__Description">
|
||||
<p class="LP-Paragraph">{{ place.description }}</p>
|
||||
</div>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Map-Links</h1>
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
<li class="LP-LinkList__Item"><a target="_blank"
|
||||
href="https://www.google.com/maps?q={{place.latitude}},{{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">
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Photoalben</h1>
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
{% for photo_album in place.photo_albums.all %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
|
||||
<span class="LP-Text">{{photo_album.label}}</span>
|
||||
</a>
|
||||
{% if user == photo_album.submitted_by or user == place.submitted_by %}
|
||||
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
{% icon 'trash' className="RV-Iconized__Icon" %}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a href="{% url 'photo_album_create' place_id=place.id %}" class="LP-Link">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
<svg class="RV-Iconized__Icon" version="1.1" id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216
|
||||
{% url 'place_tag_submit' place_id=place.id as tag_submit_url%}
|
||||
{% include 'partials/tagging.html' with tag_list=place.tags.all config=tagging_config all_tags=all_tags %}
|
||||
|
||||
</section>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Map-Links</h1>
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{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">Photoalben</h1>
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
{% for photo_album in place.photo_albums.all %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
|
||||
<span class="LP-Text">{{photo_album.label}}</span>
|
||||
</a>
|
||||
{% if user == photo_album.submitted_by or user == place.submitted_by %}
|
||||
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
{% icon 'trash' className="RV-Iconized__Icon" %}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a href="{% url 'photo_album_create' place_id=place.id %}" class="LP-Link">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
<svg class="RV-Iconized__Icon" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216
|
||||
v216c0,11.046,8.954,20,20,20s20-8.954,20-20V276h216c11.046,0,20-8.954,20-20C512,244.954,503.046,236,492,236z" />
|
||||
</g>
|
||||
</svg>
|
||||
<span class="RV-Iconized__Text">Fotoalbum hinzufügen</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="RV-Iconized__Text">Fotoalbum hinzufügen</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Bilder</h1>
|
||||
<div class="LP-ImageGrid">
|
||||
<ul class="LP-ImageGrid__Container">
|
||||
{% 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>
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Bilder</h1>
|
||||
<div class="LP-ImageGrid">
|
||||
<ul class="LP-ImageGrid__Container">
|
||||
{% 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 %}
|
@ -8,7 +8,9 @@ from .views import (
|
||||
PlaceUpdateView,
|
||||
PlaceDeleteView,
|
||||
PhotoAlbumCreateView,
|
||||
PhotoAlbumDeleteView
|
||||
PhotoAlbumDeleteView,
|
||||
PlaceTagSubmitView,
|
||||
PlaceTagDeleteView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@ -20,5 +22,8 @@ urlpatterns = [
|
||||
path('photo_album/delete/<int:pk>', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'),
|
||||
path('place/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'),
|
||||
path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'),
|
||||
path('place/', PlaceListView.as_view(), name='place_list')
|
||||
path('place/', PlaceListView.as_view(), name='place_list'),
|
||||
# POST-only URL for tag submission
|
||||
path('place/tag/<int:place_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
|
||||
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete')
|
||||
]
|
||||
|
@ -11,7 +11,9 @@ from django.urls import reverse_lazy
|
||||
|
||||
from lostplaces_app.models import Place, PlaceImage
|
||||
from lostplaces_app.views import IsAuthenticated, IsPlaceSubmitter
|
||||
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm
|
||||
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm
|
||||
|
||||
from taggit.models import Tag
|
||||
|
||||
class PlaceListView(IsAuthenticated, ListView):
|
||||
paginate_by = 5
|
||||
@ -30,7 +32,14 @@ class PlaceDetailView(IsAuthenticated, View):
|
||||
context = {
|
||||
'place': place,
|
||||
'place_list': [ place ],
|
||||
'place_map_center': [ place.latitude, place.longitude ]
|
||||
'place_map_center': [ place.latitude, place.longitude ],
|
||||
'all_tags': Tag.objects.all(),
|
||||
'tagging_config': {
|
||||
'submit_url': reverse_lazy('place_tag_submit', kwargs={'place_id': place.id}),
|
||||
'submit_form': TagSubmitForm(),
|
||||
'tagged_item': place,
|
||||
'delete_url_name': 'place_tag_delete'
|
||||
}
|
||||
}
|
||||
return render(request, 'place/place_detail.html', context)
|
||||
|
||||
|
@ -5,14 +5,19 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib import messages
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponseForbidden
|
||||
|
||||
from lostplaces_app.forms import ExplorerCreationForm
|
||||
from lostplaces_app.forms import ExplorerCreationForm, TagSubmitForm
|
||||
from lostplaces_app.models import Place, PhotoAlbum
|
||||
from lostplaces_app.views.base_views import IsAuthenticated
|
||||
|
||||
from lostplaces_app.views.base_views import (
|
||||
PlaceAssetCreateView,
|
||||
PlaceAssetDeleteView
|
||||
PlaceAssetDeleteView,
|
||||
)
|
||||
|
||||
from taggit.models import Tag
|
||||
|
||||
class SignUpView(SuccessMessageMixin, CreateView):
|
||||
form_class = ExplorerCreationForm
|
||||
success_url = reverse_lazy('login')
|
||||
@ -39,4 +44,26 @@ class PhotoAlbumDeleteView(PlaceAssetDeleteView):
|
||||
model = PhotoAlbum
|
||||
pk_url_kwarg = 'pk'
|
||||
success_message = 'Photo Album deleted'
|
||||
permission_denied_messsage = 'You do not have permissions to alter this photo album'
|
||||
permission_denied_messsage = 'You do not have permissions to alter this photo album'
|
||||
|
||||
class PlaceTagSubmitView(IsAuthenticated, View):
|
||||
def post(self, request, place_id, *args, **kwargs):
|
||||
place = Place.objects.get(pk=place_id)
|
||||
form = TagSubmitForm(request.POST)
|
||||
if form.is_valid():
|
||||
tag_list_raw = form.cleaned_data['tag_list']
|
||||
tag_list_raw = tag_list_raw.strip().split(',')
|
||||
tag_list = []
|
||||
for tag in tag_list_raw:
|
||||
tag_list.append(tag.strip())
|
||||
place.tags.add(*tag_list)
|
||||
place.save()
|
||||
|
||||
return redirect(reverse_lazy('place_detail', kwargs={'pk': place.id}))
|
||||
|
||||
class PlaceTagDeleteView(IsAuthenticated, View):
|
||||
def get(self, request, tagged_id, tag_id, *args, **kwargs):
|
||||
place = Place.objects.get(pk=tagged_id)
|
||||
tag = Tag.objects.get(pk=tag_id)
|
||||
place.tags.remove(tag)
|
||||
return redirect(reverse_lazy('place_detail', kwargs={'pk': tagged_id}))
|
Loading…
Reference in New Issue
Block a user