File size: 2,969 Bytes
43203b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
(in-package :forms)

(defparameter +default-date-format+
  local-time:+iso-8601-date-format+)

(defclass date-form-field (form-field)
  ((date-min :initarg :date-min
             :accessor date-min
             :initform nil)
   (date-max :initarg :date-max
             :accessor date-max
             :initform nil)
   (date-format :initarg :date-format
                :accessor date-format
                :initform +default-date-format+)
   (widget :initarg :widget
           :accessor field-widget
           :initform nil))
  (:default-initargs
   :placeholder "YYYY-MM-DD")
  (:documentation "A date input field"))

(defclass date-validator (clavier:validator)
  ((date-format :initarg :date-format
                :accessor date-format
                :initform +default-date-format+))
  (:metaclass c2mop:funcallable-standard-class)
  (:default-initargs
   :message (lambda (validator object)
              (declare (ignorable validator object))
              "The date is invalid"))
  (:documentation "A date validator. TODO: should perhaps be part of clavier validators"))

(defmethod clavier::%validate ((validator date-validator) object &rest args)
  (declare (ignore args))
  (and
   (local-time:parse-timestring object
                                :fail-on-error nil
                                :date-separator #\-
                                :allow-missing-time-part t)
   t))

;;(clavier:validate (make-instance 'date-validator) "2003-10-10")

(defmethod validate-form-field ((form-field date-form-field))
  (let ((validator (clavier:fn (lambda (date)
                                 (typep date 'local-time:timestamp))
                               (or (field-invalid-message form-field)
                                   "The date is invalid"))))
    (multiple-value-bind (valid-p error)
        (funcall validator
                 (field-value form-field))
      (multiple-value-bind (valid-constraints-p errors)
          (call-next-method)
        (values (and valid-p valid-constraints-p)
                (if error (cons error errors)
                    errors))))))

(defmethod field-read-from-request ((field date-form-field) form parameters)
  (let ((value (cdr (assoc (field-request-name field form) parameters :test #'string=))))
    (setf (field-value field)
          (or
           (and value
                (local-time:parse-timestring
                 value
                 :date-separator #\-
                 :allow-missing-time-part t
                 :fail-on-error nil))
           (if (string= value "")
               nil
               value)))))

(defmethod format-field-value ((field date-form-field) value &optional (stream *standard-output*))
  (if (typep value 'local-time:timestamp)
      (local-time:format-timestring stream value :format (date-format field))
      (call-next-method)))

(defmethod make-form-field ((field-type (eql :date)) &rest args)
  (apply #'make-instance 'date-form-field args))