fixed zuper inclusion, documentation updates for release

This commit is contained in:
Jaromil 2015-10-12 18:54:46 +02:00
parent aec4db9146
commit b2a0c6d98a
6 changed files with 767 additions and 10 deletions

View File

@ -1,18 +1,31 @@
0.3 - 30 March 2015 # 0.4
## 12 October 2015
Live preview function, rendering locally and refreshing the
browser on save (for Linux only). EXIF functions for jpeg images:
optional cleanup of metadata (location, camera model, time), auto
rotate and addition of a comment field. Social network SEO:
opengraph and new image tags. The backend is now using zuper
extended library of zfunctions.
# 0.3
## 30 March 2015
Several bugfixes and enhancements. Added support for recursive Several bugfixes and enhancements. Added support for recursive
indexing of directory structures, integration with Jaro Mail for indexing of directory structures, integration with Jaro Mail for
public maildir archiving, Spanish translation of documentation, public maildir archiving, Spanish translation of documentation,
render test function for local preview. render test function for local preview.
0.2 - 2 September 2013 # 0.2
## 2 September 2013
Refactoring based on bootstrap-2 and bootswatch themes plus Refactoring based on bootstrap-2 and bootswatch themes plus
the blueimp gallery. Project renamed to WebNomad. Possibility the blueimp gallery. Project renamed to WebNomad. Possibility
to interlace <markdown> sections in HTML. Slideshow functiond to interlace <markdown> sections in HTML. Slideshow functiond
developed and used for the website of http://yurt.in developed and used for the website of http://yurt.in
0.1 - 19 October 2012 # 0.1
## 19 October 2012
Initial release following a minimalist design and a directory Initial release following a minimalist design and a directory
structure inspired by ruby projects. Called JaroWeb for lack structure inspired by ruby projects. Called JaroWeb for lack
@ -20,7 +33,7 @@
http://jaromil.dyne.org as well stuff like http://jaromil.dyne.org as well stuff like
http://pirati5stelle.it or http://democrazialiquida.eu http://pirati5stelle.it or http://democrazialiquida.eu
Earlier around 2010 # Earlier around 2010
Various scripts descend from a rewrite of some Dyne.org Various scripts descend from a rewrite of some Dyne.org
websites made by Jaromil and Hellekin in Ruysdael Squat. websites made by Jaromil and Hellekin in Ruysdael Squat.

View File

@ -3,7 +3,7 @@
.--.--.--.-----| |--.-----.-----.--------.---.-.--| | .--.--.--.-----| |--.-----.-----.--------.---.-.--| |
| | | | -__| _ | | _ | | _ | _ | | | | | -__| _ | | _ | | _ | _ |
|________|_____|_____|__|__|_____|__|__|__|___._|_____| |________|_____|_____|__|__|_____|__|__|__|___._|_____|
Un astuto publicador de sitios web estáticos v 0.3 Un astuto publicador de sitios web estáticos
http://dyne.org/software/webnomad http://dyne.org/software/webnomad

View File

@ -41,12 +41,17 @@ Now go customise files in `tmpl/` with your favorite HTML editor and
then go as well in `views/` to create your web pages, better start from then go as well in `views/` to create your web pages, better start from
index.html. index.html.
## LIVE PREVIEW ON SAVE
To preview all changes, start `./webnomad/preview`: this will launch a To preview all changes, start `./webnomad/preview`: this will launch a
firefox browser on the locally rendered `test/index.html` and will firefox browser on the locally rendered `test/index.html` and will
refresh it every time a file is modified in views/ or tmpl/. A refresh it every time a file is modified in views/ or tmpl/.
different browser than firefox can be specified as an argument, for
A different browser than firefox can be specified as an argument, for
instance `./webnomad/preview chromium`. instance `./webnomad/preview chromium`.
## RENDER FINAL RESULT
To upload results, run `./webnomad/render` and your webpage will be in To upload results, run `./webnomad/render` and your webpage will be in
the `pub/` directory ready for upload with a recursive Scp or Rsync the `pub/` directory ready for upload with a recursive Scp or Rsync
on any webserver, for instance `rsync -PraX pub/* dyne.org:public_html`. on any webserver, for instance `rsync -PraX pub/* dyne.org:public_html`.

