#!/bin/sh
#
# /usr/local/bin/localikiwikicreatesite
# Copyright 2008 Jonas Smedegaard <dr@jones.dk>
#
# $Id: localikiwikicreatesite,v 1.18 2008-06-03 11:20:16 jonas Exp $
#
# Initialize ikiwiki site
#
# Origin: http://ikiwiki.info/setup/
#
# TODO: Explicitly replace ~ with $HOME for shell use, and $ENV{'HOME'} for Perl
# TODO: Quote variables everywhere
# TODO: Implement --verbose and --quiet options (and fail if both are set)
# TODO: Implement --force option
# TODO: check for required helper commands (like make for originated sites)
# TODO: when using origin, do these replacements on all setup files:
# 	  s/samplewiki/$project/
# 	  s/wiki\.example\.org/$host/
# 	  s/git\.example\.org/$srchost/
# 	and when --wikiname, --adminemail and --user is implemented:
# 	  s/MyWiki/$wikiname/
# 	  s/me\@example\.org/$adminemail/
# 	  s/\bme\b/$user/
# TODO: Print a final note on missing tasks:
# 	* setup webserver
# 	* setup Git server
# 	* include Git hooks in main post-update hook of *_content Git
# 	* enable Git post-update hooks of all Gits
# 	* add git-daemon-export-ok file in all Gits
# 	* write proper description in all Gits
#

set -e

PRG=$(basename "$0")

TEMP=$(getopt -s sh -o d:s:o:b:fh -l domain:,srcdomain:,origin:,branch:,force,help -n "$PRG" -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"

if [ -f /etc/local/ikiwiki.cfg ]; then
	. /etc/local/ikiwiki.cfg
fi

gitsrcuri="http://source.jones.dk"
domain="${domain:-example.com}"
srcdomain="$srcdomain"
origin="$origin"
branch="$branch"
force="$force"

showhelp() {
	cat <<EOF
Usage:   $PRG [opts...] PROJECT [HOST [SRCHOST]]

Options:
  -d, --domain               If HOST is a FQDN: Ignore the DOMAIN value.
                             If HOST is a shortname: FQDN is HOST.DOMAIN.
                             If HOST is omitted: FQDN is PROJECT.DOMAIN.
                             (default: $domain)
  -s, --srcdomain            If SRCHOST is a FQDN: Ignore the SRCDOMAIN value.
                             If SRCHOST is a shortname: FQDN is HOST.SRCDOMAIN.
                             If SRCHOST is omitted: FQDN is source.SRCDOMAIN.
                             (default: ${srcdomain:-$domain without any leading www.})
  -o, --origin               Clone Git repositories from origin as skeleton
                             (default: ${origin:-do not clone.})
  -b, --branch               Use this branch of cloned Git repository
                             (default: ${branch:-Git default})
  -f, --force                Update without asking for confirmation
  -h, --help                 This help text

Examples:
    $PRG -d example.com wiki
    $PRG mysite www.example.com source.example.org
    $PRG -d example.com -o git://source.jones.dk/ikiwiki -b clean wiki
EOF
}

exit1()	{
	echo >&2 "Error: $1"
	echo >&2 "Exiting..."
	exit 1
}

ask() {
	echo -n "$1 (y/N)? "
	read response
	case "$response" in
	    y|Y)
		:
		;;
	    *)
		return 1
		;;
	esac
	return
}

git_setenv_work() {
	set -e

	targetdir="$1"
	shift

	export GIT_WORK_TREE="$targetdir"
	export GIT_DIR="$targetdir/.git"
}

git_unsetenv() {
	unset GIT_WORK_TREE
	unset GIT_DIR
}

git_init_pub() {
	repo="$DESTSRCDIR/$1.git"

#	export GIT_WORK_TREE="$repo"
	export GIT_DIR="$repo"

	mkdir -p $repo
	git --bare init --shared

	chmod +x $repo/hooks/post-update

	git_unsetenv
}

git_init_work() {
	set -e

	repo="$DESTSRCDIR/$1.git"
	targetdir="$2"
	shift 2

	git_setenv_work "$targetdir"

	git init
	for ignore in "$@"; do
		echo "$1" >> $targetdir/.gitignore
	done
	git add .
	git commit -m "initial commit"
	git remote add origin $repo
	git config branch.master.remote origin
	git config branch.master.merge refs/heads/master
	git push --all

	git_unsetenv
}

