#!/usr/bin/zsh # # Quick Site Generator 2 is a static website generator inspired by Nikola. # It is written for the Z shell (zsh) because that's what I use and also because I like it better than Bash. # # This script is an almost complete rewrite of my old script because it became overly complicated and # had way too many bugs, even though it worked on simple sites. # # https://github.com/kekePower/qsgen2/ # VERSION="0.3.3" # Mon-2024-02-05 QSGEN="Quick Site Generator 2" # Set to true or false # This will show debug information from every function in this script # You can also set debug=true in a single function if you want to debug only that specific one. globaldebug=false function include () { # This function is used to include other functions that will normally be in # ${HOME}/bin/include/ # Edit this path to reflect your installation local inc_file=${HOME}/bin/include/${1}.inc if [[ ! -f ${inc_file} ]]; then local inc_opt=$( echo ${1} | cut -d\/ -f2 ) echo "Supplied option \"${inc_opt}\" is not a valid include." else builtin source ${inc_file} ${2} fi } # Including some colors to the script include common/colors echo "${magenta}${blue_bg} ${QSGEN} ${end}${bold_white}${blue_bg}${VERSION} ${end}" _add_site() { local site_name="${1}" local project_dir="${2}" local sites_file="${HOME}/.config/qsgen2/sites.qsc" # Ensure the configuration directory and sites file exist mkdir -p "${HOME}/.config/qsgen2" touch "${sites_file}" # Check if the site already exists in the file if grep -q "^${site_name}|" "${sites_file}"; then echo "Site '$site_name' already exists." return 1 # Exit the function with an error status. fi # Validate project directory if [[ ! -d "${project_dir}" ]]; then echo "The specified project directory does not exist." return 1 # Exit the function with an error status. fi # Determine the new site number by counting existing lines local site_number=$(( $(wc -l < "${sites_file}") + 1 )) # Add the site to the sites file echo "${site_number}|${site_name}|${project_dir}" >> "${sites_file}" echo "Site '${site_name}' added successfully." } _list_sites() { local config_dir="${HOME}/.config/qsgen2" local sites_file="${config_dir}/sites.qsc" # Ensure the configuration directory exists; create it if it doesn't. # This might be more relevant in other parts of your script. mkdir -p "$config_dir" touch "${sites_file}" if [[ -f "$sites_file" ]]; then echo "Registered Sites:" while IFS='|' read -r site_name site_dir; do printf "${magenta}%-30s${end} - Directory: ${blue}%-50s${end}\n" "Site: $site_name" "$site_dir" done < "$sites_file" else echo "No sites registered." fi } function _version() { echo "${yellow}- Created by kekePower - 2018-$(strftime "%Y")${end}" echo "${yellow}- https://github.com/kekePower/qsgen2/${end}" echo "${yellow}- See '${1} help' for more information.${end}" exit } function _help() { echo "This is where I'll write the Help documentation." exit } if [[ "$1" == "version" || "$1" == "-v" || "$1" == "--version" ]]; then _version ${0:t} elif [[ "$1" == "help" || "$1" == "-h" || "$1" == "--help" ]]; then _help ${0:t} fi # Loading Zsh modules zmodload zsh/files zmodload zsh/datetime if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Check for, an source, the config file for this specific website if [[ -f $(pwd)/config ]]; then if (${debug}); then echo "${red}Config file found and sourced${end}\n- ${yellow} - $(pwd)/config${end}"; fi # CONFIG=$(pwd)/config builtin source $(pwd)/config else echo "${red}Cannot find configuration file.${end}" echo "${yellow} - Please create the file 'config' in your project directory.${end}" exit fi if (${debug}); then qsconfig=$( cat $(pwd)/config | grep -v \# | awk '{print substr($0, index($0, " ") + 1)}' ) echo "${red}Contents of Config file:${end}" for qslines in ${qsconfig} do echo "${yellow}${qslines}${end}" done fi # Define cache files for blogs and pages blog_cache_file="${project_dir}/.blog_cache" pages_cache_file="${project_dir}/.pages_cache" function _msg() { # Use this function to write out messages based on their type # Types are: std=green - info=yellow - debug=red other=bold_yellow # Usage: _msg "" case ${1} in std) echo "${yellow}${2}${end}" ;; info) echo "${green}${2}${end}" ;; debug) echo "${red}${2}${end}" ;; other) echo "${bold_yellow}${2}${end}" ;; sub) echo "${magenta}${2}${end}" ;; *) echo "${2}" ;; esac } case ${1} in add) if [[ $# -eq 3 ]]; then _add_site "$2" "$3" else echo "Usage: qsgen2 add \"Site Name\" \"/path/to/project\"" fi exit ;; list) _list_sites exit ;; force) _msg sub "- Forced Update: Generating Everything" : >| "$blog_cache_file" # Truncate the blog cache before doing update : >| "$pages_cache_file" # Truncate the page cache before doing update ;; *) #echo "Usage: qsgen2 [command]" ;; esac # Let's check if qsgen2 can generate this site by checking if 'generator' is available if [[ ! ${generator} ]] || [[ -d $(pwd)/.git ]]; then _msg sub "${0:t} cannot parse this site. Exiting." exit fi # We define the variable 'engine' based on what's in the 'config' file. if [[ ${generator} == "native" ]]; then # Usage: ${engine} ${1} - Where 1 is the file you want to convert engine=_zhtml export file_ext="qst" elif [[ ${generator} == "markdown" ]]; then if [[ ! -f /usr/local/bin/pandoc ]]; then _msg other "Please install Pandoc." _msg other "https://github.com/jgm/pandoc/releases" exit else # Usage: ${engine} ${1} - Where 1 is the file you want parsed engine=/usr/local/bin/pandoc export file_ext="md" fi fi if (${debug}); then _msg debug "Using the ${generator} engine for files: ${file_ext}"; fi builtin cd ${project_dir} # Let's put these here for now. export today=$(strftime "%Y-%m-%d - %T") export blogdate=$(strftime "%a-%Y-%b-%d") # Let's create arrays of all the files we'll be working on function _list_pages() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Initialize or clear the array to ensure it's empty before adding files pages_file_array=() export no_pages_found=false # Temporarily set null_glob for this function setopt local_options null_glob # Using an array to directly capture matching files local -a pages_files=(*.${file_ext}) if (( ${#pages_files} == 0 )); then if ${debug}; then _msg debug "_list_pages: No Pages found with ext ${file_ext}."; fi export no_pages_found=true return else for file in "${pages_files[@]}"; do if ${debug}; then _msg debug "_list_pages: Adding file to array: ${file}"; fi pages_file_array+=("$file") done fi } function _list_blogs() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Initialize or clear the blogs array to ensure it's empty before adding files blogs_file_array=() export no_blogs_found=false # Temporarily set null_glob for this function setopt local_options null_glob # Directly capture matching blog files into an array local -a blog_files=(blog/*.blog(On)) if (( ${#blog_files[@]} == 0 )); then if ${debug}; then _msg debug "_list_blogs: No blog files found."; fi export no_blogs_found=true return else for file in "${blog_files[@]}" do if ${debug}; then _msg debug "_list_blogs: Adding file to array: $file"; fi blogs_file_array+=("$file") done fi } # BLOG CACHE function _blog_cache() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi _list_blogs # Create an associative array for the blog cache typeset -A blog_cache # Load the existing blog cache if [[ -f $blog_cache_file ]]; then while IFS=':' read -r name hash; do blog_cache[$name]=$hash if (${debug}) _msg debug "_blog_cache: HASH VALUE: ${blog_cache[${name}]}" done < "$blog_cache_file" fi # Initialize the array for storing blog files to process make_blog_array=() # Process blog files for blog_file in ${blogs_file_array[@]}; do # Compute the current blog file hash current_hash=$(md5sum "$blog_file" | awk '{print $1}') if (${debug}) _msg debug "1. _blog_cache: ${blog_file}" if (${debug}) _msg debug "2. _blog_cache: current_cache: ${current_hash}" # Check if the blog file is new or has changed if [[ ${blog_cache[$blog_file]} != "$current_hash" ]]; then if (${debug}) _msg debug "3. _blog_cache: new_cache_file: ${blog_file}$" if (${debug}) _msg debug "4. _blog_cache: new_current_cache: ${current_hash}" # Blog file is new or has changed; add it to the processing array make_blog_array+=("$blog_file") # Update the blog cache with the new hash blog_cache[$blog_file]=$current_hash fi done # Rebuild the blog cache file from scratch : >| "$blog_cache_file" # Truncate the file before writing for name in "${(@k)blog_cache}"; do echo "$name:${blog_cache[$name]}" >> "$blog_cache_file" done } # PAGES CACHE # Returns the array pages_array() function _pages_cache() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Create an associative array for the pages cache typeset -A pages_cache _list_pages # Load the existing pages cache if [[ -f $pages_cache_file ]]; then while IFS=':' read -r name hash; do pages_cache[$name]=$hash if (${debug}) _msg debug "PAGES HASH VALUE: ${pages_cache[${name}]}" done < "$pages_cache_file" fi # Initialize the array for storing pages files to process pages_array=() # Process pages files for file in ${pages_file_array[@]}; do # Compute the current blog file hash current_hash=$(md5sum "$file" | awk '{print $1}') if (${debug}) _msg debug "1. pages_cache: ${pages_cache[$file]}" if (${debug}) _msg debug "1. _pages_cache: current_cache: ${current_hash}" # Check if the pages file is new or has changed if [[ ${pages_cache[$file]} != "$current_hash" ]]; then if (${debug}) _msg debug "2. _pages_cache: pages_file: ${pages_cache[$file]}" if (${debug}) _msg debug "2. _pages_cache: current_cache: ${current_hash}" # Pages file is new or has changed; add it to the processing array pages_array+=("$file") # Update the pages cache with the new hash pages_cache[$file]=$current_hash fi done # Rebuild the pages cache file from scratch : >| "$pages_cache_file" # Truncate the file before writing for name in "${(@k)pages_cache}"; do echo "$name:${pages_cache[$name]}" >> "$pages_cache_file" done } function _last_updated() { # This function updates #updated and #version tags in the provided string for buffers if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi if (${debug}); then _msg debug "_last_updated: Setting date and version in footer"; fi if (${debug}); then _msg debug "_last_updated: ${upd_msg}"; fi local upd_msg="Last updated ${today} by ${QSGEN} ${VERSION}" local content="${1}" # Perform the replacements local updated_content=$(echo "${content}" | sed \ -e "s|#updated|${upd_msg}|") # Return the updated content echo "${updated_content}" } function _f_last_updated() { # This function updates #updated and #version tags in the provided string in files if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi if (${debug}); then _msg debug "_f_last_updated: Setting date and version in footer of file ${1}"; fi if (${debug}); then _msg debug "_f_last_updated: ${upd_msg}"; fi local upd_msg="Last updated ${today} by ${QSGEN} ${VERSION}" local content="${1}" # Perform the replacements tee < ${content} | sed \ -e "s|#updated|${upd_msg}|" \ > ${content} } function _file_to_lower() { local filename="${1}" # Replace spaces with dashes filename="${filename// /-}" # Convert to lowercase and remove invalid characters filename=$(echo "${filename}" | sed -e 's/^[^a-zA-Z0-9_.]+//g' -e 's/[^a-zA-Z0-9_-]+/-/g') echo ${filename} } function _pages() { # This function generates all the new and updated Pages if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Load the cache for Pages if (${debug}) _msg debug "_pages: Running function _pages_cache" _pages_cache if [[ ${no_pages_found} == "true" ]]; then _msg sub "* You do not have any pages *" return fi _msg info "Generating Pages" if (( ${#pages_array[@]} > 0 )); then # If pages_array is not empty, we do work if (${debug}) _msg debug "_pages: pages_array is not empty" for pages_in_array in ${pages_array[@]} do if (${debug}) _msg debug "_pages: Setting Pages template" local pages=${project_dir}/themes/${theme}/pages.tpl # Let's check if we can access the pages.tpl file. # It not, exit script. if [[ ! -f ${pages} ]]; then _msg info "Unable to find the Pages template: ${pages}" exit else # Read template once if (${debug}) _msg debug "_pages: Reading Pages template into pages_tpl" local pages_tpl="$(<${pages})" fi _msg std "- Generating Page: ${pages_in_array%.*}.html" # Read the file once if (${debug}) _msg debug "_pages: Loading page_content once - ${pages_in_array}" local page_content="$(<${pages_in_array})" # Grab the title from the Page if (${debug}) _msg debug "_pages: Grepping for page_title" local page_title=$( echo ${page_content} | head -2 | grep \#title | cut -d= -f2 ) if (${debug}) _msg debug "_pages: ${page_title}" # Remove the #title line from the buffer. No longer needed. if (${debug}) _msg debug "_pages: Removing #title line from page_content" page_content=$( echo ${page_content} | grep -v \#title ) # HTML'ify the page content if (${debug}) _msg debug "_pages: Running engine on ${pages_in_array}" if [[ ${file_ext} == "qst" ]]; then page_content=$( ${engine} "$page_content" ) # Look for links, images and videos and convert them if present. if (${debug}) _msg debug "_pages: Checking for #link, #showimg and #ytvideo in page_content" if [[ $( echo ${page_content} | grep \#link ) ]]; then if (${debug}) _msg debug "_pages: #link is present, run _link: page_content" page_content=$( _link "${page_content}" ) fi if [[ $( echo ${page_content} | grep \#showimg ) ]]; then if (${debug}) _msg debug "_pages: #showimg is present, run _image: page_content" page_content=$( _image "${page_content}" ) fi if [[ $( echo ${page_content} | grep \#ytvideo ) ]]; then if (${debug}) _msg debug "_pages: #ytvideo is present, run _youtube: page_content" page_content=$( _youtube "${page_content}" ) fi elif [[ ${file_ext} == "md" ]]; then page_content=$(echo "$page_content" | ${engine}) fi if (${debug}) _msg debug "_pages: Replacing BODY with page_content in pages_tpl" # Use awk for multi-line and special character handling pages_tpl=$( awk -v new_body="$page_content" '{sub(/BODY/, new_body)} 1' <(echo "${pages_tpl}") ) # Replace every #pagetitle in pages_tpl if (${debug}) _msg debug "_pages: Replacing #pagetitle, #tagline and #sitename in pages_tpl" pages_tpl=$(echo "${pages_tpl}" | perl -pe "s|#pagetitle|${page_title}|gs; s|#tagline|${site_tagline}|gs; s|#sitename|${site_name}|gs") # Replace #updated with today's date and #version with Name and Version to footer if (${debug}) _msg debug "_pages: _last_updated in pages_tpl" pages_tpl=$( _last_updated ${pages_tpl} ) # Always use lowercase for file names if (${debug}) _msg debug "_pages: Lowercase filnames, always" pages_title_lower=$( _file_to_lower "${pages_in_array}" ) # Clean up unused tags, if any if (${debug}) _msg debug "_pages: Running _cleanup" pages_tpl=$( _cleanup "${pages_tpl}" ) # Write pages_tpl to disk # _msg std "Writing ${www_root}/${pages_title_lower%.*}.html to disk." echo "${pages_tpl}" > ${www_root}/${pages_title_lower%.*}.html # Insert the blog to the front page is blog_in_index is true and the file in the array is index.file_ext # and if index.tmp.html exist and is not empty if [[ ${pages_in_array} == "index.${file_ext}" && ${blog_in_index} == "true" && -s "${project_dir}/blog/index.tmp.html" ]]; then if (${debug}) _msg sub "- Parsing ${pages_in_array}" if (${debug}) _msg sub "- blog_in_index = ${blog_in_index}" if (${debug}) _msg sub "- Listing blog temp file:" if (${debug}) ls -l ${project_dir}/blog/index.tmp.html _add_blog_list_to_index fi done export new_updated_pages=true else # Insert the blog to the front page is blog_in_index is true and the file in the array is index.file_ext # and if index.tmp.html exist and is not empty if [[ ${blog_in_index} == "true" && -s "${project_dir}/blog/index.tmp.html" ]]; then _msg std "Updating index.html with new blog posts" if (${debug}) _msg sub "- Parsing ${pages_in_array}" if (${debug}) _msg sub "- blog_in_index = ${blog_in_index}" if (${debug}) _msg sub "- Listing blog temp file:" if (${debug}) ls -l ${project_dir}/blog/index.tmp.html _add_blog_list_to_index fi _msg sub "- No new or updated Pages found" export new_updated_pages=false fi } function _blogs() { # This function either generates blog files or exports metadata based on the argument if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Running function _list_blogs if (${debug}) _msg debug "_blogs: Running function _list_blogs" _list_blogs if [[ ${no_blogs_found} == "true" ]]; then _msg sub "* You do not have any blogs *" return fi _msg info "Generating Blogs" # Running function _blog_cache if (${debug}) _msg debug "_blogs: Running function _blog_cache" _blog_cache if (( ${#make_blog_array[@]} > 0 )); then # Declare the array to hold metadata strings for each blog BLOG_META_STR_ARRAY=() # Regular blog creation process if [[ -f ${project_dir}/themes/${theme}/blogs.tpl ]]; then local blog_tpl=$(<"${project_dir}/themes/${theme}/blogs.tpl") else _msg info "Unable to find theme template for Blogs." exit fi for blog in "${make_blog_array[@]}"; do if (${debug}) _msg debug "_blogs: Processing pre-data for ${blog}" local content="$(<"${blog}")" local sdate btitle ingress body blog_index blog_dir blog_url # Extract blog information sed -i "s/GETDATE/${blogdate}/" ${blog} # Array sdate = Name day=1, Year=2, Month=3, Number day=4 sdate=( $( echo ${content} | grep DATE | sed "s|DATE\ ||" | sed "s|\-|\ |g" ) ) btitle=$( echo ${content} | grep BLOG_TITLE | cut -d' ' -f2- ) ingress=$( echo ${content} | sed "s/'/\\\'/g" | xargs | grep -Po "#INGRESS_START\K(.*?)#INGRESS_STOP" | sed "s|\ \#INGRESS_STOP||" | sed "s|^\ ||" ) body=$( echo ${content} | sed "s/'/\\\'/g" | xargs | grep -Po "#BODY_START\K(.*?)#BODY_STOP" | sed "s|\ \#BODY_STOP||" | sed "s|^\ ||" ) blog_index=$(echo "${btitle:l}" | sed 's/ /_/g; s/,//g; s/\.//g; s/://g; s/[()]//g') blog_dir="/blog/${sdate[2]}/${sdate[3]:l}/${sdate[4]}" blog_url="${blog_dir}/${blog_index}.html" if (${debug}) _msg debug "_blogs: Adding data for ${blog} to array to export" # Concatenate all metadata into a single string for the current blog local metadata_str="SDATE: ${sdate[@]}||BTITLE: ${btitle}||INGRESS: ${ingress}||URL: ${blog_url}" # Append this metadata string to the array BLOG_META_STR_ARRAY+=("${metadata_str}") if (${debug}) _msg debug "_blogs: Processing ${blog}" _msg std "- Generating Blog: ${blog_index}.html" # Prepare the blog template if (${debug}) _msg debug "_blogs: Processing substitutes in ${blog}" local blog_content=$( echo "${blog_tpl}" | \ perl -pe "\ s|BLOGTITLE|${btitle}|g; \ s|BLOGURL|${blog_url}|g; \ s|\QINGRESS\E|${ingress}|g; \ s|\QBODY\E|${body}|g \ ") blog_content="${blog_content//CALNDAY/${sdate[4]}}" blog_content="${blog_content//CALYEAR/${sdate[2]}}" blog_content="${blog_content//CALMONTH/${sdate[3]}}" blog_content="${blog_content//CALADAY/${sdate[1]}}" if (${debug}) _msg debug "_blogs: Running function $engine for ${blog}" if [[ ${file_ext} == "qst" ]]; then blog_content=$( ${engine} "${blog_content}" ) # Look for links, images and videos and convert them if present. if (${debug}) _msg debug "_blogs: Checking for #link, #showimg and #ytvideo in blog_content" if [[ $( echo ${blog_content} | grep \#link ) ]]; then if (${debug}) _msg debug "_blogs: #link is present, run _link: blog_content" blog_content=$(_link "${blog_content}") fi if [[ $( echo ${blog_content} | grep \#showimg ) ]]; then if (${debug}) _msg debug "_blogs: #showimg is present, run _link: blog_content" blog_content=$(_image "${blog_content}") fi if [[ $( echo ${blog_content} | grep \#ytvideo ) ]]; then if (${debug}) _msg debug "_blogs: #ytvideo is present, run _link: blog_content" blog_content=$(_youtube "${blog_content}") fi elif [[ ${file_ext} == "md" ]]; then blog_content=$(echo "$blog_content" | ${engine}) fi # Replace every #tagline in blog_content if (${debug}) _msg debug "_blogs: Replacing tagline, sitename and pagetitle" blog_content=$( echo ${blog_content} | perl -pe "s|#tagline|${site_tagline}|gs; s|#sitename|${site_name}|gs; s|#pagetitle|${page_title}|gs" ) blog_content=$(_last_updated "${blog_content}") blog_content=$(_cleanup "${blog_content}") # Create directory if it doesn't exist if (${debug}) _msg debug "_blogs: Creating directoty ${www_root}/${blog_dir}" [[ ! -d "${www_root}/${blog_dir}" ]] && mkdir -p "${www_root}/${blog_dir}" # Write to file if (${debug}) _msg debug "_blogs: Writing blog to disk: ${www_root}${blog_url}" echo "${blog_content}" > "${www_root}${blog_url}" unset sdate btitle ingress body blog_index blog_dir blog_url done # Now BLOG_META_STR_ARRAY contains the metadata string for each blog post export BLOG_META_STR_ARRAY export new_updated_blogs=true else _msg sub "- No new or updated Blogs found" export new_updated_blogs=false fi if [[ ${new_updated_blogs} == "true" ]]; then if (${debug}) _msg sub "* _blogs: Running _blog_idx_for_index" _blog_idx_for_index if (${debug}) _msg sub "* _blogs: Running _blog_index" _blog_index fi } function _blog_idx_for_index() { # This function generates the file blog/index.tmp.html if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi _msg sub "- Populating ${project_dir}/blog/index.tmp.html" if (${debug}) _msg debug "_blog_idx_for_index: Initiating function" local blog_list_tpl=$(<${project_dir}/themes/${theme}/blog_list.tpl) # Truncate file before writing new one : >| "${project_dir}/blog/index.tmp.html" if (${debug}) _msg debug "_blog_idx_for_index: BLOG_META_STR_ARRAY: ${BLOG_META_STR_ARRAY[@]}" for meta_str in ${BLOG_META_STR_ARRAY[@]} do if (${debug}) _msg debug "_blog_idx_for_index: meta_str from BLOG_META_STR_ARRAY from _blogs" if (${debug}) _msg debug "_blog_idx_for_index: ${meta_str}" # Split meta_str into individual metadata components local -a meta_array=("${(@s/||/)meta_str}") # Initialize variables to store each component local sdate btitle ingress url # Iterate over each component and extract information if (${debug}) _msg debug "_blog_idx_for_index: Iterate over each component and extract information" for component in "${meta_array[@]}" do case "${component}" in SDATE:*) sdate=${component#SDATE: } ;; BTITLE:*) btitle=${component#BTITLE: } ;; INGRESS:*) ingress=${component#INGRESS: } ;; URL:*) url=${component#URL: } ;; esac local adate=( $( echo ${sdate} ) ) local caladay="${adate[1]}" local calyear="${adate[2]}" local calmonth="${adate[3]}" local calnday="${adate[4]}" local bdate="${adate[1]} - ${adate[4]}/${adate[3]}/${adate[2]}" blog_list_content=$( echo "${blog_list_tpl}" | \ perl -pe "\ s|BLOGURL|${site_url}${url}|g; \ s|BLOGTITLE|${btitle}|g; \ s|INGRESS|${ingress}|g; \ s|BLOGDATE|${bdate}|g; \ s|CALADAY|${caladay}|g; \ s|CALNDAY|${calnday}|g; \ s|CALMONTH|${calmonth}|g; \ s|CALYEAR|${calyear}|g \ ") done unset sdate btitle ingress url if (${debug}) _msg debug "_blog_idx_for_index: Running ${engine} on content to catch QStags in Ingress" blog_list_content=$( ${engine} ${blog_list_content} ) if (${debug}) _msg debug "_blog_idx_for_index: Writing _blog_idx_for_index to file: ${project_dir}/blog/index.tmp.html" if (${debug}) _msg debug "_blog_idx_for_index: blog_list_content = ${blog_list_content}" echo ${blog_list_content} >> ${project_dir}/blog/index.tmp.html done } function _blog_index() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # This function generates the /blog/index.html file that gets its data from _blog_list_for_index() # ${new_updated_blogs} comes from the function _blogs if anything new or updated is detected if [[ ${blog_in_index} == "false" ]] && [[ ${new_updated_blogs} = "true" ]]; then if (${debug}) _msg debug "Running function _blog_index" if (${debug}) _msg debug "_blog_index: blog_in_index = ${blog_in_index}" _msg std "- Generating the Blog index file" local blog_index_tpl=$(<${project_dir}/themes/${theme}/blog_index.tpl) local blog_index_list=$(<${project_dir}/blog/index.tmp.html) local blog_index_content=$(echo "${blog_index_tpl}" | perl -pe "s|#sitename|${site_name}|gs; s|#tagline|${site_tagline}|gs") blog_index_content=$( awk -v new_body="$blog_index_list" '{sub(/BODY/, new_body)} 1' <(echo "${blog_index_tpl}") ) echo "${blog_index_content}" > ${www_root}/blog/index.html _f_last_updated ${www_root}/blog/index.html fi } function _add_blog_list_to_index() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Let's find the file 'index.qst' and add the blog if blog_in_index is true if (${debug}) _msg debug "_add_blog_list_to_index: Inserting blog list to index.html" local blog_index_list=$(<${project_dir}/blog/index.tmp.html) local site_index_file=$(<${www_root}/index.html) echo "${site_index_file}" | awk -v new_body="${blog_index_list}" '{sub(/BLOGINDEX/, new_body)} 1' > "${www_root}/index.html" } function _sitemap() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi # Check if sitemap is set to true and if there are updated Blogs or Pages before updating the sitemap.xml file. if [[ ${sitemap} == "true" ]] && ( [[ ${new_updated_blogs} == "true" ]] || [[ ${new_updated_pages} == "true" ]] ); then _msg info "Updating sitemap.xml" local sitemap_file="${www_root}/sitemap.xml" # Find all HTML files and store them in an array local -a html_files=("${(@f)$(find "${www_root}" -type f -name "*.html")}") # Start of the XML file echo '' > ${sitemap_file} echo "" >> ${sitemap_file} echo "" >> ${sitemap_file} echo '> ${sitemap_file} echo ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' >> ${sitemap_file} echo ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' >> ${sitemap_file} echo ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"' >> ${sitemap_file} echo '>' >> ${sitemap_file} # Add each URL to the sitemap for file in "${html_files[@]}" do # Remove www_root from the path and prepend site_url local url="${site_url}${file#$www_root}" local lastmod=$(stat -c %y "${file}" 2>/dev/null | cut -d' ' -f1,2 | sed 's/ /T/' | sed 's/\..*$//') echo " " >> ${sitemap_file} echo " ${url}" >> ${sitemap_file} echo " " >> "${sitemap_file}" echo " " >> ${sitemap_file} echo " " >> ${sitemap_file} echo " " >> ${sitemap_file} done # End of the XML file echo '' >> "${sitemap_file}" if (${debug}); then _msg debug "Sitemap generated at ${sitemap_file}"; fi fi } function _link() { # This converts #link tags to actual clickable links in a provided string if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="${1}" local modified_content="" # Process the content line by line echo "${content}" | while IFS= read -r line; do if [[ ${line} == *"#link"* ]]; then if (${debug}) _msg debug "_link: URL_MAIN(line): ${line}" # Extract the URL and the link text local url_full=$(echo "${line}" | awk -F'#link ' '{print $2}' | awk -F'¤' '{print $1 "¤" $2}') local url_dest=$(echo "${url_full}" | awk -F'¤' '{print $1}') local url_txt=$(echo "${url_full}" | awk -F'¤' '{print $2}') if (${debug}) _msg debug "_link_ URL: ${url_dest}" if (${debug}) _msg debug "_link: Text: ${url_txt}" # Form the replacement HTML link local modified_link="${url_txt}" if [[ ${url_dest} =~ ^https?:// ]]; then # Add external link icon for external URLs modified_link+=" \"External" fi modified_link+="" line=${line//"#link ${url_full}"/${modified_link}} fi modified_content+="${line}\n" done # Return the modified content echo -e "${modified_content}" } function _image() { # This replaces #showimg tags with actual HTML img tags in a provided string if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="${1}" local modified_content="" # Process the content line by line echo "${content}" | while IFS= read -r line; do if [[ ${line} == *"#showimg"* ]]; then if (${debug}) _msg debug "_image: Processing line: ${line}" # Extract image link and alt text local img_link=$(echo "${line}" | awk -F'#showimg ' '{print $2}') local image=$(echo "${img_link}" | awk -F'¤' '{print $1}') local img_alt=$(echo "${img_link}" | awk -F'¤' '{print $2}') # Determine the source of the image local real_image="" if [[ ${image} =~ ^https?:// ]]; then real_image=${image} elif [[ ${image} =~ ^\/ ]]; then real_image=${image} else real_image="/images/${image}" fi # Form the replacement HTML image tag local img_tag="\"${img_alt}\"" line=${line//"#showimg ${img_link}"/${img_tag}} fi modified_content+="${line}\n" done # Return the modified content echo -e "${modified_content}" } function _youtube() { # This embeds a YouTube video in a provided string if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="${1}" local modified_content="" # Process the content line by line echo "${content}" | while IFS= read -r line; do if [[ ${line} == *"#ytvideo"* ]]; then if (${debug}) _msg debug "_youtube: Processing line: ${line}" # Extract YouTube video ID local yt_id=$(echo "${line}" | awk -F'#ytvideo ' '{print $2}') # Form the replacement YouTube iframe embed local yt_iframe="" line=${line//"#ytvideo ${yt_id}"/${yt_iframe}} fi modified_content+="${line}\n" done # Return the modified content echo -e "${modified_content}" } function _cleanup() { # This removes tags used in the templates that may be left over for some reason if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="${1}" if (${debug}) _msg debug "_cleanup: Cleaning up tags in content" # Perform the cleanup # -e "s|BLOGINDEX\ ||g" local cleaned_content=$(echo "${content}" | sed \ -e "s|¤||g" \ -e "s|#showimg\ ||g" \ -e "s|#ytvideo\ ||g" \ -e "s|#link\ ||g" \ ) # Return the cleaned content echo "${cleaned_content}" } function _html() { if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="$1" if ${debug}; then _msg debug "_html: Converting QStags in content" fi # Use perl to convert QStags to HTML perl -0777 -pe ' BEGIN { @tags = ( "#BR", "
\n", "#BD", "", "#EBD", "", "#I", "", "#EI", "\n", "#P", "

