45 Commits

Author SHA1 Message Date
5df7cc5ec6 #46 Large Map 2020-12-25 13:29:34 +01:00
e242dc4add Partial takes parameter to change size of map 2020-12-25 13:29:18 +01:00
f73fa616d0 Minor reordering 2020-12-25 13:05:59 +01:00
629a609ff5 Forgott imports for #45 2020-12-25 13:00:30 +01:00
04fe93c7bb #45 Redirect after asset deletion 2020-12-25 12:57:05 +01:00
b42be489dc #44 Text in place teaser is cut of 2020-12-25 12:39:13 +01:00
dc79b9d05c #43 Lat/Long Check when place is submitted 2020-12-24 19:47:17 +01:00
b845cc054a Small Tweaks 2020-12-24 19:25:37 +01:00
6f6bd6376d Merge branch 'develop' of mowoe.com:reverend/lostplaces-backend into develop 2020-12-24 17:33:02 +01:00
dc2a703c39 More fuckery 2020-12-24 17:27:48 +01:00
063777ece4 Merge branch '27-Favorite-System' into develop 2020-12-24 17:27:20 +01:00
4675e5814f Indention and stupid mistakes fix 2020-12-24 17:26:07 +01:00
4a43a4bf37 typo 2020-12-24 16:43:43 +01:00
61cf148417 Displaying favorite icon on detail page 2020-12-24 16:43:33 +01:00
d547ee9db3 favorite partial 2020-12-24 16:43:14 +01:00
c7b699f615 Changed link location in place teaser 2020-12-24 16:43:01 +01:00
86f95a5dd0 New icon 2020-12-24 16:42:34 +01:00
916c4b80f7 Filed not required in django admin 2020-12-24 16:42:27 +01:00
b31dc9fc5f Url name changed 2020-12-24 16:41:30 +01:00
8f048369bf Favorite/Unfavorite Views 2020-12-24 15:56:33 +01:00
f974469996 formatting 2020-12-24 15:56:02 +01:00
dbbd7b0802 rename migrationfile 2020-12-24 15:55:37 +01:00
c7368f5c44 Merge commit '20dd880a11a008ba95cd5aedce59590445430831' into develop 2020-12-24 14:57:09 +01:00
abca946883 Model for favorite system 2020-12-24 14:48:11 +01:00
20dd880a11 Indention Error 2020-12-19 15:31:13 +01:00
946c3091c0 Typo "fix" 2020-12-19 15:31:04 +01:00
4bacd1a28d Recopied migration files 2020-12-19 14:47:59 +01:00
086789aa88 delete miration for release 2020-12-19 14:35:20 +01:00
3c5b447d4c New CSS 2020-11-29 19:23:44 +01:00
cfbb876d2c #41 2020-11-29 19:23:39 +01:00
4e0bfef483 Incorporating Profile View into Project 2020-11-29 19:23:22 +01:00
ccdb49c8ea Profile View 2020-11-29 19:22:55 +01:00
90dd281f7c Common functionalities 2020-11-29 19:22:38 +01:00
d9df6e67a8 Hassling with migrations 2020-11-29 19:22:27 +01:00
cd20e730ba Merge branch 'master' into develop 2020-11-27 07:58:53 +01:00
c613d15e89 Updated Readme 2020-11-27 07:57:56 +01:00
2380398fa9 #36 and #37 Place Teaser 2020-10-12 20:57:04 +02:00
4c5b9c7648 Dumped a nice missing-image image. Needs resizing and logic. 2020-10-11 23:08:14 +02:00
91a3f3171d Merge branch 'feature/localization' into develop 2020-10-11 21:57:30 +02:00
4e366d94c9 Fixing Error on sign up 2020-10-06 22:53:35 +02:00
773390b09f Moved files so only the app is packed 2020-10-04 23:23:02 +02:00
ac0121d96e removed old config 2020-10-04 23:22:53 +02:00
a0c4f2cad3 Merge branch 'develop' into master 2020-10-04 22:19:01 +02:00
66bd9f648b Merge branch 'develop' into master 2020-10-04 15:43:41 +02:00
485eb7b886 Bumping version 2020-10-01 22:06:12 +02:00
29 changed files with 633 additions and 109 deletions

View File

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

View File

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

View File

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

View File

