sbrs

Simple blog and RSS system
git clone git://git.christosmarg.xyz/sbrs.git
Log | Files | Refs | README | LICENSE

commit 1d1b53389777d2d392c9000bf3963d0912c14958
parent f90e439701e070fa8919538107eefa15171d6751
Author: Christos Margiolis <christos@margiolis.net>
Date:   Tue, 17 Nov 2020 17:52:27 +0200

code cleanup and a few more elegant solutions; pending revision fix

Diffstat:
MREADME | 4++--
Msbrs | 235+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msbrs.1 | 42++++++++++++++++++++++++++++++++----------
3 files changed, 157 insertions(+), 124 deletions(-)

diff --git a/README b/README @@ -8,8 +8,8 @@ Inside the 'sbrs' script, change the 'WEBSITE' and 'AUTHOR' variables to match your website's URL and your name. In case you don't edit the script further, the structure of your -website's directory should look like this. The -files below need to exist with the same names. +website's directory should look like this. The files below need +to exist with the same names. ├── blog | └── here reside your blog posts diff --git a/sbrs b/sbrs @@ -9,169 +9,180 @@ BLOGINDEX="blogindex.html" INDEX="index.html" RSSFILE="rss.xml" TEMPLATE="template.html" -[ -z "$EDITOR" ] && EDITOR="vim" +test -z "$EDITOR" && EDITOR="vim" main() { - missing_check + test -f "$TEMPLATE" || err "$TEMPLATE: missing file" + test -f "$BLOGINDEX" || err "$BLOGINDEX: missing file" + test -f "$INDEX" || err "$INDEX: missing file" + test -f "$RSSFILE" || err "$RSSFILE: missing file" + + if ! test -d "$BLOGDIR"; then + confirmact "Blog directory doesn't exist. Intialize it here (y/n)? " "y" + mkdir -pv "$BLOGDIR" + fi + case $1 in -n*) newpost ;; - -p*) listposts $DRAFTDIR && publish ;; - -e*) listposts $DRAFTDIR && $EDITOR $DRAFTDIR/$blogpost.html ;; - -v*) listposts $DRAFTDIR && view ;; - -t*) listposts $DRAFTDIR && delete $DRAFTDIR ;; - -r*) listposts $BLOGDIR && $EDITOR $BLOGDIR/$blogpost.html ;; - -c*) listposts $BLOGDIR && title_change ;; - -o*) listposts $BLOGDIR && $BROWSER $BLOGDIR/$blogpost.html ;; - -d*) listposts $BLOGDIR && delete $BLOGDIR ;; - -l*) listposts $BLOGDIR ;; + -p*) listposts "$DRAFTDIR" && publish ;; + -e*) listposts "$DRAFTDIR" && $EDITOR "$DRAFTDIR/$blogpost.html" ;; + -v*) listposts "$DRAFTDIR" && view ;; + -t*) listposts "$DRAFTDIR" && delete "$DRAFTDIR" ;; + # edit rss too + -r*) listposts "$BLOGDIR" && $EDITOR "$BLOGDIR/$blogpost.html" ;; + -c*) listposts "$BLOGDIR" && titlechange ;; + -o*) listposts "$BLOGDIR" && $BROWSER "$BLOGDIR/$blogpost.html" ;; + -d*) listposts "$BLOGDIR" && delete "$BLOGDIR" ;; + -l*) listposts "$BLOGDIR" ;; *) usage ;; esac } -list() { - find $1 -type f -name '*\.html' ! -name '*final*' 2> /dev/null | awk -F/ '{print $NF}' +err() { + echo "${0##*/}: $@" && exit +} +psed() { + sed -i.orig "$@" && rm *.orig "$BLOGDIR/*.orig" 2> /dev/null } -listposts() { - printf "Listing posts in %s (total: %d)\n" "$1" "$(list $1 | wc -l)" - nposts=$(expr $(list $1 | wc -l)) - list $1 | nl - [ $nposts -eq 0 ] && echo "No posts available in $1" && exit - read -erp "Choose a post to by number: " num \ - && [ -z "$(echo $num | grep -E "^[0-9]+$" | grep -v "^0")" ] \ - || [ $(expr $num) -gt $nposts ] || [ -z "$num" ] \ - && echo "No post selected." && exit - blogpost=$(list $1 | nl | grep -w "$num" | awk '{print $2}') - blogpost=$(basename -s ".html" $blogpost) +confirmact() { + read -erp "$1" act && test "$act" = "$2" || exit } -newpost() { - mkdir -p $DRAFTDIR - read -erp "Title: " title && [ -z "$title" ] && - echo "Please specify a title." && exit - blogpost=$(title_fmt "$title") - [ -f "$BLOGDIR/$blogpost.html" ] && echo "File exists already." && exit - $EDITOR "$DRAFTDIR/$blogpost.html" - sed "s/TITLE/$title/g;s/HEADER/$title/g;s/AUTHOR/$AUTHOR/g;" $TEMPLATE \ - > $DRAFTDIR/$blogpost.final.html +list() { + find "$1" -type f -name '*\.html' ! -name '*\.final*' 2> /dev/null | + awk -F/ '{print $NF}' } -confirm_action() { - read -erp "$1" confirm && [ "$confirm" = "$2" ] || exit +listposts() { + printf "Listing posts in: %s (total: %d)\n" "$1" "$(list "$1" | wc -l)" + list "$1" | nl + nposts=$(expr $(list "$1" | wc -l)) + test $nposts -eq 0 && err "no posts available in: $1" + read -erp "Choose a post to by number: " num + if ! test -z "$(echo $num | grep -E "^[0-9]+$" | grep -v "^0")"; then + test $(expr $num) -gt $nposts && err "no post selected" + blogpost=$(list "$1" | nl | grep -w "$num" | awk '{print $2}') + blogpost=${blogpost%.*} + else + err "no post selected" + fi } -psed() { - sed -i.orig "$@" && rm *.orig $BLOGDIR/*.orig 2> /dev/null +newpost() { + mkdir -p "$DRAFTDIR" + read -erp "Title: " title + test -z "$title" && err "please specify a title" + blogpost=$(titlefmt "$title") + test -f "$BLOGDIR/$blogpost.html" && err "file exists already" + $EDITOR "$DRAFTDIR/$blogpost.html" + sed "s/TITLE/$title/g;s/HEADER/$title/g;s/AUTHOR/$AUTHOR/g;" "$TEMPLATE" \ + > "$DRAFTDIR/$blogpost.final.html" } -title_fmt() { - echo "$1" | iconv -cf UTF-8 -t ASCII//TRANSLIT | \ +titlefmt() { + echo "$1" | iconv -cf UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' } -title_get() { - grep "<title>" $1 | sed "s/<title>//;s/<\/title>//;s/ *//;" +titleget() { + grep "<title>" "$1" | sed "s/<title>//;s/<\/title>//;s/ *//;" } -publish() { - confirm_action "Publish post (y/N)? " "y" - title=$(title_get $DRAFTDIR/$blogpost.final.html) - psed "s/^/\ \ \ \ \ \ \ \ /" $DRAFTDIR/$blogpost.html # bad? - psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.html" $DRAFTDIR/$blogpost.final.html - sed "s/</\&lt;/g;s/>/\&gt;/g;" "$DRAFTDIR/$blogpost.html" > $DRAFTDIR/$blogpost.xml - cp $DRAFTDIR/$blogpost.final.html $BLOGDIR && - mv $BLOGDIR/$blogpost.final.html $BLOGDIR/$blogpost.html +titlechange() { + read -erp "Give post a new title: " newtitle + confirmact "Are you sure (y/N)? " "y" + oldtitle=$(titleget "$BLOGDIR/$blogpost.html") + newtitlefmt=$(titlefmt "$newtitle") + test -f "$BLOGDIR/$newtitlefmt.html" && err "file exists already" - printf "\t\t<li>%s &ndash; <a href=\"%s\">%s</a></li>\n" \ - "$(date '+%Y %b %d')" "$BLOGDIR/$blogpost.html" "$title" | \ - expand -t8 > $DRAFTDIR/$blogpost.final-htmlentry + psed "s/$blogpost/$newtitlefmt/g;s/$oldtitle/$newtitle/g" \ + "$BLOGDIR/$blogpost.html" "$INDEX" "$BLOGINDEX" "$RSSFILE" && + mv "$BLOGDIR/$blogpost.html" "$BLOGDIR/$newtitlefmt.html" && + echo "Title changed successfully: $oldtitle -> $newtitle" +} - printf "<\!--BEGIN $blogpost-->\n<item>\n\t<title>%s</title>\n\t<guid>%s</guid>\n\t<pubDate>%s</pubDate>\n\t<description>\n\t%s\n\t</description>\n</item>\n<\!--END $blogpost-->\n" "$title" "$WEBSITE/$BLOGDIR/$blogpost.html" "$(date '+%a, %d %b %Y')" "$(cat $DRAFTDIR/$blogpost.xml)" | expand -t8 > $DRAFTDIR/$blogpost.final-rssentry +rmcontents() { + # replace with find(1) + ls "$1" | grep -x "$blogpost\...*" | sed "s/^/$1\//" | xargs rm +} - # using || because of psed - blogindex_update - psed "/<\!--BLOG $(date '+%B %Y')-->/r $DRAFTDIR/$blogpost.final-htmlentry" $BLOGINDEX || - echo "Blogindex... done." - psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.final-htmlentry" $INDEX || echo "Index... done" - psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.final-rssentry" $RSSFILE || echo "RSS... done" - remove_last_index_entry || echo "Removing last entry from index file... done" - remove_contents $DRAFTDIR && echo "Cleaning up $DRAFTDIR... done" - echo "Published $blogpost." +rmlastentry() { + indexentries=$(sed "1,/<\!--BLOG-->/d" "$INDEX" | grep "<li>") + if test $(expr $(echo "$indexentries" | wc -l)) -gt 7; then + lastentry=$(echo "$indexentries" | tail -2 | head -1) && + psed "s|$lastentry||;" "$INDEX" + fi } -blogindex_update() { +blogindexupdate() { dateid=$(date '+%b %Y' | sed 's/\ //' | tr '[:upper:]' '[:lower:]') - if [ -z "$(grep "$dateid" $BLOGINDEX)" ]; then + if test -z "$(grep "$dateid" "$BLOGINDEX")"; then datename=$(date '+%B %Y') psed "/<\!--BLOG-->/a\\ \ <h2 id=\\"$dateid\\">$datename<\\/h2> \\ \ <ul> \\ \ <\\!--BLOG $datename--> \\ \ <\\/ul> \\ -" $BLOGINDEX +" "$BLOGINDEX" fi } -remove_last_index_entry() { - indexentries=$(sed "1,/<\!--BLOG-->/d" $INDEX | grep "<li>") && - [ $(expr $(echo "$indexentries" | wc -l)) -gt 7 ] && - lastentry=$(echo "$indexentries" | tail -2 | head -1) && - psed "s|$lastentry||;" $INDEX +view() { + cat "$DRAFTDIR/$blogpost.final.html" > "$DRAFTDIR/$blogpost.final-view.html" + psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.html" "$DRAFTDIR/$blogpost.final-view.html" + $BROWSER "$DRAFTDIR/$blogpost.final-view.html" } delete() { - confirm_action "Are you sure you want to delete \"$blogpost\" (y/N)? " "y" - if [ "$1" = "$BLOGDIR" ]; then - psed "/$blogpost/d" $INDEX $BLOGINDEX - psed "/<\!--BEGIN $blogpost-->/,/<\!--END $blogpost-->/d" $RSSFILE + confirmact "Are you sure you want to delete \"$blogpost\" (y/N)? " "y" + if test "$1" = "$BLOGDIR"; then + psed "/$blogpost/d" "$INDEX" "$BLOGINDEX" + psed "/<\!--BEGIN $blogpost-->/,/<\!--END $blogpost-->/d" "$RSSFILE" fi - remove_contents $1 && echo "Removed $blogpost." -} - -remove_contents() { - ls $1 | grep -x "$blogpost\...*" | sed "s/^/$1\//" | xargs rm -} - -view() { - cat $DRAFTDIR/$blogpost.final.html > $DRAFTDIR/$blogpost.final-view.html - title=$(title_get $DRAFTDIR/$blogpost.final-view.html) - psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.html" $DRAFTDIR/$blogpost.final-view.html - $BROWSER $DRAFTDIR/$blogpost.final-view.html + rmcontents "$1" && echo "Removed $blogpost." } -title_change() { - read -erp "Give post a new title: " newtitle && \ - confirm_action "Are you sure (y/N)? " "y" - oldtitle=$(title_get $BLOGDIR/$blogpost.html) - newtitle_fmt=$(title_fmt "$newtitle") - [ -f "$BLOGDIR/$newtitle_fmt.html" ] && echo "File exists already." && exit +publish() { + confirmact "Publish post (y/N)? " "y" + title=$(titleget "$DRAFTDIR/$blogpost.final.html") + # bad? + psed "s/^/\ \ \ \ \ \ \ \ /" "$DRAFTDIR/$blogpost.html" + psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.html" "$DRAFTDIR/$blogpost.final.html" + sed "s/</\&lt;/g;s/>/\&gt;/g;" "$DRAFTDIR/$blogpost.html" > "$DRAFTDIR/$blogpost.xml" + cp "$DRAFTDIR/$blogpost.final.html" "$BLOGDIR/$blogpost.html" - psed "s/$blogpost/$newtitle_fmt/g;s/$oldtitle/$newtitle/g" \ - $BLOGDIR/$blogpost.html $INDEX $BLOGINDEX $RSSFILE && - mv $BLOGDIR/$blogpost.html $BLOGDIR/$newtitle_fmt.html && - echo "Title changed successfully: $oldtitle -> $newtitle" -} + printf "\t\t<li>%s &ndash; <a href=\"%s\">%s</a></li>\n" \ + "$(date '+%Y %b %d')" "$BLOGDIR/$blogpost.html" "$title" | + expand -t8 > "$DRAFTDIR/$blogpost.final-htmlentry" + rsscreate | expand -t8 > "$DRAFTDIR/$blogpost.final-rssentry" -err() { - echo "$1 file is missing. . ." - missing=true + # using || because of psed + blogindexupdate + psed "/<\!--BLOG $(date '+%B %Y')-->/r $DRAFTDIR/$blogpost.final-htmlentry" \ + "$BLOGINDEX" || echo "Blogindex... done." + psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.final-htmlentry" "$INDEX" || echo "Index... done" + psed "/<\!--BLOG-->/r $DRAFTDIR/$blogpost.final-rssentry" "$RSSFILE" || echo "RSS... done" + rmlastentry || echo "Removing last entry from index file... done" + rmcontents "$DRAFTDIR" && echo "Cleaning up $DRAFTDIR... done" + echo "Published $blogpost." } -missing_check() { - missing=false - [ -f "$TEMPLATE" ] || err $TEMPLATE - [ -f "$INDEX" ] || err $INDEX - [ -f "$RSSFILE" ] || err $RSSFILE - [ -f "$BLOGINDEX" ] || err $BLOGINDEX - [ "$missing" = "true" ] && exit - - [ ! -d "$BLOGDIR" ] && - confirm_action "Blog directory doesn't exist. Intialize it here (y/n)? " "y" && - mkdir -pv $BLOGDIR +rsscreate() { + printf "<!--BEGIN %s-->\n" "$blogpost" + printf "<item>" + printf "\t<title>%s</title>\n" "$title" + printf "\t<guid>%s</guid>\n" "$WEBSITE/$BLOGDIR/$blogpost.html" + printf "\t<pubDate>%s</pubDate>\n" "$(date '+%a, %d %b %Y')" + printf "\t<description>\n" + printf "%s\n" "$(cat "$DRAFTDIR/$blogpost.xml")" + printf "\t</description>\n" + printf "</item>\n" + printf "<!--END %s-->\n" "$blogpost" } usage() { - printf "Usage: sbrs [OPTION]\n\n" + printf "Usage: ${0##*/} [OPTION]\n\n" printf "Options:\n" printf " -n\t\tNew post\n" printf " -p\t\tPublish draft post\n" diff --git a/sbrs.1 b/sbrs.1 @@ -23,25 +23,47 @@ generates a blog post page and RSS feed. The options are as follows: .Bl -tag -width Ds .It Fl n Ar new post -Prompt the user to write a new blog post in HTML +Prompt the user to write a new blog post in HTML. +.Nm +will open the editor set in the 'EDITOR' environmental variable. In case +it is not set, it will set 'EDITOR' +.Xr Vim 1 .It Fl p Ar publish draft -Publish one of the posts stored in the .drafts directory. +Publish one of the posts stored in the '.drafts' directory. After a post +has been published, it will be deleted from '.drafts' along with all the +files related to it and will be moved to 'blog'. This option also automatically +creates an RSS feed for the post. .It Fl e Ar edit draft -Edit one of the posts stored in the .drafts directory. +Edit one of the posts stored in the '.drafts' directory. .It Fl v Ar view draft -View one of the posts stored in the .drafts directory. +View one of the posts stored in the '.drafts' directory. +.Nm +creates a temporary file that looks like the finished post, but is deleted +after +.Nm +has exited. The 'BROWSER' environmental variable has to be set +in order for this option to work. .It Fl t Ar delete draft -Delete one of the posts stored in the .drafts directory. +Delete one of the posts stored in the '.drafts' directory. .It Fl r Ar revise published -Revise a published post stored in the blog directory. +Revise a published post stored in the 'blog' directory. After you're done +revising you don't need to re-publish the post again, since +.Nm +edits the post directly. .It Fl c Ar change title -Change a published post's title. +Change a published post's title. This option will replace every instance +of the old title with the new one both. The filename will also +be changed. .It Fl o Ar view published -View a published post stored in the blog directory on your default browser. +View a published post stored in the 'blog' directory on your default browser. +Same with the -v option; the 'BROWSER' environmental variable has to be set +in order for this option to work. .It Fl d Ar delete published -Delete a published post stored in the blog directory. +Delete a published post stored in the 'blog' directory. This option will delete +the post from the blog's index file, the RSS feed, as well as the main page in +case it is found there too. .It Fl l Ar list all published -List all posts stored in the blog directory. +List all posts stored in the 'blog' directory. .El .Pp Only one option at a time can be used.