", "#EP", "

\n", "#Q", "
", "#EQ", "
\n", "#C", "", "#EC", "\n", "#H1", "

", "#EH1", "

\n", "#H2", "

", "#EH2", "

\n", "#H3", "

", "#EH3", "

\n", "#H4", "

", "#EH4", "

\n", "#H5", "
", "#EH5", "
\n", "#H6", "
", "#EH6", "
\n", "#STRONG", "", "#ESTRONG", "\n", "#EM", "", "#EEM", "\n", "#DV", "
", "#EDV", "
\n", "#SPN", "", "#ESPN", "\n", "#UL", "
    ", "#EUL", "
\n", "#OL", "
    ", "#EOL", "
\n", "#LI", "
  • ", "#ELI", "
  • \n", "#IU", "", "#EIU", "\n", "#TBL", "", "#ETBL", "
    \n", "#TR", "", "#ETR", "\n", "#TD", "", "#ETD", "\n", "#TH", "", "#ETH", "\n", "#ART", "
    ", "#EART", "
    \n", "#SEC", "
    ", "#ESEC", "
    \n", "#ASIDE", "\n", "#NAV", "\n", "#BTN", "\n", "#SEL", "\n", "#OPT", "\n", "#LT", "<", "#GT", ">", "#NUM", "#" ); } for (my $i = 0; $i < $#tags; $i += 2) { my $tag = $tags[$i]; my $html = $tags[$i + 1]; s/\Q$tag\E/$html/g; } ' <<< "$content" } function _zhtml() { # This function uses the regex module from Zsh to parse the QStags if [[ ${globaldebug} == "true" ]]; then local debug=true else # If you want to debug this function only, set this to true local debug=false fi local content="$1" if ${debug}; then echo "_html: Converting QStags in content" fi # Load regex module zmodload zsh/regex # Define tag replacements as an associative array typeset -A tags=( "#BR" "
    \n" "#BD" "" "#EBD" "" "#I" "" "#EI" "\n" "#P" "

    " "#EP" "

    \n" "#Q" "
    " "#EQ" "
    \n" "#C" "" "#EC" "\n" "#H1" "

    " "#EH1" "

    \n" "#H2" "

    " "#EH2" "

    \n" "#H3" "

    " "#EH3" "

    \n" "#H4" "

    " "#EH4" "

    \n" "#H5" "
    " "#EH5" "
    \n" "#H6" "
    " "#EH6" "
    \n" "#STRONG" "" "#ESTRONG" "\n" "#EM" "" "#EEM" "\n" "#DV" "
    " "#EDV" "
    \n" "#SPN" "" "#ESPN" "\n" "#UL" "
      " "#EUL" "
    \n" "#OL" "
      " "#EOL" "
    \n" "#LI" "
  • " "#ELI" "
  • \n" "#IU" "" "#EIU" "\n" "#TBL" "" "#ETBL" "
    \n" "#TR" "" "#ETR" "\n" "#TD" "" "#ETD" "\n" "#TH" "" "#ETH" "\n" "#ART" "
    " "#EART" "
    \n" "#SEC" "
    " "#ESEC" "
    \n" "#ASIDE" "\n" "#NAV" "\n" "#BTN" "\n" "#SEL" "\n" "#OPT" "\n" "#LT" "<" "#GT" ">" "#NUM" "#" ) for tag html (${(kv)tags}) do # Escape tag for regex use local escapedTag=$(printf '%s' "$tag" | sed 's/[].\[^$*]/\\&/g') if [[ "$content" =~ "$escapedTag" ]]; then content=${content//($tag)/$html} fi done echo "$content" } _blogs _pages _sitemap