@@ -44,7 +44,15 @@ class PlaceForm(forms.ModelForm):
class Meta: class Meta:
model = Place model = Place
fields = '__all__' fields = '__all__'
exclude = ['submitted_by'] exclude = ['submitted_by', 'latitude', 'longitute']
latitude = forms.IntegerField(
widget=forms.NumberInput(attrs={'min':-90,'max': 90,'type': 'number'})
)
longitude = forms.IntegerField(
widget=forms.NumberInput(attrs={'min':-180,'max': 180,'type': 'number'})
)
class PlaceImageForm(forms.ModelForm): class PlaceImageForm(forms.ModelForm):
class Meta: class Meta:

View File

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

View File

@@ -0,0 +1,22 @@
# Generated by Django 3.1.1 on 2020-10-04 19:37
# Edited by reverend
import datetime
from django.db import migrations, models
import django.utils.timezone
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('lostplaces', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='Voucher'
),
migrations.DeleteModel(
name='Expireable'
)
]

View File

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

View File

@@ -14,6 +14,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from lostplaces.models.abstract_models import Expireable from lostplaces.models.abstract_models import Expireable
from lostplaces.models.place import Place
class Explorer(models.Model): class Explorer(models.Model):
""" """
@@ -27,6 +28,13 @@ class Explorer(models.Model):
related_name='explorer' related_name='explorer'
) )
favorite_places = models.ManyToManyField(
Place,
related_name='explorer_favorites',
verbose_name='Explorers favorite places',
blank=True
)
def __str__(self): def __str__(self):
return self.user.username return self.user.username

View File

@@ -72,7 +72,7 @@ class PlaceAsset(Submittable):
null=True null=True
) )
class PlaceImage (Submittable): class PlaceImage (PlaceAsset):
""" """
PlaceImage defines an image file object that points to a file in uploads/. PlaceImage defines an image file object that points to a file in uploads/.
Intermediate image sizes are generated as defined in THUMBNAIL_ALIASES. Intermediate image sizes are generated as defined in THUMBNAIL_ALIASES.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

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

View File

@@ -0,0 +1,107 @@
{% extends 'global.html'%}
{% load static %}
{% load i18n %}
{% load svg_icon %}
{% block maincontent %}
<div class="LP-UserProfile">
<div class="LP-UserProfile__Info">
<div class="LP-UserInfo">
<div class="LP-UserInfo__UserName">
<h1 class="LP-Headline">{{explorer.user.username}}</h1>
</div>
<div class="LP-UserInfo__Meta">
<table>
<tr>
<td class="LP-UserInfo__Key">
<span class="LP-Paragraph">{% trans 'Joined' %}</span>
</td>
<td class="LP-UserInfo__Value">
<span class="LP-Paragraph">{{explorer.user.date_joined|date:'d.m.Y'}}</span>
</td>
</tr>
<tr>
<td class="LP-UserInfo__Key">
<span class="LP-Paragraph">{% trans 'Places'%}</span>
</td>
<td class="LP-UserInfo__Value">
<span class="LP-Paragraph">{{place_count}}</span>
</td>
</tr>
<tr>
<td class="LP-UserInfo__Key">
<span class="LP-Paragraph">{% trans 'Place Assets'%}</span>
</td>
<td class="LP-UserInfo__Value">
<span class="LP-Paragraph">{{asset_count}}</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<section class="LP-Section">
<div class="LP-PlaceList">
<h1 class="LP-Headline">{% trans 'Places submitted by' %} {{explorer.user.username}}</h1>
<ul class="LP-PlaceList__List">
{% for place in place_list %}
<li class="LP-PlaceList__Item">
{% include 'partials/place_teaser.html' with place=place extended=True %}
</li>
{% endfor %}
</ul>
{% include 'partials/nav/pagination.html' %}
</div>
</section>
<section class="LP-Section">
<h1 class="LP-Headline">{% trans 'Images submitted by' %} {{explorer.user.username}}</h1>
<div class="LP-ImageGrid">
<ul class="LP-ImageGrid__Container">
{% for place_image in assets.placeimages.all %}
<li class="LP-ImageGrid__Item">
<a href="{{ place_image.filename.large.url }}" class="LP-Link">
<img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}">
</a>
{% if user.explorer == place_image.submitted_by%}
<span class="LP-ImageGrid__DeleteItem" title="Bild löschen">
<a href="{% url 'place_image_delete' pk=place_image.id %}" class="LP-Link">
<img class="LP-Icon" src="{% static 'icons/cancel.svg' %}" />
</a>
</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</section>
<section class=" LP-Section">
<h1 class="LP-Headline">{% trans 'Photo albums submitted by' %} {{explorer.user.username}}</h1>
<div class="LP-LinkList">
<ul class="LP-LinkList__Container">
{% for photo_album in assets.photoalbums.all %}
<li class="LP-LinkList__Item">
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
<span class="LP-Text">{{photo_album.label}}</span>
</a>
{% if user.explorer == photo_album.submitted_by%}
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
<div class="RV-Iconized__Container RV-Iconized__Container--small">
{% icon 'trash' className="RV-Iconized__Icon" %}
</div>
</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</section>
{% endblock maincontent %}

