9 Commits

22 changed files with 943 additions and 40 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
django_web_galleries/media/* django_web_galleries/media/*
django_web_galleries/static/*

View File

@@ -8,6 +8,7 @@ django = "*"
django-responsive-images = "*" django-responsive-images = "*"
pillow = "*" pillow = "*"
django-widget-tweaks = "*" django-widget-tweaks = "*"
names = "*"
[requires] [requires]
python_version = "3.8" python_version = "3.8"

View File

@@ -16,7 +16,20 @@ Including another URLconf
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('', include('web_galleries.urls')) path('', include('web_galleries.urls'))
] ]
if settings.DEBUG:
urlpatterns += static(
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT
)
urlpatterns += static(
settings.STATIC_URL,
document_root=settings.STATIC_ROOT
)

View File

@@ -1,9 +1,38 @@
from django import forms from django import forms
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from .models import Image from .models import (
Image,
Gallery
)
class ImageUploadForm(forms.ModelForm): class ImageUploadForm(forms.ModelForm):
class Meta: class Meta:
model = Image model = Image
fields = ['description', 'image_file', 'private'] fields = ['description', 'image_file', 'private', 'title']
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)
class ImageUploadForm(forms.Form):
images = forms.ImageField(
widget=forms.ClearableFileInput(attrs={'multiple': True})
)

View File

@@ -0,0 +1,109 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-28 15:24+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: web_galleries/templates/gallery/create_gallery.html:14
msgid "Create"
msgstr "Erstellen"
#: web_galleries/templates/gallery/gallery_detail.html:10
msgid "Upload image to this gallery"
msgstr "Bild in diese Gallerie hochladen"
#: web_galleries/templates/gallery/gallery_detail.html:21
msgid "You have gained access to the following people"
msgstr "Du hast folgenden Leuten Zugriff erteilt"
#: web_galleries/templates/gallery/gallery_detail.html:23
msgid "Revoke access"
msgstr "Zugriff entziehen"
#: web_galleries/templates/gallery/gallery_no_access.html:9
msgid "You do not have access to this private gallery"
msgstr "Du hast keinen Zugriff auf diese privaten Gallerie"
#: web_galleries/templates/gallery/gallery_no_access.html:10
msgid "But you can gain access by entering the access code below:"
msgstr "Du kannst aber Zugriff erhalten indem du den Zugangscode eingibst:"
#: web_galleries/templates/gallery/gallery_no_access.html:16
msgid "Enter"
msgstr "Abschicken"
#: web_galleries/templates/gallery/list_galleries.html:12
#: web_galleries/templates/gallery/my_galleries.html:14
#: web_galleries/templates/gallery/my_galleries.html:51
msgid "no images"
msgstr "keine Bilder"
#: web_galleries/templates/gallery/my_galleries.html:9
msgid "Your galleries "
msgstr "Deine Gallerien"
#: web_galleries/templates/gallery/my_galleries.html:46
msgid "Galleries you were invited to"
msgstr "Gallerien in die du eingeladen wurdest"
#: web_galleries/templates/gallery/upload_image.html:8
msgid "Upload image to"
msgstr "Bild hochladen in"
#: web_galleries/templates/gallery/upload_image.html:13
#: web_galleries/templates/upload_image/upload.html:16
msgid "Upload"
msgstr "Hochladen"
#: web_galleries/templates/global.html:22
msgid "Home"
msgstr "Startseite"
#: web_galleries/templates/global.html:27
msgid "Upload image"
msgstr "Bild hochladen"
#: web_galleries/templates/global.html:32
msgid "My images"
msgstr "Meine Bilder"
#: web_galleries/templates/global.html:37
msgid "My galleries"
msgstr "Meine Gallerien"
#: web_galleries/templates/global.html:42
msgid "Create gallery"
msgstr "Gallerie erstellen"
#: web_galleries/templates/global.html:48
msgid "Hello there"
msgstr "Hallo"
#: web_galleries/templates/global.html:53
msgid "My Profile"
msgstr "Dein Profil"
#: web_galleries/templates/image/my_images.html:9
msgid "Images you uploaded"
msgstr "Bilder die du hochgeladen hast"
#: web_galleries/templates/image/my_images.html:21
msgid "Images shared with you"
msgstr "Bilder die mit dir geteilt wurden"
#: web_galleries/templates/visitor/visitor_settings.html:12
msgid "Save"
msgstr "Speichern"

View File

@@ -1,7 +1,15 @@
import uuid
import names
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _ 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): class Visitor(models.Model):
""" """
Stores information about a visitor of this application. Stores information about a visitor of this application.
@@ -10,16 +18,20 @@ class Visitor(models.Model):
""" """
session_id = models.CharField( session_id = models.CharField(
_('A cookie set by this application to identify a visitor by session'), _('A cookie set by this application to identify a visitor by session'),
max_length=50 max_length=50,
default=get_uuid
) )
name = models.CharField( name = models.CharField(
_('Human readable, self assigned name of the visitor'), _('Human readable, self assigned name of the visitor'),
null=True, null=True,
default=None, default=get_visitor_name,
max_length=50, max_length=50,
unique=True unique=True
) )
def __str__(self):
return self.name
class Gallery(models.Model): class Gallery(models.Model):
""" """
@@ -39,6 +51,7 @@ class Gallery(models.Model):
access_code = models.CharField( access_code = models.CharField(
_('Code to access private galleries'), _('Code to access private galleries'),
null=True, null=True,
blank=True,
max_length=32 max_length=32
) )
@@ -51,22 +64,26 @@ class Gallery(models.Model):
visitors = models.ManyToManyField( visitors = models.ManyToManyField(
Visitor, Visitor,
blank=True,
verbose_name=_('Visitors that a part of this gallery'), verbose_name=_('Visitors that a part of this gallery'),
related_name='linked_galleries' related_name='linked_galleries'
) )
def clean(self): def __str__(self):
if self.private and access_code is None: return self.title
raise ValidationErrorr('Private gallery needs an access code')
if len(self.visitors.all()) > 10:
raise ValidationError('Gallery can not have more than 10 visitors assigned')
class Image(models.Model): class Image(models.Model):
""" """
An image contains the path to the image file and several information An image contains the path to the image file and several information
like description, uploader and affiliation to galleries. like description, uploader and affiliation to galleries.
""" """
title = models.CharField(
_('Title of the image'),
max_length=100,
null=True,
blank=True
)
description = models.TextField( description = models.TextField(
_('An optional description of the Image'), _('An optional description of the Image'),
null=True null=True
@@ -84,6 +101,7 @@ class Image(models.Model):
access_code = models.CharField( access_code = models.CharField(
_('Code to access private images'), _('Code to access private images'),
null=True, null=True,
blank=True,
max_length=32 max_length=32
) )
@@ -101,7 +119,24 @@ class Image(models.Model):
galleries = models.ManyToManyField( galleries = models.ManyToManyField(
Gallery, Gallery,
blank=True,
verbose_name=_('What galleries this image is in'), verbose_name=_('What galleries this image is in'),
related_name='images' 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__()

View File

@@ -39,6 +39,10 @@
} }
.RV-Image__link { .RV-Image__link {
display: flex;
flex-direction: row;
flex-wrap: wrap;
transition: transform 300ms ease-in-out; transition: transform 300ms ease-in-out;
} }
@@ -46,6 +50,15 @@
transform: scale(1.1); transform: scale(1.1);
} }
.RV-Image__title {
flex-basis: 100%;
display: block;
}
.RV-Image {
max-width: 100%;
}
.RV-Fieldset { .RV-Fieldset {
margin: 50px 200px; margin: 50px 200px;
display: flex; display: flex;
@@ -53,6 +66,14 @@
gap: 30px; gap: 30px;
} }
.RV-Navigation {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
}
.RV-Input { .RV-Input {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -0,0 +1,17 @@
{% extends '../global.html' %}
{% load i18n %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="RV-Fieldset">
{% include 'partials/form_input.html' with field=form.title %}
{% include 'partials/form_input.html' with field=form.private classes='RV-Input--compact RV-Input--reverse' %}
{% include 'partials/form_input.html' with field=form.images %}
</div>
<div class="RV-Fieldset">
<button type="submit">{% translate 'Create' %}</button>
</div>
</form>
{% endblock content %}

View File

@@ -0,0 +1,28 @@
{% extends '../global.html' %}
{% load i18n %}
{% load responsive_images %}
{% block content %}
{% if gallery.title %}
<h1>{{gallery.title}}</h1>
{% endif %}
<a href="{% url 'gallery_upload' gallery_id=gallery.id %}">{% translate 'Upload image to this gallery' %}</a>
<section class="RV-Images">
<li class="RV-Images__list">
{% for image in gallery.images.all %}
<a href="{% url 'image_through_gallery' gallery_id=gallery.id image_id=image.id %}" class="RV-Image__link RV-Image__link--detail">
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
</a>
{% endfor %}
</li>
{% if gallery.private and gallery.visitors.all|length > 0 and gallery.created_by == visitor %}
<div>
<h2>{% translate 'You have gained access to the following people' %}</h2>
{% for visitor in gallery.visitors.all %}
{{visitor.name}} <a href="{% url 'gallery_revoke_access' gallery_id=gallery.id visitor_id=visitor.id %}">{% translate 'Revoke access' %}</a>
{% endfor %}
</div>
{% endif %}
</section>
{% endblock content %}

View File

@@ -0,0 +1,19 @@
{% extends '../global.html' %}
{% load i18n %}
{% block content %}
<form method="POST">
{% csrf_token %}
<div class="RV-Fieldset">
<h1>{% translate 'You do not have access to this private gallery' %}</h1>
{% translate 'But you can gain access by entering the access code below:' %}'
</div>
<div class="RV-Fieldset">
{% include 'partials/form_input.html' with field=form.access_code %}
</div>
<div class="RV-Fieldset">
<button type="submit">{% translate 'Enter' %}</button>
</div>
</form>
{% endblock content %}

View File

@@ -0,0 +1,41 @@
{% extends '../global.html' %}
{% load i18n %}
{% load responsive_images %}
{% block content %}
<section class="RV-Images">
<li class="RV-Images__list">
{% for gallery in galleries %}
<a href="{% url 'gallery' pk=gallery.pk %}" class="RV-Image__link RV-Image__link--detail">
{% if gallery.images.all|length <= 0 %}
{% translate 'no images' %}
{% endif %}
{% if gallery.images.all|length == 1 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 2 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 3 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length >= 4 %}
{% for image in gallery.images.all|slice:":4" %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.title %}
<p class="RV-Image__title">{{gallery.title}}</p>
{% endif %}
</a>
{% endfor %}
</li>
</section>
{% endblock content %}

View File

@@ -0,0 +1,81 @@
{% extends '../global.html' %}
{% load i18n %}
{% load responsive_images %}
{% block content %}
{% if created_galleries|length > 0 %}
<section class="RV-Images">
<h2>{% translate 'Your galleries '%}</h2>
<li class="RV-Images__list">
{% for gallery in created_galleries %}
<a href="{% url 'gallery' pk=gallery.pk %}" class="RV-Image__link RV-Image__link--detail">
{% if gallery.images.all|length <= 0 %}
{% translate 'no images' %}
{% endif %}
{% if gallery.images.all|length == 1 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 2 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 3 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length >= 4 %}
{% for image in gallery.images.all|slice:":4" %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.title %}
<p class="RV-Image__title">{{gallery.title}}</p>
{% endif %}
</a>
{% endfor %}
</li>
</section>
{% endif %}
{% if invited_galleries|length > 0 %}
<section class="RV-Images">
<h2>{% translate 'Galleries you were invited to' %}</h2>
<li class="RV-Images__list">
{% for gallery in invited_galleries %}
<a href="{% url 'gallery' pk=gallery.pk %}" class="RV-Image__link RV-Image__link--detail">
{% if gallery.images.all|length <= 0 %}
{% translate 'no images' %}
{% endif %}
{% if gallery.images.all|length == 1 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 2 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x200 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length == 3 %}
{% for image in gallery.images.all %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.images.all|length >= 4 %}
{% for image in gallery.images.all|slice:":4" %}
<img class="RV-Image__source" src="{% src image.image_file 100x100 %}">
{% endfor %}
{% endif %}
{% if gallery.title %}
<p class="RV-Image__title">{{gallery.title}}</p>
{% endif %}
</a>
{% endfor %}
</li>
</section>
{% endif %}
{% endblock content %}

View File

@@ -0,0 +1,16 @@
{% extends '../global.html' %}
{% load i18n %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<h2>{% translate 'Upload image to' %} "{{gallery.title}}"</h2>
<div class="RV-Fieldset">
{% include 'partials/form_input.html' with field=form.images %}
</div>
<div class="RV-Fieldset">
<button type="submit">{% translate 'Upload' %}</button>
</div>
</form>
{% endblock content %}

View File

@@ -1,5 +1,7 @@
{% load static %} {% load static %}
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@@ -7,6 +9,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Gallery</title> <title>Web Gallery</title>
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'web-galleries.css' %}"> <link rel="stylesheet" href="{% static 'web-galleries.css' %}">
</head> </head>
<body> <body>
@@ -16,27 +19,48 @@
<ul class="RV-Navigation__list"> <ul class="RV-Navigation__list">
<li class="RV-Navigation__item"> <li class="RV-Navigation__item">
<a href="{% url 'home' %}" class="RV-Navigation__link"> <a href="{% url 'home' %}" class="RV-Navigation__link">
Home {% translate 'Home' %}
</a> </a>
</li> </li>
<li class="RV-Navigation__item"> <li class="RV-Navigation__item">
<a href="{% url 'upload_image' %}" class="RV-Navigation__link"> <a href="{% url 'upload_image' %}" class="RV-Navigation__link">
Upload image {% translate 'Upload image' %}
</a> </a>
</li> </li>
<li class="RV-Navigation__item"> <li class="RV-Navigation__item">
<a href="#" class="RV-Navigation__link"> <a href="{% url 'my_images' %}" class="RV-Navigation__link">
My galleries {% translate 'My images' %}
</a> </a>
</li> </li>
<li class="RV-Navigation__item"> <li class="RV-Navigation__item">
<a href="#" class="RV-Navigation__link"> <a href="{% url 'my_galleries' %}" class="RV-Navigation__link">
Create gallery {% translate 'My galleries' %}
</a>
</li>
<li class="RV-Navigation__item">
<a href="{% url 'create_gallery' %}" class="RV-Navigation__link">
{% translate 'Create gallery' %}
</a> </a>
</li> </li>
</ul> </ul>
<div class="RV-UserInfo">
<div class="RV-UserInfo__name">
{% translate 'Hello there' %}, {{ visitor.name }}
</div>
<ul class="RV-UserInfo__links">
<li class="RV-UserInfo__link">
<a href="{% url 'visitor_profile' %}">
{% translate 'My Profile' %}
</a>
</li>
</ul>
</div>
</nav> </nav>
</header> </header>
{% if request.META.HTTP_REFERER %}
<a href="{{ request.META.HTTP_REFERER }}">back</a>
{% endif %}
<main class="RV-Content"> <main class="RV-Content">
{% block content %} {% block content %}
{% endblock content %} {% endblock content %}

View File

@@ -0,0 +1,7 @@
{% extends '../global.html' %}
{% load responsive_images %}
{% block content %}
<img class="RV-Image" src="{% src image.image_file 1920x1200 nocrop %}" />
{% endblock content %}

View File

@@ -0,0 +1,7 @@
{% extends '../global.html' %}
{% block content %}
<h1>You have no access to this private image</h1>
But you can gain access by entering the access code below:
{% endlbock content %}

View File

@@ -0,0 +1,31 @@
{% extends '../global.html' %}
{% load i18n %}
{% load responsive_images %}
{% block content %}
{% if uploaded_images|length > 0 %}
<section class="RV-Images">
<h2>{% translate 'Images you uploaded' %}</h2>
<li class="RV-Images__list">
{% for image in uploaded_images %}
<a href="{% url 'image' pk=image.id %}" class="RV-Image__link RV-Image__link--detail">
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
</a>
{% endfor %}
</li>
</section>
{% endif %}
{% if shared_images|length > 0 %}
<section class="RV-Images">
<h2>{% translate 'Images shared with you' %}</h2>
<li class="RV-Images__list">
{% for image in shared_images %}
<a href="{% url 'image' pk=image.id %}" class="RV-Image__link RV-Image__link--detail">
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
</a>
{% endfor %}
</li>
</section>
{% endif %}
{% endblock content %}

View File

@@ -3,13 +3,13 @@
{% load responsive_images %} {% load responsive_images %}
{% block content %} {% block content %}
<sectoin class="RV-Images"> <section class="RV-Images">
<li class="RV-Images__list"> <li class="RV-Images__list">
{% for image in images %} {% for image in images %}
<a href="#" class="RV-Image__link RV-Image__link--detail"> <a href="{% url 'image' pk=image.id %}" class="RV-Image__link RV-Image__link--detail">
<img class="RV-Image__source" src="{% src image.image_file 200x200 %}"> <img class="RV-Image__source" src="{% src image.image_file 200x200 %}">
</a> </a>
{% endfor %} {% endfor %}
</li> </li>
</sectoin> </section>
{% endblock content %} {% endblock content %}

View File

@@ -1,5 +1,7 @@
{% extends '../global.html' %} {% extends '../global.html' %}
{% load i18n %}
{% block content %} {% block content %}
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
@@ -11,7 +13,7 @@
</div> </div>
<div class="RV-Fieldset"> <div class="RV-Fieldset">
<button type="submit">Upload</button> <button type="submit">{% translate 'Upload' %}</button>
</div> </div>
</form> </form>
{% endblock content %} {% endblock content %}

View File

@@ -0,0 +1,15 @@
{% extends '../global.html' %}
{% load i18n %}
{% block content %}
<form method="POST">
{% csrf_token %}
<div class="RV-Fieldset">
{% include 'partials/form_input.html' with field=name_form.name %}
</div>
<div class="RV-Fieldset">
<button type="submit">{% translate 'Save' %}</button>
</div>
</form>
{% endblock content %}

View File

@@ -1,19 +1,31 @@
from django.urls import path from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from .views import ( from .views import (
ImageUploadView, ImageUploadView,
PublicImageListView PublicImageListView,
MyImagesListView,
GalleryCreateView,
MyGalleriesListView,
GalleryDetailView,
ImageDetailView,
GainAccessToGalleryView,
VisitorSettingsView,
RevokeAccessToGallery,
UploadToGalleryView,
ImageThroughGalleryView
) )
urlpatterns = [ urlpatterns = [
path('', PublicImageListView.as_view(), name='home'), path('', PublicImageListView.as_view(), name='home'),
path('upload/', ImageUploadView.as_view(), name='upload_image') path('upload/', ImageUploadView.as_view(), name='upload_image'),
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/<int:pk>', GalleryDetailView.as_view(), name='gallery'),
path('image/<int:pk>', ImageDetailView.as_view(), name='image'),
path('gallery/gain_access/<int:gallery_id>/', GainAccessToGalleryView.as_view(), name='gallery_gain_access'),
path('visitor/my_profile', VisitorSettingsView.as_view(), name='visitor_profile'),
path('gallery/revoke_access/<int:gallery_id>/<int:visitor_id>', RevokeAccessToGallery.as_view(), name='gallery_revoke_access'),
path('gallery/upload/<int:gallery_id>', UploadToGalleryView.as_view(), name='gallery_upload'),
path('gallery/image/<int:gallery_id>/<int:image_id>', ImageThroughGalleryView.as_view(), name='image_through_gallery')
] ]
if settings.DEBUG:
urlpatterns += static(
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT
)

View File

@@ -2,20 +2,68 @@ import secrets
from django.views import View from django.views import View
from django.views.generic.list import ListView 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.urls import reverse
from django.shortcuts import (
render,
redirect,
get_object_or_404
)
from .forms import ImageUploadForm from .forms import (
from .models import Image ImageUploadForm,
GalleryCreatingForm,
AccessCodeForm,
VisitorNameForm,
ImageUploadForm
)
from .models import (
Image,
Visitor,
Gallery
)
class ImageUploadView(View): class VisitorSessionMixin(View):
def get_visitor(self):
if self.request.session:
if 'visitor_session' in self.request.session:
if Visitor.objects.filter(session_id=self.request.session['visitor_session']).exists():
return Visitor.objects.get(session_id=self.request.session['visitor_session'])
else:
visitor = Visitor.objects.create()
self.request.session['visitor_session'] = visitor.session_id
return visitor
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): def get(self, request):
form = ImageUploadForm() form = ImageUploadForm()
return render( return render(
request, request,
'upload_image/upload.html', 'upload_image/upload.html',
{ {
'form': form 'form': form,
'visitor': self.get_visitor()
} }
) )
@@ -29,8 +77,9 @@ class ImageUploadView(View):
image = form.save(commit=False) image = form.save(commit=False)
if image.private: if image.private:
image.access_code = secrets.token_hex(32) image.access_code = secrets.token_hex(16)
image.uploaded_by = self.get_visitor()
image.save() image.save()
return redirect( return redirect(
reverse('home') reverse('home')
@@ -41,15 +90,360 @@ class ImageUploadView(View):
request, request,
'upload_image/upload.html', 'upload_image/upload.html',
{ {
'form': form 'form': form,
'visitor': self.get_visitor()
} }
) )
class PublicImageListView(ListView): class PublicImageListView(VisitorSessionMixin, ListView):
model = Image model = Image
paginate_by = 20 paginate_by = 20
context_object_name = 'images' context_object_name = 'images'
template_name = 'list_images/image_list.html' template_name = 'list_images/image_list.html'
def get_queryset(self): 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, 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(
'gallery',
kwargs={
'pk': gallery.id
}
)
)
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
template_name = 'image/image_detail.html'
context_object_name = 'image'
def test_func(self):
return self.can_access_image(
self.get_object()
)
def handle_no_permission(self):
return redirect(
reverse('home')
)
class ImageThroughGalleryView(VisitorSessionMixin, View):
def _redirect_gallery_no_access(self, gallery):
return redirect(
reverse(
'gallery_gain_access',
kwargs={
'gallery_id': gallery.id
}
)
)
def get(self, request, gallery_id, image_id):
gallery = get_object_or_404(Gallery, id=gallery_id)
if self.get_visitor() in gallery.visitors.all() or self.get_visitor() == gallery.created_by:
image = get_object_or_404(Image, id=image_id)
if image in gallery.images.all():
return render(
request,
'image/image_detail.html',
{
'image': image,
'visitor': self.get_visitor()
}
)
else:
return redirect(
reverse(
'gallery',
kwargs={
'pk': gallery.id
}
)
)
else:
return self._redirect_gallery_no_access(gallery)
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()
}
)
class UploadToGalleryView(VisitorSessionMixin, View):
def get(self, request, gallery_id):
upload_form = ImageUploadForm()
gallery = get_object_or_404(Gallery, id=gallery_id)
return render(
request,
'gallery/upload_image.html',
{
'form': upload_form,
'gallery': gallery
}
)
def post(self, request, gallery_id):
upload_form = ImageUploadForm(
request.POST,
request.FILES
)
if upload_form.is_valid():
gallery = get_object_or_404(Gallery, id=gallery_id)
for image in request.FILES.getlist('images'):
imageObject = Image.objects.create(
image_file=image,
uploaded_by=self.get_visitor(),
private=gallery.private,
access_code=gallery.access_code
)
imageObject.galleries.add(gallery)
imageObject.save()
gallery.save()
return redirect(
reverse(
'gallery',
kwargs={
'pk': gallery.id
}
)
)
else:
return render(
request,
'gallery/upload_image.html',
{
'form': upload_form,
'gallery': gallery
}
)