diff --git a/rophako/model/photo.py b/rophako/model/photo.py index 1b1fa66..909a70f 100644 --- a/rophako/model/photo.py +++ b/rophako/model/photo.py @@ -28,11 +28,25 @@ def list_albums(): index = get_index() result = [] + # Missing settings? + if not "settings" in index: + index["settings"] = dict() + 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] pic = index["albums"][album][cover]["thumb"] result.append(dict( name=album, + format=index["settings"][album]["format"], + description=index["settings"][album]["description"], cover=pic, data=index["albums"][album], )) @@ -40,6 +54,68 @@ def list_albums(): 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): """List the photos in an album.""" album = sanitize_name(album) @@ -125,18 +201,18 @@ def get_image_dimensions(pic): return img.size -def update_photo(album, key, data): - """Update photo meta-data in the album.""" - index = get_index() +# def update_photo(album, key, data): +# """Update photo meta-data in the album.""" +# index = get_index() - if not album in index["albums"]: - index["albums"][album] = {} - if not key in index["albums"][album]: - index["albums"][album][key] = {} +# if not album in index["albums"]: +# index["albums"][album] = {} +# if not key in index["albums"][album]: +# index["albums"][album][key] = {} - # Update! - index["albums"][album][key].update(data) - write_index(index) +# # Update! +# index["albums"][album][key].update(data) +# write_index(index) def crop_photo(key, x, y, length): @@ -201,6 +277,18 @@ def edit_photo(key, data): 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): """Rotate a photo 90 degrees to the left or right.""" photo = get_photo(key) @@ -404,6 +492,7 @@ def process_photo(form, filename): # What album are the photos going to? album = form.get("album", "") new_album = form.get("new-album", None) + new_desc = form.get("new-description", None) if album == "" and new_album: album = new_album @@ -425,12 +514,18 @@ def process_photo(form, filename): # Update the photo data. if not album in index["albums"]: index["albums"][album] = {} + if not album in index["settings"]: + index["settings"][album] = { + "format": "classic", + "description": new_desc, + } index["albums"][album][key] = dict( ip=request.remote_addr, author=g.info["session"]["uid"], uploaded=int(time.time()), caption=form.get("caption", ""), + description=form.get("description", ""), **sizes ) diff --git a/rophako/modules/photo/__init__.py b/rophako/modules/photo/__init__.py index b33c364..08bd84b 100644 --- a/rophako/modules/photo/__init__.py +++ b/rophako/modules/photo/__init__.py @@ -6,7 +6,8 @@ from flask import Blueprint, g, request, redirect, url_for, session, flash import rophako.model.user as User 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.log import logger from config import * @@ -40,8 +41,15 @@ def album_index(name): flash("That album doesn't exist.") return redirect(url_for(".albums")) - g.info["album"] = name - g.info["photos"] = photos + 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 + + # 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") @@ -61,6 +69,7 @@ def view_photo(key): g.info["photo"] = photo g.info["photo"]["key"] = key 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") @@ -196,9 +205,10 @@ def edit(key): return redirect(url_for(".albums")) if request.method == "POST": - caption = request.form.get("caption", "") - rotate = request.form.get("rotate", "") - Photo.edit_photo(key, dict(caption=caption)) + caption = request.form.get("caption", "") + description = request.form.get("description", "") + rotate = request.form.get("rotate", "") + Photo.edit_photo(key, dict(caption=caption, description=description)) # Rotating the photo? if rotate in ["left", "right", "180"]: @@ -234,6 +244,43 @@ def delete(key): return template("photos/delete.html") +@mod.route("/edit_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"]) @login_required def arrange_albums(): @@ -253,6 +300,34 @@ def arrange_albums(): return template("photos/arrange_albums.html") +@mod.route("/edit_captions/", 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/", methods=["GET", "POST"]) @login_required def delete_album(album): diff --git a/rophako/modules/photo/templates/photos/album.html b/rophako/modules/photo/templates/photos/album.html index 86143dd..310121c 100644 --- a/rophako/modules/photo/templates/photos/album.html +++ b/rophako/modules/photo/templates/photos/album.html @@ -4,22 +4,45 @@