View File

@@ -29,9 +29,10 @@
<span class="LP-Paragraph"> <span class="LP-Paragraph">
{% if user.is_authenticated %} {% if user.is_authenticated %}
Hi {{ user.username }}! Hi {{ user.username }}!
<a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a> <a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% trans 'Logout' %}</span></a> |
<a class="LP-Link" href="{% url 'explorer_profile' explorer_id=user.pk%}"><span class="LP-Link__Text">{% trans 'Profile' %}</span></a>
{% if user.is_superuser %} {% if user.is_superuser %}
<a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a> | <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% trans 'Admin' %}</span></a>
{% endif %} {% endif %}
{% else %} {% else %}
@@ -49,7 +50,7 @@
<ul class="LP-Menu__List"> <ul class="LP-Menu__List">
<li class="LP-Menu__Item"><a href="{% url 'lostplaces_home' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Home' %}</span></a></li> <li class="LP-Menu__Item"><a href="{% url 'lostplaces_home' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Home' %}</span></a></li>
<li class="LP-Menu__Item"><a href="{% url 'flatpage' slug='codex' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'UrBex Codex' %}</span></a></li> <li class="LP-Menu__Item"><a href="{% url 'flatpage' slug='codex' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'UrBex Codex' %}</span></a></li>
<li class="LP-Menu__Item"><a href="{% url 'osm' %}" class="LP-Link"><span class="LP-Link__Text">{% trans 'Map' %}</span></a></li>
{% block additional_menu_items %} {% block additional_menu_items %}
{% endblock additional_menu_items %} {% endblock additional_menu_items %}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@
<article class="LP-PlaceDetail"> <article class="LP-PlaceDetail">
<header class="LP-PlaceDetail__Header"> <header class="LP-PlaceDetail__Header">
<h1 class="LP-Headline">{{ place.name }}</h1> <h1 class="LP-Headline">{{ place.name }} {% include 'partials/icons/place_favorite.html' %}</h1>
{% if place.placeimages.first.filename.hero.url %} {% if place.placeimages.first.filename.hero.url %}
<figure class="LP-PlaceDetail__Image"> <figure class="LP-PlaceDetail__Image">
<img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" /> <img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" />
@@ -44,7 +44,7 @@
<section class="LP-Section"> <section class="LP-Section">
<h1 class="LP-Headline">{% trans 'Map links' %}</h1> <h1 class="LP-Headline">{% trans 'Map links' %}</h1>
{% include 'partials/osm_map.html' with config=mapping_config%} {% include 'partials/osm_map.html' with config=mapping_config modifier='wide' %}
<div class="LP-LinkList"> <div class="LP-LinkList">
<ul class="LP-LinkList__Container"> <ul class="LP-LinkList__Container">
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude|safe}},{{place.longitude|safe}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li> <li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude|safe}},{{place.longitude|safe}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>

View File

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

View File

@@ -11,11 +11,15 @@ from lostplaces.views import (
PlaceDeleteView, PlaceDeleteView,
PlaceTagDeleteView, PlaceTagDeleteView,
PlaceTagSubmitView, PlaceTagSubmitView,
PlaceFavoriteView,
PlaceUnfavoriteView,
PhotoAlbumCreateView, PhotoAlbumCreateView,
PhotoAlbumDeleteView, PhotoAlbumDeleteView,
PlaceImageCreateView, PlaceImageCreateView,
PlaceImageDeleteView, PlaceImageDeleteView,
FlatView FlatView,
ExplorerProfileView,
OSMMapView
) )
urlpatterns = [ urlpatterns = [
@@ -33,5 +37,13 @@ urlpatterns = [
# POST-only URLs for tag submission # POST-only URLs for tag submission
path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'), path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete') path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete'),
path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
path('explorer/favorite/<int:place_id>/', PlaceFavoriteView.as_view(), name='place_favorite'),
path('explorer/unfavorite/<int:place_id>/', PlaceUnfavoriteView.as_view(), name='place_unfavorite'),
path('osm/', OSMMapView.as_view(), name='osm')
] ]

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.views import View
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from lostplaces.common import get_all_subclasses
from lostplaces.views.base_views import IsAuthenticatedMixin
from lostplaces.models.models import Explorer
from lostplaces.models.place import Place, PlaceAsset
class ExplorerProfileView(IsAuthenticatedMixin, View):
def get(self, request, explorer_id):
explorer = get_object_or_404(Explorer, pk=explorer_id)
place_list = explorer.places.all()
place_count = place_list.count()
context={
'explorer': explorer,
'place_count': place_count,
'place_list': place_list,
'assets': {}
}
asset_count = 0
for subclass in get_all_subclasses(PlaceAsset): # kinda ugly, but PlaceAsset cannot be abstract according to django ORM
objects = subclass.objects.filter(submitted_by=explorer)
context['assets'][subclass.__name__.lower()+'s'] = objects
asset_count += objects.count()
context['asset_count'] = asset_count
print(context['assets'])
return render(
request=request,
template_name='explorer/profile.html',
context=context
)

