terom@14: # DeGAL - A pretty simple web image gallery terom@14: # Copyright (C) 2007 Tero Marttila terom@14: # http://marttila.de/~terom/degal/ terom@14: # terom@14: # This program is free software; you can redistribute it and/or modify terom@14: # it under the terms of the GNU General Public License as published by terom@14: # the Free Software Foundation; either version 2 of the License, or terom@14: # (at your option) any later version. terom@14: # terom@14: # This program is distributed in the hope that it will be useful, terom@14: # but WITHOUT ANY WARRANTY; without even the implied warranty of terom@14: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terom@14: # GNU General Public License for more details. terom@14: # terom@14: # You should have received a copy of the GNU General Public License terom@14: # along with this program; if not, write to the terom@14: # Free Software Foundation, Inc., terom@14: # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. terom@14: # terom@14: terom@12: import os, os.path terom@12: terom@13: import settings, image, utils, helpers terom@12: from log import index, render terom@12: from template import gallery as gallery_tpl terom@12: from helpers import url_for_page terom@12: terom@12: def dirUp (count=1) : terom@12: """ terom@12: Returns a relative path to the directly count levels above the current one terom@12: """ terom@12: terom@12: if not count : terom@12: return '.' terom@12: terom@12: return os.path.join(*(['..']*count)) terom@12: terom@12: class Folder (object) : terom@12: def __init__ (self, name='.', parent=None) : terom@12: # the directory name, no trailing / terom@12: self.name = name.rstrip(os.sep) terom@12: terom@12: # our parent Folder, or None terom@12: self.parent = parent terom@12: terom@12: # the path to this dir, as a relative path to the root of the image gallery, always starts with . terom@12: if parent and name : terom@12: self.path = parent.pathFor(self.name) terom@12: else : terom@12: self.path = self.name terom@12: terom@12: # the url-path to the index.html file terom@12: self.html_path = self.path terom@12: terom@12: # dict of fname -> Folder terom@12: self.subdirs = {} terom@12: terom@12: # dict of fname -> Image terom@12: self.images = {} terom@12: terom@12: # our human-friendly title terom@12: self.title = None terom@12: terom@12: # our long-winded description terom@12: self.descr = '' terom@12: terom@12: # is this folder non-empty? terom@12: self.alive = None terom@12: terom@12: # self.images.values(), but sorted by filename terom@12: self.sorted_images = [] terom@12: terom@12: # the ShortURL key to this dir terom@12: self.shorturl_code = None terom@12: terom@12: # were we filtered out? terom@12: self.filtered = False terom@12: terom@12: def pathFor (self, *fnames) : terom@12: """ terom@12: Return a root-relative path to the given path inside this dir terom@12: """ terom@12: return os.path.join(self.path, *fnames) terom@12: terom@12: def index (self, filters=None) : terom@12: """ terom@12: Look for other dirs and images inside this dir. Filters must be either None, terom@12: whereupon all files will be included, or a dict of {filename -> next_filter}. terom@12: If given, only filenames that are present in the dict will be indexed, and in terom@12: the case of dirs, the next_filter will be passed on to that Folder's index terom@12: method. terom@12: """ terom@12: terom@12: index.info("Indexing %s", self.path) terom@12: terom@12: if filters : terom@12: self.filtered = True terom@12: terom@12: # iterate through listdir terom@12: for fname in os.listdir(self.path) : terom@12: # the full filesystem path to it terom@12: fpath = self.pathFor(fname) terom@12: terom@12: # ignore dotfiles terom@12: if fname.startswith('.') : terom@12: index.debug("Skipping dotfile %s", fname) terom@12: continue terom@12: terom@12: # apply filters terom@12: if filters : terom@12: if fname in filters : terom@12: next_filter = filters[fname] terom@12: else : terom@12: index.debug("Skip `%s' as we have a filter", fname) terom@12: continue terom@12: else : terom@12: next_filter = None terom@12: terom@12: # recurse into subdirs, but not thumbs/previews terom@12: if (os.path.isdir(fpath) terom@12: and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR)) terom@12: and (self.parent or fname not in settings.ROOT_IGNORE) terom@12: ) : terom@12: index.debug("Found subdir %s", fpath) terom@12: f = self.subdirs[fname] = Folder(fname, self) terom@12: if f.index(next_filter) : # recursion terom@12: # if a subdir is alive, we are alive as well terom@12: self.alive = True terom@12: terom@12: # handle images terom@23: elif os.path.isfile(fpath) and utils.isImage(fname) : terom@12: index.debug("Found image %s", fname) terom@12: self.images[fname] = image.Image(self, fname) terom@12: terom@12: # ignore everything else terom@12: else : terom@12: index.debug("Ignoring file %s", fname) terom@12: terom@12: # sort and link the images terom@12: if self.images : terom@12: self.alive = True terom@12: terom@12: # sort the images terom@12: fnames = self.images.keys() terom@12: fnames.sort() terom@12: terom@12: prev = None terom@12: terom@12: # link terom@12: for fname in fnames : terom@12: img = self.images[fname] terom@12: terom@12: img.prev = prev terom@12: terom@12: if prev : terom@12: prev.next = img terom@12: terom@12: prev = img terom@12: terom@12: # add to the sorted images list terom@12: self.sorted_images.append(img) terom@12: terom@26: # figure out our title/ descr. Must be done before our parent dir is rendered (self.title) terom@26: title_path = self.pathFor(settings.TITLE_FILE) terom@26: terom@26: self.title, self.descr = utils.readTitleDescr(title_path) terom@26: terom@26: # default title for the root dir terom@26: if self.title : terom@26: self.alive = True terom@26: pass # use what was in the title file terom@12: terom@26: elif not self.parent : terom@26: self.title = 'Index' terom@12: terom@26: else : terom@26: self.title = self.name terom@26: terom@26: if self.descr : terom@26: self.alive = True terom@12: terom@12: return self.alive terom@12: terom@12: def getObjInfo (self) : terom@12: """ terom@12: Metadata for shorturls2.db terom@12: """ terom@12: return 'dir', self.path, '' terom@12: terom@13: def breadcrumb (self, forImg=None) : terom@12: """ terom@12: Returns a [(fname, title)] list of this dir's parent dirs terom@12: """ terom@12: terom@12: f = self terom@12: b = [] terom@12: d = 0 terom@12: terom@12: while f : terom@13: # functionality of the slightly-hacked-in variety terom@13: if f is self and forImg is not None : terom@13: url = helpers.url_for_page(self.getPageNumber(forImg)) terom@13: else : terom@13: url = dirUp(d) terom@13: terom@13: b.insert(0, (url, f.title)) terom@12: terom@12: d += 1 terom@12: f = f.parent terom@12: terom@12: return b terom@13: terom@13: def getPageNumber (self, img) : terom@13: """ terom@13: Get the page number that the given image is on terom@13: """ terom@13: terom@13: return self.sorted_images.index(img) // settings.IMAGE_COUNT terom@12: terom@12: def countParents (self, acc=0) : terom@12: if self.parent : terom@12: return self.parent.countParents(acc+1) terom@12: else : terom@12: return acc terom@12: terom@12: def inRoot (self, *fnames) : terom@12: """ terom@12: Return a relative URL from this dir to the given path in the root dir terom@12: """ terom@12: terom@12: c = self.countParents() terom@12: terom@12: return utils.url_join(*((['..']*c) + list(fnames))) terom@12: terom@12: def render (self) : terom@12: """ terom@12: Render the index.html, Images, and recurse into subdirs terom@12: """ terom@12: terom@12: # ded folders are skipped terom@12: if not self.alive : terom@12: render.info("Skipping dir %s (no images)", self.path) terom@12: return terom@12: terom@12: # if this dir's contents were filtered out, then we can't render the index.html, as we aren't aware of all the images in here terom@12: if self.filtered : terom@12: render.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path) terom@12: terom@12: else : terom@12: # create the thumb/preview dirs if needed terom@12: for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) : terom@12: path = self.pathFor(dir) terom@12: terom@12: if not os.path.isdir(path) : terom@12: render.info("Creating dir %s", path) terom@12: os.mkdir(path) terom@12: terom@12: # sort the subdirs terom@12: subdirs = self.subdirs.values() terom@12: subdirs.sort(key=lambda d: d.name) terom@12: terom@12: render.info("Rendering %s", self.path) terom@12: terom@12: # paginate! terom@12: images = self.sorted_images terom@12: image_count = len(images) terom@12: pages = [] terom@12: terom@12: while images : terom@12: pages.append(images[:settings.IMAGE_COUNT]) terom@12: images = images[settings.IMAGE_COUNT:] terom@12: terom@12: pagination_required = len(pages) > 1 terom@12: terom@12: if pagination_required : terom@12: render.info("Index split into %d pages of %d images each", len(pages), settings.IMAGE_COUNT) terom@26: elif not pages : terom@26: render.info("Dir with no images, render for subdirs") terom@26: pages = [[]] terom@26: terom@12: for cur_page, images in enumerate(pages) : terom@12: if pagination_required and cur_page > 0 : terom@12: shorturl = "%s/%s" % (self.shorturl_code, cur_page+1) terom@12: else : terom@12: shorturl = self.shorturl_code terom@26: terom@12: # render to index.html terom@12: gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), terom@12: stylesheet_url = self.inRoot('style.css'), terom@12: title = self.title, terom@12: breadcrumb = self.breadcrumb(), terom@12: terom@12: dirs = subdirs, terom@12: images = images, terom@12: terom@12: num_pages = len(pages), terom@12: cur_page = cur_page, terom@12: terom@12: description = self.descr, terom@12: terom@12: shorturl = self.inRoot('s', shorturl), terom@12: shorturl_code = shorturl, terom@12: ) terom@12: terom@12: # render images terom@12: for img in self.images.itervalues() : terom@12: img.render() terom@12: terom@12: # recurse into subdirs terom@12: for dir in self.subdirs.itervalues() : terom@12: dir.render() terom@23: