From c861598763ea53cd598d6f14342bbe91272d2b54 Mon Sep 17 00:00:00 2001 From: Jaromil Date: Wed, 21 May 2014 08:23:30 +0200 Subject: [PATCH] added directory index feature --- README.txt | 25 ++++- index | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++ init | 6 +- render | 32 +++++- 4 files changed, 362 insertions(+), 10 deletions(-) create mode 100755 index diff --git a/README.txt b/README.txt index 472dd0a..a545ddf 100644 --- a/README.txt +++ b/README.txt @@ -64,9 +64,9 @@ ** IMAGE SLIDESHOW - To create an image slideshow simply create a page with file extension - .gal inside the views/ directory, for example one can call it - views/vacation_in_Italy.gal + To create an image slideshow simply create a page with file + extension .gal or .gallery inside the views/ directory, for example + one can call it views/vacation_in_Italy.gal To add images into it one should create a -files directory inside views/ better if named after the gallery page, something like: @@ -90,6 +90,25 @@ At last run webnomad/render and the slideshow will be ready at the page in pub/ which in our case is pub/vacation_in_Italy. +## DIRECTORY INDEXES + + Using webnomad One can also create static web pages indexing all + files into any filesystem directories recursively, making thumbnail + previews of images and linking to the originals. + + In order to do so create a file in views with extension .idx or + .index, then fill it in with the needed configurations, for + instance a file `my_pictures_folder.index` can contain + +``` +index /home/jaromil/pics +``` + + This will produce a `pub/my_pictures_folder.html` file which will + list all files inside that directory with previews and further + links to subfolders that are indexed the same way. + + * DEVELOPERS Bleeding edge is on GitHub. see https://github.com/dyne/webnomad diff --git a/index b/index new file mode 100755 index 0000000..c92b0e6 --- /dev/null +++ b/index @@ -0,0 +1,309 @@ +#!/usr/bin/env zsh +# +# ZShelf, file directory indexing for the web +# +# Copyright (C) 2014 Denis Roio +# +# This source code is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This source code 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. +# Please refer to the GNU Public License for more details. +# +# You should have received a copy of the GNU Public License along with +# this source code; if not, write to: +# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +VERSION=0.1 + +QUIET=0 +RECURSIVE=0 +PARAM=() +LINK_PREFIX="" + +autoload colors; colors + +# standard output message routines +# it's always useful to wrap them, in case we change behaviour later +notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[default] $1" >&2; fi } +error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[default] $1" >&2; fi } +func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[default] $1" >&2; fi } +act() { + if [[ $QUIET == 0 ]]; then + if [ "$1" = "-n" ]; then + print -n "$fg_bold[white] . $fg_no_bold[default] $2" >&2; + else + print "$fg_bold[white] . $fg_no_bold[default] $1" >&2; + fi + fi +} + +# quick bold +B="$fg_bold[white]" +r="$reset_color" + +# honor quiet and debug flags as early as possible +if [[ ${@} == *-q* ]]; then QUIET=1; fi +if [[ ${@} == *-D* ]]; then DEBUG=1; fi + +# what operating system are we in? use os_detect() +# simplifying modes of operation: GNU or MAC +case $(uname) in + Linux) OS=GNU + notice "ZShelf v$VERSION running on GNU/Linux" ;; + + Darwin) OS=MAC + notice "ZShelf v$VERSION running on Mac/OSX" ;; + + *) OS=GNU # default + error "Running on an unknown operating system, assuming GNU" ;; +esac + +if [[ ${@} == *-r* ]]; then RECURSIVE=1; fi + +# file list map for current dir +# format: filename;size;date +typeset -alU files + +# TODO +usage() { act "no help ATM sry" } +index_dir() { + { test -d "$1" } || { error "cannot index: not a directory '$1'"; return 1 } + files=() + ttmp=`ls -l --time-style=long-iso "$1" | awk ' +/^total/ { next } +/^$/ { next } +{ printf "files+=(\"%s;%s;%s\")", $8, $5, $6 } +'` + { test $? = 0 } || { + error "Error parsing directory: $1" + return 1 } + + eval "$ttmp" + act "${#files} files parsed in dir $1" + + cat < +FilenameSizeDate +EOF + for f in ${files}; do + name="${f[(ws:;:)1]}" + size="${f[(ws:;:)2]}" + date="${f[(ws:;:)3]}" + + ext="${name##*.}" # file extension + file="${1}/${name}" # file path + + func "$name \t $size \t $date" + + preview=`preview_file "$file"` + + link="$name" + # link ${file}.html if its existing, else link file directly + { test -r "${name%%.*}.html" } && { link="${name%%.*}.html" } + + { test "$preview" = "" } || { + func "file preview produced" + + case $ext:l in + jpg|jpeg|png|gif|pdf) + func "preview is a thumbnail" + preview="\"$name\"" ;; + esac + } + +cat < +${name} +$size$date +$preview +EOF + done +print "" +} + +preview_file() { + { test "$1" = "" } && { error "no file to preview."; return 1 } + # get the file extension using zsh builtins + # %% is for deleting prefix and ## is for deleting suffix + for f in ${1}; do + filename="${f##*/}" + name="${filename%%.*}" + ext="${f##*.}" + + # lowercase + case $ext:l in + md) # markdown + notice "$f: rendering markdown using maruku" + output="$name".html + maruku --html --html-frag $f -o "$output" + print "$output" + ;; + jpg|jpeg|png|gif) + output="${name}-thumb.${ext}" + { test -r "$output" } && { + act "$f: thumbnail found, skip rendering" + print "$output" + continue } + notice "$f: rendering thumbnail using ImageMagick" + convert $f -resize 100 $output + print "$output" + ;; + pdf) + output="${name}-thumb.jpg" + { test -r "$output" } && { + act "$f: thumbnail found, skip rendering" + print "$output" + continue } + notice "$f: rendering thumbnail using ImageMagick" + convert "${f}[0]" -resize 100 $output + print "$output" + ;; + esac + done + return 0 +} +autostart() { + if [ -d "$1" ]; then + index_dir "$1" + elif [ -f "$1" ]; then + preview_file "$1" + else + return 1 + fi + return $? +} + +typeset -A subcommands_opts + +### Options configuration +#Hi, dear developer! Are you trying to add a new subcommand, or to add some options? +#Well, keep in mind that: +# 1. An option CAN'T have differente meanings/behaviour in different subcommands. +# For example, "-s" means "size" and accept an argument. If you are tempted to add +# an option "-s" (that means, for example "silent", and doesn't accept an argument) +# DON'T DO IT! +# There are two reasons for that: +# I. usability; user expect that "-s" is "size +# II. Option parsing WILL EXPLODE if you do this kind of bad things +# (it will say "option defined more than once, and he's right) + + + +# recursive, quiet, Debug, help, verbose, n=dryrun +main_opts=(r q D h v n) + +# p: prefix (string arg) = link prefix +subcommands_opts[index]="p: " +subcommands_opts[preview]="" + +option_is_set() { + #First argument, the option (something like "-s") + #Second (optional) argument: if it's "out", command will print it out 'set'/'unset' + # This is useful for if conditions + #Return 0 if is set, 1 otherwise + [[ -n ${(k)opts[$1]} ]]; + r=$? + if [[ $2 == out ]]; then + if [[ $r == 0 ]]; then print 'set' + else print 'unset'; fi + fi + return $r; +} +option_value() { #First argument, the option (something like "-s") + <<< ${opts[$1]} +} + +### Detect subcommand +local -aU every_opts #every_opts behave like a set; that is, an array with unique elements +for optspec in $subcommands_opts$main_opts; do + for opt in ${=optspec}; do + every_opts+=${opt} + done +done +local -a oldstar +oldstar=($argv) +zparseopts -M -E -D -Adiscardme ${every_opts} +unset discardme +subcommand=$1 +{ test "$subcommand" = "" } && { subcommand="__default" } + +argv=(${oldstar}) +unset oldstar + +### Parsing global + command-specific options +# zsh magic: ${=string} will split to multiple arguments when spaces occur +set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]} +if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing + zparseopts -M -E -D -Aopts ${cmd_opts} + if [[ $? != 0 ]]; then + error "Some error occurred during option processing." + exitcode=1 + return 1 + fi +fi +#build PARAM (array of arguments) and check if there are unrecognized options +local ok=0 +for arg in $*; do + if [[ $arg == '--' || $arg == '-' ]]; then + ok=1 + continue #it shouldnt be appended to PARAM + elif [[ $arg[1] == '-' ]]; then + if [[ $ok == 0 ]]; then + error "unrecognized option $arg" + exitcode=1 + return 1 + fi + fi + PARAM+=$arg +done + +# if 1st parameter is a known subcommand omit it from params +{ test "$subcommand" = "__default" } || { PARAM[1]=(); shift } + +### End parsing command-specific options + +{ option_is_set -v } && { + cat $ZSHELFEXEC | awk '/^#/ {print $0 } !/^#/ {exit}' + echo } +{ option_is_set -h } && { usage; return 0 } +{ option_is_set -v } && { CLEANEXIT=0 + cat $JAROMAILEXEC | awk 'BEGIN { v=1 } !/^#/ { exit }' + return 0 } +{ option_is_set -q } && { QUIET=1 } +{ option_is_set -D } && { DEBUG=1; QUIET=0; func "All debug messages ON" } +{ option_is_set -n } && { DRYRUN=1; act "Dry run, show operations without executing them." } +{ option_is_set -r } && { RECURSIVE=1 } +{ option_is_set -p } && { LINK_PREFIX="`option_value -p`" } + +# we take a file or directory as the only one parameter +ARG=${PARAM[1]} +case "$subcommand" in + index) + index_dir "$ARG" + { test -r "${ARG}/README.md" } && { + res=`preview_file "${ARG}/README.md"` + print "
" + cat "$res" + } + ;; + preview) preview_file ${PARAM} ;; + help) usage ;; + 'source') return 0 ;; + *) # unknown command, pass it to autostart + func "unknown command, using autostart" + autostart ${=PARAM} + exitcode=$? + { test $exitcode = 0 } || { + error "command \"$subcommand\" not recognized" + act "try -h for help" + } + ;; +esac +exitcode=$? +return $exitcode diff --git a/init b/init index 6e2ebc1..fc76b87 100755 --- a/init +++ b/init @@ -21,10 +21,7 @@ SYS=`dirname $0` source $SYS/utils - - -act "Initializing $B $DIR $r" -pushd $DIR +act "Initializing Webnomad" act "Below is the log of files copied into this directory." act "Init does not rewrite any existing file" @@ -87,4 +84,3 @@ act "Look in your new WebNomad project: $B $DIR $r" act "Customize $B config.zsh $r and then files in the $B tmpl/ $r folder" act "Create new HTML pages in $B views/ $r" -{ test "$DIR" = "." } || { sleep 10 } diff --git a/render b/render index 37afb57..b607bd3 100755 --- a/render +++ b/render @@ -200,7 +200,7 @@ EOF pics=`grep -v '^#'` for p in ${(f)pics}; do file=${p[(ws: :)1]} - desc=`echo $p | awk '{ for(c=2;c<=NF;c++) printf("%s ",$c) }'` + desc=`pecho $p | awk '{ for(c=2;c<=NF;c++) printf("%s ",$c) }'` cat << EOF >> pub/$dst_js slides.push({ href: '${file}', @@ -260,6 +260,23 @@ EOF } +index() { +# render_header "" + { test -d "$1" } || { error "cannot index directory not found: $1"; return 1 } + source $SYS/index source + dirs=`find "$1" -type d` + base=`dirname "$1"` + for d in ${(f)dirs}; do + dir="${d##*${base}}" + mkdir -p "pub/$dir" + render_header > "pub/$dir/index.html" + pushd "pub/$dir" + index_dir "$d" >> index.html + popd + render_footer >> "pub/$dir/index.html" + done +} + read_meta() { tmp=`head -n 3 | awk ' !/^#/ { next } @@ -345,6 +362,7 @@ done # render all image galleries gals=(`find views -type f -name '*.gal'`) +gals+=(`find views -type f -name '*.gallery'`) for src in $gals; do cat ${src} | read_meta dst="pub/`basename ${src%.*}`" @@ -353,6 +371,16 @@ for src in $gals; do print "done" done + +# render all directory indexes +idxs=(`find views -type f -name '*.idx'`) +idxs+=(`find views -type f -name '*.index'`) +for idx in $idxs; do + notice "Directory index rendering: $idx" + source ${idx} +done + + for m in `find views -mindepth 1 -type d `; do act -n "publishing $B $m $r ... " rsync -r $m pub/ @@ -373,4 +401,4 @@ done notice "Website refreshed." -{ test "$DIR" = "." } || { sleep 10 } +# { test "$DIR" = "." } || { sleep 10 }