webnomad/index

361 lines
10 KiB
Bash
Executable File

#!/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.
# API in brief:
#
# filetype_icon() - takes a filename as arg (can be file only or real path)
# - returns a filename for the icon to be used from icons/
#
# index_dir() - indexes a directory into an html file linking all its contents
#
# preview_file() - takes a filename as arg
# renders the file in a usable format (thumb if image, html if markdown, etc.)
# returns the filename of the rendered result
#
# file list map for current dir
# format: filename;size;date
typeset -alU files
filetype_icon() {
{ test "$1" = "" } && {
error "filetype_icon called without argument"; return 1 }
fpath="$1"
filename="${fpath##*/}"
ext="${filename##*.}"
name="${filename%%.*}"
res="default.png"
# analize extensions, will be overridden by name
case $ext:l in
txt) res=text-plain.png ;;
html) res=text-html.png ;;
xml) res=text-xml.png ;;
md5|sha*) res=hash.png ;;
asc|gpg) res=signature.png ;;
tex|md|org) res=text-x-texinfo.png ;;
patch) res=text-x-patch.png ;;
sql) res=text-x-sql.png ;;
vcart) res=text-x-vcard.png ;;
dmg) res=dmg.png ;;
esac
# analize name
case $name:l in
changelog) res=text-x-changelog.png ;;
readme) res=text-x-readme.png ;;
install) res=text-x-install.png ;;
authors) res=text-x-authors.png ;;
makefile) res=text-x-makefile.png ;;
news|usage) res=text-x-nfo.png ;;
copying) res=text-x-copying.png ;;
known_bugs|todo) res=text-x-log.png ;;
sources) res=folder-development.png ;;
doc|docs) res=folder-documents.png ;;
releases|binaries) res=folder-downloads.png ;;
esac
# todo using mimetype -b and more
print "$res"
}
index_short() {
func "index_long_preview \"$1\" \"$2\" \"$3\""
dir="${1}${3}"
{ test -d "$dir" } || { error "cannot index: not a directory '$dir'"; return 1 }
files=()
ttmp=`ls "$dir" | awk '
/^total/ { next }
/^$/ { next }
{ printf "files+=(\"$1\");" }
'`
{ test $? = 0 } || {
error "Error parsing directory: $dir"
return 1 }
eval "$ttmp"
act "${#files} files parsed in $dir"
diralias="$2"
dirbase="$3"
# setup paths for test
if [ "$CMD" = "test" ]; then
LINK_PREFIX="file://${dir}"
else
LINK_PREFIX="${diralias}${dirbase}"
fi
func "LINK_PREFIX = $LINK_PREFIX"
cat <<EOF
<table class="table table-hover table-condensed">
<thead><tr>
<th class="col-sm-1 col-md-1 col-lg-1"><!-- filetype icon --></th>
<th class="col-sm-3 col-md-4 col-lg-4"><!-- file name --></th>
</tr></thead>
EOF
for f in ${files}; do
name="${f}"
ext="${name##*.}" # file extension
file="${1}/${name}" # file path
typefield=""
namefield=""
previewfield=""
link=""
icon=""
icon_width="width=\"50px\""
if [ -L "$file" ]; then # is a symlink
if [ -d "$file" ]; then # symlink to folder
link="<a href=\"${name}/index${EXTENSION}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/symlink.png\" alt=\"symlink\" ${icon_width}></a>"
namefield="${link}${name}</a>"
else # symlink to file
link="<a href=\"${LINK_PREFIX}/${name}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/symlink.png\" alt=\"symlink\" ${icon_width}></a>"
namefield="${link}${name}</a>"
fi
elif [ -d "$file" ]; then # is a folder
link="<a href=\"${name}/index${EXTENSION}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/folder.png\" alt=\"folder\" ${icon_width}></a>"
namefield="${link}${name}</a>"
else # is a file
{ test "$icon" = "" } && { icon="`filetype_icon ${file}`" }
link="<a href=\"${LINK_PREFIX}/${name}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/${icon}\" alt=\"${icon}\" ${icon_width}></a>"
namefield="${link}${name}</a>"
fi
# render it all
cat <<EOF
<tr>
<td style="vertical-align:middle;">${typefield}</td>
<td style="vertical-align:middle;font-size:1.5em;word-wrap:break-word">${namefield}</td>
</tr>
EOF
tpwd="`pwd`"
{ test "$icon" = "" } && { continue }
popd
# copy the icon file
{ test -r ${destination}/icons/${icon} } || {
func "copy icon in place: $icon (PWD: `pwd`)"
cp $SYS/icons/$THUMB_SIZE/${icon} \
${destination}/icons/${icon}
}
pushd "$tpwd"
done
print "</table>"
}
index_long_preview() {
func "index_long_preview \"$1\" \"$2\" \"$3\""
dir="${1}${3}"
{ test -d "$dir" } || { error "cannot index: not a directory '$dir'"; return 1 }
files=()
ttmp=`ls -l --time-style=long-iso "$dir" | awk '
/^total/ { next }
/^$/ { next }
{ printf "files+=(\"%s;%s;%s\");", $8, $5, $6 }
'`
{ test $? = 0 } || {
error "Error parsing directory: $dir"
return 1 }
eval "$ttmp"
act "${#files} files parsed in $dir"
diralias="$2"
dirbase="$3"
# setup paths for test
if [ "$CMD" = "test" ]; then
LINK_PREFIX="file://${dir}"
else
LINK_PREFIX="${diralias}${dirbase}"
fi
func "LINK_PREFIX = $LINK_PREFIX"
# human size dividers
_mb=$((1024 * 1024))
_gb=$((1024 * 1024 * 1024))
cat <<EOF
<table class="table table-hover table-condensed">
<thead><tr>
<th class="col-sm-1 col-md-1 col-lg-1"><!-- filetype icon --></th>
<th class="col-sm-3 col-md-4 col-lg-4">Filename</th>
<th class="col-sm-1 col-md-2 col-lg-3">Size</th>
<th class="col-sm-1 col-md-2 col-lg-3">Date</th>
<th class="col-sm-3 col-md-4 col-lg-5">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
# format size to human readable form
if [[ $size -lt 1024 ]]; then hsize="$size B"
elif [[ $size -gt 1024 ]]; then hsize="$(( $size / 1024 )) KB"
elif [[ $size -gt 1048576 ]]; then hsize="$(( $size / $mb )) MB"
else hsize="$(( $size / $gb )) GB"
fi
# format date to human readable form
hdate=`date -d "$date" +'%d %b %Y'`
func "$name \t $size \t $date"
typefield=""
namefield=""
previewfield=""
link=""
icon=""
preview=`preview_file "$file"`
{ test "$preview" = "" } || {
func "file preview produced"
case $ext:l in
jpg|jpeg|png|gif|pdf|svg|eps)
func "preview is a thumbnail"
preview="<img src=\"$preview\" alt=\"$name\" title=\"$name\">"
icon="image-x-generic.png"
;;
esac
}
icon_width="width=\"50px\""
if [ -L "$file" ]; then # is a symlink
if [ -d "$file" ]; then # symlink to folder
link="<a href=\"${name}/index${EXTENSION}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/symlink.png\" alt=\"symlink\" ${icon_width}></a>"
namefield="${link}${name}</a>"
previewfield="<!-- folder -->"
else # symlink to file
link="<a href=\"${LINK_PREFIX}/${name}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/symlink.png\" alt=\"symlink\" ${icon_width}></a>"
namefield="${link}${name}</a>"
previewfield="${link}${preview}</a>"
fi
elif [ -d "$file" ]; then # is a folder
link="<a href=\"${name}/index${EXTENSION}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/folder.png\" alt=\"folder\" ${icon_width}></a>"
namefield="${link}${name}</a>"
previewfield="<!-- folder -->"
else # is a file
{ test "$icon" = "" } && { icon="`filetype_icon ${file}`" }
link="<a href=\"${LINK_PREFIX}/${name}\">"
typefield="${link}<img src=\"${WEB_ROOT}/icons/${icon}\" alt=\"${icon}\" ${icon_width}></a>"
namefield="${link}${name}</a>"
previewfield="${link}${preview}</a>"
fi
# render it all
cat <<EOF
<tr>
<td style="vertical-align:middle;">${typefield}</td>
<td style="vertical-align:middle;font-size:1.5em;word-wrap:break-word">${namefield}</td>
<td style="vertical-align:middle">$hsize</td>
<td style="vertical-align:middle">$hdate</td>
<td style="vertical-align:middle">${previewfield}</td>
</tr>
EOF
tpwd="`pwd`"
{ test "$icon" = "" } && { continue }
popd
# copy the icon file
{ test -r ${destination}/icons/${icon} } || {
func "copy icon in place: $icon (PWD: `pwd`)"
cp $SYS/icons/$THUMB_SIZE/${icon} \
${destination}/icons/${icon}
}
pushd "$tpwd"
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)
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|gif) # only first page / animation frame
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"
;;
svg|eps)
output="${name}-thumb.jpg"
{ test -r "$output" } && {
act "$f: thumbnail found, skip rendering"
print "$output"
continue }
notice "$f: rendering thumbnail using ImageMagick"
convert -density 144 "${f}" -resize 100 $output
print "$output"
;;
esac
done
return 0
}