
Author: achiumenti Date: Sat May 24 13:18:39 2008 New Revision: 48 Modified: trunk/main/claw-core/claw.asd trunk/main/claw-core/src/components.lisp trunk/main/claw-core/src/lisplet.lisp trunk/main/claw-core/src/misc.lisp trunk/main/claw-core/src/packages.lisp trunk/main/claw-core/src/server.lisp trunk/main/claw-core/src/tags.lisp trunk/main/claw-core/src/validators.lisp Log: a lot of bug fixes Modified: trunk/main/claw-core/claw.asd ============================================================================== --- trunk/main/claw-core/claw.asd (original) +++ trunk/main/claw-core/claw.asd Sat May 24 13:18:39 2008 @@ -31,7 +31,7 @@ :name "claw" :author "Andrea Chiumenti" :description "Common Lisp Active Web.A famework to write web applications" - :depends-on (:closer-mop :hunchentoot :alexandria :cl-ppcre :cl-fad :local-time :split-sequence) + :depends-on (:closer-mop :hunchentoot :alexandria :cl-ppcre :cl-fad :local-time :split-sequence :parenscript) :components ((:module src :components ((:file "packages") (:file "misc" :depends-on ("packages")) Modified: trunk/main/claw-core/src/components.lisp ============================================================================== --- trunk/main/claw-core/src/components.lisp (original) +++ trunk/main/claw-core/src/components.lisp Sat May 24 13:18:39 2008 @@ -55,7 +55,8 @@ (defun component-validation-errors (component &optional (request *request*)) "Resurns possible validation errors occurred during form rewinding bound to a specific component" (let ((client-id (htcomponent-client-id component))) - (assoc client-id (validation-errors request) :test #'equal))) + (getf (validation-errors request) (make-symbol client-id)))) + ;-------------------------------------------------------------------------------- (defclass cform (wcomponent) @@ -87,7 +88,7 @@ (defmethod wcomponent-template((cform cform)) (let ((client-id (htcomponent-client-id cform)) (class (css-class cform)) - (validation-errors (aux-request-value :validation-errors))) + (validation-errors (validation-errors))) (when validation-errors (if (or (null class) (string= class "")) (setf class "error") @@ -105,7 +106,7 @@ (setf (page-current-form pobj) obj)) (defmethod wcomponent-after-rewind ((obj cform) (pobj page)) - (let ((validation-errors (aux-request-value :validation-errors)) + (let ((validation-errors (validation-errors)) (action (action obj))) (unless validation-errors (when (or action (cform-rewinding-p obj pobj)) @@ -177,7 +178,7 @@ :reader input-type :documentation "The html <input> TYPE attribute. For submit type, use the CSUBMIT> function.")) (:metaclass metacomponent) - (:default-initargs :reserved-parameters (list :value :name) :empty t) + (:default-initargs :reserved-parameters (list :value :name) :empty t :type "text") (:documentation "Request cycle aware component the renders as an INPUT tag class")) (let ((class (find-class 'cinput))) Modified: trunk/main/claw-core/src/lisplet.lisp ============================================================================== --- trunk/main/claw-core/src/lisplet.lisp (original) +++ trunk/main/claw-core/src/lisplet.lisp Sat May 24 13:18:39 2008 @@ -196,8 +196,14 @@ (let ((resource-full-path (merge-pathnames (uri-to-pathname (subseq (script-name) (+ (length (clawserver-base-path (current-server))) - (length (lisplet-base-path (lisplet-base-path lisplet)))))) + (length (lisplet-base-path lisplet)) + (length location) 1))) resource-path))) + (log-message :info "--------------------------------------------- ~% +script-name: \"~a\"~% +resource-path: \"~a\"~% +resource-full-path :\"~a\"~% +--------------------------------------------" (script-name) resource-path resource-full-path) (handle-static-file resource-full-path content-type))) #'(lambda () (handle-static-file resource-path content-type)))) pages))))) @@ -208,10 +214,9 @@ (loop for dispatcher in dispatchers for url = (car dispatcher) for action = (cdr dispatcher) - do (cond - ((and (string< url rel-script-name) - (null (starts-with-subseq rel-script-name url))) (return nil)) - ((starts-with-subseq rel-script-name url) (return (funcall action))))))) + do (progn + (log-message :info "rel-script-name: \"~a\" url: \"~a\" --- (starts-with-subseq rel-script-name url) : ~a" rel-script-name url (starts-with-subseq rel-script-name url)) + (when (starts-with-subseq rel-script-name url) (return (funcall action))))))) (defmethod lisplet-dispatch-method ((lisplet lisplet)) (let ((base-path (build-lisplet-location lisplet)) Modified: trunk/main/claw-core/src/misc.lisp ============================================================================== --- trunk/main/claw-core/src/misc.lisp (original) +++ trunk/main/claw-core/src/misc.lisp Sat May 24 13:18:39 2008 @@ -217,6 +217,22 @@ "Resurns possible validation errors occurred during form rewinding" (aux-request-value :validation-errors request)) +(defun (setf validation-errors) (value &optional (request *request*)) + "Sets possible validation errors occurred during form rewinding" + (setf (aux-request-value :validation-errors request) value)) + +(defun validation-compliances (&optional (request *request*)) + "Resurns the list of components that pass validation during form rewinding" + (aux-request-value :validation-compliances request)) + +(defun (setf validation-compliances) (value &optional (request *request*)) + "Sets the list of components that pass validation during form rewinding" + (setf (aux-request-value :validation-compliances request) value)) + +(defun add-validation-compliance (id &optional (request *request*)) + "Adds a component id to the list of components that pass validation during form rewinding" + (setf (validation-compliances request) (nconc (validation-compliances request) (list id)))) + (defclass metacomponent (standard-class) () (:documentation "This is the meta class the must be set for every WCOMPONENT. Modified: trunk/main/claw-core/src/packages.lisp ============================================================================== --- trunk/main/claw-core/src/packages.lisp (original) +++ trunk/main/claw-core/src/packages.lisp Sat May 24 13:18:39 2008 @@ -211,6 +211,7 @@ :lisplet-protect :lisplet-authentication-type :claw-start-session + :build-lisplet-location ;; clawserver :clawserver :clawserver-base-path @@ -234,6 +235,8 @@ #-:hunchentoot-no-ssl :clawserver-ssl-certificate-file #-:hunchentoot-no-ssl :clawserver-ssl-privatekey-file #-:hunchentoot-no-ssl :clawserver-ssl-privatekey-password + :add-exception + :component-exceptions :msie-p :*id-and-static-id-description* :describe-component-behaviour @@ -273,6 +276,8 @@ :*locales* :validate :validation-errors + :validation-compliances + :add-validation-compliance :component-validation-errors :validate-required :validate-size Modified: trunk/main/claw-core/src/server.lisp ============================================================================== --- trunk/main/claw-core/src/server.lisp (original) +++ trunk/main/claw-core/src/server.lisp Sat May 24 13:18:39 2008 @@ -385,21 +385,15 @@ (when (starts-with-subseq script-name base-path) (setf rel-script-name (subseq script-name (length base-path)) rel-script-name-libs (subseq script-name (1+ (length base-path)))) - (or + (or (loop for dispatcher in *claw-libraries-resources* for url = (car dispatcher) for action = (cdr dispatcher) - do (cond - ((and (string< url rel-script-name-libs) - (null (starts-with-subseq rel-script-name-libs url))) (return nil)) - ((starts-with-subseq rel-script-name-libs url) (return (funcall action))))) + do (when (starts-with-subseq rel-script-name-libs url) (funcall action))) (loop for dispatcher in dispatchers for url = (car dispatcher) for action = (cdr dispatcher) - do (cond - ((and (string< url rel-script-name) - (null (starts-with-subseq rel-script-name url))) (return nil)) - ((starts-with-subseq rel-script-name url) (return (funcall action))))))))) + do (when (starts-with-subseq rel-script-name url) (return (funcall action)))))))) (defmethod clawserver-dispatch-method ((clawserver clawserver)) (let ((result (clawserver-dispatch-request clawserver))) Modified: trunk/main/claw-core/src/tags.lisp ============================================================================== --- trunk/main/claw-core/src/tags.lisp (original) +++ trunk/main/claw-core/src/tags.lisp Sat May 24 13:18:39 2008 @@ -15,7 +15,7 @@ ;;; disclaimer in the documentation and/or other materials ;;; provided with the distribution. -;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSEDse +;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY @@ -105,9 +105,10 @@ This internal method is called to render these scripts. - PAGE is the page instance that must be given")) -(defgeneric htbody-init-scripts-tag (page) +(defgeneric htbody-init-scripts-tag (page &optional on-load) (:documentation "Encloses the init inscance scripts injected into the page into a <script> tag component -See PAGE-BODY-INIT-SCRIPTS form more info. +See PAGE-BODY-INIT-SCRIPTS form more info. If the ON-LOAD parameter it not nil, then the script will be executed +on the onload document event. - PAGE is the page instance that must be given")) (defgeneric page-current-component (page) @@ -370,6 +371,8 @@ :accessor page-lasttag :documentation "Last rendered tag. Needed for page output rendering") (json-component-count :initarg :json-component-count :accessor page-json-component-count :documentation "Need to render the json object after an xhr call.") + (json-component-id-list :initform () + :accessor page-json-component-id-list :documentation "The current component that will ber rendered into json reply object in an xhr call.") (request-parameters :initarg :request-parameters :documentation "This slot is used to avoid PAGE-REQUEST-PARAMETERS multimple computations, saving the result of this function on the first call and then using the cached value.") (components-stack :initform nil @@ -398,6 +401,9 @@ (defclass htcomponent (i18n-aware) ((page :initarg :page :reader htcomponent-page :documentation "The owner page") + (json-render-on-validation-errors-p :initarg :json-render-on-validation-errors-p + :reader htcomponent-json-render-on-validation-errors-p + :documentation "If from submission contains exceptions and the value is not nil, the component is rendered into the xhr json reply.") (body :initarg :body :accessor htcomponent-body :documentation "The tag body") (client-id :initarg :client-id @@ -416,6 +422,7 @@ :accessor htcomponent-instance-initscript :documentation "Page injectable javascript instance derectives")) (:default-initargs :page nil :body nil + :json-render-on-validation-errors-p nil :client-id nil :attributes nil :empty nil @@ -585,14 +592,19 @@ (defun json-validation-errors () "Composes the error part for the json reply" - (let ((validation-errors (aux-request-value :validation-errors))) + (let ((validation-errors (validation-errors))) (if validation-errors - (strings-to-jsarray - (loop for component-exceptions in validation-errors - collect (format "{~a:~a}"(car component-exceptions) - (strings-to-jsarray (loop for message in (cdr component-exceptions) - collect (prin1-to-string message)))))) + (let* ((errors (loop for (component-id messages) on validation-errors by #'cddr + collect (symbol-name component-id) + collect (push 'array messages))) + (js-struct (ps:ps* `(create ,@errors)))) + (subseq js-struct 0 (1- (length js-struct)))) "null"))) + +(defun json-validation-compliances () + "Composes the compliances part to form validation for the json reply" + (let ((js-array (ps:ps* `(array ,@(validation-compliances))))) + (subseq js-array 0 (1- (length js-array))))) (defmethod page-render ((page page)) (let ((body (page-content page)) @@ -624,6 +636,8 @@ (htcomponent-render init-scripts page))) (page-format-raw page "\",errors:") (page-format-raw page (json-validation-errors)) + (page-format-raw page ",valid:") + (page-format-raw page (json-validation-compliances)) (page-format-raw page "}")))))) (defmethod page-body-init-scripts ((page page)) @@ -687,26 +701,39 @@ (let* ((id (htcomponent-client-id htcomponent)) (page (htcomponent-page htcomponent)) (print-status (page-can-print page)) - (render-p (member id (page-json-id-list page) :test #'string=))) - (or print-status render-p))) + (validation-errors (validation-errors)) + (json-render-on-validation-errors-p (htcomponent-json-render-on-validation-errors-p htcomponent)) + (render-p (or (and (member id (page-json-id-list page) :test #'string=) + (null validation-errors)) + print-status))) + #|json-render-on-validation-errors-p|# + (or json-render-on-validation-errors-p print-status render-p))) (defmethod htcomponent-json-print-start-component ((htcomponent htcomponent)) (let* ((page (htcomponent-page htcomponent)) (jsonp (page-json-id-list page)) - (id (htcomponent-client-id htcomponent))) + (id (htcomponent-client-id htcomponent)) + (validation-errors (validation-errors))) (when (and jsonp - (member id jsonp :test #'string-equal)) + (or (and (null validation-errors) + (member id jsonp :test #'string-equal)) + (htcomponent-json-render-on-validation-errors-p htcomponent))) (when (> (page-json-component-count page) 0) (page-format page ",")) (page-format-raw page "~a:\"" id) + (push id (page-json-component-id-list page)) (incf (page-json-component-count page))))) (defmethod htcomponent-json-print-end-component ((htcomponent htcomponent)) (let* ((page (htcomponent-page htcomponent)) (jsonp (page-json-id-list page)) - (id (htcomponent-client-id htcomponent))) + (id (htcomponent-client-id htcomponent)) + (validation-errors (validation-errors))) (when (and jsonp - (member id jsonp :test #'string-equal)) + (or (and (null validation-errors) + (member id jsonp :test #'string-equal)) + (htcomponent-json-render-on-validation-errors-p htcomponent))) + (pop (page-json-component-id-list page)) (page-format-raw page "\"")))) (defmethod htcomponent-rewind :before ((htcomponent htcomponent) (page page)) @@ -776,7 +803,7 @@ (page-format page " ~a=\"~a\"" (string-downcase (if (eq k :static-id) "id" - (symbol-name k))) + (parenscript::symbol-to-js k))) (let ((s (if (eq k :id) (prin1-to-string (htcomponent-client-id tag)) (prin1-to-string v)))) ;escapes double quotes @@ -784,26 +811,32 @@ (defmethod tag-render-starttag ((tag tag) (page page)) (let ((tagname (tag-name tag)) + (id (htcomponent-client-id tag)) + (jsonp (page-json-id-list page)) (emptyp (htcomponent-empty tag)) (xml-p (page-xmloutput page))) (setf (page-lasttag page) tagname) - (page-newline page) - (page-print-tabulation page) - (page-format page "<~a" tagname) - (tag-render-attributes tag page) - (if (null emptyp) - (progn - (page-format page ">") - (incf (page-tabulator page))) - (if (null xml-p) + (unless (and jsonp (string= id (first (page-json-component-id-list page)))) + (page-newline page) + (page-print-tabulation page) + (page-format page "<~a" tagname) + (tag-render-attributes tag page) + (if (null emptyp) + (progn (page-format page ">") - (page-format page "/>"))))) + (incf (page-tabulator page))) + (if (null xml-p) + (page-format page ">") + (page-format page "/>")))))) (defmethod tag-render-endtag ((tag tag) (page page)) (let ((tagname (tag-name tag)) + (id (htcomponent-client-id tag)) + (jsonp (page-json-id-list page)) (previous-tagname (page-lasttag page)) (emptyp (htcomponent-empty tag))) - (when (null emptyp) + (when (and (null emptyp) (not (and jsonp + (string= id (first (page-json-component-id-list page)))))) (progn (decf (page-tabulator page)) (if (string= tagname previous-tagname) @@ -906,8 +939,8 @@ (dolist (element body) (when element (cond - ((stringp element) (htcomponent-render ($> element) page)) - ((functionp element) (htcomponent-render ($> (funcall element)) page)) + ((stringp element) (htcomponent-render ($raw> element) page)) + ((functionp element) (htcomponent-render ($raw> (funcall element)) page)) (t (htcomponent-render element page))))) (if (null xml-p) (page-format page "~%//-->") @@ -952,20 +985,22 @@ ((functionp child-tag) (htcomponent-render ($> (funcall child-tag)) page)) (t (htcomponent-render child-tag page))))) (when (page-can-print page) - (htcomponent-render (htbody-init-scripts-tag page) page) + (htcomponent-render (htbody-init-scripts-tag page t) page) (tag-render-endtag htbody page)) (when (or (page-can-print page) previous-print-status) (setf (page-can-print page) nil) (htcomponent-json-print-end-component htbody)))) -(defmethod htbody-init-scripts-tag ((page page)) +(defmethod htbody-init-scripts-tag ((page page) &optional on-load) (let ((js (script> :type "text/javascript")) - (js-start-directive (if (msie-p) - "window.attachEvent\('onload', function\(e) {" - "document.addEventListener\('DOMContentLoaded', function\(e) {")) - (js-end-directive (if (msie-p) - "});" - "}, false);")) + (js-start-directive (if on-load (if (msie-p) + "window.attachEvent\('onload', function\(e) {" + "document.addEventListener\('DOMContentLoaded', function\(e) {") + "")) + (js-end-directive (if on-load (if (msie-p) + "});" + "}, false);") + "")) (page-body-init-scripts (page-body-init-scripts page))) (setf (htcomponent-page js) page (htcomponent-body js) (when page-body-init-scripts @@ -982,6 +1017,9 @@ :accessor wcomponent-reserved-parameters :type cons :documentation "Parameters that may not be used in the constructor function") + (json-error-monitor-p :initarg :json-error-monitor-p + :accessor htcomponent-json-error-monitor-p + :documentation "When not nil, if the client has sent a XHR call, let the page to fill the errorComponents property of the json reply.") (informal-parameters :initform () :accessor wcomponent-informal-parameters :type cons Modified: trunk/main/claw-core/src/validators.lisp ============================================================================== --- trunk/main/claw-core/src/validators.lisp (original) +++ trunk/main/claw-core/src/validators.lisp Sat May 24 13:18:39 2008 @@ -39,44 +39,47 @@ (decode-local-time local-time) (declare (ignore nsec)) (loop for result = "" then (concatenate 'string result (if (stringp element) - element - (ccase element - (:second (format nil "~2,'0D" sec)) - (:minute (format nil "~2,'0D" min)) - (:hour (format nil "~2,'0D" hour)) - (:date (format nil "~2,'0D" day)) - (:month (format nil "~2,'0D" month)) - (:year (format nil "~4,'0D" year))))) + element + (ccase element + (:second (format nil "~2,'0D" sec)) + (:minute (format nil "~2,'0D" min)) + (:hour (format nil "~2,'0D" hour)) + (:date (format nil "~2,'0D" day)) + (:month (format nil "~2,'0D" month)) + (:year (format nil "~4,'0D" year))))) for element in format finally (return result)))) (defun add-exception (id reason) -"Adds an exception for the given input component identified by its ID with the message expressed by REASON" - (let* ((validation-errors (aux-request-value :validation-errors)) - (component-exceptions (assoc id validation-errors :test #'equal))) - (if component-exceptions - (setf (cdr component-exceptions) (append (cdr component-exceptions) (list reason))) - (if validation-errors - (setf (aux-request-value :validation-errors) (append validation-errors (list (cons id (list reason))))) - (setf (aux-request-value :validation-errors) (list (cons id (list reason)))))))) - + "Adds an exception for the given input component identified by its ID with the message expressed by REASON" + (let* ((validation-errors (validation-errors)) + (symbol-id (make-symbol id)) + (errors (getf validation-errors symbol-id))) + (setf (getf validation-errors symbol-id) (nconc errors (list reason)) + (validation-errors *request*) validation-errors))) + +(defun component-exceptions (id) + "Returns a list of exception connectd to the given component" + (let ((symbol-id (make-symbol id))) + (getf (validation-errors) symbol-id))) (defun validate (test &key component message) -"When test is nil, an exception message given by MESSAGE is added for the COMPONENT. See: ADD-EXCEPTION..." + "When test is nil, an exception message given by MESSAGE is added for the COMPONENT. See: ADD-EXCEPTION..." (let ((client-id (htcomponent-client-id component))) - (unless test - (add-exception client-id message)))) + (if test + (add-validation-compliance client-id) + (add-exception client-id message)))) (defun validate-required (component value) - "Checks if the required input field VALUE is present. If not, a localizable message \"Field ~a may not be null.\" is sent with key \"VALIDATE-REQUIRED\". + "Checks if the required input field VALUE is present. If not, a localizable message \"Field ~a may not be empty.\" is sent with key \"VALIDATE-REQUIRED\". The argument for the message will be the :label attribute of the COMPONENT." (when (stringp value) (validate (and value (string-not-equal value "")) - :component component - :message (format nil (do-message "VALIDATE-REQUIRED" "Field ~a may not be null.") (label component))))) + :component component + :message (format nil (do-message "VALIDATE-REQUIRED" "Field ~a may not be empty.") (label component))))) (defun validate-size (component value &key min-size max-size) -"Checks if the input field VALUE legth is less then or greater then rispectively of the form keywords :MIN-SIZE and :MAX-SIZE. + "Checks if the input field VALUE legth is less then or greater then rispectively of the form keywords :MIN-SIZE and :MAX-SIZE. If less then :MIN-SIZE, a localizable message \"Size of ~a may not be less then ~a chars.\" is sent with key \"VALIDATE-SIZE-MIN\". The argument for the message will be the :label attribute of the COMPONENT and the :MIN-ZIZE value. If greater then :MAX-SIZE, a localizable message \"Size of ~a may not be more then ~a chars\" is sent with key \"VALIDATE-SIZE-MAX\". @@ -86,64 +89,64 @@ (setf value (format nil "~a" value)) (setf value-len (length value)) (and (= value-len 0) - (when min-size - (validate (>= value-len min-size) - :component component - :message (format nil (do-message "VALIDATE-SIZE-MIN" "Size of ~a may not be less then ~a chars." ) - (label component) - min-size))) - (when max-size - (validate (<= value-len max-size) - :component component - :message (format nil (do-message "VALIDATE-SIZE-MAX" "Size of ~a may not be more then ~a chars." ) - (label component) - max-size))))))) + (when min-size + (validate (>= value-len min-size) + :component component + :message (format nil (do-message "VALIDATE-SIZE-MIN" "Size of ~a may not be less then ~a chars." ) + (label component) + min-size))) + (when max-size + (validate (<= value-len max-size) + :component component + :message (format nil (do-message "VALIDATE-SIZE-MAX" "Size of ~a may not be more then ~a chars." ) + (label component) + max-size))))))) (defun validate-range (component value &key min max) -"Checks if the numeric input field VALUE is less then or greater then rispectively of the form keywords :MIN and :MAX. + "Checks if the numeric input field VALUE is less then or greater then rispectively of the form keywords :MIN and :MAX. If less then :MIN, a localizable message \"Field ~a is not less then or equal to ~d.\" is sent with key \"VALIDATE-RANGE-MIN\". The argument for the message will be the :label attribute of the COMPONENT and the :MIN value. If greater then :MIN, a localizable message \"Field ~a is not greater then or equal to ~d.\" is sent with key \"VALIDATE-RANGE-MAX\". The argument for the message will be the :label attribute of the COMPONENT and the :MAX value." (when value (and (when min - (validate (>= value min) - :component component - :message (format nil (do-message "VALIDATE-RANGE-MIN" "Field ~a is not greater then or equal to ~d") - (label component) - (if (typep min 'ratio) - (coerce min 'float) - min)))) - (when max - (validate (<= value max) - :component component - :message (format nil (do-message "VALIDATE-RANGE-MAX" "Field ~a is not less then or equal to ~d") - (label component) - (if (typep max 'ratio) - (coerce max 'float) - max))))))) + (validate (>= value min) + :component component + :message (format nil (do-message "VALIDATE-RANGE-MIN" "Field ~a is not greater then or equal to ~d") + (label component) + (if (typep min 'ratio) + (coerce min 'float) + min)))) + (when max + (validate (<= value max) + :component component + :message (format nil (do-message "VALIDATE-RANGE-MAX" "Field ~a is not less then or equal to ~d") + (label component) + (if (typep max 'ratio) + (coerce max 'float) + max))))))) (defun validate-number (component value &key min max) -"Checks if the input field VALUE is a valid number and then passes the validation to VALIDATION-RANGE. + "Checks if the input field VALUE is a valid number and then passes the validation to VALIDATION-RANGE. If not a number, a localizable message \"Field ~a is not a valid number.\" is sent with key \"VALIDATE-NUMBER\". The argument for the message will be the :label attribute of the COMPONENT." (when value (let ((test (numberp value))) (and (validate test - :component component - :message (format nil (do-message "VALIDATE-NUMBER" "Field ~a is not a valid number.") (label component))) - (validate-range component value :min min :max max))))) + :component component + :message (format nil (do-message "VALIDATE-NUMBER" "Field ~a is not a valid number.") (label component))) + (validate-range component value :min min :max max))))) (defun validate-integer (component value &key min max) -"Checks if the input field VALUE is a valid number and then passes the validation to VALIDATION-RANGE. + "Checks if the input field VALUE is a valid number and then passes the validation to VALIDATION-RANGE. If not a number, a localizable message \"Field ~a is not a valid integer.\" is sent with key \"VALIDATE-INTEGER\". The argument for the message will be the :label attribute of the COMPONENT." (when value (let ((test (integerp value))) (and (validate test - :component component - :message (format nil (do-message "VALIDATE-INTEGER" "Field ~a is not a valid integer.") (label component))) - (validate-range component value :min min :max max))))) + :component component + :message (format nil (do-message "VALIDATE-INTEGER" "Field ~a is not a valid integer.") (label component))) + (validate-range component value :min min :max max))))) (defun validate-date-range (component value &key min max (use-date-p t) use-time-p) @@ -157,64 +160,69 @@ The argument for the message will be the :label attribute of the COMPONENT and the value passed to :MAX parsed with the :LOCAL-TIME-FORMAT keyword." (unless (component-validation-errors component) (let ((local-time-format '(:date "-" :month "-" :year)) - (new-value (make-instance 'local-time - :nsec (nsec-of value) - :sec (sec-of value) - :day (day-of value) - :timezone (timezone-of value)))) + (new-value (make-instance 'local-time + :nsec (nsec-of value) + :sec (sec-of value) + :day (day-of value) + :timezone (timezone-of value)))) (when (and use-date-p (not use-time-p)) - (setf (local-time:nsec-of new-value) 0 - (local-time:sec-of new-value) 0) - (when min - (setf (local-time:nsec-of min) 0 - (local-time:sec-of min) 0)) - (when max - (setf (local-time:nsec-of max) 0 - (local-time:sec-of max) 0))) + (setf (local-time:nsec-of new-value) 0 + (local-time:sec-of new-value) 0) + (when min + (setf (local-time:nsec-of min) 0 + (local-time:sec-of min) 0)) + (when max + (setf (local-time:nsec-of max) 0 + (local-time:sec-of max) 0))) (when (and (not use-date-p) use-time-p) - (setf (local-time:day-of new-value) 0) - (when min - (setf (local-time:day-of min) 0)) - (when max - (setf (local-time:day-of max) 0))) + (setf (local-time:day-of new-value) 0) + (when min + (setf (local-time:day-of min) 0)) + (when max + (setf (local-time:day-of max) 0))) (and (when min - (validate (local-time> new-value min) - :component component - :message (format nil (do-message "VALIDATE-DATE-RANGE-MIN" "Field ~a is less then ~a.") - (label component) - (local-time-to-string min local-time-format)))) - (when max - (validate (local-time< new-value max) - :component component - :message (format nil (do-message "VALIDATE-DATE-RANGE-MAX" "Field ~a is greater then ~a.") - (label component) - (local-time-to-string max local-time-format)))))))) - + (validate (local-time> new-value min) + :component component + :message (format nil (do-message "VALIDATE-DATE-RANGE-MIN" "Field ~a is less then ~a.") + (label component) + (local-time-to-string min local-time-format)))) + (when max + (validate (local-time< new-value max) + :component component + :message (format nil (do-message "VALIDATE-DATE-RANGE-MAX" "Field ~a is greater then ~a.") + (label component) + (local-time-to-string max local-time-format)))))))) + ;; ------------------------------------------------------------------------------------ (defclass exception-monitor (wcomponent) () (:metaclass metacomponent) - (:default-initargs :empty t) + (:default-initargs :json-render-on-validation-errors-p t) (:documentation "If from submission contains exceptions. It displays exception messages")) (let ((class (find-class 'exception-monitor))) (closer-mop:ensure-finalized class) (setf (documentation (find-symbol (format nil "~a>" (class-name class))) 'function) - (format nil "Description: ~a~%Parameters:~%~a~a~%~%~a" - "If from submission contains exceptions. It displays exception messages with a <ul> list" - *id-and-static-id-description* - (describe-html-attributes-from-class-slot-initargs class) - (describe-component-behaviour class)))) + (format nil "Description: ~a~%Parameters:~%~a~a~%~%~a" + "If from submission contains exceptions. It displays exception messages with a <ul> list" + *id-and-static-id-description* + (describe-html-attributes-from-class-slot-initargs class) + (describe-component-behaviour class)))) (defmethod wcomponent-template ((exception-monitor exception-monitor)) (let ((client-id (htcomponent-client-id exception-monitor)) - (validation-errors (aux-request-value :validation-errors))) - (when validation-errors - (ul> :static-id client-id - (wcomponent-informal-parameters exception-monitor) - (loop for component-exceptions in validation-errors - collect (loop for message in (cdr component-exceptions) - collect (li> message))))))) + (validation-errors (validation-errors)) + (body (htcomponent-body exception-monitor))) + (div> :static-id client-id + (wcomponent-informal-parameters exception-monitor) + (when validation-errors + (if body + body + (ul> + (loop for component-exceptions in (rest validation-errors) by #'cddr + do (loop for message in component-exceptions + collect (li> message))))))))) + ;;-------------------------------------------------------------------------------------------