View File

@@ -13,12 +13,13 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy, reverse
from lostplaces.models import Place, PlaceImage from lostplaces.models import Place, PlaceImage
from lostplaces.views.base_views import IsAuthenticatedMixin, IsPlaceSubmitterMixin from lostplaces.views.base_views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
from lostplaces.views.place_image_views import MultiplePlaceImageUploadMixin from lostplaces.views.place_image_views import MultiplePlaceImageUploadMixin
from lostplaces.forms import PlaceForm, PlaceImageForm, TagSubmitForm from lostplaces.forms import PlaceForm, PlaceImageForm, TagSubmitForm
from lostplaces.common import redirect_referer_or
from taggit.models import Tag from taggit.models import Tag
@@ -104,7 +105,14 @@ class PlaceCreateView(MultiplePlaceImageUploadMixin, IsAuthenticatedMixin, View)
self.request, self.request,
_('Please fill in all required fields.') _('Please fill in all required fields.')
) )
return render(request, 'place/place_create.html', context={'form': place_form}) return render(
request=request,
template_name='place/place_create.html',
context={
'place_form': place_form,
'place_image_form': PlaceImageForm()
}
)
class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView): class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
template_name = 'place/place_delete.html' template_name = 'place/place_delete.html'
@@ -119,3 +127,23 @@ class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
def get_place(self): def get_place(self):
return self.get_object() return self.get_object()
class PlaceFavoriteView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
request.user.explorer.favorite_places.add(place)
request.user.explorer.save()
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
request.user.explorer.favorite_places.remove(place)
request.user.explorer.save()
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))

View File

@@ -6,7 +6,7 @@ from django.views.generic.edit import CreateView
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.contrib import messages from django.contrib import messages
from django.urls import reverse_lazy from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponseForbidden from django.http import HttpResponseForbidden
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@@ -14,6 +14,7 @@ from django.utils.translation import ugettext_lazy as _
from lostplaces.forms import ExplorerCreationForm, TagSubmitForm from lostplaces.forms import ExplorerCreationForm, TagSubmitForm
from lostplaces.models import Place, PhotoAlbum from lostplaces.models import Place, PhotoAlbum
from lostplaces.views.base_views import IsAuthenticatedMixin from lostplaces.views.base_views import IsAuthenticatedMixin
from lostplaces.common import redirect_referer_or
from lostplaces.views.base_views import ( from lostplaces.views.base_views import (
PlaceAssetCreateView, PlaceAssetCreateView,
@@ -79,10 +80,22 @@ class PlaceTagDeleteView(IsAuthenticatedMixin, View):
place = get_object_or_404(Place, pk=tagged_id) place = get_object_or_404(Place, pk=tagged_id)
tag = get_object_or_404(Tag, pk=tag_id) tag = get_object_or_404(Tag, pk=tag_id)
place.tags.remove(tag) place.tags.remove(tag)
return redirect(reverse_lazy('place_detail', kwargs={'pk': tagged_id}))
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': tagged_id}))
def FlatView(request, slug): def FlatView(request, slug):
if request.LANGUAGE_CODE == 'de': if request.LANGUAGE_CODE == 'de':
return render(request, 'flat/' + slug + '-de' + '.html') return render(request, 'flat/' + slug + '-de' + '.html')
else: else:
return render(request, 'flat/' + slug + '.html') return render(request, 'flat/' + slug + '.html')
class OSMMapView(IsAuthenticatedMixin, View):
def get(self, request):
place_list = Place.objects.all()
context = {
'mapping_config': {
'all_points': place_list,
'map_center': Place.average_latlon(place_list)
}
}
return render(request, 'osm_map_full.html', context)

View File

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

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

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