Compare commits
10 Commits
78f0e80136
...
66581a9d2d
Author | SHA1 | Date | |
---|---|---|---|
66581a9d2d | |||
490a0e9f3e | |||
7a3b8529f8 | |||
538b43c2a1 | |||
ca2eac533f | |||
2007b512c7 | |||
751d4c81fe | |||
7a757bcf35 | |||
55b8d16751 | |||
f34d70edd5 |
1
Pipfile
1
Pipfile
@ -12,6 +12,7 @@ easy-thumbnails = "*"
|
||||
image = "*"
|
||||
django-widget-tweaks = "*"
|
||||
django-svg-icons = "*"
|
||||
django-taggit = "*"
|
||||
|
||||
# Commented out to not explicitly specify Python 3 subversion.
|
||||
# [requires]
|
||||
|
@ -43,7 +43,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks'
|
||||
'widget_tweaks',
|
||||
'taggit'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -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)
|
@ -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,7 @@ class Place (models.Model):
|
||||
longitude = models.FloatField()
|
||||
description = models.TextField()
|
||||
|
||||
tags = TaggableManager(blank=True)
|
||||
# Get center position of LP-geocoordinates.
|
||||
|
||||
def average_latlon(place_list):
|
||||
@ -63,10 +65,13 @@ class Place (models.Model):
|
||||
longitude = 0
|
||||
latitude = 0
|
||||
|
||||
for place in place_list:
|
||||
longitude += place.longitude
|
||||
latitude += place.latitude
|
||||
return (latitude / amount, longitude / amount)
|
||||
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
|
||||
@ -142,21 +147,21 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
|
||||
|
||||
|
||||
class ExternalLink(models.Model):
|
||||
url = models.URLField(max_length=200)
|
||||
label = models.CharField(max_length=100)
|
||||
submitted_by = models.ForeignKey(
|
||||
url = models.URLField(max_length=200)
|
||||
label = models.CharField(max_length=100)
|
||||
submitted_by = models.ForeignKey(
|
||||
Explorer,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='external_links'
|
||||
)
|
||||
submitted_when = models.DateTimeField(auto_now_add=True, null=True)
|
||||
submitted_when = models.DateTimeField(auto_now_add=True, null=True)
|
||||
|
||||
class PhotoAlbum(ExternalLink):
|
||||
place = models.ForeignKey(
|
||||
place = models.ForeignKey(
|
||||
Place,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='photo_albums',
|
||||
null=True
|
||||
null=True
|
||||
)
|
@ -17,109 +17,127 @@
|
||||
{% block title %}{{place.name}}{% endblock %}
|
||||
|
||||
{% block additional_menu_items %}
|
||||
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_edit' pk=place.pk %}" class="LP-Link"><span
|
||||
class="LP-Link__Text">Edit place</span></a></li>
|
||||
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_delete' pk=place.pk %}" class="LP-Link"><span
|
||||
class="LP-Link__Text">Delete place</span></a></li>
|
||||
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_edit' pk=place.pk %}" class="LP-Link"><span class="LP-Link__Text">Edit place</span></a></li>
|
||||
<li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_delete' pk=place.pk %}" class="LP-Link"><span class="LP-Link__Text">Delete place</span></a></li>
|
||||
{% endblock additional_menu_items %}
|
||||
|
||||
{% block maincontent %}
|
||||
<article class="LP-PlaceDetail">
|
||||
|
||||
<header class="LP-PlaceDetail__Header">
|
||||
<h1 class="LP-Headline">{{ place.name }}</h1>
|
||||
{% if place.images.first.filename.hero.url %}
|
||||
<figure class="LP-PlaceDetail__Image">
|
||||
<img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
|
||||
</figure>
|
||||
{% endif %}
|
||||
</header>
|
||||
<header class="LP-PlaceDetail__Header">
|
||||
<h1 class="LP-Headline">{{ place.name }}</h1>
|
||||
{% if place.images.first.filename.hero.url %}
|
||||
<figure class="LP-PlaceDetail__Image">
|
||||
<img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
|
||||
</figure>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div class="LP-PlaceDetail__Description">
|
||||
<p class="LP-Paragraph">{{ place.description }}</p>
|
||||
</div>
|
||||
<div class="LP-PlaceDetail__Description">
|
||||
<p class="LP-Paragraph">{{ place.description }}</p>
|
||||
</div>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Map-Links</h1>
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
<li class="LP-LinkList__Item"><a target="_blank"
|
||||
href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}"
|
||||
class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>
|
||||
<li class="LP-LinkList__Item"><a target="_blank"
|
||||
href="https://www.tim-online.nrw.de/tim-online2/?center={{place.latitude}},{{place.longitude}}&icon=true&bg=dop"
|
||||
class="LP-Link"><span class="LP-Text">TIM Online</span></a></li>
|
||||
<li class="LP-LinkList__Item"><a target="_blank"
|
||||
href="http://www.openstreetmap.org/?mlat={{place.latitude}}&mlon={{place.longitude}}&zoom=16"
|
||||
class="LP-Link"><span class="LP-Text">OSM</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<section class="LP-Section">
|
||||
<!--
|
||||
<input name='basic' value='tag1, tag2'>
|
||||
<script>
|
||||
// The DOM element you wish to replace with Tagify
|
||||
var input = document.querySelector('input[name=basic]');
|
||||
|
||||
<section class="LP-Section">
|
||||
<input name='basic' value='tag1, tag2'>
|
||||
<script>
|
||||
// The DOM element you wish to replace with Tagify
|
||||
var input = document.querySelector('input[name=basic]');
|
||||
// initialize Tagify on the above input node reference
|
||||
new Tagify(input, {
|
||||
'whitelist': ['wurstwasser']
|
||||
})
|
||||
</script>-->
|
||||
|
||||
// initialize Tagify on the above input node reference
|
||||
new Tagify(input, {
|
||||
'whitelist': ['wurstwasser']
|
||||
})
|
||||
</script>
|
||||
</section>
|
||||
<div class="LP-TagList">
|
||||
<ul class="LP-TagList__List">
|
||||
{% for tag in place.tags.all %}
|
||||
<li class="LP-TagList__Item">
|
||||
<div class="LP-Tag">
|
||||
<p class="LP-Paragraph">{{tag}}</p>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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
|
||||
<form class="LP-Form LP-Form--inline" method="POST" action="{% url 'place_tag_submit' place_id=place.id %}">
|
||||
<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">
|
||||
{% include 'partials/form/inputField.html' with field=tagging_form.tag_list %}
|
||||
</div>
|
||||
<div class="LP-Form__Field LP-Form__Button LP-Input">
|
||||
<button class="LP-Button">hinzufügen</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Map-Links</h1>
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>
|
||||
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.tim-online.nrw.de/tim-online2/?center={{place.latitude}},{{place.longitude}}&icon=true&bg=dop" class="LP-Link"><span class="LP-Text">TIM Online</span></a></li>
|
||||
<li class="LP-LinkList__Item"><a target="_blank" href="http://www.openstreetmap.org/?mlat={{place.latitude}}&mlon={{place.longitude}}&zoom=16" class="LP-Link"><span class="LP-Text">OSM</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class=" LP-Section">
|
||||
<h1 class="LP-Headline">Photoalben</h1>
|
||||
<div class="LP-LinkList">
|
||||
<ul class="LP-LinkList__Container">
|
||||
{% for photo_album in place.photo_albums.all %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a target="_blank" href="{{photo_album.url}}" class="LP-Link">
|
||||
<span class="LP-Text">{{photo_album.label}}</span>
|
||||
</a>
|
||||
{% if user == photo_album.submitted_by or user == place.submitted_by %}
|
||||
<a href="{% url 'photo_album_delete' pk=photo_album.pk%}" class="LP-Link LP-LinkList__ItemHover" title="Delete Photo Album">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
{% icon 'trash' className="RV-Iconized__Icon" %}
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="LP-LinkList__Item">
|
||||
<a href="{% url 'photo_album_create' place_id=place.id %}" class="LP-Link">
|
||||
<div class="RV-Iconized__Container RV-Iconized__Container--small">
|
||||
<svg class="RV-Iconized__Icon" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216
|
||||
v216c0,11.046,8.954,20,20,20s20-8.954,20-20V276h216c11.046,0,20-8.954,20-20C512,244.954,503.046,236,492,236z" />
|
||||
</g>
|
||||
</svg>
|
||||
<span class="RV-Iconized__Text">Fotoalbum hinzufügen</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="RV-Iconized__Text">Fotoalbum hinzufügen</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Bilder</h1>
|
||||
<div class="LP-ImageGrid">
|
||||
<ul class="LP-ImageGrid__Container">
|
||||
{% for place_image in place.images.all %}
|
||||
<li class="LP-ImageGrid__Item">
|
||||
<a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image"
|
||||
src="{{ place_image.filename.thumbnail.url }}"></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<section class="LP-Section">
|
||||
<h1 class="LP-Headline">Bilder</h1>
|
||||
<div class="LP-ImageGrid">
|
||||
<ul class="LP-ImageGrid__Container">
|
||||
{% for place_image in place.images.all %}
|
||||
<li class="LP-ImageGrid__Item">
|
||||
<a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}"></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
{% endblock maincontent %}
|
@ -1,11 +1,12 @@
|
||||
import json
|
||||
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 = 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()
|
||||
|
@ -8,7 +8,8 @@ from .views import (
|
||||
PlaceUpdateView,
|
||||
PlaceDeleteView,
|
||||
PhotoAlbumCreateView,
|
||||
PhotoAlbumDeleteView
|
||||
PhotoAlbumDeleteView,
|
||||
PlaceTagSubmitView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@ -20,5 +21,6 @@ urlpatterns = [
|
||||
path('photo_album/delete/<int:pk>', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'),
|
||||
path('place/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'),
|
||||
path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'),
|
||||
path('place/', PlaceListView.as_view(), name='place_list')
|
||||
path('place/', PlaceListView.as_view(), name='place_list'),
|
||||
path('place/tag/<int:place_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
|
||||
]
|
||||
|
@ -1,245 +0,0 @@
|
||||
#!/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.detail import SingleObjectMixin
|
||||
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.contrib.messages.views import SuccessMessageMixin
|
||||
|
||||
from .forms import (
|
||||
ExplorerCreationForm,
|
||||
PlaceForm,
|
||||
PlaceImageCreateForm
|
||||
)
|
||||
from .models import Place, PlaceImage, Voucher, PhotoAlbum
|
||||
|
||||
# Create your views here.
|
||||
|
||||
# BaseView that checks if user is logged in.
|
||||
class IsAuthenticated(LoginRequiredMixin, View):
|
||||
redirect_field_name = 'redirect_to'
|
||||
login_required_message = 'Please login to proceed'
|
||||
|
||||
def handle_no_permission(self):
|
||||
messages.error(self.request, self.login_required_message)
|
||||
return super().handle_no_permission()
|
||||
|
||||
# BaseView that checks if logged in user is submitter of place.
|
||||
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 SignUpView(SuccessMessageMixin, CreateView):
|
||||
form_class = ExplorerCreationForm
|
||||
success_url = reverse_lazy('login')
|
||||
template_name = 'signup.html'
|
||||
success_message = 'User created.'
|
||||
|
||||
class PlaceListView(IsAuthenticated, ListView):
|
||||
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,
|
||||
'place_list': [ place ],
|
||||
'place_map_center': [ place.latitude, place.longitude ]
|
||||
}
|
||||
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]
|
||||
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 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):
|
||||
place_image_form = PlaceImageCreateForm()
|
||||
place_form = PlaceForm()
|
||||
|
||||
context = {
|
||||
'place_form': place_form,
|
||||
'place_image_form': place_image_form
|
||||
}
|
||||
return render(request, 'place/place_create.html', context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
place_form = PlaceForm(request.POST)
|
||||
|
||||
if place_form.is_valid():
|
||||
submitter = request.user
|
||||
place = place_form.save(commit=False)
|
||||
# Save logged in user as "submitted_by"
|
||||
place.submitted_by = submitter
|
||||
place.save()
|
||||
|
||||
if request.FILES:
|
||||
self._apply_multipart_image_upload(
|
||||
files=request.FILES.getlist('filename'),
|
||||
place=place,
|
||||
submitter=submitter
|
||||
)
|
||||
|
||||
kwargs_to_pass = {
|
||||
'pk': place.pk
|
||||
}
|
||||
|
||||
messages.success(
|
||||
self.request, 'Successfully created place.')
|
||||
return redirect(reverse_lazy('place_detail', kwargs=kwargs_to_pass))
|
||||
|
||||
else:
|
||||
context = {
|
||||
'form': form_place
|
||||
}
|
||||
|
||||
# Usually the browser should have checked the form before sending.
|
||||
messages.error(
|
||||
self.request, 'Please fill in all required fields.')
|
||||
return render(request, 'place/place_create.html', context)
|
||||
|
||||
def _apply_multipart_image_upload(self, files, place, submitter):
|
||||
for image in files:
|
||||
place_image = PlaceImage.objects.create(
|
||||
filename=image,
|
||||
place=place,
|
||||
submitted_by=submitter
|
||||
)
|
||||
place_image.save()
|
||||
|
||||
class PlaceDeleteView(IsAuthenticated, 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()
|
||||
|
||||
class AlbumCreateView(IsAuthenticated, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
url = request.GET['url']
|
||||
place_id = request.GET['place_id']
|
||||
place = Place.objects.get(pk=place_id)
|
||||
photo_album = PhotoAlbum()
|
||||
photo_album.url = url
|
||||
photo_album.place = place
|
||||
photo_album.submitted_by = request.user
|
||||
photo_album.save()
|
||||
print(photo_album)
|
||||
return redirect(reverse_lazy('place_detail', kwargs={'pk': place_id}))
|
||||
|
||||
class PhotoAlbumCreateView(IsAuthenticated, SuccessMessageMixin, CreateView):
|
||||
model = PhotoAlbum
|
||||
fields = ['url', 'label']
|
||||
template_name = 'photo_album/photo_album_create.html'
|
||||
success_message = 'Photo Album submitted'
|
||||
|
||||
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 PhotoAlbumDeleteView(IsAuthenticated, IsPlaceSubmitter, SingleObjectMixin, View):
|
||||
model = PhotoAlbum
|
||||
pk_url_kwarg = 'pk'
|
||||
success_message = 'Photo Album deleted'
|
||||
|
||||
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, 'You do not have permissions to alter this photo album')
|
||||
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}))
|
3
lostplaces/lostplaces_app/views/__init__.py
Normal file
3
lostplaces/lostplaces_app/views/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from lostplaces_app.views.base_views import *
|
||||
from lostplaces_app.views.views import *
|
||||
from lostplaces_app.views.place_views import *
|
98
lostplaces/lostplaces_app/views/base_views.py
Normal file
98
lostplaces/lostplaces_app/views/base_views.py
Normal file
@ -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}))
|
120
lostplaces/lostplaces_app/views/place_views.py
Normal file
120
lostplaces/lostplaces_app/views/place_views.py
Normal file
@ -0,0 +1,120 @@
|
||||
from django.views import View
|
||||
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 django.shortcuts import render, redirect
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from lostplaces_app.models import Place, PlaceImage
|
||||
from lostplaces_app.views import IsAuthenticated, IsPlaceSubmitter
|
||||
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm
|
||||
|
||||
class PlaceListView(IsAuthenticated, ListView):
|
||||
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,
|
||||
'place_list': [ place ],
|
||||
'place_map_center': [ place.latitude, place.longitude ],
|
||||
'tagging_form': TagSubmitForm()
|
||||
}
|
||||
return render(request, 'place/place_detail.html', context)
|
||||
|
||||
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):
|
||||
place_image_form = PlaceImageCreateForm()
|
||||
place_form = PlaceForm()
|
||||
|
||||
context = {
|
||||
'place_form': place_form,
|
||||
'place_image_form': place_image_form
|
||||
}
|
||||
return render(request, 'place/place_create.html', context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
place_form = PlaceForm(request.POST)
|
||||
|
||||
if place_form.is_valid():
|
||||
submitter = request.user
|
||||
place = place_form.save(commit=False)
|
||||
# Save logged in user as "submitted_by"
|
||||
place.submitted_by = submitter
|
||||
place.save()
|
||||
|
||||
if request.FILES:
|
||||
self._apply_multipart_image_upload(
|
||||
files=request.FILES.getlist('filename'),
|
||||
place=place,
|
||||
submitter=submitter
|
||||
)
|
||||
|
||||
kwargs_to_pass = {
|
||||
'pk': place.pk
|
||||
}
|
||||
|
||||
messages.success(
|
||||
self.request, 'Successfully created place.')
|
||||
return redirect(reverse_lazy('place_detail', kwargs=kwargs_to_pass))
|
||||
|
||||
else:
|
||||
context = {
|
||||
'form': form_place
|
||||
}
|
||||
|
||||
# Usually the browser should have checked the form before sending.
|
||||
messages.error(
|
||||
self.request, 'Please fill in all required fields.')
|
||||
return render(request, 'place/place_create.html', context)
|
||||
|
||||
def _apply_multipart_image_upload(self, files, place, submitter):
|
||||
for image in files:
|
||||
place_image = PlaceImage.objects.create(
|
||||
filename=image,
|
||||
place=place,
|
||||
submitted_by=submitter
|
||||
)
|
||||
place_image.save()
|
||||
|
||||
class PlaceDeleteView(IsAuthenticated, 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()
|
58
lostplaces/lostplaces_app/views/views.py
Normal file
58
lostplaces/lostplaces_app/views/views.py
Normal file
@ -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}))
|
Loading…
Reference in New Issue
Block a user