Album: {{ album }}

-
    +{% if markdown %} + {{ markdown|safe }}

    +{% endif %} +{% if album_info["format"] == "vertical" %} {% for photo in photos %} -

  • -
    - -
  • - {% endfor %} + {% set data = photo["data"] %} + {% if data["caption"] %} +

    {{ data["caption"] }}

    + {% endif %} -
-
+ + +

+ + {% if data["description"] %} +

{{ data["markdown"]|safe }}
+ {% endif %} + + {% if loop.index < photos|length %}
{% endif %} + {% endfor %} +{% else %}{# classic layout #} + +
+{% endif %} {% if session["login"] %}

Administrative Options

@@ -27,6 +50,8 @@
  • Upload a Photo
  • {% if photos|length > 0 %} +
  • Edit Album Settings
  • +
  • Edit Image Titles/Descriptions
  • Rearrange Photos
  • Delete Album
  • {% endif %} diff --git a/rophako/modules/photo/templates/photos/edit.html b/rophako/modules/photo/templates/photos/edit.html index a2aebcb..b00cf17 100644 --- a/rophako/modules/photo/templates/photos/edit.html +++ b/rophako/modules/photo/templates/photos/edit.html @@ -12,6 +12,10 @@ Photo Caption:

    + Description:
    +
    + Use Markdown syntax.

    + Rotate:

    Edit Album: {{ album }}

    + +
    + + + Album Title:
    +

    + + Description:
    +
    + Use Markdown syntax.

    + + Display Format:
    +
    +

    + + + +

    + +{% endblock %} \ No newline at end of file diff --git a/rophako/modules/photo/templates/photos/edit_captions.html b/rophako/modules/photo/templates/photos/edit_captions.html new file mode 100644 index 0000000..cae5628 --- /dev/null +++ b/rophako/modules/photo/templates/photos/edit_captions.html @@ -0,0 +1,33 @@ +{% extends "layout.html" %} +{% block title %}Edit Captions{% endblock %} +{% block content %} + +

    Edit Captions in {{ album }}

    + +All captions use Markdown syntax.

    + +

    + + + + {% for photo in photos %} + + + + + {% endfor %} +
    + Photo + + Caption:
    +

    + + Description:
    + +

    + + + +

    + +{% endblock %} \ No newline at end of file diff --git a/rophako/modules/photo/templates/photos/upload.html b/rophako/modules/photo/templates/photos/upload.html index 676c754..e29338c 100644 --- a/rophako/modules/photo/templates/photos/upload.html +++ b/rophako/modules/photo/templates/photos/upload.html @@ -50,15 +50,17 @@ somewhere else on the Internet. {% endfor %} -

    +

    New album:
    - -
    +

    - Caption:
    - + Album Description:
    +
    + Shows up at the top of the album. + Use Markdown formatting. +

    diff --git a/rophako/modules/photo/templates/photos/view.html b/rophako/modules/photo/templates/photos/view.html index 4220e54..105c376 100644 --- a/rophako/modules/photo/templates/photos/view.html +++ b/rophako/modules/photo/templates/photos/view.html @@ -22,15 +22,17 @@ {{ nav_links() }} -

    - - -
    - {{ photo["caption"] }} -
    -

    +{% if photo["caption"] %} +

    {{ photo["caption"] }}

    +{% endif %} + + +

    +{% if photo["markdown"] %} +

    {{ photo["markdown"]|safe }}
    +{% endif %} -Uploaded by {{ author["name"] }} on {{ photo["pretty_time"] }}. +Uploaded by {{ author["name"] }} on {{ photo["pretty_time"] }}. {{ nav_links() }} diff --git a/rophako/www/smoke/style.css b/rophako/www/smoke/style.css index 42811ab..502306a 100644 --- a/rophako/www/smoke/style.css +++ b/rophako/www/smoke/style.css @@ -219,6 +219,18 @@ ul.photo-grid li .dummy { 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 */ a.blog-title-index:link, a.blog-title-index:visited { font-size: 32pt;