Public archive for the Return to Ritherdon project.
https://www.nicolaellisandritherdon.com
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
7.5 KiB
200 lines
7.5 KiB
(in-package #:cl-user) |
|
(defpackage #:utils |
|
(:use #:cl |
|
#:caveman2 |
|
#:log4cl |
|
#:xml-emitter |
|
#:app-constants |
|
#:storage) |
|
(:export #:request-params |
|
#:separate-files-in-web-request |
|
#:set-alert |
|
#:get-alert |
|
#:get-and-reset-alert |
|
#:checkbox-to-bool |
|
#:asciify |
|
#:slugify |
|
#:get-image-dimensions |
|
#:run-bash-command |
|
#:create-thumbnail |
|
#:create-timestamp-id |
|
#:format-filename |
|
#:format-keywords |
|
#:build-alert-string |
|
#:month-number-to-name |
|
#:build-url-root |
|
#:build-url |
|
#:create-timestamp-text) |
|
(:documentation "Utilities that do not depend on models.")) |
|
|
|
(in-package #:utils) |
|
|
|
(defun asciify (string) |
|
(str:downcase (slug:asciify string))) |
|
|
|
(defun slugify (string) |
|
"Turns a string of text into a slug." |
|
(str:downcase (slug:slugify string))) |
|
|
|
(defun format-filename (string) |
|
"Changes the filename into the system's standard. |
|
Replaces whitespace with '-' and changes everything to lowecase." |
|
(str:replace-all " " "-" (asciify string))) |
|
|
|
(defun format-keywords (string) |
|
"Formats the keywords used in the `ARCHIVE-ENTRY' class. |
|
This is mostly just over-prep'ing the keywords assuming the user |
|
enters them in the incorrect format. Meilisearch exects something like |
|
'art,welding,green paint,ritherdon'. The comma is the seperator which |
|
allows each 'keyword' to have (white-)spaces." |
|
(str:replace-all ", " "," (asciify string))) |
|
|
|
(defun request-params (request) |
|
(loop :for (key . value) :in request |
|
:collect (let ((*package* (find-package :keyword))) |
|
(read-from-string key)) |
|
:collect value)) |
|
|
|
(defun separate-files-in-web-request (request &optional request-value) |
|
"Creates a new list of 'upload' files from a web `REQUEST'. |
|
You will mostly use this for processing a multi-file upload (HTML) |
|
form. The standard value for the 'name' attribute in (file) input tag |
|
in the HTML form is `CONTENT-FILES' but you can use a different |
|
name. Just specify it in this function's `REQUEST-VALUE' argument." |
|
(loop :for item :in request |
|
if (or (string= "CONTENT-FILES" (car item)) |
|
(string= request-value (car item))) |
|
collect item)) |
|
|
|
(defun set-alert (message &optional alert-type) |
|
"Sets the alert `MESSAGE' stored in session, provide info. to users. |
|
The intention is store a `MESSAGE' across a redirect during a HTTP |
|
POST request." |
|
(cond ((string= "error" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "vomit-cat.png" message))) |
|
((string= "success" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "success-cat.png" message))) |
|
((string= "missing-data" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "sherlock-cat.png" message))) |
|
((string= "invalid-data" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "confused-cat.png" message))) |
|
((string= "created" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "disco-cat.png" message))) |
|
((string= "warning" alert-type) |
|
(setf (gethash :alert ningle:*session*) |
|
(build-alert-string alert-type "workout-cat.png" message))) |
|
(t (setf (gethash :alert ningle:*session*) message)))) |
|
|
|
(defun build-alert-string (alert-text src-image message) |
|
(format nil |
|
"<p class=\"~a\"><img alt=\"~a\" src=\"/images/alerts/~a\">~a</p>" |
|
alert-text |
|
alert-text |
|
src-image |
|
message)) |
|
|
|
(defun get-alert () |
|
"Get alert message from session data." |
|
(gethash :alert ningle:*session*)) |
|
|
|
(defun get-and-reset-alert () |
|
"Returns the `ALERT' message and clears its content from the session hash." |
|
(let ((message (get-alert))) |
|
(set-alert nil) |
|
message)) |
|
|
|
(defun checkbox-to-bool (value) |
|
"Converts a HTML Checkbox `VALUE' to a Boolean. |
|
The `VALUE' will either be 'on' or 'off'. 'Boolean' in this instance |
|
is assuming you are using SQLite and need to convert `VALUE' to an |
|
integer/number. If you are needing a traditional Boolean value, DO NOT USE |
|
THIS FUNCTION." |
|
(cond ((or (string= "checked" value) (string= "on" value)) +true+) |
|
((or (string= "off" value) (null value)) +false+) |
|
((null value) +false+))) |
|
|
|
(defun get-image-dimensions (filepath) |
|
"Uses Image Magick (via Bash) to get the resolution of an image as 'WxH'. |
|
The `FILEPATH' must be already merged with |
|
`ritherdon-archive.config::*application-root*' before you call this function." |
|
(let* ((command |
|
(format nil "identify -format \"%wx%h\" ~a" filepath)) |
|
(out-message (uiop:run-program command :output :string |
|
:ignore-error-status t |
|
:error-output :string))) |
|
out-message)) |
|
|
|
(defun run-bash-command (command) |
|
"Runs the Bash command." |
|
(uiop:run-program command :output :string |
|
:ignore-error-status t |
|
:error-output :string)) |
|
|
|
(defun create-thumbnail (storage-sub-directory file-name &optional (overwrite t)) |
|
"Runs a Bash command to convert a file to a thumbnail in /storage/media dir. |
|
The file is reduced to 512x512 pixels if bigger than that. A new file |
|
is then created with a 'thumbnail-' pre-fix. This process relies on |
|
Image Magick. So, it must be installed on the system for this function |
|
to operate properly." |
|
(run-bash-command |
|
(format nil "convert ~a -resize 512x512\\> ~a" |
|
(storage:file-exists-p "" storage-sub-directory file-name) |
|
(if (eq overwrite t) |
|
(storage:file-exists-p "" storage-sub-directory file-name) |
|
(storage:make-path "" storage-sub-directory |
|
(format nil "thumbnail-~a" file-name)))))) |
|
|
|
(defun create-timestamp-id () |
|
"Creates a integer based on time the function is called, in YYYYMMDD format." |
|
(multiple-value-bind |
|
(second minute hour day month year) |
|
(get-decoded-time) |
|
(format nil "~d~2,'0d~d~2,'0d~2,'0d~2,'0d" year month day hour minute second))) |
|
|
|
(defun create-timestamp-text () |
|
"Creates a text-based timestamp (value being the time function was called)." |
|
(multiple-value-bind |
|
(second minute hour day month year) |
|
(get-decoded-time) |
|
(format nil "~d-~2,'0d-~d_~2,'0d-~2,'0d-~2,'0d" year month day hour minute second))) |
|
|
|
(defun month-number-to-name (month-number) |
|
"Converts `MONTHS-NUMBER' to its name (E.G. 1 to 'January')." |
|
(cond ((= 1 month-number) "January") |
|
((= 2 month-number) "February") |
|
((= 3 month-number) "March") |
|
((= 4 month-number) "April") |
|
((= 5 month-number) "May") |
|
((= 6 month-number) "June") |
|
((= 7 month-number) "July") |
|
((= 8 month-number) "August") |
|
((= 9 month-number) "September") |
|
((= 10 month-number) "October") |
|
((= 11 month-number) "November") |
|
((= 12 month-number) "December") |
|
(t nil))) |
|
|
|
(defun build-url (request) |
|
"Concatenates parts of the web request to form the full URL the user requested." |
|
(format nil "~a://~a~a~a" |
|
(lack.request:request-uri-scheme request) |
|
(lack.request:request-server-name request) |
|
(if (string= "localhost" (lack.request:request-server-name request)) |
|
(format nil ":~a" (lack.request:request-server-port request)) |
|
"") |
|
(lack.request:request-uri request))) |
|
|
|
(defun build-url-root (request) |
|
"Concatenates parts of the web request to form site's root URL. |
|
This is mostly used for generating the site map (XML file for crawlers)." |
|
(format nil "~a://~a~a" |
|
(lack.request:request-uri-scheme request) |
|
(lack.request:request-server-name request) |
|
(if (string= "localhost" (lack.request:request-server-name request)) |
|
(format nil ":~a" (lack.request:request-server-port request)) |
|
"")))
|
|
|