diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-11 09:55:39 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-11 09:55:39 +0300 |
| commit | cbf67b99530aaddf52cb85c8edc80b3f13279ce3 (patch) | |
| tree | a0158896af17ec905bbae87723a80744c67f4054 /src/lib/template.source.sh | |
| parent | 94f73164b460da559fcae98169eabfb9ab274bdd (diff) | |
Split photoalbum source into lib modules
Diffstat (limited to 'src/lib/template.source.sh')
| -rw-r--r-- | src/lib/template.source.sh | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/src/lib/template.source.sh b/src/lib/template.source.sh new file mode 100644 index 0000000..5c00176 --- /dev/null +++ b/src/lib/template.source.sh @@ -0,0 +1,502 @@ +_html_escape() { + local text="$1"; shift + + text=${text//&/\&} + text=${text//</\<} + text=${text//>/\>} + text=${text//\"/\"} + text=${text//\'/\'} + + printf '%s\n' "$text" +} + +html_escape_to() { + local -n output_ref="$1"; shift + local text="$1"; shift + + text=${text//&/\&} + text=${text//</\<} + text=${text//>/\>} + text=${text//\"/\"} + text=${text//\'/\'} + + output_ref="$text" +} + +_css_string_escape() { + local text="$1"; shift + + text=${text//\\/\\\\} + text=${text//&/\\000026} + text=${text//</\\00003c} + text=${text//>/\\00003e} + text=${text//\"/\\000022} + text=${text//\'/\\000027} + + printf '%s\n' "$text" +} + +css_string_escape_to() { + local -n output_ref="$1"; shift + local text="$1"; shift + + text=${text//\\/\\\\} + text=${text//&/\\000026} + text=${text//</\\00003c} + text=${text//>/\\00003e} + text=${text//\"/\\000022} + text=${text//\'/\\000027} + + output_ref="$text" +} + +_json_string_escape() { + local text="$1"; shift + local char + local escaped='' + local escaped_char + local -i code + local -i i + local LC_ALL=C + + for (( i = 0; i < ${#text}; i++ )); do + char="${text:i:1}" + case "$char" in + $'\\') + escaped+=$'\\\\' + ;; + '"') + escaped+='\"' + ;; + $'\b') + escaped+='\b' + ;; + $'\f') + escaped+='\f' + ;; + $'\n') + escaped+='\n' + ;; + $'\r') + escaped+='\r' + ;; + $'\t') + escaped+='\t' + ;; + *) + code=$(printf '%d' "'$char") + if (( code < 32 )); then + printf -v escaped_char '\\u%04x' "$code" + escaped+="$escaped_char" + else + escaped+="$char" + fi + ;; + esac + done + + printf '%s\n' "$escaped" +} + +_json_string() { + local -r text="$1"; shift + + printf '"%s"' "$(_json_string_escape "$text")" +} + +_json_bool() { + local -r value="$1"; shift + + case "$value" in + yes) + printf 'true' + ;; + *) + printf 'false' + ;; + esac +} + +_display_path() { + local -r path="$1"; shift + local -r final_dist="${PHOTOALBUM_FINAL_DIST_DIR:-}" + + if [[ -n "$final_dist" && "$path" == "$DIST_DIR"* ]]; then + printf '%s%s\n' "$final_dist" "${path#"$DIST_DIR"}" + else + printf '%s\n' "$path" + fi +} + +current_date_text() { + if random_seed_is_set; then + printf 'Thu Jan 1 00:00:00 UTC 1970\n' + else + command date + fi +} + +current_date_text_to() { + local -n output_ref="$1"; shift + + if [ -z "$PHOTOALBUM_CURRENT_DATE_TEXT" ]; then + if random_seed_is_set; then + PHOTOALBUM_CURRENT_DATE_TEXT='Thu Jan 1 00:00:00 UTC 1970' + else + PHOTOALBUM_CURRENT_DATE_TEXT=$(command date) + fi + fi + + output_ref="$PHOTOALBUM_CURRENT_DATE_TEXT" +} + +current_timestamp_slug() { + if random_seed_is_set; then + printf '1970-01-01-000000\n' + else + command date +'%Y-%m-%d-%H%M%S' + fi +} + +current_timestamp_iso() { + if random_seed_is_set; then + printf '1970-01-01T00:00:00Z\n' + else + command date -u +'%Y-%m-%dT%H:%M:%SZ' + fi +} + +template_context_value() { + local -n context_ref="$1"; shift + local -r name="$1"; shift + + printf '%s\n' "${context_ref[$name]:-}" +} + +template_context_value_to() { + local -n output_ref="$1"; shift + local -n context_ref="$1"; shift + local -r name="$1"; shift + + # shellcheck disable=SC2034 + output_ref="${context_ref[$name]:-}" +} + +require_template_context_vars() { + local -r template_name="$1"; shift + local -n context_ref="$1"; shift + local name + local -i missing=0 + + for name in "$@"; do + if [ -z "${context_ref[$name]+x}" ]; then + config_error "template $template_name requires render variable $name" + missing=1 + fi + done + + return "$missing" +} + +validate_template_context() { + local -r template_name="$1"; shift + local -r context_name="$1"; shift + local -n context_ref="$context_name" + local -a required_vars=(html_dir) + + case "$template_name" in + footer) + required_vars+=(backhref tarball_name) + ;; + header) + required_vars+=( + backhref + background_image + blurs_dir + show_header_bar + ) + ;; + next) + required_vars+=(next prev) + ;; + prev) + required_vars+=(prev) + ;; + preview) + required_vars+=( + animation_class + backhref + page_num + photo + preview_num + thumbs_dir + ) + ;; + redirect) + required_vars+=(redirect_page) + ;; + splash) + required_vars+=( + backhref + background_image + blurs_dir + enter_page + photo + photos_dir + ) + ;; + details) + required_vars+=( + animation_class + backhref + exif_details + page_num + photo + photos_dir + preview_num + ) + ;; + view) + required_vars+=( + animation_class + backhref + page_num + photo + photos_dir + preview_num + ) + ;; + esac + + require_template_context_vars "$template_name" "$context_name" \ + "${required_vars[@]}" +} + +source_template_file() { + local -r template_path="$1"; shift + local -r output_path="$1"; shift + local context_file + local -i status=0 + + context_file=$(mktemp) + { + serialize_template_render_context + printf 'unset BASH_ENV\n' + } > "$context_file" + + if env -i PATH="$PATH" BASH_ENV="$context_file" \ + bash -euo pipefail -- "$template_path" >> "$output_path"; then + rm -f "$context_file" + else + status=$? + rm -f "$context_file" + return "$status" + fi +} + +parse_template_context() { + local -r template_name="$1"; shift + local -n context_ref="$1"; shift + local context_key + local context_value + + while (( $# > 0 )); do + if (( $# < 2 )); then + config_error "template $template_name render context is incomplete" + return 1 + fi + + context_key="$1" + context_value="$2" + shift 2 + + if [[ ! "$context_key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + config_error \ + "template $template_name render variable $context_key is invalid" + return 1 + fi + + # shellcheck disable=SC2034 + context_ref["$context_key"]="$context_value" + done +} + +serialize_template_render_var() { + local -r name="$1"; shift + local -r value="$1"; shift + + printf '%s=%q\n' "$name" "$value" +} + +serialize_template_render_context() { + serialize_template_render_var \ + render_animation_class_html "$render_animation_class_html" + serialize_template_render_var render_backhref_css "$render_backhref_css" + serialize_template_render_var render_backhref_html "$render_backhref_html" + serialize_template_render_var \ + render_background_image_css "$render_background_image_css" + serialize_template_render_var render_blurs_dir_css "$render_blurs_dir_css" + serialize_template_render_var \ + render_current_date_text "$render_current_date_text" + serialize_template_render_var render_enter_page_html "$render_enter_page_html" + serialize_template_render_var \ + render_exif_details_html "$render_exif_details_html" + serialize_template_render_var render_height_html "$render_height_html" + serialize_template_render_var render_html_dir_html "$render_html_dir_html" + serialize_template_render_var \ + render_maxpreviews_html "$render_maxpreviews_html" + serialize_template_render_var render_next_html "$render_next_html" + serialize_template_render_var \ + render_original_basepath_is_set "$render_original_basepath_is_set" + serialize_template_render_var \ + render_original_basepath_html "$render_original_basepath_html" + serialize_template_render_var render_page_num_html "$render_page_num_html" + serialize_template_render_var render_photo_html "$render_photo_html" + serialize_template_render_var render_photos_dir_html "$render_photos_dir_html" + serialize_template_render_var render_prev_html "$render_prev_html" + serialize_template_render_var \ + render_preview_num_html "$render_preview_num_html" + serialize_template_render_var \ + render_redirect_page_html "$render_redirect_page_html" + serialize_template_render_var render_show_header_bar "$render_show_header_bar" + serialize_template_render_var render_tarball_include "$render_tarball_include" + serialize_template_render_var \ + render_tarball_name_html "$render_tarball_name_html" + serialize_template_render_var render_thumbheight_html "$render_thumbheight_html" + serialize_template_render_var render_thumbs_dir_html "$render_thumbs_dir_html" + serialize_template_render_var render_title_html "$render_title_html" + serialize_template_render_var render_view_next_html "$render_view_next_html" + serialize_template_render_var render_view_prev_html "$render_view_prev_html" +} + +prepare_template_render_vars() { + local -r context_name="$1"; shift + local context_value + + template_context_value_to context_value "$context_name" animation_class + html_escape_to render_animation_class_html "$context_value" + template_context_value_to context_value "$context_name" backhref + css_string_escape_to render_backhref_css "$context_value" + html_escape_to render_backhref_html "$context_value" + template_context_value_to context_value "$context_name" background_image + css_string_escape_to render_background_image_css "$context_value" + template_context_value_to context_value "$context_name" blurs_dir + css_string_escape_to render_blurs_dir_css "$context_value" + current_date_text_to context_value + html_escape_to render_current_date_text "$context_value" + template_context_value_to context_value "$context_name" enter_page + html_escape_to render_enter_page_html "$context_value" + template_context_value_to render_exif_details_html \ + "$context_name" exif_details + html_escape_to render_height_html "${HEIGHT:-}" + html_escape_to render_html_dir_html "$render_html_dir" + html_escape_to render_maxpreviews_html "${MAXPREVIEWS:-}" + template_context_value_to context_value "$context_name" next + html_escape_to render_next_html "$context_value" + if [ -n "${ORIGINAL_BASEPATH:-}" ]; then + render_original_basepath_is_set='yes' + else + render_original_basepath_is_set='no' + fi + html_escape_to render_original_basepath_html "${ORIGINAL_BASEPATH:-}" + template_context_value_to context_value "$context_name" page_num + html_escape_to render_page_num_html "$context_value" + template_context_value_to context_value "$context_name" photo + html_escape_to render_photo_html "$context_value" + template_context_value_to context_value "$context_name" photos_dir + html_escape_to render_photos_dir_html "$context_value" + template_context_value_to context_value "$context_name" prev + html_escape_to render_prev_html "$context_value" + template_context_value_to render_preview_num "$context_name" preview_num + html_escape_to render_preview_num_html "$render_preview_num" + template_context_value_to context_value "$context_name" redirect_page + html_escape_to render_redirect_page_html "$context_value" + template_context_value_to render_show_header_bar \ + "$context_name" show_header_bar + render_tarball_include="${TARBALL_INCLUDE:-no}" + template_context_value_to context_value "$context_name" tarball_name + html_escape_to render_tarball_name_html "$context_value" + html_escape_to render_thumbheight_html "${THUMBHEIGHT:-}" + template_context_value_to context_value "$context_name" thumbs_dir + html_escape_to render_thumbs_dir_html "$context_value" + html_escape_to render_title_html "${TITLE:-}" + + if [ -n "$render_preview_num" ]; then + html_escape_to render_view_next_html "$(( render_preview_num + 1 ))" + html_escape_to render_view_prev_html "$(( render_preview_num - 1 ))" + else + render_view_next_html='' + render_view_prev_html='' + fi +} + +validate_template_render_request() { + local -r template_name="$1"; shift + local -r context_name="$1"; shift + local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" + + if [ ! -r "$template_path" ]; then + config_error "template file $template_path must be readable" + return 1 + fi + + validate_template_context "$template_name" "$context_name" +} + +render_template() { + local -r template_name="$1"; shift + local -r html="$1"; shift + local -r context_name="$1"; shift + local -r template_path="$TEMPLATE_DIR/$template_name.tmpl" + local dist_html + local render_animation_class_html + local render_backhref_css + local render_backhref_html + local render_background_image_css + local render_blurs_dir_css + local render_current_date_text + local render_enter_page_html + local render_exif_details_html + local render_height_html + local render_html_dir + local render_html_dir_html + local render_maxpreviews_html + local render_next_html + local render_original_basepath_is_set + local render_original_basepath_html + local render_page_num_html + local render_photo_html + local render_photos_dir_html + local render_prev_html + local render_preview_num + local render_preview_num_html + local render_redirect_page_html + local render_show_header_bar + local render_tarball_include + local render_tarball_name_html + local render_thumbheight_html + local render_thumbs_dir_html + local render_title_html + local render_view_next_html + local render_view_prev_html + + render_html_dir=$(template_context_value "$context_name" html_dir) + dist_html="$DIST_DIR/$render_html_dir" + + log_info "Rendering $template_name template into $(_display_path "$dist_html")/$html" + + mkdir -p "$dist_html" + prepare_template_render_vars "$context_name" + source_template_file "$template_path" "$dist_html/$html" +} + +template() { + local -r template_name="$1"; shift + local -r html="$1"; shift + # shellcheck disable=SC2034 + local -A render_context=() + + parse_template_context "$template_name" render_context "$@" + validate_template_render_request "$template_name" render_context + render_template "$template_name" "$html" render_context +} |