while true ; do
	case "$1" in
		-d|--domain) domain="$2"; shift 2;;
		-s|--srcdomain) srcdomain="$2"; shift 2;;
		-o|--origin) origin="$2"; shift 2;;
		-b|--branch) branch="$2"; shift 2;;
		-f|--force) force="1"; shift;;
		-h|--help) showhelp; exit 0;;
		--) shift; break;;
		*) exit1 "Internal error!";;
	esac
done

# Resolve FQDNs
project="$1"
[ -n "$project" ] || exit1 "Project name must be supplied!"
host="$2"
[ -n "$host" ] || host="$project"
echo "$host" | grep -q '\.' || host="$host.$domain"
srchost="$3"
[ -n "$srchost" ] || srchost="source"
if echo "$srchost" | grep -qv '\.'; then
	[ -n "$srcdomain" ] || srcdomain=$(echo "$host" | sed 's/^www\.//')
	srchost="$srchost.$srcdomain"
fi

SRCDIR=~/private_webdata/$project/content
CFGDIR=~/private_webdata/$project
DESTDIR=~/public_websites/$host
DESTSRCDIR=~/public_websites/$srchost

# Create parent if missing / remove if already existing
# (eg. leave out SRCDIR if subdir of CFGDIR)
mkdirchildlist="CFGDIR DESTDIR DESTSRCDIR"
rmdirlist="CFGDIR DESTDIR DESTSRCDIR"

mkdirchildfoundlist=''
for item in $mkdirchildlist; do
	eval "childdir=\"\$$item\""
	parentdir="`dirname "$childdir"`"
	if [ ! -d "$parentdir" ]; then
		eval "mkdirchild_$item='(will create parent dir)'"
		mkdirchildfoundlist="$mkdirchildfoundlist $item"
	fi
done

rmdirfoundlist=''
for item in $rmdirlist; do
	eval "dir=\"\$$item\""
	if [ -d "$dir" ]; then
		eval "rmdir_$item='(will replace existing!)'"
		rmdirfoundlist="$rmdirfoundlist $item"
	fi
done

if [ "$quiet" != 'yes' ]; then
	cat <<EOF
You chose to make an iki site with the following settings:

Project:    $project
Host:       $host
Sourcehost: $srchost

The following directories will be added to your system:

Project dir: $CFGDIR $rmdir_CFGDIR$mkdir_CFGDIR
Content dir: $SRCDIR $rmdir_SRCDIR$mkdir_SRCDIR
Central Git: $DESTSRCDIR $rmdir_DESTSRCDIR$mkdir_DESTSRCDIR
Website dir: $DESTDIR $rmdir_DESTDIR$mkdir_DESTDIR
    
EOF
fi

if [ -n "$mkdirchildfoundlist$rmdirfoundlist" ] || [ "$verbose" = 'yes' ]; then
	[ "$force" = 'yes' ] \
	  || ask 'Continue' \
	  || exit1 "User aborted (no changes made)"
fi

for item in $mkdirchildfoundlist; do
	eval "childdir=\"\$$item\""
	parentdir="`dirname "$childdir"`"
	if [ ! -d "$parentdir" ]; then
		mkdir -p "$parentdir"
	fi
done

for item in $rmdirfoundlist; do
	eval "dir=\"\$$item\""
	if [ -d "$dir" ]; then
		rm -rf "$dir"
	fi
done

## Init public RCS repositories
git_init_pub "$project"
git_init_pub "${project}_content"

## Create initial basedir
if [ -n "$origin" ]; then
	git clone $origin $CFGDIR
	git_setenv_work "$CFGDIR"
	if [ -n "$branch" ]; then
		git branch -f "$branch" "origin/$branch"
		# git checkout ignores GIT_DIR and GIT_WORKING_TREE
		( cd "$CFGDIR" && git checkout "$branch" )
	fi
	git config remote.origin.url "$DESTSRCDIR/$project.git"
	git push --all
	git_unsetenv
else
	mkdir -p $CFGDIR
	cp /usr/share/doc/ikiwiki/html/ikiwiki.setup $CFGDIR
fi

## Init working RCS repositories
if [ -n "$origin" ]; then
	make -C $CFGDIR
else
	git_init_work "$project" "$CFGDIR" "/content"
fi

## Create initial content
if ! [ -e $SRCDIR ]; then
	mkdir -p $SRCDIR
	cat <<'EOF' >$SRCDIR/index.mdwn
