* move cgi-bin to de-cgi-bin so it doesn't conflict with my default alias... need to come up with a real solution to this
authorterom
Wed, 16 Jan 2008 14:58:03 +0000
changeset 19 8d3ffd87cb0b
parent 18 46536daf9e04
child 20 6c774496bb00
* move cgi-bin to de-cgi-bin so it doesn't conflict with my default alias... need to come up with a real solution to this
* refactored series/shorturl into the new codebase
cgi-bin/inc.py
cgi-bin/series.py
cgi-bin/shorturl.py
cgi-bin/taggr.py
de-cgi-bin/inc.py
de-cgi-bin/inc.py
de-cgi-bin/series.py
de-cgi-bin/series.py
de-cgi-bin/shorturl.py
de-cgi-bin/shorturl.py
de-cgi-bin/taggr.py
lib/shorturl.py
lib/utils.py
templates/gallery.html
templates/image.html
--- a/cgi-bin/inc.py	Sat Dec 22 21:31:01 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-if __name__ == '__main__' :
-    raise Exception("Don't access inc.py directly")
-
-# setup env
-
-import sys
-import os, os.path
-
-def splitn (path, n) :
-    for i in xrange(0, n) :
-        path = os.path.split(path)[0]      
-    
-    return path
-        
-degal_path = splitn(os.path.join(os.getcwd(), __file__), 2)
-
-os.chdir(degal_path)
-sys.path.append(degal_path)
--- a/cgi-bin/series.py	Sat Dec 22 21:31:01 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +0,0 @@
-#!/usr/bin/env python2.4
-#
-# DeGAL - A pretty simple web image gallery
-# Copyright (C) 2007 Tero Marttila
-# http://marttila.de/~terom/degal/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the
-# Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#
-
-import shelve
-import cgi
-import Cookie
-import os, os.path
-
-import degal
-
-#
-# load request params
-#
-vars = cgi.FieldStorage()
-
-# these are interpeted different ways, hence the generic naming
-arg1 = vars["keys"].value
-if 'index' in vars :
-    arg2 = vars["index"].value
-else :
-    arg2 = None
-
-# the cookie with the user's current series
-cookie = Cookie.SimpleCookie(os.environ.get('HTTP_COOKIE', None))
-
-# a special action?
-if arg1 and arg1 in ('add', 'del', 'clear', 'view') or arg2 == 'load' :
-    # load the keys from the cookie
-    if 'series' in cookie :
-        keys = cookie["series"].value.split()
-    else :
-        keys = []
-    
-    if arg2 == 'load' :
-        # set the keys in the user's cookie to those in the URL
-        keys = arg1.split()
-
-    elif arg1 == 'add' and arg2 not in keys :
-        # add a code to the list of keys
-        keys.append(arg2)
-
-    elif arg1 == 'del' and arg2 in keys :
-        # remove a key from the list of keys
-        keys.remove(arg2)
-
-    elif arg1 == 'clear' :
-        # clear out the set of keys
-        keys = []
-
-    elif arg1 == 'view' :
-        # just view them
-        pass
-   
-    # set the series cookie value
-    cookie['series'] = ' '.join(keys)
-    cookie['series']['path'] = '/'
-    
-    # if we have keys, redirect to them, otherwise, back to index we go
-    if keys :
-        redirect_to = "../%s/" % ('+'.join(keys))
-    else :
-        redirect_to = "../.."
-    
-    # do the redirect
-    print "Status: 302"
-    print "Location: %s" % redirect_to
-    print cookie
-    print
-    print "Redirect..."
-else :
-    # we're just viewing
-    keys = arg1.split()
-    
-    # is this "My Series"?
-    my_series = 'series' in cookie and cookie['series'].value.split() == keys
-        
-    if arg2 :
-        index = int(arg2)
-    else :
-        index = None
-
-    # load DB
-    db = shelve.open('shorturls2', 'r')
-
-    # get the Image objects
-    photos = []
-    
-    #
-    # Start ugly code-misreuse
-    #
-
-    # monkey-patch the templates
-    rendered_templates = []
-
-    def _myRenderTo (self, path, **vars) :
-        rendered_templates.append(self.render(**vars))
-
-    # lalalalala... ooh, look;
-
-    degal.Template.renderTo = _myRenderTo
-    #
-    #         vvvvvvvvvvvvvvvvvvvv
-    #         vvvvvvvvvvvvvvvvvvvv
-    #
-    #    >>>> SHINY PINK ELEPHANT! <<<<
-    #
-    #         ^^^^^^^^^^^^^^^^^^^^
-    #         ^^^^^^^^^^^^^^^^^^^^
-
-    # our own version of Folder
-    class Series (degal.Folder) :
-        def __init__ (self) :
-            super(Series, self).__init__()
-            
-            self.alive = True
-            self.filtered = False
-            self.subdirs = {}
-            self.images = {}
-            self.sorted_images = []
-            self.title = "Series"
-
-            if my_series :
-                self.descr = '<a href="../clear/" rel="nofollow">Clear your series</a>'
-            else :
-                self.descr = '<a href="load" rel="nofollow">Load as your series</a>'
-
-            self.shorturl_code = ''
-        
-        def breadcrumb (self) :
-            return [('../..', 'Gallery'), ('.', 'Series')]
-
-        def inRoot (self, *fnames) :
-            return os.path.join('../..', *fnames)
-
-    class Image (degal.Image) :
-        def __init__ (self, series, key, i) :
-            type, self.dir_name, self.image_name = db[key]
-
-            super(Image, self).__init__(series, self.image_name)
-            
-            self.path = self._path()
-            self.shorturl_code = key
-            self.html_path = self.html_name = str(i + 1)
-            self.title = 'Image %d' % (i + 1, )
-            self.descr = '<span style="font-size: x-small">Standalone image: <a href="%s.html">%s</a></span>' % (self._path(), self._path().lstrip('./'))
-            self.img_size = (-1, -1)
-            self.filesize = 0
-            self.timestamp = 0
-
-            if my_series :
-                self.series_act = "del"
-                self.series_verb = "Remove from"
-            else :
-                self.series_act = self.series_verb = ""
-
-        def breadcrumb (self) :
-            return [('../..', 'Gallery'), ('.', 'Series'), (self.html_name, self.title)]
-
-        def _path (self, blaa='') :
-            return os.path.join('../..', self.dir_name, blaa, self.image_name)
-
-        def thumbImgTag (self) :
-            return degal.link(self.html_name, "<img src='%s' alt='%s' title='%s'>" % (self._path(degal.THUMB_DIR), '', self.title))
-
-        def previewImgTag (self) :
-            return degal.link(self._path(), "<img src='%s' alt='%s' title='%s'>" % (self._path(degal.PREVIEW_DIR), '', self.title))
-
-    series = Series()
-
-    try :
-        prev = None
-
-        for i, key in enumerate(keys) :
-            img = Image(series, key, i)
-
-            if prev :
-                prev.next = img
-
-            img.prev = prev
-            prev = img
-
-            series.sorted_images.append(img)
-    finally :
-        db.close()
-
-    if index :
-        img = series.sorted_images[index - 1]
-
-        img.render()
-    else :
-        series.render()
-
-    print "Content-Type: text/html"
-    print
-    print rendered_templates[0]
-
--- a/cgi-bin/shorturl.py	Sat Dec 22 21:31:01 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-#!/usr/bin/env python2.4
-#
-# DeGAL - A pretty simple web image gallery
-# Copyright (C) 2007 Tero Marttila
-# http://marttila.de/~terom/degal/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the
-# Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#
-
-import shelve
-import cgi
-import os, os.path
-
-import inc
-
-vars = cgi.FieldStorage()
-
-key = vars['key'].value
-
-if 'index' in vars :
-    index = int(vars['index'].value.lstrip('/'))
-else :
-    index = None
-
-db = shelve.open('shorturls2', 'r')
-
-try :
-    type, dirpath, fname = db[key]
-
-    if type == 'img' :
-        fname += '.html'
-    elif type == 'dir' :
-        fname = ''
-
-    if index :
-        if index > 1 : 
-            fname = 'index_%s.html' % (index - 1)
-
-        dirpath = '../%s' % dirpath
-
-    path = os.path.join(dirpath, fname)
-
-    print "Status: 302"
-    print "Location: ../%s" % path
-    print
-    print "../%s" % path
-
-finally :
-    db.close()
--- a/cgi-bin/taggr.py	Sat Dec 22 21:31:01 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-#!/usr/bin/env python2.4
-#
-# DeGAL - A pretty simple web image gallery
-# Copyright (C) 2007 Tero Marttila
-# http://marttila.de/~terom/degal/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the
-# Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#
-
-
-import cgi
-import shelve
-import os, os.path
-
-import degal
-
-images = shelve.open('images', 'c')
-tag_images = shelve.open('tag_images', 'c')
-tag_tags = shelve.open('tag_tags', 'c')
-
-# request params
-vars = cgi.FieldStorage()
-
-if 'path' in vars :
-    path = vars['path'].value
-else :
-    path = '.'
-
-image_list = []
-
-if 'bulk_tag' in vars :
-    bulk_tags = vars['bulk_tag'].value.split()
-else :
-    bulk_tags = None
-
-for fname in os.listdir(path) :
-    if degal.isImage(fname) :
-        image_path = os.path.join(path, fname)
-        thumb_path = os.path.join(path, degal.THUMB_DIR, fname)
-        html_path = image_path + '.html'
-
-        title, descr, tags = images.get(image_path, (None, None, []))
-        
-        if 'img_%s_title' % fname in vars :
-            title = vars['img_%s_title' % fname].value
-        
-        if 'img_%s_descr' % fname in vars :
-            descr = vars['img_%s_descr' % fname].value
-
-        if 'img_%s_tags' % fname in vars :
-            tags = vars['img_%s_tags' % fname].value.split()
-
-        if bulk_tags and 'img_%s_chk' % fname in vars :
-            tags.extend(bulk_tags)
-        
-        if title or descr or tags :
-           images[image_path] = (title, descr, tags)
-
-        html = """
-<li>
-    <span class="inputs">
-        <label for="img_%(fname)s_title">Title</label> <input type="text" name="img_%(fname)s_title" size="60" value="%(title)s" /> <br/>
-        <label for="img_%(fname)s_descr">Description</label> <input type="text" name="img_%(fname)s_descr" size="60" value="%(descr)s" /> <br/>
-        <label for="img_%(fname)s_tags">Tags</label> <input type="text" name="img_%(fname)s_tags" size="60" value="%(tags)s" /> <br/>
-    </span>
-    
-    <input type="checkbox" name="img_%(fname)s_chk" class="chk" />
-    
-    <span class="thumb"><a href="%(html_path)s"><img src="%(thumb_path)s" /></a></span>
-</li>
-        """ % dict(
-            html_path       = html_path,
-            thumb_path      = thumb_path,
-            fname           = fname,
-            title           = title and title or '',
-            descr           = descr and descr or '',
-            tags            = tags and ' '.join(tags) or '',
-        )
-
-        image_list.append((fname, html))
-
-image_list.sort()
-
-print "Content-Type: text/html"
-print
-print degal.Template('taggr').render(
-    TITLE       = "Taggr - %s" % path,
-    BREADCRUMB  = "TODO",
-    CONTENT     = "<ul>" + ''.join([h for fname, h in image_list]) + "</ul>",
-    PATH        = path,
-)
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/de-cgi-bin/inc.py	Wed Jan 16 14:58:03 2008 +0000
@@ -0,0 +1,27 @@
+# config
+
+# location of DeGAL itself
+
+DEGAL_PATH = "/mnt/photos/public"
+
+
+
+if __name__ == '__main__' :
+    raise Exception("Don't access inc.py directly")
+
+# setup env
+
+import sys
+import os, os.path
+
+#def splitn (path, n) :
+#    for i in xrange(0, n) :
+#        path = os.path.split(path)[0]      
+#    
+#    return path
+#        
+#degal_path = splitn(os.path.join(os.getcwd(), __file__), 2)
+
+os.chdir(DEGAL_PATH)
+sys.path.append(DEGAL_PATH)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/de-cgi-bin/series.py	Wed Jan 16 14:58:03 2008 +0000
@@ -0,0 +1,214 @@
+#!/usr/bin/env python2.4
+#
+# DeGAL - A pretty simple web image gallery
+# Copyright (C) 2007 Tero Marttila
+# http://marttila.de/~terom/degal/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+import os
+import cgi
+import Cookie
+
+import inc
+from lib import shorturl, template, utils, settings
+
+#
+# load request params
+#
+vars = cgi.FieldStorage()
+
+# these are interpeted different ways, hence the generic naming
+arg1 = vars["keys"].value
+if 'index' in vars :
+    arg2 = vars["index"].value
+else :
+    arg2 = None
+
+# the cookie with the user's current series
+cookie = Cookie.SimpleCookie(os.environ.get('HTTP_COOKIE', None))
+
+# a special action?
+if arg1 and arg1 in ('add', 'del', 'clear', 'view') or arg2 == 'load' :
+    # load the keys from the cookie
+    if 'series' in cookie :
+        keys = cookie["series"].value.split()
+    else :
+        keys = []
+    
+    if arg2 == 'load' :
+        # set the keys in the user's cookie to those in the URL
+        keys = arg1.split()
+
+    elif arg1 == 'add' and arg2 not in keys :
+        # add a code to the list of keys
+        keys.append(arg2)
+
+    elif arg1 == 'del' and arg2 in keys :
+        # remove a key from the list of keys
+        keys.remove(arg2)
+
+    elif arg1 == 'clear' :
+        # clear out the set of keys
+        keys = []
+
+    elif arg1 == 'view' :
+        # just view them
+        pass
+   
+    # set the series cookie value
+    cookie['series'] = ' '.join(keys)
+    cookie['series']['path'] = '/'
+    
+    # if we have keys, redirect to them, otherwise, back to index we go
+    if keys :
+        redirect_to = "../%s/" % ('+'.join(keys))
+    else :
+        redirect_to = "../.."
+    
+    # do the redirect
+    print "Status: 302"
+    print "Location: %s" % redirect_to
+    print cookie
+    print
+    print "Redirect..."
+else :
+    # we're just viewing
+    keys = arg1.split()
+    
+    # is this "My Series"?
+    my_series = 'series' in cookie and cookie['series'].value.split() == keys
+    
+    index = fname = None
+
+    if arg2 :
+        try :
+            index = int(arg2)
+        except ValueError :
+            fname = arg2
+
+    # load DB
+    db = shorturl.DB()
+    
+    # our custom Series/Image classes, because they do act slightly differently
+
+    class Series (object) :
+        def __init__ (self, keys) :
+            self.images = []
+            prev = None
+
+            self.image_dict = dict()
+
+            for index, key in enumerate(keys) :
+                dir, fname = db.image_info(key)
+
+                img = Image(self, key, dir, fname, index)
+                self.images.append(img)
+                self.image_dict[fname] = img
+
+                img.prev = prev
+
+                if prev :
+                    prev.next = img
+
+                prev = img
+
+        def render (self) :
+            if my_series :
+                descr = '<a href="../clear/" rel="nofollow">Clear your series</a>'
+            else :
+                descr = '<a href="load" rel="nofollow">Load as your series</a>'
+   
+            return template.gallery.render(
+                stylesheet_url      = utils.url("style.css", up=2),
+                
+                breadcrumb          = [(utils.url(up=1), "Index"), (utils.url(), "Series")],
+
+                dirs                = None,
+                title               = "Series",
+
+                num_pages           = 1,
+                cur_page            = 0,
+
+                images              = self.images,
+
+                description         = descr,
+
+                shorturl            = None,
+                shorturl_code       = None,
+            )
+    
+    class Image (object) :
+        def __init__ (self, series, key, dir, fname, index) :
+            self.fname = fname
+            self.name = utils.url_join(dir, fname, abs=True)
+            self.html_name = utils.url(fname)
+            self.real_html_name = utils.url_join(dir, fname + ".html", abs=True)
+
+            self.thumb_name = utils.url_join(dir, settings.THUMB_DIR, fname, abs=True)
+            self.preview_name = utils.url_join(dir, settings.PREVIEW_DIR, fname, abs=True)
+
+            self.shorturl = key
+
+            self.prev = self.next = None
+
+        def render (self) :
+            descr = '<span style="font-size: x-small"><a href="%s.html">Standalone image</a></span>' % self.real_html_name
+            
+            if my_series :
+                series_url = utils.url_join("del", self.shorturl, up=1)
+                series_verb = "Remove from"
+            else :
+                series_url = series_verb = ""
+
+            return template.image.render(
+                stylesheet_url      = utils.url("style.css", up=3),
+                
+                breadcrumb          = [(utils.url(up=2), "Index"), (utils.url("."), "Series"), (self.html_name, self.fname)],
+
+                title               = self.fname,
+
+                prev                = self.prev,
+                img                 = self,
+                next                = self.next,
+                
+                description         = descr,
+    
+                img_size            = None,
+                file_size           = None,
+                timestamp           = None,
+                
+                shorturl            = utils.url_join("s", self.shorturl, abs=True),
+                shorturl_code       = self.shorturl,
+                
+                series_url          = series_url,
+                series_verb         = series_verb,
+            )
+    
+    series = Series(keys)
+
+    if fname :
+        html = series.image_dict[fname].render()
+    elif index :
+        html = series.images[index - 1].render()
+    else :
+        html = series.render()
+
+    print "Content-Type: text/html"
+    print
+    print html
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/de-cgi-bin/shorturl.py	Wed Jan 16 14:58:03 2008 +0000
@@ -0,0 +1,45 @@
+#!/usr/bin/env python2.4
+#
+# DeGAL - A pretty simple web image gallery
+# Copyright (C) 2007 Tero Marttila
+# http://marttila.de/~terom/degal/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+import cgi
+
+import inc
+from lib import shorturl
+
+vars = cgi.FieldStorage()
+
+key = vars['key'].value
+
+if 'index' in vars :
+    index = int(vars['index'].value.lstrip('/'))
+else :
+    index = None
+
+db = shorturl.DB()
+
+path = db.html_path(key, index)
+
+print "Status: 302"
+print "Location: ../%s" % path
+print
+print "../%s" % path
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/de-cgi-bin/taggr.py	Wed Jan 16 14:58:03 2008 +0000
@@ -0,0 +1,105 @@
+#!/usr/bin/env python2.4
+#
+# DeGAL - A pretty simple web image gallery
+# Copyright (C) 2007 Tero Marttila
+# http://marttila.de/~terom/degal/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+
+import cgi
+import shelve
+import os, os.path
+
+import degal
+
+images = shelve.open('images', 'c')
+tag_images = shelve.open('tag_images', 'c')
+tag_tags = shelve.open('tag_tags', 'c')
+
+# request params
+vars = cgi.FieldStorage()
+
+if 'path' in vars :
+    path = vars['path'].value
+else :
+    path = '.'
+
+image_list = []
+
+if 'bulk_tag' in vars :
+    bulk_tags = vars['bulk_tag'].value.split()
+else :
+    bulk_tags = None
+
+for fname in os.listdir(path) :
+    if degal.isImage(fname) :
+        image_path = os.path.join(path, fname)
+        thumb_path = os.path.join(path, degal.THUMB_DIR, fname)
+        html_path = image_path + '.html'
+
+        title, descr, tags = images.get(image_path, (None, None, []))
+        
+        if 'img_%s_title' % fname in vars :
+            title = vars['img_%s_title' % fname].value
+        
+        if 'img_%s_descr' % fname in vars :
+            descr = vars['img_%s_descr' % fname].value
+
+        if 'img_%s_tags' % fname in vars :
+            tags = vars['img_%s_tags' % fname].value.split()
+
+        if bulk_tags and 'img_%s_chk' % fname in vars :
+            tags.extend(bulk_tags)
+        
+        if title or descr or tags :
+           images[image_path] = (title, descr, tags)
+
+        html = """
+<li>
+    <span class="inputs">
+        <label for="img_%(fname)s_title">Title</label> <input type="text" name="img_%(fname)s_title" size="60" value="%(title)s" /> <br/>
+        <label for="img_%(fname)s_descr">Description</label> <input type="text" name="img_%(fname)s_descr" size="60" value="%(descr)s" /> <br/>
+        <label for="img_%(fname)s_tags">Tags</label> <input type="text" name="img_%(fname)s_tags" size="60" value="%(tags)s" /> <br/>
+    </span>
+    
+    <input type="checkbox" name="img_%(fname)s_chk" class="chk" />
+    
+    <span class="thumb"><a href="%(html_path)s"><img src="%(thumb_path)s" /></a></span>
+</li>
+        """ % dict(
+            html_path       = html_path,
+            thumb_path      = thumb_path,
+            fname           = fname,
+            title           = title and title or '',
+            descr           = descr and descr or '',
+            tags            = tags and ' '.join(tags) or '',
+        )
+
+        image_list.append((fname, html))
+
+image_list.sort()
+
+print "Content-Type: text/html"
+print
+print degal.Template('taggr').render(
+    TITLE       = "Taggr - %s" % path,
+    BREADCRUMB  = "TODO",
+    CONTENT     = "<ul>" + ''.join([h for fname, h in image_list]) + "</ul>",
+    PATH        = path,
+)
+
--- a/lib/shorturl.py	Sat Dec 22 21:31:01 2007 +0000
+++ b/lib/shorturl.py	Wed Jan 16 14:58:03 2008 +0000
@@ -96,3 +96,32 @@
 
     db['_id'] = id
     db.close()
