cl-ds / data /repos /cl-forms /src /fields /file.lisp
j14i's picture
3375 CL macro transformation examples from 85 libraries
43203b4 verified
(in-package :forms)
(defclass file-form-field (form-field)
((multiple :initarg :multiple-p
:accessor multiple-p
:initform nil
:documentation "If this fields handles multiple file uploads")
(path :initarg :path
:accessor file-path
:initform nil)
(file-name :initarg :file-name
:accessor file-name
:initform nil)
(content-type :initarg :content-type
:accessor file-content-type
:initform nil)
(upload-handler :initarg :upload-handler
:accessor upload-handler
:initform nil
:documentation "Function that handles the file upload")
(accept :initarg :accept
:accessor file-accept
:type (or null string)
:initform nil
:documentation "Files accepted. See https://www.w3schools.com/tags/att_input_accept.asp"))
(:documentation "A file input field"))
(defmethod field-read-from-request ((field file-form-field) form parameters)
(let ((fvalue
(cdr (assoc (field-request-name field form) parameters :test #'string=))))
(if (and fvalue (listp fvalue))
(destructuring-bind (path file-name content-type) fvalue
(setf (file-path field) path)
(setf (file-name field) file-name)
(setf (file-content-type field) content-type)
(when (upload-handler field)
(funcall (upload-handler field) field)))
;; else
(progn
(warn "CL-FORMS: Could not read file field file: ~a." field)
(when (not (equalp (form-enctype form) "multipart/form-data"))
(warn "CL-FORMS: Encoding type in forms should be set to 'multipart/form-data' for file uploads."))))
;; Always set the field value
(setf (field-value field) fvalue)))
(defmethod make-form-field ((field-type (eql :file)) &rest args)
(apply #'make-instance 'file-form-field args))
;; Stateful file field
(defclass stateful-file-field (forms::file-form-field)
((download-link :initarg :download-link
:type (or null string symbol function)
:initform nil
:documentation "Function to generate the download link to the file"))
(:documentation "An stateful file field is a file field that mantains its value (the uploaded file data) and renders the the uploaded file download link and a file upload widget"))
(defmethod download-link ((field stateful-file-field))
(with-slots (download-link) field
(if (stringp download-link)
download-link
(funcall download-link field))))
(defmethod forms::field-read-from-request ((field stateful-file-field) form parameters)
(let ((fvalue
(cdr (assoc (forms::field-request-name field form) parameters :test #'string=))))
(if (and fvalue (listp fvalue))
;; A file was uploaded
(destructuring-bind (path file-name content-type) fvalue
(setf (forms::file-path field) path)
(setf (forms::file-name field) file-name)
(setf (forms::file-content-type field) content-type)
(when (forms::upload-handler field)
(funcall (forms::upload-handler field) field)))
;; else, recover file info, if it is there
(let ((file-info
(cdr (assoc (format nil "~A.info" (forms::field-request-name field form)) parameters :test #'string=))))
(when file-info
(let ((finfo (read-from-string (base64:base64-string-to-string file-info))))
(setf (forms::file-path field) (getf finfo :path)
(forms::file-name field) (getf finfo :file-name)
(forms::file-content-type field) (getf finfo :content-type))))))))
(defmethod forms::make-form-field ((field-type (eql :stateful-file)) &rest args)
(apply #'make-instance 'stateful-file-field args))