Enhanced photo album features
Support album descriptions and layout options, and add photo descriptions
This commit is contained in:
parent
1035fe287c
commit
1c32d08ab8
|
@ -28,11 +28,25 @@ def list_albums():
|
||||||
index = get_index()
|
index = get_index()
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
|
# Missing settings?
|
||||||
|
if not "settings" in index:
|
||||||
|
index["settings"] = dict()
|
||||||
|
|
||||||
for album in index["album-order"]:
|
for album in index["album-order"]:
|
||||||
|
if not album in index["settings"]:
|
||||||
|
# Need to initialize its settings.
|
||||||
|
index["settings"][album] = dict(
|
||||||
|
format="classic",
|
||||||
|
description="",
|
||||||
|
)
|
||||||
|
write_index(index)
|
||||||
|
|
||||||
cover = index["covers"][album]
|
cover = index["covers"][album]
|
||||||
pic = index["albums"][album][cover]["thumb"]
|
pic = index["albums"][album][cover]["thumb"]
|
||||||
result.append(dict(
|
result.append(dict(
|
||||||
name=album,
|
name=album,
|
||||||
|
format=index["settings"][album]["format"],
|
||||||
|
description=index["settings"][album]["description"],
|
||||||
cover=pic,
|
cover=pic,
|
||||||
data=index["albums"][album],
|
data=index["albums"][album],
|
||||||
))
|
))
|
||||||
|
@ -40,6 +54,68 @@ def list_albums():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_album(name):
|
||||||
|
"""Get details about an album."""
|
||||||
|
index = get_index()
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if not name in index["albums"]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
album = index["albums"][name]
|
||||||
|
cover = index["covers"][name]
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
name=name,
|
||||||
|
format=index["settings"][name]["format"],
|
||||||
|
description=index["settings"][name]["description"],
|
||||||
|
cover=album[cover]["thumb"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rename_album(old_name, new_name):
|
||||||
|
"""Rename an existing photo album.
|
||||||
|
|
||||||
|
Returns True on success, False if the new name conflicts with another
|
||||||
|
album's name."""
|
||||||
|
old_name = sanitize_name(old_name)
|
||||||
|
newname = sanitize_name(new_name)
|
||||||
|
index = get_index()
|
||||||
|
|
||||||
|
# New name is unique?
|
||||||
|
if new_name in index["albums"]:
|
||||||
|
logger.error("Can't rename album: new name already exists!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def transfer_key(obj, old_key, new_key):
|
||||||
|
# Reusable function to do a simple move on a dict key.
|
||||||
|
obj[new_key] = obj[old_key]
|
||||||
|
del obj[old_key]
|
||||||
|
|
||||||
|
# Simple moves.
|
||||||
|
transfer_key(index["albums"], old_name, new_name)
|
||||||
|
transfer_key(index["covers"], old_name, new_name)
|
||||||
|
transfer_key(index["photo-order"], old_name, new_name)
|
||||||
|
transfer_key(index["settings"], old_name, new_name)
|
||||||
|
|
||||||
|
# Update the photo -> album maps.
|
||||||
|
for photo in index["map"]:
|
||||||
|
if index["map"][photo] == old_name:
|
||||||
|
index["map"][photo] = new_name
|
||||||
|
|
||||||
|
# Fix the album ordering.
|
||||||
|
new_order = list()
|
||||||
|
for name in index["album-order"]:
|
||||||
|
if name == old_name:
|
||||||
|
name = new_name
|
||||||
|
new_order.append(name)
|
||||||
|
index["album-order"] = new_order
|
||||||
|
|
||||||
|
# And save.
|
||||||
|
write_index(index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def list_photos(album):
|
def list_photos(album):
|
||||||
"""List the photos in an album."""
|
"""List the photos in an album."""
|
||||||
album = sanitize_name(album)
|
album = sanitize_name(album)
|
||||||
|
@ -125,18 +201,18 @@ def get_image_dimensions(pic):
|
||||||
return img.size
|
return img.size
|
||||||
|
|
||||||
|
|
||||||
def update_photo(album, key, data):
|
# def update_photo(album, key, data):
|
||||||
"""Update photo meta-data in the album."""
|
# """Update photo meta-data in the album."""
|
||||||
index = get_index()
|
# index = get_index()
|
||||||
|
|
||||||
if not album in index["albums"]:
|
# if not album in index["albums"]:
|
||||||
index["albums"][album] = {}
|
# index["albums"][album] = {}
|
||||||
if not key in index["albums"][album]:
|
# if not key in index["albums"][album]:
|
||||||
index["albums"][album][key] = {}
|
# index["albums"][album][key] = {}
|
||||||
|
|
||||||
# Update!
|
# # Update!
|
||||||
index["albums"][album][key].update(data)
|
# index["albums"][album][key].update(data)
|
||||||
write_index(index)
|
# write_index(index)
|
||||||
|
|
||||||
|
|
||||||
def crop_photo(key, x, y, length):
|
def crop_photo(key, x, y, length):
|
||||||
|
@ -201,6 +277,18 @@ def edit_photo(key, data):
|
||||||
write_index(index)
|
write_index(index)
|
||||||
|
|
||||||
|
|
||||||
|
def edit_album(album, data):
|
||||||
|
"""Update an album's settings (description, format, etc.)"""
|
||||||
|
album = sanitize_name(album)
|
||||||
|
index = get_index()
|
||||||
|
if not album in index["albums"]:
|
||||||
|
logger.error("Failed to edit album: not found!")
|
||||||
|
return
|
||||||
|
|
||||||
|
index["settings"][album].update(data)
|
||||||
|
write_index(index)
|
||||||
|
|
||||||
|
|
||||||
def rotate_photo(key, rotate):
|
def rotate_photo(key, rotate):
|
||||||
"""Rotate a photo 90 degrees to the left or right."""
|
"""Rotate a photo 90 degrees to the left or right."""
|
||||||
photo = get_photo(key)
|
photo = get_photo(key)
|
||||||
|
@ -404,6 +492,7 @@ def process_photo(form, filename):
|
||||||
# What album are the photos going to?
|
# What album are the photos going to?
|
||||||
album = form.get("album", "")
|
album = form.get("album", "")
|
||||||
new_album = form.get("new-album", None)
|
new_album = form.get("new-album", None)
|
||||||
|
new_desc = form.get("new-description", None)
|
||||||
if album == "" and new_album:
|
if album == "" and new_album:
|
||||||
album = new_album
|
album = new_album
|
||||||
|
|
||||||
|
@ -425,12 +514,18 @@ def process_photo(form, filename):
|
||||||
# Update the photo data.
|
# Update the photo data.
|
||||||
if not album in index["albums"]:
|
if not album in index["albums"]:
|
||||||
index["albums"][album] = {}
|
index["albums"][album] = {}
|
||||||
|
if not album in index["settings"]:
|
||||||
|
index["settings"][album] = {
|
||||||
|
"format": "classic",
|
||||||
|
"description": new_desc,
|
||||||
|
}
|
||||||
|
|
||||||
index["albums"][album][key] = dict(
|
index["albums"][album][key] = dict(
|
||||||
ip=request.remote_addr,
|
ip=request.remote_addr,
|
||||||
author=g.info["session"]["uid"],
|
author=g.info["session"]["uid"],
|
||||||
uploaded=int(time.time()),
|
uploaded=int(time.time()),
|
||||||
caption=form.get("caption", ""),
|
caption=form.get("caption", ""),
|
||||||
|
description=form.get("description", ""),
|
||||||
**sizes
|
**sizes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ from flask import Blueprint, g, request, redirect, url_for, session, flash
|
||||||
|
|
||||||
import rophako.model.user as User
|
import rophako.model.user as User
|
||||||
import rophako.model.photo as Photo
|
import rophako.model.photo as Photo
|
||||||
from rophako.utils import template, pretty_time, login_required, ajax_response
|
from rophako.utils import (template, pretty_time, render_markdown,
|
||||||
|
login_required, ajax_response)
|
||||||
from rophako.plugin import load_plugin
|
from rophako.plugin import load_plugin
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
from config import *
|
from config import *
|
||||||
|
@ -41,7 +42,14 @@ def album_index(name):
|
||||||
return redirect(url_for(".albums"))
|
return redirect(url_for(".albums"))
|
||||||
|
|
||||||
g.info["album"] = name
|
g.info["album"] = name
|
||||||
|
g.info["album_info"] = Photo.get_album(name)
|
||||||
|
g.info["markdown"] = render_markdown(g.info["album_info"]["description"])
|
||||||
g.info["photos"] = photos
|
g.info["photos"] = photos
|
||||||
|
|
||||||
|
# Render Markdown descriptions for photos.
|
||||||
|
for photo in g.info["photos"]:
|
||||||
|
photo["data"]["markdown"] = render_markdown(photo["data"].get("description", ""))
|
||||||
|
|
||||||
return template("photos/album.html")
|
return template("photos/album.html")
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +69,7 @@ def view_photo(key):
|
||||||
g.info["photo"] = photo
|
g.info["photo"] = photo
|
||||||
g.info["photo"]["key"] = key
|
g.info["photo"]["key"] = key
|
||||||
g.info["photo"]["pretty_time"] = pretty_time(PHOTO_TIME_FORMAT, photo["uploaded"])
|
g.info["photo"]["pretty_time"] = pretty_time(PHOTO_TIME_FORMAT, photo["uploaded"])
|
||||||
|
g.info["photo"]["markdown"] = render_markdown(photo.get("description", ""))
|
||||||
return template("photos/view.html")
|
return template("photos/view.html")
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,8 +206,9 @@ def edit(key):
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
caption = request.form.get("caption", "")
|
caption = request.form.get("caption", "")
|
||||||
|
description = request.form.get("description", "")
|
||||||
rotate = request.form.get("rotate", "")
|
rotate = request.form.get("rotate", "")
|
||||||
Photo.edit_photo(key, dict(caption=caption))
|
Photo.edit_photo(key, dict(caption=caption, description=description))
|
||||||
|
|
||||||
# Rotating the photo?
|
# Rotating the photo?
|
||||||
if rotate in ["left", "right", "180"]:
|
if rotate in ["left", "right", "180"]:
|
||||||
|
@ -234,6 +244,43 @@ def delete(key):
|
||||||
return template("photos/delete.html")
|
return template("photos/delete.html")
|
||||||
|
|
||||||
|
|
||||||
|
@mod.route("/edit_album/<album>", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def edit_album(album):
|
||||||
|
photos = Photo.list_photos(album)
|
||||||
|
if photos is None:
|
||||||
|
flash("That album doesn't exist.")
|
||||||
|
return redirect(url_for(".albums"))
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
# Collect the form details.
|
||||||
|
new_name = request.form["name"]
|
||||||
|
description = request.form["description"]
|
||||||
|
layout = request.form["format"]
|
||||||
|
|
||||||
|
# Renaming the album?
|
||||||
|
if new_name != album:
|
||||||
|
ok = Photo.rename_album(album, new_name)
|
||||||
|
if not ok:
|
||||||
|
flash("Failed to rename album: already exists?")
|
||||||
|
return redirect(url_for(".edit_album", album=album))
|
||||||
|
album = new_name
|
||||||
|
|
||||||
|
# Update album settings.
|
||||||
|
Photo.edit_album(album, dict(
|
||||||
|
description=description,
|
||||||
|
format=layout,
|
||||||
|
))
|
||||||
|
|
||||||
|
return redirect(url_for(".albums"))
|
||||||
|
|
||||||
|
g.info["album"] = album
|
||||||
|
g.info["album_info"] = Photo.get_album(album)
|
||||||
|
g.info["photos"] = photos
|
||||||
|
|
||||||
|
return template("photos/edit_album.html")
|
||||||
|
|
||||||
|
|
||||||
@mod.route("/arrange_albums", methods=["GET", "POST"])
|
@mod.route("/arrange_albums", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def arrange_albums():
|
def arrange_albums():
|
||||||
|
@ -253,6 +300,34 @@ def arrange_albums():
|
||||||
return template("photos/arrange_albums.html")
|
return template("photos/arrange_albums.html")
|
||||||
|
|
||||||
|
|
||||||
|
@mod.route("/edit_captions/<album>", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def bulk_captions(album):
|
||||||
|
"""Bulk edit captions and titles in an album."""
|
||||||
|
photos = Photo.list_photos(album)
|
||||||
|
if photos is None:
|
||||||
|
flash("That album doesn't exist.")
|
||||||
|
return redirect(url_for(".albums"))
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
# Do it.
|
||||||
|
for photo in photos:
|
||||||
|
caption_key = "{}:caption".format(photo["key"])
|
||||||
|
desc_key = "{}:description".format(photo["key"])
|
||||||
|
if caption_key in request.form and desc_key in request.form:
|
||||||
|
caption = request.form[caption_key]
|
||||||
|
description = request.form[desc_key]
|
||||||
|
Photo.edit_photo(photo['key'], dict(caption=caption, description=description))
|
||||||
|
|
||||||
|
flash("The photos have been updated.")
|
||||||
|
return redirect(url_for(".albums"))
|
||||||
|
|
||||||
|
g.info["album"] = album
|
||||||
|
g.info["photos"] = photos
|
||||||
|
|
||||||
|
return template("photos/edit_captions.html")
|
||||||
|
|
||||||
|
|
||||||
@mod.route("/delete_album/<album>", methods=["GET", "POST"])
|
@mod.route("/delete_album/<album>", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def delete_album(album):
|
def delete_album(album):
|
||||||
|
|
|
@ -4,7 +4,29 @@
|
||||||
|
|
||||||
<h1>Album: {{ album }}</h1>
|
<h1>Album: {{ album }}</h1>
|
||||||
|
|
||||||
<ul class="photo-grid">
|
{% if markdown %}
|
||||||
|
{{ markdown|safe }}<p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if album_info["format"] == "vertical" %}
|
||||||
|
{% for photo in photos %}
|
||||||
|
{% set data = photo["data"] %}
|
||||||
|
{% if data["caption"] %}
|
||||||
|
<h2>{{ data["caption"] }}</h2>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<a href="{{ url_for('photo.view_photo', key=photo['key']) }}">
|
||||||
|
<img src="{{ app['photo_url'] }}/{{ data['large'] }}" class="portrait">
|
||||||
|
</a><p>
|
||||||
|
|
||||||
|
{% if data["description"] %}
|
||||||
|
<div class="photo-description">{{ data["markdown"]|safe }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if loop.index < photos|length %}<hr>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}{# classic layout #}
|
||||||
|
<ul class="photo-grid">
|
||||||
|
|
||||||
{% for photo in photos %}
|
{% for photo in photos %}
|
||||||
<li class="portrait">
|
<li class="portrait">
|
||||||
|
@ -18,8 +40,9 @@
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if session["login"] %}
|
{% if session["login"] %}
|
||||||
<h1>Administrative Options</h1>
|
<h1>Administrative Options</h1>
|
||||||
|
@ -27,6 +50,8 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ url_for('photo.upload') }}">Upload a Photo</a></li>
|
<li><a href="{{ url_for('photo.upload') }}">Upload a Photo</a></li>
|
||||||
{% if photos|length > 0 %}
|
{% if photos|length > 0 %}
|
||||||
|
<li><a href="{{ url_for('photo.edit_album', album=album) }}">Edit Album Settings</a></li>
|
||||||
|
<li><a href="{{ url_for('photo.bulk_captions', album=album) }}">Edit Image Titles/Descriptions</a></li>
|
||||||
<li><a href="{{ url_for('photo.arrange_photos', album=album) }}">Rearrange Photos</a></li>
|
<li><a href="{{ url_for('photo.arrange_photos', album=album) }}">Rearrange Photos</a></li>
|
||||||
<li><a href="{{ url_for('photo.delete_album', album=album) }}">Delete Album</a></li>
|
<li><a href="{{ url_for('photo.delete_album', album=album) }}">Delete Album</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
<strong>Photo Caption:</strong><br>
|
<strong>Photo Caption:</strong><br>
|
||||||
<input type="text" size="40" name="caption" value="{{ photo['caption'] }}"><p>
|
<input type="text" size="40" name="caption" value="{{ photo['caption'] }}"><p>
|
||||||
|
|
||||||
|
<strong>Description:</strong><br>
|
||||||
|
<textarea cols="50" rows="6" name="description">{{ photo['description'] }}</textarea><br>
|
||||||
|
<small>Use <a href="/markdown">Markdown</a> syntax.</small><p>
|
||||||
|
|
||||||
Rotate:
|
Rotate:
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="rotate" value="" checked> Leave alone
|
<input type="radio" name="rotate" value="" checked> Leave alone
|
||||||
|
|
31
rophako/modules/photo/templates/photos/edit_album.html
Normal file
31
rophako/modules/photo/templates/photos/edit_album.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block title %}Edit Album{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1>Edit Album: {{ album }}</h1>
|
||||||
|
|
||||||
|
<form id="album-editor" action="{{ url_for('photo.edit_album', album=album) }}" method="POST">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
|
<strong>Album Title:</strong><br>
|
||||||
|
<input type="text" size="40" name="name" value="{{ album }}"><p>
|
||||||
|
|
||||||
|
<strong>Description:</strong><br>
|
||||||
|
<textarea cols="50" rows="6" name="description">{{ album_info["description"] }}</textarea><br>
|
||||||
|
<small>Use <a href="/markdown" target="_blank">Markdown</a> syntax.</small><p>
|
||||||
|
|
||||||
|
<strong>Display Format:</strong><br>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="format" value="classic"{% if album_info["format"] == "classic" %} checked{% endif %}>
|
||||||
|
<strong>Classic:</strong> Display a grid of thumbnails that must be clicked to view full size images.
|
||||||
|
</label><br>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="format" value="vertical"{% if album_info["format"] == "vertical" %} checked{% endif %}>
|
||||||
|
<strong>Vertical:</strong> Display all full size photos in one vertical view.
|
||||||
|
</label><p>
|
||||||
|
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
33
rophako/modules/photo/templates/photos/edit_captions.html
Normal file
33
rophako/modules/photo/templates/photos/edit_captions.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block title %}Edit Captions{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1>Edit Captions in {{ album }}</h1>
|
||||||
|
|
||||||
|
All captions use <a href="/markdown">Markdown</a> syntax.<p>
|
||||||
|
|
||||||
|
<form id="caption-editor" action="{{ url_for('photo.bulk_captions', album=album) }}" method="POST">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
|
<table width="100%" border="0" cellspacing="4" cellpadding="4">
|
||||||
|
{% for photo in photos %}
|
||||||
|
<tr>
|
||||||
|
<td width="100" align="center" valign="top">
|
||||||
|
<img src="{{ app['photo_url'] }}/{{ photo['data']['avatar'] }}" alt="Photo">
|
||||||
|
</td>
|
||||||
|
<td align="left" valign="top">
|
||||||
|
<strong>Caption:</strong><br>
|
||||||
|
<input type="text" size="40" name="{{ photo['key'] }}:caption" value="{{ photo['data']['caption'] }}"><p>
|
||||||
|
|
||||||
|
<strong>Description:</strong><br>
|
||||||
|
<textarea cols="50" rows="6" name="{{ photo['key'] }}:description">{{ photo['data']['description'] }}</textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table><p>
|
||||||
|
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -50,15 +50,17 @@ somewhere else on the Internet.
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<option value="">Create a new album</option>
|
<option value="">Create a new album</option>
|
||||||
</select><p>
|
</select>
|
||||||
|
|
||||||
<blockquote id="create-album">
|
<blockquote id="create-album">
|
||||||
<strong>New album:</strong><br>
|
<strong>New album:</strong><br>
|
||||||
<input type="text" size="20" id="new-album" name="new-album">
|
<input type="text" size="20" id="new-album" name="new-album"><p>
|
||||||
</blockquote>
|
|
||||||
|
|
||||||
<strong>Caption:</strong><br>
|
<strong>Album Description:</strong><br>
|
||||||
<input type="text" size="40" name="caption">
|
<textarea cols="50" rows="6" name="new-description"></textarea><br>
|
||||||
|
<small>Shows up at the top of the album.
|
||||||
|
Use <a href="/markdown" target="_blank">Markdown</a> formatting.</small>
|
||||||
|
</blockquote>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,17 @@
|
||||||
|
|
||||||
{{ nav_links() }}
|
{{ nav_links() }}
|
||||||
|
|
||||||
<div class="center">
|
{% if photo["caption"] %}
|
||||||
<a href="{{ url_for('photo.view_photo', key=photo['next']) }}">
|
<h2>{{ photo["caption"] }}</h2>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('photo.view_photo', key=photo['next']) }}">
|
||||||
<img src="{{ app['photo_url'] }}/{{ photo['large'] }}" class="portrait">
|
<img src="{{ app['photo_url'] }}/{{ photo['large'] }}" class="portrait">
|
||||||
</a><br>
|
</a><p>
|
||||||
<strong>{{ photo["caption"] }}</strong>
|
{% if photo["markdown"] %}
|
||||||
</div>
|
<div class="photo-description">{{ photo["markdown"]|safe }}</div>
|
||||||
<p>
|
{% endif %}
|
||||||
|
|
||||||
Uploaded by {{ author["name"] }} on {{ photo["pretty_time"] }}.
|
<em>Uploaded by {{ author["name"] }} on {{ photo["pretty_time"] }}.</em>
|
||||||
|
|
||||||
{{ nav_links() }}
|
{{ nav_links() }}
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,18 @@ ul.photo-grid li .dummy {
|
||||||
box-shadow: 0px 0px 4px #FF4444;
|
box-shadow: 0px 0px 4px #FF4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Photo description blocks */
|
||||||
|
.photo-description {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
box-shadow: 0px 0px 4px #000000;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 20px 0px;
|
||||||
|
background-color: #646464;
|
||||||
|
color: #FFFFFF;
|
||||||
|
width: 790px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Blog titles when shown on index view */
|
/* Blog titles when shown on index view */
|
||||||
a.blog-title-index:link, a.blog-title-index:visited {
|
a.blog-title-index:link, a.blog-title-index:visited {
|
||||||
font-size: 32pt;
|
font-size: 32pt;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user