import os
from tempfile import gettempdir
from zipfile import ZipFile
from django.contrib.auth.decorators import login_required
from django.core.paginator import EmptyPage, Paginator
from django.http import Http404
from django.shortcuts import get_object_or_404, render
from django.utils.translation import get_language
from django_sendfile import sendfile
from photos.models import Album, Photo
from photos.services import (
check_shared_album_token,
get_annotated_accessible_albums,
is_album_accessible,
)
COVER_FILENAME = "cover.jpg"
[docs]@login_required
def index(request):
keywords = request.GET.get("keywords", "").split()
# Only show published albums
albums = Album.objects.filter(hidden=False)
for key in keywords:
albums = albums.filter(**{f"title_{get_language()}__icontains": key})
albums = get_annotated_accessible_albums(request, albums)
albums = albums.order_by("-date")
paginator = Paginator(albums, 16)
page = request.GET.get("page")
page = 1 if page is None or not page.isdigit() else int(page)
try:
albums = paginator.page(page)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
albums = paginator.page(paginator.num_pages)
page = paginator.num_pages
# Show the two pages before and after the current page
page_range_start = max(1, page - 2)
page_range_stop = min(page + 3, paginator.num_pages + 1)
# Add extra pages if we show less than 5 pages
page_range_start = min(page_range_start, page_range_stop - 5)
page_range_start = max(1, page_range_start)
# Add extra pages if we still show less than 5 pages
page_range_stop = max(page_range_stop, page_range_start + 5)
page_range_stop = min(page_range_stop, paginator.num_pages + 1)
page_range = range(page_range_start, page_range_stop)
return render(
request,
"photos/index.html",
{"albums": albums, "page_range": page_range, "keywords": keywords},
)
def _render_album_page(request, album):
context = {"album": album, "photos": album.photo_set.filter(hidden=False)}
return render(request, "photos/album.html", context)
[docs]@login_required
def album(request, slug):
album = get_object_or_404(Album, slug=slug)
if is_album_accessible(request, album):
return _render_album_page(request, album)
raise Http404("Sorry, you're not allowed to view this album")
[docs]def shared_album(request, slug, token):
album = get_object_or_404(Album, slug=slug)
check_shared_album_token(album, token)
return _render_album_page(request, album)
def _photo_path(album, filename):
photoname = os.path.basename(filename)
albumpath = os.path.join(album.photosdir, album.dirname)
photopath = os.path.join(albumpath, photoname)
get_object_or_404(Photo.objects.filter(album=album, file=photopath))
return photopath
def _download(request, album, filename):
"""This function provides a layer of indirection for shared albums"""
photopath = _photo_path(album, filename)
photo = get_object_or_404(Photo.objects.filter(album=album, file=photopath))
return sendfile(request, photo.file.path, attachment=True)
def _album_download(request, album):
"""This function provides a layer of indirection for shared albums"""
albumpath = os.path.join(album.photospath, album.dirname)
zipfilename = os.path.join(gettempdir(), "{}.zip".format(album.dirname))
if not os.path.exists(zipfilename):
with ZipFile(zipfilename, "w") as f:
pictures = [os.path.join(albumpath, x) for x in os.listdir(albumpath)]
for picture in pictures:
f.write(picture, arcname=os.path.basename(picture))
return sendfile(request, zipfilename, attachment=True)
[docs]@login_required
def download(request, slug, filename):
album = get_object_or_404(Album, slug=slug)
if is_album_accessible(request, album):
return _download(request, album, filename)
raise Http404("Sorry, you're not allowed to view this album")
[docs]@login_required
def album_download(request, slug):
album = get_object_or_404(Album, slug=slug)
if is_album_accessible(request, album):
return _album_download(request, album)
raise Http404("Sorry, you're not allowed to view this album")
[docs]def shared_download(request, slug, token, filename):
album = get_object_or_404(Album, slug=slug)
check_shared_album_token(album, token)
return _download(request, album, filename)
[docs]def shared_album_download(request, slug, token):
album = get_object_or_404(Album, slug=slug)
check_shared_album_token(album, token)
return _album_download(request, album)