2
render
View File

@ -39,7 +39,7 @@ arrs+=(custom_fonts)
source $SYS/zuper.init source $SYS/zuper.init
VERSION=0.5 VERSION=0.4
# fill path to the source website root # fill path to the source website root
DIR="`pwd`" DIR="`pwd`"

1
zuper
View File

@ -1 +0,0 @@
/home/jrml/devel/zuper/zuper

706
zuper Normal file
View File

@ -0,0 +1,706 @@
#!/usr/bin/env zsh
#
# Zuper - Zsh Ultimate Programmer's Extensions Refurbished
#
# Copyright (C) 2015 Dyne.org Foundation
#
# Zuper is designed, written and maintained by 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.
##########################
typeset -aU vars
typeset -aU arrs
typeset -aU maps
vars=(DEBUG QUIET LOG)
arrs=(req freq)
vars+=(zuper_version)
zuper_version=0.2
# Messaging function with pretty coloring
autoload colors
colors
vars+=(last_act last_func last_notice)
function _msg() {
local msg="$2"
command -v gettext 1>/dev/null 2>/dev/null && msg="$(gettext -s "$2")"
for i in $(seq 3 ${#});
do
msg=${(S)msg//::$(($i - 2))*::/$*[$i]}
done
local command="print -P"
local progname="$fg[magenta]${PROGRAM##*/}$reset_color"
local message="$fg_bold[normal]$fg_no_bold[normal]$msg$reset_color"
local -i returncode
case "$1" in
inline)
command+=" -n"; pchars=" > "; pcolor="yellow"
;;
message)
last_act="$msg"
pchars=" . "; pcolor="white"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
verbose)
last_func="$msg"
pchars="[D]"; pcolor="blue"
;;
success)
last_notice="$msg"
pchars="(*)"; pcolor="green"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
warning)
pchars="[W]"; pcolor="yellow"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
failure)
pchars="[E]"; pcolor="red"; message="$fg_no_bold[$pcolor]$msg$reset_color"
returncode=1
;;
print)
progname=""
;;
*)
pchars="[F]"; pcolor="red"
message="Developer oops! Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\""
returncode=127
zerr
;;
esac
${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >&2
# write the log if its configured
[[ "$LOG" = "" ]] || {
touch $LOG || return $?
${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >> $LOG
}
return $returncode
}
function _message say act() {
local notice="message"
[[ "$1" = "-n" ]] && shift && notice="inline"
[[ $QUIET = 1 ]] || _msg "$notice" $@
return 0
}
function _verbose xxx func() {
[[ $DEBUG = 1 ]] && _msg verbose $@
return 0
}
function _success yes notice() {
[[ $QUIET = 1 ]] || _msg success $@
return 0
}
function _warning no warn warning() {
[[ $QUIET = 1 ]] || _msg warning $@
return 0
}
function _failure fatal die error() {
# typeset -i exitcode=${exitv:-1}
[[ $QUIET = 1 ]] || _msg failure $@
return 1
}
function _print() {
[[ $QUIET = 1 ]] || _msg print $@
return 0
}
fn() {
fun="$@"
req=()
freq=()
func "$fun"
}
zerr() {
error "error in: ${fun:-$last_notice}"
[[ "$last_func" = "" ]] || warn "called in: $last_func"
[[ "$last_act" = "" ]] || warn "called in: $last_act"
[[ "$last_notice" = "" ]] || warn "called in: $last_notice"
# [[ "$fun" = "" ]] || warn "called in: $fun"
TRAPEXIT() {
error "error reported, operation aborted."
}
return 1
}
ckreq reqck() {
err=0
for v in $req; do
[[ "${(P)v}" = "" ]] && {
warn "required setting is blank: $v"
err=1
}
done
[[ $err = 1 ]] && return $err
for f in $freq; do
# exists and has size greater than zero
[[ -s $f ]] || {
warn "required file empty: $f"
err=1
}
done
[[ $err == 1 ]] && zerr
return $err
}
zdump() {
fn zdump
[[ ${#vars} -gt 0 ]] && {
print "Global variables:"
for _v in $vars; do
print " $_v = \t ${(P)_v}"
done
}
[[ ${#arrs} -gt 0 ]] && {
print "Global arrays:"
for _a in $arrs; do
print " $_a \t ( ${(P)_a} )"
done
}
[[ ${#maps} -gt 0 ]] && {
print "Global maps:"
for _m in $maps; do
print " $_m [key] \t ( ${(Pk)_m} )"
print " $_m [val] \t ( ${(Pv)_m} )"
done
}
}
# handy wrappers for throw/catch execution of blocks where we need the
# program to exit on any error (non-zero) returned by any function
throw() { function TRAPZERR() { zerr; return 1 } }
catch() { function TRAPZERR() { } }
##########################
# Endgame handling
arrs+=(destruens)
destruens=()
# Trap functions for the endgame event
TRAPINT() { endgame INT; return $? }
# TRAPEXIT() { endgame EXIT; return $? }
TRAPHUP() { endgame HUP; return $? }
TRAPQUIT() { endgame QUIT; return $? }
TRAPABRT() { endgame ABORT; return $? }
TRAPKILL() { endgame KILL; return $? }
# TRAPPIPE() { endgame PIPE; return $? }
TRAPTERM() { endgame TERM; return $? }
TRAPSTOP() { endgame STOP; return $? }
# TRAPZERR() { func "function returns non-zero." }
endgame() {
fn "endgame $*"
# execute all no matter what
TRAPZERR() { }
# process registered destructors
for d in $destruens; do
fn "destructor: $d"
$d
done
return 0
}
# Register endgame() to be called at exit.
# unlike TRAPEXIT, the zshexit() hook is not called when functions exit.
zshexit() { endgame EXIT; return $? }
##########################
# Temp file handling
vars+=(ztmpfile)
# ztmp() fills in $ztmpfile global. Caller must copy that variable as
# it will be overwritten at every call.
ztmp() {
fn ztmp
ztmpfile=`mktemp`
tmpfiles+=($ztmpfile)
}
# All tempfiles are freed in endgame()
_ztmp_destructor() {
fn _ztmp_destructor
for f in $tmpfiles; do
rm -f "$f"
done
tmpfiles=()
}
arrs+=(tmpfiles)
destruens+=(_ztmp_destructor)
# tokenizer, works only with one char length delimiters
# saves everything in global array tok=()
arrs+=(tok)
strtok() {
fun="strtok $*"
_string="$1"
_delim="$2"
req=(_string _delim)
ckreq || return $?
tok=()
local f=0
local c=0
for c in {1..${#_string}}; do
if [[ "${_string[(e)$c]}" == "$_delim" ]]; then
# check if not empty
t=${_string[(e)$(($f + 1)),$(($c - 1))]}
[[ "$t" == "" ]] || tok+=($t)
# save last found
f=$c
fi
done
# add last token
t=${_string[(e)$(($f + 1)),$c]}
[[ "$t" == "" ]] || tok+=($t)
}
# optional: define zkv=1 on source
[[ "$zkv" = "" ]] || {
##########################
# Key/Value file storage using ZSh associative maps
zmodload zsh/system
# load a map from a file
# map must be already instantiated with typeset -A by called
# name of map is defined inside the file
function zkv.load() {
fn "zkv-load $*"
file=$1
[[ "$file" = "" ]] && {
error "zkv-open() missing argument: file-path"
zerr
return 1 }
[[ -r "$file" ]] || {
error "zkv-open() file not found $file"
zerr
return 1 }
[[ -s "$file" ]] || {
error "zkv-open() file is empty"
zerr
return 1 }
source $file
}
# save a map in a file
# $1 = name of the map associative array
# $2 = full path to the file
function zkv.save() {
fn "zkv.save $*"
_map=$1
_path=$2
[[ "$_path" = "" ]] && {
error "zkv.save() missing argument: map-name path-to-file"
zerr
return 1
}
[[ -r $_path ]] && {
func "zkv.close() overwriting $_path"
func "backup turd left behind: ${_path}~"
mv $_path $_path~
}
touch $_path
# wondering about http://www.zsh.org/mla/users/2015/msg00286.html
# meanwhile solved using a double array, wasting a full map memcpy
_karr=(${(Pk)_map})
_varr=(${(Pv)_map})
_num="${#_karr}"
for c in {1..$_num}; do
# can also be cat here, however for speed we use builtins
# switch to cat if compatibility is an issue
sysread -o 1 <<EOF >> $_path
$_map+=("${_karr[$c]}" "${(v)_varr[$c]}")
EOF
done
func "$_num key/values stored in $_path"
}
}
# optional: define restful=1 on source
[[ "$restful" = "" ]] || {
########
# Restful API client
# there is a clear zsh optimization here in get/set kv
# using zsh/tcp instead of spawning curl
# and perhaps querying with one call using ?recursive
zmodload zsh/net/tcp
function restful.put() {
# $1 = hostname
# $2 = port
# $3 = path
# $4 = key
# $5 = value
fn "restful.put $*"
# to check if the http service is running is up to the caller
_host=${1} # ip address
_port=${2}
_path=${3}
_k="$4" # key name
_v="$5" # value
req=(_host _k _v)
ckreq || return $?
if ztcp $_host $_port; then
# TODO: work out various parsers, this one works with consul.io
_fd=$REPLY
# func "tcp open on fd $fd"
cat <<EOF >& $_fd
PUT ${_path}${_k} HTTP/1.1
User-Agent: Zuper/$zuper_version
Host: ${_host}:${_port}
Accept: */*
Content-Length: ${#_v}
Content-Type: application/x-www-form-urlencoded
EOF
print -n "$_v" >& $_fd
sysread -i $_fd _res
# close connection
ztcp -c $_fd
[[ "$_res" =~ "true" ]] || {
warn "failed PUT on restful key/value"
warn "endpoint: ${_host}:${_port}${_path}"
warn "resource: $_k = $_v"
print - "$_res"
zerr
return 1
}
else
error "cannot connect to restful service: $_host:$_port"
zerr
return 1
fi
return 0
}
restful.get() {
fn "restful.get $*"
_host=${1}
_port=${2}
_path=${3}
_k=$4 # key name
req=(_host _k)
ckreq || return $?
_k=$1
ztcp $_host $_port || {
zerr
return 1
}
_fd=$REPLY
# TODO: work out various parsers, this one works with consul.io
cat <<EOF >& $_fd
GET ${_path}${_k} HTTP/1.1
User-Agent: Zuper/$zuper_version
Host: $_host:$_port
Accept: */*
EOF
sysread -i $_fd -o 1 | awk -F: '
/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
# close connection
ztcp -c $_fd
return 0
}
}
# {{{ Helpers
[[ "$helpers" = "" ]] || {
function helper.isfound isfound() {
command -v $1 1>/dev/null 2>/dev/null
return $?
}
# remote leading and trailing spaces in a string taken from stdin
function helper.trim trim() {
sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g'
}
zmodload zsh/mapfile
# faster substitute for cat
function helper.printfile printfile() {
print ${mapfile[$1]}
}
# extract all emails found in a text from stdin
# outputs them one per line
function helper.extract-emails extract_emails() {
awk '{ for (i=1;i<=NF;i++)
if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) {
gsub(/<|>|,/ , "" , $i); print $i } }'
}
zmodload zsh/regex
# takes a string as argument, returns success if is an email
function helper.isemail isemail() {
[[ "$1" -regex-match "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" ]] && return 0
return 1
}
# takes a numeric argument and prints out a human readable size
function helper.human-size human_size() {
[[ $1 -gt 0 ]] || {
error "human_size() called with invalid argument"
return 1
}
# we use the binary operation for speed
# shift right 10 is divide by 1024
# gigabytes
[[ $1 -gt 1073741824 ]] && {
print -n "$(( $1 >> 30 )) GB"
return 0
}
# megabytes
[[ $1 -gt 1048576 ]] && {
print -n "$(( $1 >> 20 )) MB"
return 0
}
# kilobytes
[[ $1 -gt 1024 ]] && {
print -n "$(( $1 >> 10 )) KB"
return 0
}
# bytes
print -n "$1 Bytes"
return 0
}
# strips out all html/xml tags (everything between < >)
function helper.html-strip xml_strip html_strip() { sed 's/<[^>]\+>//g' }
# changes stdin string special chars to be shown in html
function helper.escape-html escape_html() {
sed -e '
s/\&/\&amp;/g
s/>/\&gt;/g
s/</\&lt;/g
s/"/\&quot;/g
'
}
# escapes special chars in urls
function helper.decode-url decode_url urldecode() {
sed -e '
s/%25/%/gi
s/%20/ /gi
s/%09/ /gi
s/%21/!/gi
s/%22/"/gi
s/%23/#/gi
s/%24/\$/gi
s/%26/\&/gi
s/%27/'\''/gi
s/%28/(/gi
s/%29/)/gi
s/%2a/\*/gi
s/%2b/+/gi
s/%2c/,/gi
s/%2d/-/gi
s/%2e/\./gi
s/%2f/\//gi
s/%3a/:/gi
s/%3b/;/gi
s/%3d/=/gi
s/%3e//gi
s/%3f/?/gi
s/%40/@/gi
s/%5b/\[/gi
s/%5c/\\/gi
s/%5d/\]/gi
s/%5e/\^/gi
s/%5f/_/gi
s/%60/`/gi
s/%7b/{/gi
s/%7c/|/gi
s/%7d/}/gi
s/%7e/~/gi
s/%09/ /gi
'
}
function helper.encode-url encode_url urlencode() {
sed -e '
s/%/%25/g
s/ /%20/g
s/ /%09/g
s/!/%21/g
s/"/%22/g
s/#/%23/g
s/\$/%24/g
s/\&/%26/g
s/'\''/%27/g
s/(/%28/g
s/)/%29/g
s/\*/%2a/g
s/+/%2b/g
s/,/%2c/g
s/-/%2d/g
s/\./%2e/g
s/\//%2f/g
s/:/%3a/g
s/;/%3b/g
s//%3e/g
s/?/%3f/g
s/@/%40/g
s/\[/%5b/g
s/\\/%5c/g
s/\]/%5d/g
s/\^/%5e/g
s/_/%5f/g
s/`/%60/g
s/{/%7b/g
s/|/%7c/g
s/}/%7d/g
s/~/%7e/g
s/ /%09/g
'
}
}
# }}} Helpers
# {{{ Config
# This is not a full config parser, but its a mechanism to read single
# sections of configuration files that are separated using various
# syntax methods. The only method supported is now org-mode whose
# sections start with #+ . It fills in the global array
# $config_section which can be read out to a file or interpreted in
# memory, whatever syntax it may contain.
vars+=(config_section_type)
arrs+=(config_section)
config_section_type=org-mode
config.section.type() {
fn config.section.type
_type=$1
req=(_type)
ckreq || return $?
case $_type in
org-mode)
config_section_type=org-mode
;;
*)
error "Unknown config type:$_type"
return 1
;;
esac
act "$_type config section parser initialized"
return 0
}
# fills in contents of section in array config_section
config.section.read() {
fn config.section.read
_file=$1
_section=$2
req=(_file _section)
freq=($_file)
ckreq || return $?
case $config_section_type in
org-mode)
_contents=`awk '
BEGIN { found=0 }
/^#\+ '"$_section"'/ { found=1; next }
/^#\+/ { if(found==1) exit 0 }
/^$/ { next }
{ if(found==1) print $0 }
' $_file`
;;
*)
error "Unknown config type:$_type"
;;
esac
config_section=()
for c in ${(f)_contents}; do
config_section+=("$c")
done
return 0
}
# }}}

View File

@ -1 +0,0 @@
/home/jrml/devel/zuper/zuper.init

35
zuper.init Normal file
View File

@ -0,0 +1,35 @@
##########################
# Zuper Init
# initialize globals only after sourcing everything
# since zlibs may contain more variable declarations
for _v in $vars; do
typeset -h $_v
done
for _a in $arrs; do
typeset -aU $_a
done
for _m in $maps; do
typeset -A $_m
done
# reset defaults
DEBUG=${DEBUG:-0}
QUIET=${QUIET:-0}
LOG=${LOG:-""}
req=()
freq=()
last_act=()
last_func=()
last_notice=()
tmpfiles=()
config_section=()
config_section_type=${config_section_type:-org-mode}
func "Zuper $zuper_version initialized"
func "${#vars} global variables registered"
func "${#arrs} global arrays registered"
func "${#maps} global maps registered"