Commit 27794969 authored by Thom Wiggers's avatar Thom Wiggers 📐
Browse files

Merge branch '30-fotoalbums-delen-met-niet-leden-mogelijk-maken' into 'master'

Allow albums to be shared with 3rd parties

Closes #30

See merge request !25
parents 795a2688 9f89306c
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-06 14:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('photos', '0001_initial'),
operations = [
......@@ -3,6 +3,7 @@ from django.conf import settings
from django.utils.functional import cached_property
from django.db import models
import hashlib
import os
import random
......@@ -14,6 +15,7 @@ class Album(models.Model):
dirname = models.CharField(max_length=200)
date = models.DateField()
slug = models.SlugField()
shareable = models.BooleanField(default=False)
photosdir = 'photos'
photospath = os.path.join(settings.MEDIA_ROOT, photosdir)
......@@ -47,3 +49,8 @@ class Album(models.Model):
if is None:
self.dirname = self.slug
super(Album, self).save(*args, **kwargs)
def access_token(self):
return hashlib.sha256('{}album{}'.format(settings.SECRET_KEY,
{% extends 'base.html' %}
{% load staticfiles i18n thumbnail %}
{% load staticfiles i18n thumbnail shared_thumbnail %}
{% block page_title %}{% trans "Photos" %}{% endblock %}
{% block body %}
<h1>{{ album.title }}</h1>
<h2>{{|date:"d-m-Y" }}</h2>
{% if album.shareable %}
<p class="tcenter">
{% trans "Note: This album can be shared with people outside the association by sending them the following link:" %}<br>
<small><a href="{% url 'photos:shared_album' album.slug album.access_token %}">
{{ request.get_host }}{% url 'photos:shared_album' album.slug album.access_token %}
{% endif %}
<div class="gallery">
<ul class="gallery-photos row">
{% for photo in %}
<li class="post gallery-photo span3 has-overlay {% if forloop.counter0|divisibleby:4 %}first-child{% endif %}">
<a class="gallery-box" rel="gallery" data-download="{% url 'photos:download' photo %}" href="{% thumbnail photo '1024x768' fit=False %}">
<a class="gallery-box" rel="gallery"
{% if album.shareable %}
data-download="{% url 'photos:shared-download' album.slug album.access_token photo %}" href="{% shared_thumbnail album.slug album.access_token photo '1024x768' fit=False %}"
{% else %}
data-download="{% url 'photos:download' photo %}" href="{% thumbnail photo '1024x768' fit=False %}"
{% endif %}>
<span class="post-inner">
<span class="inner-img">
{% if album.shareable %}
<img src="{% shared_thumbnail album.slug album.access_token photo '220x220' %}" alt="" />
{% else %}
<img src="{% thumbnail photo '220x220' %}" alt="" />
{% endif %}
<span class="post-overlay">
from django import template
from django.core.urlresolvers import resolve, reverse
from utils.templatetags.thumbnail import thumbnail
register = template.Library()
def shared_thumbnail(slug, token, path, size, fit=True):
thumb = resolve(thumbnail(path, size, fit))
args = [slug, token, thumb.kwargs['size_fit'], thumb.kwargs['path']]
return reverse('photos:shared-thumbnail', args=args)
......@@ -5,6 +5,9 @@ from . import views
urlpatterns = [
url(r'^download/(?P<path>.*)',, name='download'),
url(r'^shared-download/(?P<slug>[-\w]+)/(?P<token>[a-zA-Z0-9]+)/(?P<path>.*)', views.shared_download, name='shared-download'),
url(r'^shared-thumbnail/(?P<slug>[-\w]+)/(?P<token>[a-zA-Z0-9]+)/(?P<size_fit>\d+x\d+_[01])/(?P<path>.*)', views.shared_thumbnail, name='shared-thumbnail'),
url(r'^(?P<slug>[-\w]+)/$', views.album, name='album'),
url(r'^(?P<slug>[-\w]+)/(?P<token>[a-zA-Z0-9]+)$', views.shared_album, name='shared_album'),
url(r'^$', views.index, name='index'),
......@@ -6,6 +6,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Album
from utils.snippets import sanitize_path
from utils.views import _private_thumbnails_unauthed
from sendfile import sendfile
import os
......@@ -36,10 +37,37 @@ def album(request, slug):
return render(request, 'photos/album.html', {'album': album})
def download(request, path):
def _checked_shared_album(slug, token):
album = get_object_or_404(Album, slug=slug)
if token != album.access_token:
raise Http404("Invalid token.")
return album
def shared_album(request, slug, token):
album = _checked_shared_album(slug, token)
return render(request, 'photos/album.html', {'album': album})
def _download(request, path):
"""This function provides a layer of indirection for shared albums"""
path = sanitize_path(path)
path = os.path.join(settings.MEDIA_ROOT, 'photos', *path.split('/')[1:])
if not os.path.isfile(path):
raise Http404("Photo not found.")
return sendfile(request, path, attachment=True)
def download(request, path):
return _download(request, path)
def shared_download(request, slug, token, path):
_checked_shared_album(slug, token)
return _download(request, path)
def shared_thumbnail(request, slug, token, size_fit, path):
_checked_shared_album(slug, token)
return _private_thumbnails_unauthed(request, size_fit, path)
......@@ -8,10 +8,17 @@ import os
from .snippets import sanitize_path
def private_thumbnails(request, size_fit, path):
def _private_thumbnails_unauthed(request, size_fit, path):
"""This layer of indirection makes it possible to make exceptions
to the authentication requirements for thumbnails, e.g. when sharing
photo albums with external parties using access tokens."""
path = sanitize_path(path)
path = os.path.join(settings.MEDIA_ROOT, 'thumbnails', size_fit, path)
if not os.path.isfile(path):
raise Http404("Thumbnail not found.")
return sendfile(request, path)
def private_thumbnails(request, size_fit, path):
return _private_thumbnails_unauthed(request, size_fit, path)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment