File size: 3,055 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
81
(in-package :forms)

(defparameter +default-datetime-format+
  '((:YEAR 4) #\- (:MONTH 2) #\- (:DAY 2) #\T (:HOUR 2) #\: (:MIN 2) #\: (:SEC 2)))

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

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

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

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

(defmethod validate-form-field ((form-field datetime-form-field))
  (let ((validator (clavier:fn (lambda (datetime)
                                 (typep datetime 'local-time:timestamp))
                               (or (field-invalid-message form-field)
                                   "The datetime 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 datetime-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 nil
                 :fail-on-error nil))
           (if (string= value "")
               nil
               value)))))

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

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