+
+class DB (object) :
+    def __init__ (self, read_only=True) :
+        self.db = shelve.open('shorturls2', read_only and 'r' or 'c')
+
+    def html_path (self, key, index) :
+        type, dirpath, fname = self.db[key]
+
+        if type == 'img' :
+            fname += '.html'
+        elif type == 'dir' :
+            fname = ''
+
+        if index :
+            dirpath = '../%s' % dirpath
+            
+            if type == 'dir' and index > 1 : 
+                fname = 'index_%s.html' % (index - 1)
+
+        return os.path.join(dirpath, fname)
+   
+    def image_info (self, key) :
+        type, dirpath, fname = self.db[key]
+
+        if type != 'img' :
+            raise ValueError("%s is not an img" % key)
+
+        return dirpath, fname
+
--- a/lib/utils.py	Sat Dec 22 21:31:01 2007 +0000
+++ b/lib/utils.py	Wed Jan 16 14:58:03 2008 +0000
@@ -44,8 +44,13 @@
 
     return "", ""
 
-def url_join (*parts) :
-    return '/'.join(parts)
+def url (*parts, **kwargs) :
+    abs = kwargs.pop('abs', False)
+    up = kwargs.pop('up', 0)
     
+    return '/'.join(([""]*int(abs)) + ([".."]*up) + list(parts))
+
+url_join = url
+
 def path_join (*parts) :
     return os.path.join(*[part for part in parts if part is not None])
--- a/templates/gallery.html	Sat Dec 22 21:31:01 2007 +0000
+++ b/templates/gallery.html	Wed Jan 16 14:58:03 2008 +0000
@@ -51,6 +51,8 @@
     <p id="description">
 ${description}
     </p>
+% if shorturl :    
     <div id="info">
         <p>ShortURL: <a href="${shorturl}" rel="nofollow">${shorturl_code}</a></p>
     </div>
+% endif    
--- a/templates/image.html	Sat Dec 22 21:31:01 2007 +0000
+++ b/templates/image.html	Wed Jan 16 14:58:03 2008 +0000
@@ -18,10 +18,14 @@
         </p>
     </div>
     <div id="info">
+% if img_size and file_size and timestamp :    
       <p>${filename}</p>
       <p>${h.format_imgsize(img_size)}</p>
       <p>${h.format_filesize(file_size)}</p>
       <p>${h.format_timestamp(timestamp)}</p>
+% endif    
       <p>ShortURL: <a href="${shorturl}" rel="nofollow">${shorturl_code}</a></p>
+% if series_url :      
       <p><a href="${series_url}" rel="nofollow">${series_verb}</a> series</p>
+% endif      
     </div>