_html_escape() { local text="$1"; shift 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//\'/\'} output_ref="$text" } _css_string_escape() { local text="$1"; shift text=${text//\\/\\\\} text=${text//&/\\000026} 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///\\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 exif_tooltip 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_exif_tooltip_html "$render_exif_tooltip_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 template_context_value_to context_value "$context_name" exif_tooltip html_escape_to render_exif_tooltip_html "$context_value" 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_exif_tooltip_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 }