added directory index feature

This commit is contained in:
Jaromil 2014-05-21 08:23:30 +02:00
parent 4e277ed539
commit c861598763
4 changed files with 362 additions and 10 deletions

View File

@ -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

309
index Executable file
View File

@ -0,0 +1,309 @@
#!/usr/bin/env zsh
#
# ZShelf, file directory indexing for the web
#
# Copyright (C) 2014 Denis Roio <jaromil@dyne.org>
#
# 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 <<EOF
<table class="table table-hover table-condensed">
<thead><tr><th>Filename</th><th>Size</th><th>Date</th><th><!-- preview --></th></tr></thead>
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="<img src=\"$preview\" alt=\"$name\" title=\"$name\">" ;;
esac
}
cat <<EOF
<tr>
<td><a href="${LINK_PREFIX}${link}">${name}</a></td>
<td>$size</td><td>$date</td>
<td><a href="${LINK_PREFIX}${link}">$preview</a></td></tr>
EOF
done
print "</table>"
}
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) = <a href> 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 "<hr>"
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

6
init
View File

@ -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 }

32
render
View File

@ -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 "<link rel=\"stylesheet\" href=\"css/blueimp-gallery.min.css\" />"
{ 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 }