diff --git a/django_web_galleries/web_galleries/forms.py b/django_web_galleries/web_galleries/forms.py
index dee0cfe..7821d31 100644
--- a/django_web_galleries/web_galleries/forms.py
+++ b/django_web_galleries/web_galleries/forms.py
@@ -1,7 +1,10 @@
from django import forms
from django.utils.translation import gettext as _
-from .models import Image
+from .models import (
+ Image,
+ Gallery
+)
class ImageUploadForm(forms.ModelForm):
class Meta:
@@ -10,3 +13,21 @@ class ImageUploadForm(forms.ModelForm):
labels = {
'private': 'Make this image private'
}
+
+class GalleryCreatingForm(forms.ModelForm):
+ class Meta:
+ model = Gallery
+ fields = ['title', 'private']
+ labels = {
+ 'private': 'Make this gallery private'
+ }
+
+ images = forms.ImageField(
+ widget=forms.ClearableFileInput(attrs={'multiple': True})
+ )
+
+class AccessCodeForm(forms.Form):
+ access_code = forms.CharField(max_length=32)
+
+class VisitorNameForm(forms.Form):
+ name = forms.CharField(max_length=50)
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/models.py b/django_web_galleries/web_galleries/models.py
index 78cedef..e6aed26 100644
--- a/django_web_galleries/web_galleries/models.py
+++ b/django_web_galleries/web_galleries/models.py
@@ -1,4 +1,5 @@
import uuid
+import names
from django.db import models
from django.core.exceptions import ValidationError
@@ -6,6 +7,8 @@ from django.utils.translation import gettext as _
def get_uuid():
return str(uuid.uuid4())
+def get_visitor_name():
+ return names.get_first_name()
class Visitor(models.Model):
"""
@@ -21,11 +24,14 @@ class Visitor(models.Model):
name = models.CharField(
_('Human readable, self assigned name of the visitor'),
null=True,
- default=None,
+ default=get_visitor_name,
max_length=50,
unique=True
)
+ def __str__(self):
+ return self.name
+
class Gallery(models.Model):
"""
@@ -45,6 +51,7 @@ class Gallery(models.Model):
access_code = models.CharField(
_('Code to access private galleries'),
null=True,
+ blank=True,
max_length=32
)
@@ -57,11 +64,15 @@ class Gallery(models.Model):
visitors = models.ManyToManyField(
Visitor,
+ blank=True,
verbose_name=_('Visitors that a part of this gallery'),
related_name='linked_galleries'
)
- def clean(self):
+ def __str__(self):
+ return self.title
+
+ def mettwurst(self):
if self.private and access_code is None:
raise ValidationErrorr('Private gallery needs an access code')
if len(self.visitors.all()) > 10:
@@ -97,6 +108,7 @@ class Image(models.Model):
access_code = models.CharField(
_('Code to access private images'),
null=True,
+ blank=True,
max_length=32
)
@@ -114,15 +126,24 @@ class Image(models.Model):
galleries = models.ManyToManyField(
Gallery,
+ blank=True,
verbose_name=_('What galleries this image is in'),
related_name='images'
)
+ visible_to = models.ManyToManyField(
+ Visitor,
+ blank=True,
+ verbose_name=_('Visitors that can see this picture if it is marked private'),
+ related_name='visible_images'
+ )
+
def __str__(self):
if self.title:
return self.title
elif self.description:
return self.description[:100]
else:
- return super.__str__()
+ return super().__str__()
+
diff --git a/django_web_galleries/web_galleries/static/web-galleries.css b/django_web_galleries/web_galleries/static/web-galleries.css
index 7381f51..c770a26 100644
--- a/django_web_galleries/web_galleries/static/web-galleries.css
+++ b/django_web_galleries/web_galleries/static/web-galleries.css
@@ -39,6 +39,10 @@
}
.RV-Image__link {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+
transition: transform 300ms ease-in-out;
}
@@ -46,6 +50,15 @@
transform: scale(1.1);
}
+.RV-Image__title {
+ flex-basis: 100%;
+ display: block;
+}
+
+.RV-Image {
+ max-width: 100%;
+}
+
.RV-Fieldset {
margin: 50px 200px;
display: flex;
@@ -53,6 +66,14 @@
gap: 30px;
}
+.RV-Navigation {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
.RV-Input {
display: flex;
flex-direction: column;
diff --git a/django_web_galleries/web_galleries/templates/gallery/create_gallery.html b/django_web_galleries/web_galleries/templates/gallery/create_gallery.html
new file mode 100644
index 0000000..2578528
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/gallery/create_gallery.html
@@ -0,0 +1,15 @@
+{% extends '../global.html' %}
+
+{% block content %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/gallery/gallery_detail.html b/django_web_galleries/web_galleries/templates/gallery/gallery_detail.html
new file mode 100644
index 0000000..ff4aea2
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/gallery/gallery_detail.html
@@ -0,0 +1,26 @@
+{% extends '../global.html' %}
+
+{% load responsive_images %}
+
+{% block content %}
+ {% if gallery.title %}
+ {{gallery.title}}
+ {% endif %}
+
+
+ {% for image in gallery.images.all %}
+
+
+
+ {% endfor %}
+
+ {% if gallery.private and gallery.visitors.all|length > 0 and gallery.created_by == visitor %}
+
+
You have gained access to the following people
+ {% for visitor in gallery.visitors.all %}
+ {{visitor.name}}
Revoke access
+ {% endfor %}
+
+ {% endif %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/gallery/gallery_no_access.html b/django_web_galleries/web_galleries/templates/gallery/gallery_no_access.html
new file mode 100644
index 0000000..436fa3d
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/gallery/gallery_no_access.html
@@ -0,0 +1,17 @@
+{% extends '../global.html' %}
+
+{% block content %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/gallery/list_galleries.html b/django_web_galleries/web_galleries/templates/gallery/list_galleries.html
new file mode 100644
index 0000000..4c86e1c
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/gallery/list_galleries.html
@@ -0,0 +1,40 @@
+{% extends '../global.html' %}
+
+{% load responsive_images %}
+
+{% block content %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/gallery/my_galleries.html b/django_web_galleries/web_galleries/templates/gallery/my_galleries.html
new file mode 100644
index 0000000..b0e5c51
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/gallery/my_galleries.html
@@ -0,0 +1,80 @@
+{% extends '../global.html' %}
+
+{% load responsive_images %}
+
+{% block content %}
+{% if created_galleries|length > 0 %}
+
+{% endif %}
+{% if invited_galleries|length > 0 %}
+
+{% endif %}
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/global.html b/django_web_galleries/web_galleries/templates/global.html
index aa6fbb5..21c8b68 100644
--- a/django_web_galleries/web_galleries/templates/global.html
+++ b/django_web_galleries/web_galleries/templates/global.html
@@ -30,18 +30,34 @@
-
+
My galleries
-
+
Create gallery
+
+
+ Hello there, {{ visitor.name }}
+
+
+
+
+ {% if request.META.HTTP_REFERER %}
+ back
+ {% endif %}
{% block content %}
{% endblock content %}
diff --git a/django_web_galleries/web_galleries/templates/image/image_detail.html b/django_web_galleries/web_galleries/templates/image/image_detail.html
new file mode 100644
index 0000000..5545b22
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/image/image_detail.html
@@ -0,0 +1,7 @@
+{% extends '../global.html' %}
+
+{% load responsive_images %}
+
+{% block content %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/image/image_no_access.html b/django_web_galleries/web_galleries/templates/image/image_no_access.html
new file mode 100644
index 0000000..dc699a2
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/image/image_no_access.html
@@ -0,0 +1,7 @@
+{% extends '../global.html' %}
+
+{% block content %}
+ You have no access to this private image
+ But you can gain access by entering the access code below:
+
+{% endlbock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/image/my_images.html b/django_web_galleries/web_galleries/templates/image/my_images.html
new file mode 100644
index 0000000..96d9fe2
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/image/my_images.html
@@ -0,0 +1,30 @@
+{% extends '../global.html' %}
+
+{% load responsive_images %}
+
+{% block content %}
+ {% if uploaded_images|length > 0 %}
+
+ Images you uploaded
+
+ {% for image in uploaded_images %}
+
+
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% if shared_images|length > 0 %}
+
+ Images shared with you
+
+ {% for image in shared_images %}
+
+
+
+ {% endfor %}
+
+
+ {% endif %}
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/list_images/image_list.html b/django_web_galleries/web_galleries/templates/list_images/image_list.html
index 4227257..30b5224 100644
--- a/django_web_galleries/web_galleries/templates/list_images/image_list.html
+++ b/django_web_galleries/web_galleries/templates/list_images/image_list.html
@@ -3,13 +3,13 @@
{% load responsive_images %}
{% block content %}
-
+
{% for image in images %}
-
+
{% endfor %}
-
+
{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/templates/visitor/visitor_settings.html b/django_web_galleries/web_galleries/templates/visitor/visitor_settings.html
new file mode 100644
index 0000000..d83eecb
--- /dev/null
+++ b/django_web_galleries/web_galleries/templates/visitor/visitor_settings.html
@@ -0,0 +1,13 @@
+{% extends '../global.html' %}
+
+{% block content %}
+
+{% endblock content %}
\ No newline at end of file
diff --git a/django_web_galleries/web_galleries/urls.py b/django_web_galleries/web_galleries/urls.py
index 5f4897c..1adf16b 100644
--- a/django_web_galleries/web_galleries/urls.py
+++ b/django_web_galleries/web_galleries/urls.py
@@ -3,11 +3,25 @@ from django.urls import path
from .views import (
ImageUploadView,
PublicImageListView,
- MyImagesListView
+ MyImagesListView,
+ GalleryCreateView,
+ MyGalleriesListView,
+ GalleryDetailView,
+ ImageDetailView,
+ GainAccessToGalleryView,
+ VisitorSettingsView,
+ RevokeAccessToGallery
)
urlpatterns = [
path('', PublicImageListView.as_view(), name='home'),
path('upload/', ImageUploadView.as_view(), name='upload_image'),
- path('my_images/', MyImagesListView.as_view(), name='my_images')
+ path('my_images/', MyImagesListView.as_view(), name='my_images'),
+ path('create_gallery/', GalleryCreateView.as_view(), name='create_gallery'),
+ path('my_galleries/', MyGalleriesListView.as_view(), name='my_galleries'),
+ path('gallery/', GalleryDetailView.as_view(), name='gallery'),
+ path('image/', ImageDetailView.as_view(), name='image'),
+ path('gallery/gain_access//', GainAccessToGalleryView.as_view(), name='gallery_gain_access'),
+ path('visitor/my_profile', VisitorSettingsView.as_view(), name='visitor_profile'),
+ path('gallery/revoke_access//', RevokeAccessToGallery.as_view(), name='gallery_revoke_access'),
]
diff --git a/django_web_galleries/web_galleries/views.py b/django_web_galleries/web_galleries/views.py
index f18792f..c641bc2 100644
--- a/django_web_galleries/web_galleries/views.py
+++ b/django_web_galleries/web_galleries/views.py
@@ -2,13 +2,25 @@ import secrets
from django.views import View
from django.views.generic.list import ListView
-from django.shortcuts import render, redirect
+from django.views.generic.detail import DetailView
+from django.contrib.auth.mixins import UserPassesTestMixin
from django.urls import reverse
+from django.shortcuts import (
+ render,
+ redirect,
+ get_object_or_404
+)
-from .forms import ImageUploadForm
+from .forms import (
+ ImageUploadForm,
+ GalleryCreatingForm,
+ AccessCodeForm,
+ VisitorNameForm
+)
from .models import (
Image,
- Visitor
+ Visitor,
+ Gallery
)
class VisitorSessionMixin(View):
@@ -24,6 +36,23 @@ class VisitorSessionMixin(View):
else:
return None
+ def can_access_image(self, image):
+ if image.private:
+ return self.get_visitor() == image.uploaded_by or self.get_visitor() in image.visible_to.all()
+ else:
+ return True
+
+ def can_access_gallery(self, gallery):
+ if gallery.private:
+ return self.get_visitor() == gallery.created_by or self.get_visitor() in gallery.visitors.all()
+ else:
+ return True
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['visitor'] = self.get_visitor()
+ return context
+
class ImageUploadView(VisitorSessionMixin, View):
def get(self, request):
@@ -32,7 +61,8 @@ class ImageUploadView(VisitorSessionMixin, View):
request,
'upload_image/upload.html',
{
- 'form': form
+ 'form': form,
+ 'visitor': self.get_visitor()
}
)
@@ -46,10 +76,9 @@ class ImageUploadView(VisitorSessionMixin, View):
image = form.save(commit=False)
if image.private:
- image.access_code = secrets.token_hex(32)
+ image.access_code = secrets.token_hex(16)
image.uploaded_by = self.get_visitor()
- print(image.uploaded_by)
image.save()
return redirect(
reverse('home')
@@ -60,7 +89,8 @@ class ImageUploadView(VisitorSessionMixin, View):
request,
'upload_image/upload.html',
{
- 'form': form
+ 'form': form,
+ 'visitor': self.get_visitor()
}
)
@@ -71,14 +101,253 @@ class PublicImageListView(ListView):
template_name = 'list_images/image_list.html'
def get_queryset(self):
- return Image.objects.all().filter(private=False)
+ return Image.objects.all().filter(private=False).order_by('-uploaded_when')[:10]
-class MyImagesListView(VisitorSessionMixin, ListView):
+class MyImagesListView(VisitorSessionMixin, View):
+ def get(self, request):
+ visitor = self.get_visitor()
+ uploaded_images = Image.objects.all().filter(
+ uploaded_by=self.get_visitor()
+ )
+ shared_images = Image.objects.all().filter(
+ visible_to__in=(visitor, )
+ )
+
+ return render(
+ request,
+ 'image/my_images.html',
+ {
+ 'uploaded_images': uploaded_images,
+ 'shared_images': shared_images,
+ 'visitor': self.get_visitor()
+ }
+ )
+
+
+class GalleryCreateView(VisitorSessionMixin, View):
+
+ def get(self, request):
+ form = GalleryCreatingForm()
+ return render(
+ request,
+ 'gallery/create_gallery.html',
+ {
+ 'form': form,
+ 'visitor': self.get_visitor()
+ }
+ )
+
+ def post(self, request):
+ form = GalleryCreatingForm(
+ request.POST,
+ request.FILES
+ )
+
+ if form.is_valid():
+ gallery = form.save(commit=False)
+
+ if gallery.private:
+ gallery.access_code = secrets.token_hex(16)
+
+ gallery.created_by = self.get_visitor()
+
+ gallery.save()
+ for image in request.FILES.getlist('images'):
+ imageObject = Image.objects.create(
+ image_file=image,
+ uploaded_by=gallery.created_by,
+ private=gallery.private,
+ access_code=gallery.access_code
+
+ )
+ imageObject.galleries.add(gallery)
+ imageObject.save()
+
+ gallery.save()
+ return redirect(
+ reverse('home')
+ )
+ else:
+ form = ImageUploadForm()
+ return render(
+ request,
+ 'upload_image/upload.html',
+ {
+ 'form': form,
+ 'visitor': self.get_visitor()
+ }
+ )
+
+class MyGalleriesListView(VisitorSessionMixin, View):
+
+ def get(self, request):
+ visitor = self.get_visitor()
+ created_galleries = Gallery.objects.all().filter(
+ created_by=self.get_visitor()
+ )
+ invited_galleries = Gallery.objects.all().filter(
+ visitors__in=(visitor, )
+ )
+
+ return render(
+ request,
+ 'gallery/my_galleries.html',
+ {
+ 'created_galleries': created_galleries,
+ 'invited_galleries': invited_galleries,
+ 'visitor': self.get_visitor()
+ }
+ )
+
+class GalleryDetailView(VisitorSessionMixin, UserPassesTestMixin, DetailView):
+ model = Gallery
+ template_name = 'gallery/gallery_detail.html'
+ context_object_name = 'gallery'
+
+ def test_func(self):
+ return self.can_access_gallery(
+ self.get_object()
+ )
+
+ def handle_no_permission(self):
+ return redirect(
+ reverse(
+ 'gallery_gain_access',
+ kwargs={
+ 'gallery_id': self.get_object().id
+ }
+ )
+ )
+
+class ImageDetailView(VisitorSessionMixin, UserPassesTestMixin, DetailView):
model = Image
- paginated_by = 20
- context_object_name = 'images'
- template_name = 'list_images/image_list.html'
+ template_name = 'image/image_detail.html'
+ context_object_name = 'image'
- def get_queryset(self):
- return Image.objects.all().filter(uploaded_by=self.get_visitor())
-
\ No newline at end of file
+ def test_func(self):
+ return self.can_access_image(
+ self.get_object()
+ )
+
+ def handle_no_permission(self):
+ return redirect(
+ reverse('home')
+ )
+
+class GainAccessToGalleryView(VisitorSessionMixin, View):
+ def _redirect_to_gallery(self, gallery):
+ return redirect(
+ reverse(
+ 'gallery',
+ kwargs={
+ 'pk': gallery.id
+ }
+ )
+ )
+
+ def get(self, request, gallery_id):
+ gallery = get_object_or_404(
+ Gallery,
+ id=gallery_id
+ )
+ if self.can_access_gallery(gallery):
+ return self._redirect_to_gallery(gallery)
+ else:
+ return render(
+ self.request,
+ 'gallery/gallery_no_access.html',
+ {
+ 'form': AccessCodeForm(),
+ 'visitor': self.get_visitor()
+ }
+ )
+
+ def post(self, request, gallery_id):
+ gallery = get_object_or_404(
+ Gallery,
+ id=gallery_id
+ )
+ if self.can_access_gallery(gallery):
+ return self._redirect_to_gallery(gallery)
+ else:
+ form = AccessCodeForm(
+ request.POST
+ )
+ if form.is_valid():
+ access_code = form.cleaned_data['access_code']
+ if access_code == gallery.access_code:
+ gallery.visitors.add(self.get_visitor())
+ gallery.save()
+ self.get_visitor().save()
+ return self._redirect_to_gallery(gallery)
+
+ return render(
+ self.request,
+ 'gallery/gallery_no_access.html',
+ {
+ 'form': AccessCodeForm(),
+ 'visitor': self.get_visitor()
+ }
+ )
+
+class RevokeAccessToGallery(VisitorSessionMixin, View):
+ def get(self, request, gallery_id, visitor_id):
+ gallery = get_object_or_404(Gallery, id=gallery_id)
+ visitor = self.get_visitor()
+
+ if visitor == gallery.created_by:
+ visitor_to_remove = get_object_or_404(Visitor, id=visitor_id)
+ if visitor_to_remove in gallery.visitors.all():
+ gallery.visitors.remove(visitor_to_remove)
+ gallery.access_code = secrets.token_hex(16)
+ gallery.save()
+
+ return redirect(
+ reverse(
+ 'gallery',
+ kwargs={
+ 'pk': gallery.id
+ }
+ )
+ )
+
+class VisitorSettingsView(VisitorSessionMixin, View):
+ def get(self, request):
+ name_form = VisitorNameForm(
+ initial={
+ 'name': self.get_visitor().name
+ }
+ )
+
+ return render(
+ request,
+ 'visitor/visitor_settings.html',
+ {
+ 'name_form': name_form,
+ 'visitor': self.get_visitor()
+ }
+ )
+
+ def post(self, request):
+ name_form = VisitorNameForm(
+ request.POST
+ )
+
+ if name_form.is_valid():
+ visitor = self.get_visitor()
+ visitor.name = name_form.cleaned_data['name']
+ visitor.save()
+
+ name_form = VisitorNameForm(
+ initial={
+ 'name': self.get_visitor().name
+ }
+ )
+ return render(
+ request,
+ 'visitor/visitor_settings.html',
+ {
+ 'name_form': name_form,
+ 'visitor': self.get_visitor()
+ }
+ )