Create photos front-end using filebased backend

This is still wants a nice admin back-end;
currently the way to manage photos is simply managing JPGs

This also wants a few more tweaks in the front-end,
solving some CSS problems and adding album pagination
parent 8cd922e8
# from django.contrib import admin
# TODO figure out a nice way to make an admin interface
from django.apps import AppConfig
class PhotosConfig(AppConfig):
name = 'photos'
{% extends 'base.html' %}
{% load staticfiles i18n thumbnail %}
{% block page_title %}{% trans "Photos" %}{% endblock %}
{% block body %}
<h1>{{ album.title }}</h1>
<h2>{{ album.date|date:"d-m-Y" }}</h2>
<div class="gallery">
{% comment %}TODO figure out why whitespace is not rendered between tiles{% endcomment %}
{% comment %}TODO download link{% endcomment %}
<ul class="gallery-photos row">
{% for photo in photos %}
<li class="post gallery-photo span3 has-overlay first-child">
<a class="gallery-box" rel="gallery" data-download="{% url 'photos:download' photo %}" href="{% thumbnail photo '1024x768' fit=True %}">
<span class="post-inner">
<span class="inner-img">
<img src="{% thumbnail photo '220x200' %}" alt="" />
</span>
<span class="post-overlay">
</span>
</span>
</a>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load i18n thumbnail %}
{% block page_title %}{% trans "Photos" %}{% endblock %}
{% block body %}
<h1>{% trans "Photos" %}</h1>
<div class="gallery">
{% comment %}TODO figure out why whitespace is not rendered between tiles{% endcomment %}
<ul class="gallery-albums row">
{% for album in albums %}
<li class="post gallery-album span3 has-overlay first-child">
<a href="{% url 'photos:album' album.slug %}">
<span class="post-inner">
<span class="inner-img">
<img src="{% thumbnail album.cover '220x220' %}" alt="" />
</span>
<span class="post-overlay">
<span class="post-overlay-meta">
<h2>{{ album.title }}</h2>
<p>{{ album.date|date:"d-m-Y" }}</p>
</span>
</span>
</span>
</a>
</li>
{% endfor %}
</ul>
{% comment %}TODO pagination{% endcomment %}
</div>
{% endblock %}
\ No newline at end of file
from django.conf.urls import url
from . import views
urlpatterns = [
url('^album/(?P<slug>[0-9a-z-_]*)', views.album, name='album'),
url('^download/(?P<slug>[0-9a-z-_]*)', views.download, name='download'),
url('^', views.index, name='index'),
]
from django.shortcuts import render
from django.conf import settings
from django.utils.text import slugify
from django.contrib.auth.decorators import login_required
import os
import re
import random
from datetime import datetime
COVER_FILENAME = 'cover.jpg'
# TODO consider refactoring this to models.py instead, for separation.
def _album_dict(photodir):
"""Creates a dict with album information based on a parent directory."""
albums = [(name, name.split('_')) for name in os.listdir(photodir)]
albums = [{'date': datetime.strptime(re.sub(r"\D", "", date), '%Y%m%d'),
'title': title,
'name': name,
} for name, (date, title) in albums]
for album in albums:
album['slug'] = slugify('{}-{}'
.format(album['date'].strftime('%Y-%m-%d'),
album['title']))
return albums
@login_required
def index(request):
photodir = os.path.join(settings.MEDIA_ROOT, 'photos')
albums = _album_dict(photodir)
for album in albums:
albumdir = os.path.join(photodir, album['name'])
if os.path.isfile(os.path.join(albumdir, COVER_FILENAME)):
album['cover'] = COVER_FILENAME
else:
random.seed(album['name'])
album['cover'] = random.choice(os.listdir(albumdir))
album['cover'] = os.path.join('photos',
album['name'],
album['cover'])
return render(request, 'photos/index.html', {'albums': albums})
@login_required
def album(request, slug):
photodir = os.path.join(settings.MEDIA_ROOT, 'photos')
for album in _album_dict(photodir):
if album['slug'] == slug:
break
albumdir = os.path.join(photodir, album['name'])
photos = [os.path.join('photos', album['name'], photo)
for photo in os.listdir(albumdir) if photo != COVER_FILENAME]
return render(request, 'photos/album.html', {'photos': photos})
@login_required
def download(request, filename):
# TODO actually send the photo that is request for download
pass
......@@ -13,7 +13,7 @@ main = [
{'title': _('Thabloid'), 'name': '#'},
]},
{'title': _('For Members'), 'name': '#', 'submenu': [
{'title': _('Photos'), 'name': '#'},
{'title': _('Photos'), 'name': 'photos:index'},
{'title': _('Statistics'), 'name': '#'},
{'title': _('Become Active'), 'name': 'become-active'},
{'title': _('Wiki'), 'name': '#'},
......
......@@ -46,6 +46,8 @@ INSTALLED_APPS = [
'members',
'documents',
'committees',
'photos',
'utils',
]
MIDDLEWARE = [
......
......@@ -17,6 +17,8 @@ from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from utils.views import private_thumbnails
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
url(r'^admin/', admin.site.urls),
......@@ -28,8 +30,10 @@ urlpatterns = [
])),
url(r'^for-members/', include([
url(r'^become-active', TemplateView.as_view(template_name='singlepages/become_active.html'), name='become-active'),
url(r'^photos/', include('photos.urls', namespace='photos')),
])),
url(r'^contact$', TemplateView.as_view(template_name='singlepages/contact.html'), name='contact'),
url(r'^private-thumbnails/(?P<path>.*)', private_thumbnails, name='private-thumbnails'),
# Default login helpers
url(r'^', include('django.contrib.auth.urls')),
url(r'^i18n/', include('django.conf.urls.i18n')),
......
from django.utils import timezone
from django.utils.six.moves.urllib.parse import unquote
import os
def datetime_to_lectureyear(date):
......@@ -6,3 +9,22 @@ def datetime_to_lectureyear(date):
if date.date() < sept_1.date():
return date.year - 1
return date.year
def sanitize_path(path):
"""Cleans up an insecure path, i.e. against directory traversal.
This code is partially copied from django.views.static"""
path = os.path.normpath(unquote(path))
path = path.lstrip('/')
newpath = ''
for part in path.split('/'):
if not part:
# Strip empty path components.
continue
drive, part = os.path.splitdrive(part)
head, part = os.path.split(part)
if part in (os.curdir, os.pardir):
# Strip '.' and '..' in path.
continue
newpath = os.path.join(newpath, part).replace('\\', '/')
return newpath
from django import template
from django.conf import settings
from django.core.urlresolvers import reverse
import os
import shutil
register = template.Library()
@register.simple_tag
def thumbnail(path, size, fit=False):
parts = path.split('/')
# If we're dealing with an image in media/public/..
if parts[0] == 'public':
# We actually want it to end up in media/public/thumbnails/..
# since those files can be accessed without passing through a view.
thumbpath = os.path.join(parts[0], 'thumbnails', *parts[1:])
else:
# Otherwise simply place it in media/thumbnails.
thumbpath = os.path.join('thumbnails', path)
full_thumbpath = os.path.join(settings.MEDIA_ROOT, thumbpath)
full_path = os.path.join(settings.MEDIA_ROOT, path)
os.makedirs(os.path.dirname(full_thumbpath), exist_ok=True)
# TODO actually create a thumbnail instead of copying
shutil.copyfile(full_path, full_thumbpath)
if parts[0] == 'public':
return '/'.join([settings.MEDIA_URL, thumbpath])
else:
return reverse('private-thumbnails', args=[path])
from django.contrib.auth.decorators import login_required
from django.conf import settings
from sendfile import sendfile
import os
from .snippets import sanitize_path
@login_required
def private_thumbnails(request, path):
# TODO do a bit more error handling if the path does not exist?
# 'path' is supplied as a URL parameter, so treat with care!
path = os.path.join(settings.MEDIA_ROOT, 'thumbnails', sanitize_path(path))
return sendfile(request, path)
Markdown is supported
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