2024-03-30 07:46:39 +01:00
|
|
|
;;;; -*- mode: lisp; coding: utf-8; fill-column: 84; indent-tabs-mode: nil; -*-
|
2024-02-05 18:34:46 +01:00
|
|
|
;;;; main.lisp
|
|
|
|
;;;; Provide general functions.
|
2024-03-30 07:46:39 +01:00
|
|
|
|
2023-11-25 09:59:03 +01:00
|
|
|
(defpackage dev.metalisp.sbt
|
2023-07-01 16:30:10 +02:00
|
|
|
(:use :cl)
|
2024-06-18 17:45:15 +02:00
|
|
|
(:import-from #:spinneret #:*html-lang*)
|
2023-07-14 16:20:55 +02:00
|
|
|
(:export
|
2024-01-28 19:29:34 +01:00
|
|
|
:*l10n*
|
|
|
|
:find-l10n
|
2024-01-25 16:07:26 +01:00
|
|
|
:*use-cdn*
|
2024-05-08 12:49:24 +02:00
|
|
|
:*cdn-url-css*
|
|
|
|
:*cdn-url-js*
|
2024-01-28 13:03:35 +01:00
|
|
|
:*local-css-url*
|
2024-05-08 12:49:24 +02:00
|
|
|
:*local-url-js*
|
2024-01-25 16:07:26 +01:00
|
|
|
:*bs-version*
|
|
|
|
:*color-theme*
|
2024-06-19 18:16:25 +02:00
|
|
|
:bs-url-css
|
|
|
|
:bs-url-js
|
2024-03-08 21:50:14 +01:00
|
|
|
:download-bs-css
|
|
|
|
:download-bs-js
|
2024-01-26 16:30:01 +01:00
|
|
|
:write-html-to-file
|
2024-03-30 07:46:39 +01:00
|
|
|
:with-page
|
2024-06-22 14:27:10 +02:00
|
|
|
:body-header
|
|
|
|
:body-main
|
2024-03-30 07:46:39 +01:00
|
|
|
:remove-special-chars
|
|
|
|
:clean-form-str
|
2024-04-03 10:34:08 +02:00
|
|
|
:build-str-name
|
|
|
|
:build-str-value
|
|
|
|
:build-str-value-prop
|
|
|
|
:build-str-class
|
|
|
|
:build-str-id))
|
2023-07-01 16:30:10 +02:00
|
|
|
|
2023-11-25 09:59:03 +01:00
|
|
|
(in-package :dev.metalisp.sbt)
|
2023-07-03 14:43:58 +02:00
|
|
|
|
2024-06-16 12:54:11 +02:00
|
|
|
(defparameter *document-root-dir*
|
|
|
|
"public/"
|
|
|
|
"Defines directory name for use in document-root to serve static files.")
|
|
|
|
|
2024-03-08 21:50:14 +01:00
|
|
|
(defparameter spinneret:*fill-column* 120)
|
2023-07-16 13:57:42 +02:00
|
|
|
|
2024-01-25 16:07:26 +01:00
|
|
|
(defparameter *bs-version* "5.3.2")
|
|
|
|
|
|
|
|
(defparameter *use-cdn* t)
|
|
|
|
|
2024-05-08 12:49:24 +02:00
|
|
|
(defparameter *cdn-url-css*
|
2024-01-25 16:07:26 +01:00
|
|
|
(concatenate 'string
|
|
|
|
"https://cdn.jsdelivr.net/npm/bootstrap@"
|
|
|
|
*bs-version*
|
|
|
|
"/dist/css/bootstrap.min.css"))
|
|
|
|
|
2024-05-08 12:49:24 +02:00
|
|
|
(defparameter *cdn-url-js*
|
2024-01-25 16:07:26 +01:00
|
|
|
(concatenate 'string
|
|
|
|
"https://cdn.jsdelivr.net/npm/bootstrap@"
|
|
|
|
*bs-version*
|
|
|
|
"/dist/js/bootstrap.bundle.min.js"))
|
|
|
|
|
2024-03-06 22:12:13 +01:00
|
|
|
(defparameter *bs-path*
|
|
|
|
(concatenate 'string
|
2024-06-16 12:54:11 +02:00
|
|
|
"/"
|
2024-03-06 22:12:13 +01:00
|
|
|
*bs-version*
|
|
|
|
"/"))
|
|
|
|
|
2024-05-08 12:49:24 +02:00
|
|
|
(defparameter *local-url-css*
|
2024-03-06 22:12:13 +01:00
|
|
|
(concatenate 'string
|
|
|
|
*bs-path*
|
2024-06-16 12:54:11 +02:00
|
|
|
"bootstrap.min.css")
|
|
|
|
"Constructs path for HTML to load local bootstrap css from disk.")
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-05-08 12:49:24 +02:00
|
|
|
(defparameter *local-url-js*
|
2024-03-06 22:12:13 +01:00
|
|
|
(concatenate 'string
|
|
|
|
*bs-path*
|
2024-06-16 12:54:11 +02:00
|
|
|
"bootstrap.bundle.min.js")
|
|
|
|
"Constructs path for HTML to load local bootstrap js from disk.")
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-06-16 12:54:11 +02:00
|
|
|
(defparameter *color-theme* "light")
|
2024-01-25 16:07:26 +01:00
|
|
|
|
2024-05-09 13:55:44 +02:00
|
|
|
(defun bs-url-css ()
|
2024-01-26 16:30:01 +01:00
|
|
|
(if *use-cdn*
|
2024-05-08 12:49:24 +02:00
|
|
|
*cdn-url-css*
|
|
|
|
*local-url-css*))
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-05-09 13:55:44 +02:00
|
|
|
(defun bs-url-js ()
|
2024-01-26 16:30:01 +01:00
|
|
|
(if *use-cdn*
|
2024-05-08 12:49:24 +02:00
|
|
|
*cdn-url-js*
|
|
|
|
*local-url-js*))
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-06-16 12:54:11 +02:00
|
|
|
(defun bs-download-full-path ()
|
|
|
|
(truename (concatenate 'string *document-root-dir* *bs-version*)))
|
|
|
|
|
2024-01-25 16:07:26 +01:00
|
|
|
(defun download-file (url directory)
|
|
|
|
"Downloads a file from a given URL and saves it to the specified directory."
|
|
|
|
(let* ((filename (car (last (uiop:split-string url :separator "/"))))
|
|
|
|
(filepath (merge-pathnames filename directory)))
|
2024-01-25 19:01:54 +01:00
|
|
|
(ensure-directories-exist directory)
|
2024-03-06 22:12:13 +01:00
|
|
|
(let ((content (dex:get url)))
|
|
|
|
(with-open-file (stream filepath
|
|
|
|
:direction :output
|
|
|
|
:if-exists :supersede
|
|
|
|
:if-does-not-exist :create)
|
|
|
|
(write-string content stream)))
|
2024-01-25 16:07:26 +01:00
|
|
|
filepath))
|
|
|
|
|
2024-03-08 21:40:52 +01:00
|
|
|
(defmacro define-download-function (name url directory)
|
|
|
|
`(defun ,name (&optional (directory ,directory))
|
|
|
|
(download-file ,url directory)))
|
|
|
|
|
2024-06-16 12:54:11 +02:00
|
|
|
(define-download-function download-bs-css *cdn-url-css* (bs-download-full-path))
|
|
|
|
(define-download-function download-bs-js *cdn-url-js* (bs-download-full-path))
|
2024-03-06 22:12:13 +01:00
|
|
|
|
2024-03-30 07:57:29 +01:00
|
|
|
(defun write-html-str-to-file (filename string
|
|
|
|
&key (lang "en") (style :tree) (fc 120))
|
2023-08-25 15:10:01 +02:00
|
|
|
(let ((spinneret:*html-lang* lang)
|
|
|
|
(spinneret:*html-style* style)
|
|
|
|
(spinneret:*fill-column* fc))
|
|
|
|
(with-open-file (stream filename :direction :output :if-exists :supersede)
|
|
|
|
(write-string string stream))))
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-03-30 17:47:17 +01:00
|
|
|
(defmacro with-page ((&key
|
|
|
|
meta (title "Web page")
|
|
|
|
main-con
|
|
|
|
add-css-urls
|
2024-03-30 07:57:29 +01:00
|
|
|
add-js-urls)
|
|
|
|
&body body)
|
2024-03-03 12:14:31 +01:00
|
|
|
"This macro simplifies the process of creating an HTML web page.
|
|
|
|
|
|
|
|
META: The meta-information for the web page.
|
|
|
|
|
|
|
|
TITLE: Specifies the title of the web page. Defaults to 'Web page'.
|
|
|
|
|
2024-03-27 11:13:32 +01:00
|
|
|
MAIN-CON: If t add css class `container` to <main>.
|
2024-03-03 12:14:31 +01:00
|
|
|
|
|
|
|
ADD-CSS-URLS: An optional parameter for additional CSS file URLs.
|
|
|
|
|
|
|
|
ADD-JS-URLS: An optional parameter for additional JavaScript file URLs.
|
|
|
|
|
|
|
|
BODY: Denotes the markup for the body of the web page.
|
|
|
|
|
|
|
|
Example usage:
|
|
|
|
(with-page (:meta (:author \"John Doe\") :title \"My Page\" :main-con t) \"foo\")"
|
2024-05-17 17:14:29 +02:00
|
|
|
`(spinneret:with-html-string
|
2024-01-26 16:30:01 +01:00
|
|
|
(:doctype)
|
|
|
|
(:html :data-bs-theme ,*color-theme*
|
|
|
|
(:head (:meta :charset "utf-8")
|
2024-03-30 07:57:29 +01:00
|
|
|
(:meta :name "viewport"
|
|
|
|
:content "width=device-width, initial-scale=1")
|
2024-01-26 16:30:01 +01:00
|
|
|
,@(loop for (key value) on meta by #'cddr
|
2024-01-26 22:23:29 +01:00
|
|
|
collect `(:meta :name
|
|
|
|
,(string-downcase (symbol-name key))
|
|
|
|
:content ,(getf meta key)))
|
2024-01-26 16:30:01 +01:00
|
|
|
|
|
|
|
(:title ,title)
|
|
|
|
|
2024-05-09 13:55:44 +02:00
|
|
|
(:link :type "text/css" :rel "stylesheet" :href ,(bs-url-css))
|
2024-01-26 16:30:01 +01:00
|
|
|
,@(loop for url in add-css-urls
|
2024-03-30 07:57:29 +01:00
|
|
|
collect `(:link :type "text/css"
|
2024-06-18 17:45:15 +02:00
|
|
|
:rel "stylesheet" :href ,url)))
|
2024-06-22 14:24:55 +02:00
|
|
|
(:body ,@body)
|
|
|
|
|
|
|
|
(:script :src ,(bs-url-js))
|
|
|
|
,@(loop for url in add-js-urls
|
|
|
|
collect `(:script :src ,url)))))
|
|
|
|
|
|
|
|
(defmacro body-header (main-heading &body body)
|
|
|
|
`(spinneret:with-html
|
|
|
|
(:header
|
|
|
|
(:div :class "container text-center py-3"
|
|
|
|
(:a :href "#main-content"
|
|
|
|
:class "skip-link"
|
|
|
|
(find-l10n "skip-link" *html-lang* *l10n*)))
|
|
|
|
,@body
|
2024-06-22 14:32:11 +02:00
|
|
|
(:h1 :class "container" ,main-heading))))
|
2024-06-22 14:24:55 +02:00
|
|
|
|
|
|
|
(defmacro body-main (&optional main-con &body body)
|
|
|
|
`(spinneret:with-html
|
|
|
|
(:main :id "main-content"
|
|
|
|
,@(when main-con (list :class "container"))
|
|
|
|
,@body)))
|
2024-01-26 16:30:01 +01:00
|
|
|
|
2024-03-30 07:46:39 +01:00
|
|
|
|
|
|
|
(defun remove-special-chars (str)
|
|
|
|
"Removes all special characters from the string STR except numbers and alphabets.
|
|
|
|
|
|
|
|
STR: The input string from which special characters need to be removed.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
(remove-special-chars \"a1b!@#$%^&*()c2\") will return \"a1bc2\"
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new string with special characters removed."
|
2024-03-30 17:46:48 +01:00
|
|
|
(remove-if-not (lambda (char)
|
|
|
|
(or (alpha-char-p char) (digit-char-p char)))
|
2024-03-30 07:46:39 +01:00
|
|
|
str))
|
|
|
|
|
|
|
|
(defun clean-form-str (str)
|
|
|
|
"Cleans a form string for use as a name or identifier.
|
|
|
|
|
|
|
|
STR: The string to clean. Removes leading and trailing spaces, replaces spaces
|
|
|
|
with dashes, and converts to lowercase.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new string which can be used as HTML class."
|
|
|
|
(string-downcase (substitute #\- #\Space (string-trim '(#\Space) str))))
|
|
|
|
|
2024-04-03 10:34:08 +02:00
|
|
|
(defun build-str-name (name)
|
2024-03-30 07:57:29 +01:00
|
|
|
"Builds a standardized string by adding a 'group-' prefix and applying cleaning
|
|
|
|
functions.
|
2024-03-30 07:46:39 +01:00
|
|
|
|
|
|
|
NAME: The initial name string.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new standardized string."
|
|
|
|
(concatenate 'string "group-" (clean-form-str name)))
|
|
|
|
|
2024-04-03 10:34:08 +02:00
|
|
|
(defun build-str-value (value)
|
2024-03-30 07:46:39 +01:00
|
|
|
"Trims leading and trailing spaces from the given value string.
|
|
|
|
|
|
|
|
VALUE: The string to be cleaned.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new string without leading and trailing spaces."
|
|
|
|
(string-trim '(#\Space) value))
|
|
|
|
|
2024-04-03 10:34:08 +02:00
|
|
|
(defun build-str-value-prop (value)
|
2024-03-30 07:46:39 +01:00
|
|
|
"Builds a value property string by applying various cleaning functions.
|
|
|
|
|
|
|
|
VALUE: The initial value string.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new value property string."
|
2024-04-03 10:34:08 +02:00
|
|
|
(clean-form-str (build-str-value value)))
|
2024-03-30 07:46:39 +01:00
|
|
|
|
2024-04-03 10:34:08 +02:00
|
|
|
(defun build-str-class (class name)
|
2024-03-30 07:57:29 +01:00
|
|
|
"Builds a class string by concatenating 'form-check-label' and a standardized
|
|
|
|
name string.
|
2024-03-30 07:46:39 +01:00
|
|
|
|
|
|
|
CLASS: Corresponding class property.
|
|
|
|
|
|
|
|
NAME: The initial name string.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new class string."
|
2024-04-03 10:34:08 +02:00
|
|
|
(concatenate 'string class " " (build-str-name name)))
|
2024-03-30 07:46:39 +01:00
|
|
|
|
2024-04-03 10:34:08 +02:00
|
|
|
(defun build-str-id (name value)
|
2024-03-30 07:57:29 +01:00
|
|
|
"Builds an ID string by concatenating a standardized name string and a sanitized
|
|
|
|
value property string.
|
2024-03-30 07:46:39 +01:00
|
|
|
|
|
|
|
NAME: The initial name string.
|
|
|
|
|
|
|
|
VALUE: The initial value string.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A new ID string."
|
|
|
|
(concatenate 'string
|
2024-04-03 10:34:08 +02:00
|
|
|
(build-str-name name)
|
2024-03-30 07:46:39 +01:00
|
|
|
"-"
|
2024-04-03 10:34:08 +02:00
|
|
|
(remove-special-chars (build-str-value-prop value))))
|