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 = "*" | image = "*" | ||||||
| django-widget-tweaks = "*" | django-widget-tweaks = "*" | ||||||
| django-svg-icons = "*" | django-svg-icons = "*" | ||||||
|  | django-taggit = "*" | ||||||
|  |  | ||||||
| # Commented out to not explicitly specify Python 3 subversion. | # Commented out to not explicitly specify Python 3 subversion. | ||||||
| # [requires] | # [requires] | ||||||
|   | |||||||
| @@ -43,7 +43,8 @@ INSTALLED_APPS = [ | |||||||
|     'django.contrib.messages', |     'django.contrib.messages', | ||||||
|     'django.contrib.staticfiles', |     'django.contrib.staticfiles', | ||||||
|     'easy_thumbnails', |     'easy_thumbnails', | ||||||
|     'widget_tweaks' |     'widget_tweaks', | ||||||
|  | 	'taggit' | ||||||
| ] | ] | ||||||
|  |  | ||||||
| MIDDLEWARE = [ | MIDDLEWARE = [ | ||||||
|   | |||||||
| @@ -48,3 +48,7 @@ class PlaceImageCreateForm(forms.ModelForm): | |||||||
|         super().__init__(*args, **kwargs) |         super().__init__(*args, **kwargs) | ||||||
|  |  | ||||||
|         self.fields['filename'].required = False |         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.dispatch import receiver | ||||||
| from django.contrib.auth.models import AbstractUser | from django.contrib.auth.models import AbstractUser | ||||||
| from easy_thumbnails.fields import ThumbnailerImageField | from easy_thumbnails.fields import ThumbnailerImageField | ||||||
|  | from taggit.managers import TaggableManager | ||||||
|  |  | ||||||
| # Create your models here. | # Create your models here. | ||||||
|  |  | ||||||
| @@ -29,7 +30,7 @@ class Voucher(models.Model): | |||||||
|     Creation date is being set automatically during voucher creation.  |     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) |     created = models.DateTimeField(auto_now_add=True) | ||||||
|     expires = models.DateField() |     expires = models.DateField() | ||||||
|  |  | ||||||
| @@ -55,6 +56,7 @@ class Place (models.Model): | |||||||
|     longitude = models.FloatField() |     longitude = models.FloatField() | ||||||
|     description = models.TextField() |     description = models.TextField() | ||||||
|  |  | ||||||
|  |     tags = TaggableManager(blank=True) | ||||||
|     # Get center position of LP-geocoordinates. |     # Get center position of LP-geocoordinates. | ||||||
|  |  | ||||||
|     def average_latlon(place_list): |     def average_latlon(place_list): | ||||||
| @@ -63,11 +65,14 @@ class Place (models.Model): | |||||||
|         longitude = 0 |         longitude = 0 | ||||||
|         latitude = 0 |         latitude = 0 | ||||||
|      |      | ||||||
|  |         if amount > 0: | ||||||
|             for place in place_list: |             for place in place_list: | ||||||
|                 longitude += place.longitude |                 longitude += place.longitude | ||||||
|                 latitude += place.latitude |                 latitude += place.latitude | ||||||
|             return (latitude / amount, longitude / amount) |             return (latitude / amount, longitude / amount) | ||||||
|  |  | ||||||
|  |         return (latitude, longitude) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.name |         return self.name | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,10 +17,8 @@ | |||||||
| {% block title %}{{place.name}}{% endblock %} | {% block title %}{{place.name}}{% endblock %} | ||||||
|  |  | ||||||
| {% block additional_menu_items %} | {% 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 | <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> | ||||||
| 			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_delete' pk=place.pk %}" class="LP-Link"><span |  | ||||||
| 			class="LP-Link__Text">Delete place</span></a></li> |  | ||||||
| {% endblock additional_menu_items %} | {% endblock additional_menu_items %} | ||||||
|  |  | ||||||
| {% block maincontent %} | {% block maincontent %} | ||||||
| @@ -40,24 +38,7 @@ | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <section class="LP-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"> |  | ||||||
| 			        <input name='basic' value='tag1, tag2'> | 			        <input name='basic' value='tag1, tag2'> | ||||||
| 			<script> | 			<script> | ||||||
|             // The DOM element you wish to replace with Tagify |             // The DOM element you wish to replace with Tagify | ||||||
| @@ -67,7 +48,47 @@ | |||||||
|             new Tagify(input, { |             new Tagify(input, { | ||||||
|                 'whitelist': ['wurstwasser'] |                 'whitelist': ['wurstwasser'] | ||||||
|             }) |             }) | ||||||
| 		</script> |         </script>--> | ||||||
|  |  | ||||||
|  |         <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> | ||||||
|  |  | ||||||
|  |         <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> | ||||||
|  |  | ||||||
|     <section class=" LP-Section"> |     <section class=" LP-Section"> | ||||||
| @@ -91,9 +112,7 @@ | |||||||
|                 <li class="LP-LinkList__Item"> |                 <li class="LP-LinkList__Item"> | ||||||
|                     <a href="{% url 'photo_album_create' place_id=place.id %}" class="LP-Link"> |                     <a href="{% url 'photo_album_create' place_id=place.id %}" class="LP-Link"> | ||||||
|                         <div class="RV-Iconized__Container RV-Iconized__Container--small"> |                         <div class="RV-Iconized__Container RV-Iconized__Container--small"> | ||||||
| 							<svg class="RV-Iconized__Icon" version="1.1" id="Capa_1" |                             <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"> | ||||||
| 								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> |                                 <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 |                                     <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" /> | 		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" /> | ||||||
| @@ -113,8 +132,7 @@ | |||||||
|             <ul class="LP-ImageGrid__Container"> |             <ul class="LP-ImageGrid__Container"> | ||||||
|                 {% for place_image in place.images.all %} |                 {% for place_image in place.images.all %} | ||||||
|                 <li class="LP-ImageGrid__Item"> |                 <li class="LP-ImageGrid__Item"> | ||||||
| 					<a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" |                     <a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}"></a> | ||||||
| 							src="{{ place_image.filename.thumbnail.url }}"></a> |  | ||||||
|                 </li> |                 </li> | ||||||
|                 {% endfor %} |                 {% endfor %} | ||||||
|             </ul> |             </ul> | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| import json | import json, os | ||||||
| from importlib import import_module | from importlib import import_module | ||||||
|  |  | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.template import Library, TemplateSyntaxError | 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)) | icons_json = json.load(open(icons_json_path)) | ||||||
|  |  | ||||||
| register = Library() | register = Library() | ||||||
|   | |||||||
| @@ -8,7 +8,8 @@ from .views import ( | |||||||
|     PlaceUpdateView, |     PlaceUpdateView, | ||||||
|     PlaceDeleteView, |     PlaceDeleteView, | ||||||
| 	PhotoAlbumCreateView, | 	PhotoAlbumCreateView, | ||||||
| 	PhotoAlbumDeleteView | 	PhotoAlbumDeleteView, | ||||||
|  | 	PlaceTagSubmitView | ||||||
| ) | ) | ||||||
|  |  | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| @@ -20,5 +21,6 @@ urlpatterns = [ | |||||||
| 	path('photo_album/delete/<int:pk>', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'), | 	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/update/<int:pk>/', PlaceUpdateView.as_view(), name='place_edit'), | ||||||
|     path('place/delete/<int:pk>/', PlaceDeleteView.as_view(), name='place_delete'), |     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})) | ||||||
		Reference in New Issue
	
	Block a user