Compare commits
59 Commits
0.1
...
e00c9318fa
| Author | SHA1 | Date | |
|---|---|---|---|
| e00c9318fa | |||
| 6ed6c2c990 | |||
| 328e6899a6 | |||
| 843832d978 | |||
| e4cd8bb301 | |||
| 66581a9d2d | |||
| 490a0e9f3e | |||
| 7a3b8529f8 | |||
| 538b43c2a1 | |||
| ca2eac533f | |||
| 2007b512c7 | |||
| 751d4c81fe | |||
| 7a757bcf35 | |||
| 55b8d16751 | |||
| f34d70edd5 | |||
| 78f0e80136 | |||
| c84be34a37 | |||
| 3959096c95 | |||
| 76daa71217 | |||
| 0707d2d51e | |||
| d45724b774 | |||
| c8e46d42ee | |||
| 591469799f | |||
| 3cd44959b1 | |||
| ffef52269a | |||
| 9a30ced90c | |||
| d44bf0c0f2 | |||
| cad53c070c | |||
| a0627761a7 | |||
| 04940a0901 | |||
| 3ef31eda0f | |||
| 7cb8bc6bac | |||
| 636bb880c1 | |||
| c9d9ca9de0 | |||
| 5f3be5fc83 | |||
| f916ac9742 | |||
| 03af8a0c02 | |||
| 65f3642272 | |||
| e00a0f687b | |||
| 9af55d3f24 | |||
| 719e75a449 | |||
| 6688536a78 | |||
| 7835ddcf16 | |||
| 3caada8f08 | |||
| 4113811c5f | |||
| a660763ea4 | |||
| 55fe79b82c | |||
| b34e88a3ad | |||
| f52dd733d8 | |||
| ff428beaef | |||
| e8c4faff8e | |||
| 0b0a401486 | |||
| 2b56eed759 | |||
| e387091da6 | |||
| 5c5ad9502c | |||
| baf3f54e1a | |||
| 8cb7e61274 | |||
| fa1274c595 | |||
| a5839c2c36 |
@@ -11,6 +11,8 @@ django = "*"
|
||||
easy-thumbnails = "*"
|
||||
image = "*"
|
||||
django-widget-tweaks = "*"
|
||||
django-svg-icons = "*"
|
||||
django-taggit = "*"
|
||||
|
||||
# Commented out to not explicitly specify Python 3 subversion.
|
||||
# [requires]
|
||||
|
||||
@@ -9,7 +9,11 @@ Right now it depends on the following non-core Python 3 libraries. These can be
|
||||
|
||||
* [django](https://www.djangoproject.com/) django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
|
||||
* [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.
|
||||
|
||||
|
||||
## 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.
|
||||
@@ -18,6 +22,7 @@ After having obtained the repository contents (either via .zip download or git c
|
||||
$ cd lostplaces-backend
|
||||
$ pipenv install
|
||||
$ 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
|
||||
@@ -33,4 +38,65 @@ $ pipenv shell
|
||||
Visit: [admin](http://localhost:8000/admin) for administrative backend or
|
||||
[frontend](http://localhost:8000/)
|
||||
|
||||
## Installing lostplaces
|
||||
|
||||
### Install dependencies
|
||||
Python3, Django3, easy-thumbnails, image, django-widget-tweaks
|
||||
```
|
||||
pip install --user django easy-thumbnails image django-widget-tweaks
|
||||
```
|
||||
Or, if you use pipenv
|
||||
```
|
||||
pipenv install
|
||||
```
|
||||
|
||||
|
||||
### Add 'lostplaces_app' to your INSTALLED_APPS setting like this
|
||||
|
||||
```
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'lostplaces_app',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks',
|
||||
]
|
||||
```
|
||||
|
||||
### Add this configuration to your settings.py
|
||||
|
||||
```
|
||||
from django.urls import reverse_lazy
|
||||
...
|
||||
AUTH_USER_MODEL = 'lostplaces_app.Explorer'
|
||||
|
||||
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},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Include the lostplaces URLconf in your project urls.py like this
|
||||
|
||||
```
|
||||
from django.urls import path, include
|
||||
...
|
||||
urlpatterns = [
|
||||
...
|
||||
path('lostplaces/', include('lostplaces_app.urls')),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
```
|
||||
|
||||
Run ``python manage.py migrate`` to create the lost places models.
|
||||
|
||||
Start the development server and visit http://127.0.0.1:8000/admin/
|
||||
|
||||
Visit http://127.0.0.1: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
|
||||
@@ -44,6 +44,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks',
|
||||
'taggit'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -148,3 +149,5 @@ THUMBNAIL_ALIASES = {
|
||||
'large': {'size': (1920, 1920), 'crop': False},
|
||||
},
|
||||
}
|
||||
|
||||
SVG_ICONS_SOURCE_FILE = os.path.join(BASE_DIR, 'lostplaces_app', 'static', 'icons', 'icons.icomoon.json')
|
||||
@@ -26,3 +26,4 @@ admin.site.register(Explorer, ExplorerAdmin)
|
||||
admin.site.register(Voucher, VoucherAdmin)
|
||||
admin.site.register(Place)
|
||||
admin.site.register(PlaceImage)
|
||||
admin.site.register(PhotoAlbum)
|
||||
|
||||
@@ -48,3 +48,7 @@ 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)
|
||||
@@ -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.
|
||||
|
||||
@@ -29,7 +30,7 @@ class Voucher(models.Model):
|
||||
Creation date is being set automatically during voucher creation.
|
||||
"""
|
||||
|
||||
code = models.CharField(unique=True, max_length=10)
|
||||
code = models.CharField(unique=True, max_length=30)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
expires = models.DateField()
|
||||
|
||||
@@ -55,6 +56,23 @@ 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):
|
||||
amount = len(place_list)
|
||||
# Init fill values to prevent None
|
||||
longitude = 0
|
||||
latitude = 0
|
||||
|
||||
if amount > 0:
|
||||
for place in place_list:
|
||||
longitude += place.longitude
|
||||
latitude += place.latitude
|
||||
return (latitude / amount, longitude / amount)
|
||||
|
||||
return (latitude, longitude)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@@ -126,3 +144,24 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
|
||||
if not old_file == new_file:
|
||||
if os.path.isfile(old_file.path):
|
||||
os.remove(old_file.path)
|
||||
|
||||
|
||||
class ExternalLink(models.Model):
|
||||
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)
|
||||
|
||||
class PhotoAlbum(ExternalLink):
|
||||
place = models.ForeignKey(
|
||||
Place,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='photo_albums',
|
||||
null=True
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
<svg height="427pt" viewBox="-40 0 427 427.00131" width="427pt" xmlns="http://www.w3.org/2000/svg"><path d="m232.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/><path d="m114.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/><path d="m28.398438 127.121094v246.378906c0 14.5625 5.339843 28.238281 14.667968 38.050781 9.285156 9.839844 22.207032 15.425781 35.730469 15.449219h189.203125c13.527344-.023438 26.449219-5.609375 35.730469-15.449219 9.328125-9.8125 14.667969-23.488281 14.667969-38.050781v-246.378906c18.542968-4.921875 30.558593-22.835938 28.078124-41.863282-2.484374-19.023437-18.691406-33.253906-37.878906-33.257812h-51.199218v-12.5c.058593-10.511719-4.097657-20.605469-11.539063-28.03125-7.441406-7.421875-17.550781-11.5546875-28.0625-11.46875h-88.796875c-10.511719-.0859375-20.621094 4.046875-28.0625 11.46875-7.441406 7.425781-11.597656 17.519531-11.539062 28.03125v12.5h-51.199219c-19.1875.003906-35.394531 14.234375-37.878907 33.257812-2.480468 19.027344 9.535157 36.941407 28.078126 41.863282zm239.601562 279.878906h-189.203125c-17.097656 0-30.398437-14.6875-30.398437-33.5v-245.5h250v245.5c0 18.8125-13.300782 33.5-30.398438 33.5zm-158.601562-367.5c-.066407-5.207031 1.980468-10.21875 5.675781-13.894531 3.691406-3.675781 8.714843-5.695313 13.925781-5.605469h88.796875c5.210937-.089844 10.234375 1.929688 13.925781 5.605469 3.695313 3.671875 5.742188 8.6875 5.675782 13.894531v12.5h-128zm-71.199219 32.5h270.398437c9.941406 0 18 8.058594 18 18s-8.058594 18-18 18h-270.398437c-9.941407 0-18-8.058594-18-18s8.058593-18 18-18zm0 0"/><path d="m173.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1 +1,4 @@
|
||||
<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>
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 358 B |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"trash": {
|
||||
"paths": [
|
||||
"m232.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0",
|
||||
"m114.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0",
|
||||
"m28.398438 127.121094v246.378906c0 14.5625 5.339843 28.238281 14.667968 38.050781 9.285156 9.839844 22.207032 15.425781 35.730469 15.449219h189.203125c13.527344-.023438 26.449219-5.609375 35.730469-15.449219 9.328125-9.8125 14.667969-23.488281 14.667969-38.050781v-246.378906c18.542968-4.921875 30.558593-22.835938 28.078124-41.863282-2.484374-19.023437-18.691406-33.253906-37.878906-33.257812h-51.199218v-12.5c.058593-10.511719-4.097657-20.605469-11.539063-28.03125-7.441406-7.421875-17.550781-11.5546875-28.0625-11.46875h-88.796875c-10.511719-.0859375-20.621094 4.046875-28.0625 11.46875-7.441406 7.425781-11.597656 17.519531-11.539062 28.03125v12.5h-51.199219c-19.1875.003906-35.394531 14.234375-37.878907 33.257812-2.480468 19.027344 9.535157 36.941407 28.078126 41.863282zm239.601562 279.878906h-189.203125c-17.097656 0-30.398437-14.6875-30.398437-33.5v-245.5h250v245.5c0 18.8125-13.300782 33.5-30.398438 33.5zm-158.601562-367.5c-.066407-5.207031 1.980468-10.21875 5.675781-13.894531 3.691406-3.675781 8.714843-5.695313 13.925781-5.605469h88.796875c5.210937-.089844 10.234375 1.929688 13.925781 5.605469 3.695313 3.671875 5.742188 8.6875 5.675782 13.894531v12.5h-128zm-71.199219 32.5h270.398437c9.941406 0 18 8.058594 18 18s-8.058594 18-18 18h-270.398437c-9.941407 0-18-8.058594-18-18s8.058593-18 18-18zm0 0",
|
||||
"m173.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"
|
||||
],
|
||||
"height": "427pt",
|
||||
"width": "427pt",
|
||||
"viewBox": "-40 0 427 427.00131"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width;transition:all .25s}.ol-scale-bar{position:absolute;bottom:8px;left:8px}.ol-scale-step-marker{width:1px;height:15px;background-color:#000;float:right;z-Index:10}.ol-scale-step-text{position:absolute;bottom:-5px;font-size:12px;z-Index:11;color:#000;text-shadow:-2px 0 #fff,0 2px #fff,2px 0 #fff,0 -2px #fff}.ol-scale-text{position:absolute;font-size:14px;text-align:center;bottom:25px;color:#000;text-shadow:-2px 0 #fff,0 2px #fff,2px 0 #fff,0 -2px #fff}.ol-scale-singlebar{position:relative;height:10px;z-Index:9;border:1px solid #000}.ol-unsupported{display:none}.ol-unselectable,.ol-viewport{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-overlaycontainer,.ol-overlaycontainer-stopevent{pointer-events:none}.ol-overlaycontainer-stopevent>*,.ol-overlaycontainer>*{pointer-events:auto}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.ol-control{position:absolute;background-color:rgba(255,255,255,.4);border-radius:4px;padding:2px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-control button span{pointer-events:none}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move}
|
||||
/*# sourceMappingURL=ol.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["src/ol/ol.css"],"names":[],"mappings":"AAAA,QACE,WAAY,WACZ,cAAe,IACf,OAAQ,IAAI,MAAM,KAGpB,mBACE,IAAK,IACL,MAAO,IACP,SAAU,SAGZ,eACE,WAAY,kBACZ,cAAe,IACf,OAAQ,IACR,KAAM,IACN,QAAS,IACT,SAAU,SAEZ,qBACE,OAAQ,IAAI,MAAM,KAClB,WAAY,KACZ,MAAO,KACP,UAAW,KACX,WAAY,OACZ,OAAQ,IACR,YAAa,QAAQ,CAAE,MACvB,WAAY,IAAI,KAElB,cACE,SAAU,SACV,OAAQ,IACR,KAAM,IAER,sBACE,MAAO,IACP,OAAQ,KACR,iBAAkB,KAClB,MAAO,MACP,QAAS,GAEX,oBACE,SAAU,SACV,OAAQ,KACR,UAAW,KACX,QAAS,GACT,MAAO,KACP,YAAa,KAAK,EAAE,IAAO,CAAE,EAAE,IAAI,IAAO,CAAE,IAAI,EAAE,IAAO,CAAE,EAAE,KAAK,KAEpE,eACE,SAAU,SACV,UAAW,KACX,WAAY,OACZ,OAAQ,KACR,MAAO,KACP,YAAa,KAAK,EAAE,IAAO,CAAE,EAAE,IAAI,IAAO,CAAE,IAAI,EAAE,IAAO,CAAE,EAAE,KAAK,KAEpE,oBACE,SAAU,SACV,OAAQ,KACR,QAAS,EACT,OAAQ,IAAI,MAAM,KAGpB,gBACE,QAAS,KAEG,iBAAd,aACE,sBAAuB,KACvB,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KACb,4BAA6B,YAE/B,qBAAsB,+BACpB,eAAgB,KAEQ,iCAA1B,uBACE,eAAgB,KAElB,eACE,sBAAuB,QACvB,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KAEf,aACE,OAAQ,iBACR,OAAQ,cACR,OAAQ,SAEV,SACE,OAAQ,KACR,OAAQ,aACR,OAAQ,UACR,OAAQ,KAEV,YACE,SAAU,SACV,iBAAkB,qBAClB,cAAe,IACf,QAAS,IAEX,kBACE,iBAAkB,qBAEpB,SACE,IAAK,KACL,KAAM,KAER,WACE,IAAK,KACL,MAAO,KACP,WAAY,QAAQ,KAAK,MAAM,CAAE,WAAW,GAAG,OAEjD,qBACE,QAAS,EACT,WAAY,OACZ,WAAY,QAAQ,KAAK,MAAM,CAAE,WAAW,GAAG,OAAO,KAExD,gBACE,IAAK,QACL,KAAM,KAER,gBACE,MAAO,KACP,IAAK,KAGP,mBACE,QAAS,MACT,OAAQ,IACR,QAAS,EACT,MAAO,KACP,UAAW,OACX,YAAa,IACb,gBAAiB,KACjB,WAAY,OACZ,OAAQ,QACR,MAAO,QACP,YAAa,KACb,iBAAkB,kBAClB,OAAQ,KACR,cAAe,IAEjB,qCACE,OAAQ,KACR,QAAS,EAEX,wBACE,eAAgB,KAElB,uBACE,YAAa,MAEf,YACE,QAAS,MACT,YAAa,IACb,UAAW,MACX,YAAa,UAEf,6BACE,UAAW,MAEb,0BACE,IAAK,MAGP,yBADA,yBAEE,gBAAiB,KACjB,iBAAkB,kBAEpB,qBACE,cAAe,IAAI,IAAI,EAAE,EAE3B,sBACE,cAAe,EAAE,EAAE,IAAI,IAIzB,gBACE,WAAY,MACZ,OAAQ,KACR,MAAO,KACP,UAAW,mBAGb,mBACE,OAAQ,EACR,QAAS,EAAE,KACX,MAAO,KACP,YAAa,EAAE,EAAE,IAAI,KAEvB,mBACE,QAAS,OACT,WAAY,KAEd,0CACE,QAAS,IAEX,oBACE,WAAY,IACZ,UAAW,QACX,eAAgB,OAEE,uBAApB,mBACE,QAAS,aAEX,gCACE,QAAS,KAEX,mCACE,WAAY,qBAEd,iCACE,OAAQ,EACR,MAAO,EACP,cAAe,IAAI,EAAE,EAEvB,qCACE,WAAY,MACZ,WAAY,MAEd,wCACE,QAAS,KAGX,eACE,IAAK,MACL,KAAM,KACN,OAAQ,MAEV,sBACE,SAAU,SACV,OAAQ,KAGV,yBACE,IAAK,MAGP,gBACE,KAAM,KACN,OAAQ,KAEV,iCACE,OAAQ,EACR,KAAM,EACN,cAAe,EAAE,IAAI,EAAE,EAEzB,oCACA,uBACE,QAAS,aAEX,oCACE,OAAQ,IAAI,MAAM,QAClB,OAAQ,MACR,OAAQ,IACR,MAAO,MAET,0CACE,OAAQ,IACR,KAAM,IACN,SAAU,SAEZ,iDACA,wCACE,QAAS,KAEX,mCACE,WAAY,qBAEd,oBACE,OAAQ,IAAI,OAAO,kBAGrB,0CACE,OAAQ"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
# Keeping these files up-to-date is something for CI / release management.
|
||||
# But for now I noted the source urls down here for later reference.
|
||||
|
||||
https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/css/ol.css
|
||||
https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/css/ol.css.map
|
||||
https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/build/ol.js
|
||||
https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/build/ol.js.map
|
||||
@@ -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%)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -2,95 +2,85 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="{% static 'main.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>
|
||||
<head>
|
||||
{% block additional_head %}
|
||||
{% endblock additional_head %}
|
||||
|
||||
<body>
|
||||
<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>
|
||||
{% 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>
|
||||
</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 }}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static 'main.css' %}">
|
||||
<link rel="icon" type="image/png" href="{% static 'favicon.ico' %}">
|
||||
<title>
|
||||
{% block title %}Urban Exploration{% endblock %}
|
||||
</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<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>
|
||||
{% 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>
|
||||
</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>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block maincontent %}
|
||||
{% endblock maincontent %}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
<div class="LP-Message__Text">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block maincontent %}
|
||||
{% endblock maincontent %}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,10 +1,15 @@
|
||||
{% extends 'global.html'%}
|
||||
{% load static %}
|
||||
{% block additional_head %}
|
||||
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
|
||||
<script src="{% static 'maps/ol.js' %}"></script>
|
||||
{% endblock additional_head %}
|
||||
|
||||
# {% block title %}Start{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-PlaceGrid">
|
||||
<h1 class="LP-Headline LP-Headline">Explore the latest locations</h1>
|
||||
<ul class="LP-PlaceGrid__Grid">
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
<div id="map" class="map" style="height: 300px"></div>
|
||||
<div id="info" class="map-popup"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var map = new ol.Map({
|
||||
target: 'map',
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.OSM()
|
||||
}),
|
||||
],
|
||||
view: new ol.View({
|
||||
center: ol.proj.fromLonLat([{{place_map_center|last}}, {{place_map_center|first}}]),
|
||||
zoom: 9
|
||||
})
|
||||
});
|
||||
|
||||
var vectorSource = new ol.source.Vector({
|
||||
features: [
|
||||
{% for place in place_list %}
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(
|
||||
ol.proj.fromLonLat([{{place.longitude}},{{place.latitude}}])
|
||||
),
|
||||
url: '{% url 'place_detail' pk=place.pk %}',
|
||||
name: '{{place.name}}'
|
||||
}),
|
||||
{% endfor %}
|
||||
]
|
||||
});
|
||||
|
||||
var markerVectorLayer = new ol.layer.Vector({
|
||||
source: vectorSource,
|
||||
style: new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 46],
|
||||
anchorXUnits: 'fraction',
|
||||
anchorYUnits: 'pixels',
|
||||
scale: 0.3,
|
||||
src: 'http://icons.iconarchive.com/icons/paomedia/small-n-flat/128/map-marker-icon.png'
|
||||
})
|
||||
})
|
||||
});
|
||||
map.addLayer(markerVectorLayer);
|
||||
|
||||
var overlay = new ol.Overlay({
|
||||
element: document.getElementById('info'),
|
||||
positioning: 'bottom-left'
|
||||
});
|
||||
overlay.setMap(map);
|
||||
|
||||
map.on(['singleclick'], function(evt) {
|
||||
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
window.open(feature.get('url'), '_blank');
|
||||
});
|
||||
});
|
||||
|
||||
map.on(['pointermove'], function(evt) {
|
||||
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
overlay.setPosition(evt.coordinate.map(element => element + 1));
|
||||
overlay.getElement().innerHTML = feature.get('name');
|
||||
return feature;
|
||||
});
|
||||
overlay.getElement().style.display = feature ? '' : 'none';
|
||||
document.body.style.cursor = feature ? 'pointer' : '';
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<div class="LP-TagList">
|
||||
<ul class="LP-TagList__List">
|
||||
{% for tag in tag_list %}
|
||||
<li class="LP-TagList__Item">
|
||||
<div class="LP-Tag">
|
||||
<p class="LP-Paragraph">{{tag}}</p>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form id="id_tag_submit_form" class="LP-Form LP-Form--inline LP-Form--tagging" method="POST" action="{{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=input_field classes="LP-Input--tagging" %}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const input = document.getElementById('{{input_field.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>
|
||||
@@ -0,0 +1,41 @@
|
||||
{% extends 'global.html'%}
|
||||
|
||||
{% block title %}Submit a photo album{% 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 %}
|
||||
<form class="LP-Form" method="POST">
|
||||
<fieldset class="LP-Form__Fieldset">
|
||||
<legend class="LP-Form__Legend">Submit a photo album for {{place.name}}</legend>
|
||||
{% csrf_token %}
|
||||
<div class="LP-Form__Composition">
|
||||
<div class="LP-Form__Field">
|
||||
{% include 'partials/form/inputField.html' with field=form.url %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="LP-Form__Composition">
|
||||
<div class="LP-Form__Field">
|
||||
{% include 'partials/form/inputField.html' with field=form.label %}
|
||||
</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">Submit</button>
|
||||
</div>
|
||||
<div class="LP-Form__Field LP-Form__Button LP-Input">
|
||||
<a class="LP-Link" href="{% url 'place_detail' pk=place.id%}">
|
||||
<button type="button" class="LP-Button LP-Button--cancel">Cancel</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% endblock maincontent %}
|
||||
@@ -1,23 +1,35 @@
|
||||
{% extends 'global.html'%}
|
||||
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
{% load svg_icon %}
|
||||
|
||||
{% 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>
|
||||
<figure class="LP-PlaceDetail__Image">
|
||||
<img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
|
||||
</figure>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
@@ -25,21 +37,64 @@
|
||||
<p class="LP-Paragraph">{{ place.description }}</p>
|
||||
</div>
|
||||
|
||||
<section class="LP-Section">
|
||||
|
||||
{% url 'place_tag_submit' place_id=place.id as tag_submit_url%}
|
||||
{% include 'partials/tagging.html' with tag_list=place.tags.all url=tag_submit_url input_field=tagging_form.tag_list%}
|
||||
|
||||
</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__List">
|
||||
<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>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Bilder</h1>
|
||||
<div class="LP-ImageGrid">
|
||||
<ul class="LP-ImageGrid__List">
|
||||
<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>
|
||||
@@ -48,6 +103,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</article>
|
||||
{% endblock maincontent %}
|
||||
@@ -1,9 +1,16 @@
|
||||
{% extends 'global.html'%}
|
||||
{% load static %}
|
||||
|
||||
{% block additional_head %}
|
||||
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
|
||||
<script src="{% static 'maps/ol.js' %}"></script>
|
||||
{% endblock additional_head %}
|
||||
|
||||
{% block title %}Lost Places{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-PlaceList">
|
||||
<h1 class="LP-Headline">Listing our places</h1>
|
||||
<ul class="LP-PlaceList__List">
|
||||
|
||||
+8
-8
@@ -16,16 +16,16 @@
|
||||
<div class="LP-Form__Field">
|
||||
{% include 'partials/form/inputField.html' with field=form.password %}
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
|
||||
<p class="LP-Headline">No account? <a class="LP-Link" href="{% url 'signup' %}"><span class="LP-Link__Text">Sign up here</span></a></p>
|
||||
|
||||
{% endblock maincontent %}
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="{{ width }}" height="{{ height }}" viewBox="{{viewBox}}" class="svg{% if className %} {{ className }}{% endif %}">
|
||||
{% for path in paths %}
|
||||
<path d="{{ path }}"></path>
|
||||
{% endfor %}
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 209 B |
@@ -0,0 +1,31 @@
|
||||
import json, os
|
||||
from importlib import import_module
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from django.template import Library, TemplateSyntaxError
|
||||
|
||||
#icons_json_path = getattr(settings, 'SVG_ICONS_SOURCE_FILE')
|
||||
icons_json_path = os.path.join(settings.BASE_DIR, 'lostplaces_app', 'static', 'icons', 'icons.icomoon.json')
|
||||
icons_json = json.load(open(icons_json_path))
|
||||
|
||||
register = Library()
|
||||
|
||||
@register.inclusion_tag('svg_icon/icon.html')
|
||||
def icon(name, **kwargs):
|
||||
|
||||
icon_config = icons_json.get(name, None)
|
||||
|
||||
if icon_config:
|
||||
|
||||
width = kwargs.get('width', icon_config.get('width', 16))
|
||||
height = kwargs.get('height', icon_config.get('heigh', 16))
|
||||
viewBox = kwargs.get('viewBox', icon_config.get('viewBox', '0 0 1024 1024'))
|
||||
|
||||
return {
|
||||
'width': kwargs.get('size', width),
|
||||
'height': kwargs.get('size', height),
|
||||
'className': kwargs.get('className'),
|
||||
'viewBox': viewBox,
|
||||
'paths': icon_config.get('paths', [''])
|
||||
}
|
||||
@@ -6,7 +6,10 @@ from .views import (
|
||||
SignUpView,
|
||||
PlaceCreateView,
|
||||
PlaceUpdateView,
|
||||
PlaceDeleteView
|
||||
PlaceDeleteView,
|
||||
PhotoAlbumCreateView,
|
||||
PhotoAlbumDeleteView,
|
||||
PlaceTagSubmitView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@@ -14,7 +17,10 @@ urlpatterns = [
|
||||
path('signup/', SignUpView.as_view(), name='signup'),
|
||||
path('place/<int:pk>/', PlaceDetailView.as_view(), name='place_detail'),
|
||||
path('place/create/', PlaceCreateView.as_view(), name='place_create'),
|
||||
path('photo_album/create/<int:place_id>', PhotoAlbumCreateView.as_view(), name='photo_album_create'),
|
||||
path('photo_album/delete/<int:pk>', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'),
|
||||
path('place/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'),
|
||||
path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'),
|
||||
path('place/', PlaceListView.as_view(), name='place_list')
|
||||
path('place/', PlaceListView.as_view(), name='place_list'),
|
||||
path('place/tag/<int:place_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from lostplaces_app.views.base_views import *
|
||||
from lostplaces_app.views.views import *
|
||||
from lostplaces_app.views.place_views import *
|
||||
@@ -0,0 +1,98 @@
|
||||
from django.views import View
|
||||
from django.views.generic.edit import CreateView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from lostplaces_app.models import Place
|
||||
|
||||
class IsAuthenticated(LoginRequiredMixin, View):
|
||||
redirect_field_name = 'redirect_to'
|
||||
permission_denied_message = 'Please login to proceed'
|
||||
|
||||
def handle_no_permission(self):
|
||||
messages.error(self.request, self.permission_denied_message)
|
||||
return super().handle_no_permission()
|
||||
|
||||
class IsPlaceSubmitter(UserPassesTestMixin, View):
|
||||
place_submitter_error_message = None
|
||||
|
||||
def get_place(self):
|
||||
pass
|
||||
|
||||
def test_func(self):
|
||||
""" Check if user is eligible to modify place. """
|
||||
|
||||
if not hasattr(self.request, 'user'):
|
||||
return False
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
return True
|
||||
|
||||
# Check if currently logged in user was the submitter
|
||||
place_obj = self.get_place()
|
||||
|
||||
if place_obj and hasattr(place_obj, 'submitted_by') and self.request.user == place_obj.submitted_by:
|
||||
return True
|
||||
|
||||
if self.place_submitter_error_message:
|
||||
messages.error(self.request, self.place_submitter_error_message)
|
||||
return False
|
||||
|
||||
class PlaceAssetCreateView(IsAuthenticated, SuccessMessageMixin, CreateView):
|
||||
model = None
|
||||
fields = []
|
||||
template_name = ''
|
||||
success_message = ''
|
||||
|
||||
def get(self, request, place_id, *args, **kwargs):
|
||||
self.place = Place.objects.get(pk=place_id)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, place_id, *args, **kwargs):
|
||||
self.place = Place.objects.get(pk=place_id)
|
||||
response = super().post(request, *args, **kwargs)
|
||||
self.object.place = self.place
|
||||
self.object.submitted_by = request.user
|
||||
self.object.save()
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['place'] = self.place
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('place_detail', kwargs={'pk': self.place.id})
|
||||
|
||||
class PlaceAssetDeleteView(IsAuthenticated, IsPlaceSubmitter, SingleObjectMixin, View):
|
||||
model = None
|
||||
pk_url_kwarg = 'pk'
|
||||
success_message = ''
|
||||
permission_denied_message = ''
|
||||
|
||||
def get_place(self):
|
||||
place_id = self.get_object().place.id
|
||||
return Place.objects.get(pk=place_id)
|
||||
|
||||
def test_func(self):
|
||||
can_edit_place = super().test_func()
|
||||
if can_edit_place:
|
||||
return True
|
||||
|
||||
if self.get_object().submitted_by == self.request.user:
|
||||
return True
|
||||
|
||||
messages.error(self.request, self.permission_denied_message)
|
||||
return False
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
place_id = self.get_object().place.id
|
||||
self.get_object().delete()
|
||||
messages.success(self.request, self.success_message)
|
||||
return redirect(reverse_lazy('place_detail', kwargs={'pk': place_id}))
|
||||
+33
-56
@@ -1,83 +1,56 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
''' 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, UpdateView, DeleteView
|
||||
from django.views.generic import ListView
|
||||
from django.views import View
|
||||
from django.http import Http404
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic import ListView
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
|
||||
from .forms import (
|
||||
ExplorerCreationForm,
|
||||
PlaceForm,
|
||||
PlaceImageCreateForm
|
||||
)
|
||||
from .models import Place, PlaceImage, Voucher
|
||||
from django.shortcuts import render, redirect
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
# Create your views here.
|
||||
from lostplaces_app.models import Place, PlaceImage
|
||||
from lostplaces_app.views import IsAuthenticated, IsPlaceSubmitter
|
||||
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm
|
||||
|
||||
# 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.'
|
||||
from taggit.models import Tag
|
||||
|
||||
class PlaceListView(IsAuthenticated, ListView):
|
||||
paginate_by = 2
|
||||
paginate_by = 5
|
||||
model = Place
|
||||
template_name = 'place/place_list.html'
|
||||
ordering = ['name']
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['place_map_center'] = Place.average_latlon(context['place_list'])
|
||||
return context
|
||||
|
||||
class PlaceDetailView(IsAuthenticated, View):
|
||||
def get(self, request, pk):
|
||||
place = Place.objects.get(pk=pk)
|
||||
context = {
|
||||
'place': Place.objects.get(pk=pk)
|
||||
'place': place,
|
||||
'place_list': [ place ],
|
||||
'place_map_center': [ place.latitude, place.longitude ],
|
||||
'tagging_form': TagSubmitForm(),
|
||||
'all_tags': Tag.objects.all()
|
||||
}
|
||||
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)
|
||||
|
||||
class PlaceUpdateView(IsAuthenticated, IsSubmitter, SuccessMessageMixin, UpdateView):
|
||||
class PlaceUpdateView(IsAuthenticated, IsPlaceSubmitter, SuccessMessageMixin, UpdateView):
|
||||
template_name = 'place/place_update.html'
|
||||
model = Place
|
||||
form_class = PlaceForm
|
||||
success_message = 'Successfully updated place.'
|
||||
place_submitter_error_message = 'You do no have permissions to alter this place'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('place_detail', kwargs={'pk':self.get_object().pk})
|
||||
|
||||
def get_place(self):
|
||||
return self.get_object()
|
||||
|
||||
class PlaceCreateView(IsAuthenticated, View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@@ -134,13 +107,17 @@ class PlaceCreateView(IsAuthenticated, View):
|
||||
)
|
||||
place_image.save()
|
||||
|
||||
class PlaceDeleteView(IsAuthenticated, IsSubmitter, DeleteView):
|
||||
class PlaceDeleteView(IsAuthenticated, IsPlaceSubmitter, DeleteView):
|
||||
template_name = 'place/place_delete.html'
|
||||
model = Place
|
||||
success_message = 'Successfully deleted place.'
|
||||
success_url = reverse_lazy('place_list')
|
||||
success_message = 'Place deleted'
|
||||
place_submitter_error_message = 'You do no have permission to delete this place'
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
def get_place(self):
|
||||
return self.get_object()
|
||||
@@ -0,0 +1,58 @@
|
||||
from django.views import View
|
||||
from django.views.generic.edit import CreateView
|
||||
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib import messages
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
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
|
||||
)
|
||||
class SignUpView(SuccessMessageMixin, CreateView):
|
||||
form_class = ExplorerCreationForm
|
||||
success_url = reverse_lazy('login')
|
||||
template_name = 'signup.html'
|
||||
success_message = 'User created.'
|
||||
|
||||
class HomeView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
place_list = Place.objects.all().order_by('-submitted_when')[:10]
|
||||
place_map_center = Place.average_latlon(place_list)
|
||||
context = {
|
||||
'place_list': place_list,
|
||||
'place_map_center': place_map_center
|
||||
}
|
||||
return render(request, 'home.html', context)
|
||||
|
||||
class PhotoAlbumCreateView(PlaceAssetCreateView):
|
||||
model = PhotoAlbum
|
||||
fields = ['url', 'label']
|
||||
template_name = 'photo_album/photo_album_create.html'
|
||||
success_message = 'Photo Album submitted'
|
||||
|
||||
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'
|
||||
|
||||
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}))
|
||||
Reference in New Issue
Block a user