Welcome to your new wiki.
All wikis are supposed to have a <a href="../sandbox/">SandBox</a>,
so this one does too.
----
This wiki is powered by [ikiwiki](http://ikiwiki.info).
EOF
git_init_work "${project}_content" "$SRCDIR" "/.ikiwiki"
fi

## Adjust backend to actual paths, and enable Git post-update wrapper
git_setenv_work "$CFGDIR"
# TODO: Rewrite as semi-generic functions:
#perl_param_enable_set "$CFGDIR/ikiwiki.setup" "srcdir" "$SRCDIR"
#perl_param_enable_set "$CFGDIR/ikiwiki.setup" "destdir" "$DESTDIR"
#perl_param_enable_set "$CFGDIR/ikiwiki.setup" "url" "$host"
#perl_param_disable "$CFGDIR/ikiwiki.setup" "cgiurl"
#perl_param_match_enable "$CFGDIR/ikiwiki.setup" "rcs" "git"
#perl_section_match_enable_param_set "$CFGDIR/ikiwiki.setup" "git post-update wrapper" "wrapper" "$DESTSRCDIR/${project}_content.git/hooks/post-update-$project"
#perl_section_match_enable_param_match_enable "$CFGDIR/ikiwiki.setup" "git post-update wrapper" "wrappermode" "06755"
#shell_line_match_enable_appendlinebefore "$DESTSRCDIR/${project}_content.git/hooks/post-update" "^exec" "hooks/post-update-$project\n"
for cfgfile in $CFGDIR/ikiwiki*.setup; do
	perl -pi -e "
		s,samplewiki,$project,g;
		s,wiki\.example\.org,$host,g;
		s,git\.example\.org,$srchost,g;
		s,^(\s*)#?(rcs\s*=>\s*')[^']*('\,\s*),\$1\$2git\$3,;
		s,^(\s*)#?(git_wrapper\s*=>),\$1\$2,;
		s,^(\s*)#?(git_wrappermode\s*=>),\$1\$2,;
		" "$cfgfile"
	git add "$(basename "$cfgfile")"
done
if [ -n "$origin" ]; then
	git commit -m "Adjust config files to use actual paths, and enable Git hooks."
else
	perl -pi -e "
		s,^(\s*)#?(srcdir\s*=>\s*')[^']*('\,\s*),\$1\$2$SRCDIR\$3,;
		s,^(\s*)#?(destdir\s*=>\s*')[^']*('\,\s*),\$1\$2$DESTDIR\$3,;
		s,^(\s*)#?(url\s*=>\s*')[^']*('\,\s*),\$1\$2http://$host\$3,;
		s,^(\s*)#?(cgiurl\s*=>\s*')([^']*)('\,\s*),\$1#\$2\$3\$4,;
		s,^(\s*)#?(git_wrapper\s*=>\s*')([^']*)('\,\s*),\$1\$2$DESTSRCDIR/${project}_content.git/hooks/post-update-ikiwiki\$4,;
		" $CFGDIR/ikiwiki.setup
	git add ikiwiki.setup
	git commit -m "Add customized config file with Git hook enabled."
fi
git_unsetenv
# Attach hooks to general post-update hook
# FIXME: Attach localized before master configs, and admin before non-admin
for hookfile in $DESTSRCDIR/${project}_content.git/hooks/post-update-*; do
	perl -0 -pi -e "
		s,\n(exec\s[^\n]*),\nhooks/$(basename "$hookfile")\n\n\$1,;
		" $DESTSRCDIR/${project}_content.git/hooks/post-update
done

## Add Makefile for further customization
if ! [ -e $CFGDIR/Makefile ]; then
	cat <<EOF >$CFGDIR/Makefile
#underlays = basewiki smiley templates
underlays = templates
#locale = danish
master = master
#master = master-LOCALE

all: \$(underlays)

\$(underlays):
	mkdir \$@
	cd \$@ && git init
	cd \$@ && git remote add -f -t \$(master:LOCALE=\$(locale)) -m \$(master:LOCALE=\$(locale)) origin $gitsrcuri/${project}_\$@.git
	cd \$@ && git merge origin
EOF
	git_setenv_work "$CFGDIR"
	git add Makefile
	git commit -m "Setup local paths"
	git_unsetenv
fi

if [ -n "$origin" ]; then
	make -C $CFGDIR install
else
	ikiwiki --verbose $SRCDIR $DESTDIR --url=http://"$host" --setup $CFGDIR/ikiwiki.setup
fi
git_setenv_work "$CFGDIR"
git push
git_unsetenv