File: forms2.lisp

package info (click to toggle)
kpax 20080304-2
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 812 kB
  • ctags: 952
  • sloc: lisp: 6,630; makefile: 93
file content (224 lines) | stat: -rw-r--r-- 9,427 bytes parent folder | download | duplicates (2)
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
;;;; -*- mode: lisp -*-
;;;;
;;;; $Id: forms2.lisp,v 1.19 2004/09/10 13:28:17 sven Exp $
;;;;
;;;; Testing form processing, error handling with web form abstraction
;;;; The example is the management of a list of user records
;;;;
;;;; Copyright (C) 2004 Sven Van Caekenberghe, Beta Nine BVBA. All Rights Reserved.
;;;;
;;;; You are granted the rights to distribute and use this software
;;;; as governed by the terms of the Lisp Lesser GNU Public License
;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
;;;;

(in-package :kpax-user)

;;; the demo model

(defparameter *interests* '(sex drugs rock-and-roll hacking))

(defparameter *languages* '(english french dutch german))

(defparameter *music-tastes* '(rock pop punk alternative jazz new-wave))

(defparameter *genders* '(:male :female))
 
(defclass demo-user ()
  (id fullname username password notes age gender language interests enabled-p music-tastes))

(defvar *demo-users* (make-hash-table))

(defun list-demo-users ()
  (loop for demo-user being the hash-values in *demo-users* 
        collect demo-user))
 
(defun demo-user-with-id (id)
  (gethash id *demo-users*))

(defun create-new-demo-user ()
  (let ((new-id (1+ (hash-table-count *demo-users*))) ;; this is wrong !! (cfr. nicky)
        (demo-user (make-instance 'demo-user)))
    (with-slots (id fullname username password notes age gender language interests enabled-p music-tastes) 
        demo-user
      (setf id new-id fullname nil username nil password nil notes nil age 0 
            gender nil language 'english interests nil enabled-p t music-tastes nil))
    (setf (gethash new-id *demo-users*) demo-user)
    demo-user))

(defun remove-demo-user (demo-user)
  (with-slots (id) demo-user
    (remhash id *demo-users*)))

;;; the demo web form

(defun parse-symbol-kpax-user (string)
  (parse-symbol string :kpax-user))

(defun contains-no-spaces (value)
  (if (stringp value)
      (if (find #\space value) 
          (values nil :contains-spaces) 
        t) 
    t))

(defwebform demo-web-form
  ((:group personal-info 
    :label "Personal Info"
    :members ((fullname :text :label "Fullname")
              (username :text :label "Username" 
                        :options (:size 10)
                        :validator (all required (limited-string 8 4) contains-no-spaces))
              (password :password :label "Password"
                        :options (:size 10)
                        :validator (or optional (limited-string 32)))
              (password2 :password :label "Password"
                         :options (:size 10) :comment "[Confirmation]"
                         :validator (or optional (limited-string 32)))
              (gender :choice
                      :options (:values *genders* :style :buttons)
                      :parser parse-keyword :formatter string-capitalize
                      :validator (or optional (list-element *genders*)))
              (age :text :label "Age" 
                   :options (:size 4)
                   :parser s-utils:parse-integer-safely 
                   :validator (integer-range 0 150))))
   (:group admin-info
    :label "Admin Info"
    :members ((notes :text-area :label "Notes"
                     :options (:cols 40 :rows 10)
                     :validator (or optional (limited-string 256)))
              (enabled-p :choice :label "Enabled"
                         :options (:values :boolean)
                         :parser parse-boolean
                         :validator boolean)))
   (:group preferences
    :label "Preferences"
    :members ((interests :choice
                         :options (:values *interests* :selection :multiple :style :buttons)
                         :parser parse-symbol-kpax-user :formatter string-capitalize
                         :validator (or optional (list-elements *interests*)))
              (language :choice
                        :options (:values *languages* :style :list)
                        :parser parse-symbol-kpax-user :formatter string-capitalize
                        :validator (or optional (list-element *languages*)))
              (music-tastes :choice
                            :options (:values *music-tastes* :style :list :selection :multiple)
                            :parser parse-symbol-kpax-user :formatter string-capitalize
                            :validator (or optional (list-elements *music-tastes*)))))
   (id :hidden))
  (:title "Demo User")
  (:validator validate-demo-web-form)
  (:submit process-demo-web-form))

(defun validate-demo-web-form (form)
  (let ((password-1 (field-value form 'password))
        (password-2 (field-value form 'password2)))
    (if (or (equal password-1 password-2)
            (and (null password-1) (null password-2)))
        t
      (values nil "You did not confirm your password properly: fill it in twice"))))

(defun populate-demo-web-form (demo-user)
  (let ((web-form (instanciate-web-form 'demo-web-form)))
    (copy-slots-object->form demo-user web-form 
                             '(id username fullname age notes enabled-p interests gender language music-tastes))
    (setf (get-submit-text web-form) "Update")
    web-form))

(defun new-demo-web-form ()
  (let ((web-form (instanciate-web-form 'demo-web-form)))
    (setf (get-submit-text web-form) "Create")
    (setf (field-value web-form 'age) 0)
    web-form))

(defun commit-demo-web-form (web-form)
  (let* ((id (field-value web-form 'id))
         (demo-user (demo-user-with-id (s-utils:parse-integer-safely id))))
    (when (and (null demo-user) (null id))
      (setf demo-user (create-new-demo-user)))
    (when demo-user
      ;; don't copy password when it is nil (users can't clear passwords, only change them)
      (let ((slots-to-copy '(username fullname age notes enabled-p interests gender language music-tastes)))
        (when (field-value web-form 'password) 
            (push 'password slots-to-copy))
        (copy-slots-form->object web-form demo-user slots-to-copy)))))

;;; the web app definition

(defwebapp :forms2
  (:index 'forms2-index)
  (:unsecure t))

(defun forms2-index (request-response)
  (html-page (out request-response)
    (:html 
     (:head 
      (:title "Demo Users Management")
      (:link :rel "stylesheet" :type "text/css" :href (static-url request-response :server "nx.css")))
     (:body 
      (:h1 "Demo Users Management")
      (:table :class "NX_table" :width "100%"
       (:tr (:th "ID") (:th "Username") (:th "Fullname") (:th "Actions"))
       (dolist (demo-user (list-demo-users))
         (with-slots (id username fullname) demo-user
           (htm
            (:tr 
             (:td (str id)) (:td (str username)) (:td (str fullname))
             (:td
              (:a :href (dynamic-url request-response 'edit-demo-user :id id) "Edit")
              " | "
              (:a :href (dynamic-url request-response 'delete-demo-user :id id) "Delete")))))))
      (:div :class "NX_button_group" :style "margin-top:20px"
       (:a :class "NX_button" :href (dynamic-url request-response 'new-demo-user) "New User")
       (:a :class "NX_button" :href (dynamic-url request-response nil) "Refresh"))))))

(defun new-demo-user (request-response)
  (present-demo-web-form request-response (new-demo-web-form)))

(defun edit-demo-user (request-response)
  (let* ((id (s-utils:parse-integer-safely (get-request-parameter-value request-response "id")))
         (demo-user (demo-user-with-id id)))
    (if demo-user
        (present-demo-web-form request-response (populate-demo-web-form demo-user))
      (demo-error request-response "Unknown demo user id"))))

(defun present-demo-web-form (request-response demo-web-form)
  (let ((*locale* :en)
        (*localization-source* (or *simple-localization-source* (init-localized-strings))))
    (html-page (out request-response)
      (:html 
       (:head
        (:title (str (get-title demo-web-form)))
        (:link :rel "stylesheet" :type "text/css" :href (static-url request-response :server "nx.css")))
       (:body 
        (render demo-web-form request-response (make-options '(:div-form "NX_form")
                                                             *default-web-form-render-options*))
        (:div :class "NX_button_group" :style ""
         (:a :class "NX_button" :href (dynamic-url request-response nil) "List All")))))))

(defun process-demo-web-form (request-response)
  (let ((demo-web-form (reconstruct-web-form request-response 'demo-web-form)))
    (setf (get-attribute request-response :web-form) demo-web-form) ;; for debugging
    (if (validate demo-web-form)
        (progn
          (commit-demo-web-form demo-web-form)
          (forms2-index request-response))
      (present-demo-web-form request-response demo-web-form))))

(defun delete-demo-user (request-response)
  (let* ((id (s-utils:parse-integer-safely (get-request-parameter-value request-response "id")))
         (demo-user (demo-user-with-id id)))
    (if demo-user
        (progn
          (remove-demo-user demo-user)
          (forms2-index request-response))
      (demo-error request-response "Unknown demo user id"))))

(defun demo-error (request-response message)
  (html-message request-response "Error" "~a ~a" 
                message 
                (with-html-output-to-string (out) 
                  (:a :href (dynamic-url request-response nil) "OK"))))

;;;; eof