diff --git a/.gitignore b/.gitignore index 1fcb152..5a1588d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ out +draft diff --git a/README.md b/README.md index 1d8190a..32b24f2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ 1. Give it a title: `./baker post I love pizza so much!` This command will create a markdown file that has the slug `i-love-pizza-so-much` in the `post` directory. If the `$EDITOR` environment variable is set, it will open up the post markdown file with the editor. -2. Change `draft` from `true` to `false` to publish the post. +2. Change `draft` from `true` to `false` to publish the post (or use the `./baker toggle id`). 3. Bake all posts: `./baker bake` diff --git a/baker b/baker index bf1f5a3..3289d9f 100755 --- a/baker +++ b/baker @@ -3,31 +3,42 @@ # # config # - -# POST_DIR stores all post markdown files -readonly POST_DIR=post - -# OUTPUT_DIR stores all compiled html -readonly OUTPUT_DIR=out - -# LAYOUT_DIR stores all layout markdown files -readonly LAYOUT_DIR=layout - -# PUBLIC_DIR stores css and static images -readonly PUBLIC_DIR=public - -# site -readonly SITE_NAME='a baker blog' -readonly SITE_DESC='written in bash' -readonly DISQUS='bakerbash' - -# author -readonly AUTHOR_NAME='baker' -readonly AUTHOR_DESC='a very-experienced bread baker, who also loves planting trees.' -readonly AUTHOR_EMAIL='email@example.org' -readonly AUTHOR_EMAIL_HASH="$(md5sum <<< "$AUTHOR_NAME" | awk '{ print $1 }')" -readonly AUTHOR_TWITTER='twitter' -readonly AUTHOR_GITHUB='github' +config() { + if [ -f "${1:-}" ]; then + . "${1}" + fi + # POST_DIR stores all post markdown files + export POST_DIR="${POST_DIR:=post}" + + # OUTPUT_DIR stores all compiled html + export OUTPUT_DIR="${OUTPUT_DIR:=out}" + + # DRAFT_DIR stores all compiled html + export DRAFT_DIR="${DRAFT_DIR:=draft}" + + # LAYOUT_DIR stores all layout markdown files + export LAYOUT_DIR="${LAYOUT_DIR:=layout}" + + # PUBLIC_DIR stores css and static images + export PUBLIC_DIR="${PUBLIC_DIR:=public}" + + # site + export SITE_NAME="${SITE_NAME:=a baker blog}" + export SITE_DESC="${SITE_DESC:=written in bash}" + export DISQUS="${DISQUS:=bakerbash}" + + # author + export AUTHOR_NAME="${AUTHOR_NAME:=baker}" + export AUTHOR_DESC="${AUTHOR_DESC:=a very-experienced bread baker, who also loves planting trees.}" + export AUTHOR_EMAIL="${AUTHOR_EMAIL:=email@example.org}" + export AUTHOR_EMAIL_HASH="${AUTHOR_EMAIL_HASH:=$(md5sum <<< "$AUTHOR_NAME" | awk '{ print $1 }')}" + export AUTHOR_TWITTER="${AUTHOR_TWITTER:=twitter}" + export AUTHOR_GITHUB="${AUTHOR_GITHUB:=github}" + + # categories + export TAGS_BASELIST="${TAGS_BASELIST:=mylife,internet,baker}" + export TAGS_LINK="${TAGS_LINK:=
  •  ==tagName==
  • }" +} # # helper @@ -56,7 +67,7 @@ body() { # slug creates a friendly URL like 'hello-world' slug() { - tr -cs '[:alnum:]\n' - | tr '[:upper:]' '[:lower:]' | sed 's|^-*||;s|-*$||' + iconv -f utf8 -t ascii//TRANSLIT | tr -cs '[:alnum:]\n' - | tr '[:upper:]' '[:lower:]' | sed 's|^-*||;s|-*$||' } # @@ -147,6 +158,8 @@ render_if() { local lines local line readarray -t lines + # The following regexp should be a little amended to allow more than one space|tab + # between the @if and id (AND after) [[ "${lines[0]}" =~ ^@if\ ('!')?($VAR_ID)$ ]] || return 1 # skip render on undefined_var or !defined_var @@ -155,7 +168,6 @@ render_if() { else [[ "${BASH_REMATCH[1]}" == '!' ]] || return 0 fi - # recursively render the inner block for line in "${lines[@]:1:${#lines[@]}-2}"; do echo "$line" @@ -263,66 +275,213 @@ render_file() { echo "$content" } -# -# usage -# -usage() { - cat <<-EOF - baker - post [title] draft a post - bake ship all posts - EOF - exit 1 +# post_toggle toggles the status of a post (either use ID or post name) +post_toggle() { + local _fname='' + if [ -z "${1:-}" ]; then + usage + elif [ -z "${1//[0-9]/}" ]; then + readarray -t posts < <(find "$POST_DIR" -name '*.md' | sort -r) + if [ ${1} -lt ${#posts[@]} ]; then + _fname="${posts[${1}]}" + fi + elif [ -f "${POST_DIR}/${1}" -o -f "${POST_DIR}/${1}.md" ]; then + _fname="${POST_DIR}/${1//\.md/}.md" + fi + if [ -z "${_fname}" ]; then + echo "This post doesn't exist" + exit 1 + fi + if [ "$(header draft < "${_fname}")" == 'false' ]; then + sed -i -e 's;^[[:space:]]*draft:[[:space:]]*false[[:space:]]*$;draft: true;g' "${_fname}" + else + sed -i -e 's;^[[:space:]]*draft:[[:space:]]*.*[[:space:]]*$;draft: false;g' "${_fname}" + fi } -(( $# == 0 )) && usage +# post_list list the id title and status of posts +post_list() { + local idx=0 + readarray -t posts < <(find "$POST_DIR" -name '*.md' | sort -r) + ( + echo "id title status" + for post in "${posts[@]}"; do + id="$(basename "$post" .md)" + # skip drafts + if [ "$(header draft < "$post")" == 'false' ]; then + echo "${idx} $id [Published]" + else + echo "${idx} $id [Draft]" + fi + ((idx++)) + done + ) | column -t +} -case "$1" in -bake) +# post_post creates a post (in the POST_DIR) with the minimum information +post_post() { + readonly title="${@}" + [[ "$title" ]] || usage + + mkdir -p "$POST_DIR" || ( echo "Can't create '$POST_DIR'" >&2; exit 1 ) + readonly post_file="$POST_DIR/$(date +%Y-%m-%d)-$(slug <<< "$title").md" + cat > "$post_file" <<-EOF + --- + title: $title + date: $(date -u +%FT%TZ) + tags: ${TAGS_BASELIST} + layout: post + draft: true + summary: + --- + EOF + + echo "$post_file" + [[ "$EDITOR" ]] && $EDITOR "$post_file" +} + +# post_bake it's where the magic start +post_bake() { + rm -rf "$DRAFT_DIR" + mkdir -p "$DRAFT_DIR" rm -rf "$OUTPUT_DIR" mkdir -p "$OUTPUT_DIR" [[ -d "$POST_DIR" ]] || usage [[ -d "$PUBLIC_DIR" ]] && cp -r "$PUBLIC_DIR"/. "$OUTPUT_DIR" + [[ -d "$PUBLIC_DIR" ]] && cp -r "$PUBLIC_DIR"/. "$DRAFT_DIR" + touch "$DRAFT_DIR/index.html" readarray -t posts < <(find "$POST_DIR" -name '*.md' | sort -r) - + echo "Rendering Posts..." >&2 idx=0 + id_all=0 + _allTags='' time for post in "${posts[@]}"; do + idx_prev=1;idx_next=1 id="$(basename "$post" .md)" # skip drafts - [[ "$(header draft < "$post")" == false ]] || continue + if [ "$(header draft < "$post")" != false ]; then + (( id_all++ )) + POST_PREV='index.html';POST_NEXT='index.html'; render_file "$post" > "$DRAFT_DIR/$id.html"; continue; + fi + if [ ${idx} -eq 0 ]; then # It's the first post, so the newer one is index + id_prev=0 + else # search for the previous post not draft + while [ -f "${posts[$((id_all-idx_prev))]}" -a "$(header draft < "${posts[$((id_all-idx_prev))]}" 2>/dev/null)" != false ]; do + (( idx_prev++ )) + if [ ${idx_prev} -gt ${id_all} ]; then idx_prev=${id_all}; break; fi + done 2>/dev/null + fi + # look for the next older post not draft + while [ -f "${posts[$((id_all+idx_next))]}" -a "$(header draft < "${posts[$((id_all+idx_next))]}" 2>/dev/null)" != false ]; do + (( idx_next++ )) + done 2>/dev/null - echo "$id" + if [ ${idx} -eq 0 -o $((idx_all-idx_prev)) -eq 0 -a ${id_all} -eq 0 ]; then + POST_PREV="index.html" + else + POST_PREV="$( basename "${posts[$((id_all-idx_prev))]}" )" + POST_PREV="${POST_PREV%*.md}.html" + fi + POST_NEXT="$( basename "${posts[$((id_all+idx_next))]}" )" + [[ -z "${POST_PREV:=}" ]] && POST_PREV='index.html' + [[ -z "${POST_NEXT:=}" ]] && POST_NEXT='index.md' + POST_NEXT="${POST_NEXT%*.md}.html" + # look for the tags + if [ ${__c:=0} -eq 0 ]; then # When implemented the -c will avoid tags generation + local _tag=''; TAGSLIST='' + tags="$(header tags < "$post")" + if [ ! -z "${tags}" ]; then + while read _tag; do + TAGSLIST="${TAGSLIST}${TAGS_LINK//==tagNameSlugged==/$(slug <<< "${_tag}")}" + TAGSLIST="${TAGSLIST//==tagName==/${_tag}}" + _allTags="${_allTags//${_tag},/}${_tag}," + done <<< "$(echo -e "${tags//,/\\n}")" + fi + else + unset TAGSLIST + unset tags + fi + # Render the post + echo "$idx - $idx_next - $id [Prev:${POST_PREV}][Next:${POST_NEXT}] [Tags:${tags}]" render_file "$post" > "$OUTPUT_DIR/$id.html" & declare "posts_${idx}_id"="$id" export_headers "$post" "posts_${idx}_" + POST_PREV="$id.html" + (( id_all++ )) (( idx++ )) done - + echo "Rendering Index..." >&2 render_file "$LAYOUT_DIR/index.md" > "$OUTPUT_DIR/index.html" & wait - ;; -post) - readonly title="${@:2}" - [[ "$title" ]] || usage + # Generate the indexes for each tags + if [ ${__c:=0} -eq 0 ]; then + echo "Rendering Index for categories..." >&2 + while read _tagIdx; do + # It's really important to unset all posts variables + while read a; do unset "${a}"; done <<< "$(compgen -v | grep -E "^posts_[0-9]")" + idx=0 + id_all=0 + for post in "${posts[@]}"; do + id="$(basename "$post" .md)" + if [ "$(header draft < "$post")" != false ]; then continue; fi + tags="$(header tags < "$post")" + if [ ! -z "${tags}" ]; then + while read _tag; do + if [ "${_tag}" = "${_tagIdx}" ] ; then + declare "posts_${idx}_id"="$id" + export_headers "$post" "posts_${idx}_" + (( idx++ )) + fi + done <<< "$(echo -e "${tags//,/\\n}")" + fi + done + render_file "$LAYOUT_DIR/index.md" > "$OUTPUT_DIR/index_$(slug <<< "${_tagIdx}").html" & + wait + done <<< "$(echo -e "${_allTags//,/\\n}")" + fi +} - mkdir -p "$POST_DIR" - readonly post_file="$POST_DIR/$(date +%Y-%m-%d)-$(slug <<< "$title").md" - cat > "$post_file" <<-EOF - --- - title: $title - date: $(date -u +%FT%TZ) - layout: post - draft: true - summary: - --- +# +# usage +# +usage() { + cat <<-EOF + baker + post [title] draft a post + bake ship all posts + list list all posts with state + toggle [post] toggle the draft status of a post EOF + exit 1 +} - echo "$post_file" - [[ "$EDITOR" ]] && $EDITOR "$post_file" +(( $# == 0 )) && usage + +config +readonly POST_DIR; readonly OUTPUT_DIR; readonly DRAFT_DIR; readonly LAYOUT_DIR; readonly PUBLIC_DIR +readonly SITE_NAME; readonly SITE_DESC +readonly AUTHOR_NAME; readonly AUTHOR_DESC; readonly AUTHOR_EMAIL; readonly AUTHOR_EMAIL_HASH; +readonly AUTHOR_TWITTER; readonly AUTHOR_GITHUB; readonly DISQUS +readonly TAGS_LINK; readonly TAGS_BASELINK + +case "$1" in +bake) + post_bake + ;; +post) + post_post "${@:2}" + ;; +list) + post_list + ;; +toggle) + post_toggle "${@:2}" ;; *) usage ;; esac + +# TODO : add flags management : -h help, -c no categories, -f configfile, -w workin this directory, -d change la date ... diff --git a/layout/post.md b/layout/post.md index 23f19d9..a098a65 100644 --- a/layout/post.md +++ b/layout/post.md @@ -12,6 +12,8 @@ @@ -22,6 +24,14 @@ {{ content }} +@if tags + +@end +