Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / validation.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "fmt"
9         "net/http"
10         "net/url"
11         "regexp"
12         "runtime"
13 )
14
15 // ValidationError simple struct to store the Message & Key of a validation error
16 type ValidationError struct {
17         Message, Key string
18 }
19
20 // String returns the Message field of the ValidationError struct.
21 func (e *ValidationError) String() string {
22         if e == nil {
23                 return ""
24         }
25         return e.Message
26 }
27
28 // Validation context manages data validation and error messages.
29 type Validation struct {
30         Errors     []*ValidationError
31         Request    *Request
32         Translator func(locale, message string, args ...interface{}) string
33         keep       bool
34 }
35
36 // Keep tells revel to set a flash cookie on the client to make the validation
37 // errors available for the next request.
38 // This is helpful  when redirecting the client after the validation failed.
39 // It is good practice to always redirect upon a HTTP POST request. Thus
40 // one should use this method when HTTP POST validation failed and redirect
41 // the user back to the form.
42 func (v *Validation) Keep() {
43         v.keep = true
44 }
45
46 // Clear *all* ValidationErrors
47 func (v *Validation) Clear() {
48         v.Errors = []*ValidationError{}
49 }
50
51 // HasErrors returns true if there are any (ie > 0) errors. False otherwise.
52 func (v *Validation) HasErrors() bool {
53         return len(v.Errors) > 0
54 }
55
56 // ErrorMap returns the errors mapped by key.
57 // If there are multiple validation errors associated with a single key, the
58 // first one "wins".  (Typically the first validation will be the more basic).
59 func (v *Validation) ErrorMap() map[string]*ValidationError {
60         m := map[string]*ValidationError{}
61         for _, e := range v.Errors {
62                 if _, ok := m[e.Key]; !ok {
63                         m[e.Key] = e
64                 }
65         }
66         return m
67 }
68
69 // Error adds an error to the validation context.
70 func (v *Validation) Error(message string, args ...interface{}) *ValidationResult {
71         result := v.ValidationResult(false).Message(message, args...)
72         v.Errors = append(v.Errors, result.Error)
73         return result
74 }
75
76 // Error adds an error to the validation context.
77 func (v *Validation) ErrorKey(message string, args ...interface{}) *ValidationResult {
78         result := v.ValidationResult(false).MessageKey(message, args...)
79         v.Errors = append(v.Errors, result.Error)
80         return result
81 }
82
83 // Error adds an error to the validation context.
84 func (v *Validation) ValidationResult(ok bool) *ValidationResult {
85         if ok {
86                 return &ValidationResult{Ok: ok}
87         } else {
88                 return &ValidationResult{Ok: ok, Error: &ValidationError{}, Locale: v.Request.Locale, Translator: v.Translator}
89         }
90 }
91
92 // ValidationResult is returned from every validation method.
93 // It provides an indication of success, and a pointer to the Error (if any).
94 type ValidationResult struct {
95         Error      *ValidationError
96         Ok         bool
97         Locale     string
98         Translator func(locale, message string, args ...interface{}) string
99 }
100
101 // Key sets the ValidationResult's Error "key" and returns itself for chaining
102 func (r *ValidationResult) Key(key string) *ValidationResult {
103         if r.Error != nil {
104                 r.Error.Key = key
105         }
106         return r
107 }
108
109 // Message sets the error message for a ValidationResult. Returns itself to
110 // allow chaining.  Allows Sprintf() type calling with multiple parameters
111 func (r *ValidationResult) Message(message string, args ...interface{}) *ValidationResult {
112         if r.Error != nil {
113                 if len(args) == 0 {
114                         r.Error.Message = message
115                 } else {
116                         r.Error.Message = fmt.Sprintf(message, args...)
117                 }
118         }
119         return r
120 }
121
122 // Allow a message key to be passed into the validation result. The Validation has already
123 // setup the translator to translate the message key
124 func (r *ValidationResult) MessageKey(message string, args ...interface{}) *ValidationResult {
125         if r.Error == nil {
126                 return r
127         }
128
129         // If translator found, use that to create the message, otherwise call Message method
130         if r.Translator != nil {
131                 r.Error.Message = r.Translator(r.Locale, message, args...)
132         } else {
133                 r.Message(message, args...)
134         }
135
136         return r
137 }
138
139 // Required tests that the argument is non-nil and non-empty (if string or list)
140 func (v *Validation) Required(obj interface{}) *ValidationResult {
141         return v.apply(Required{}, obj)
142 }
143
144 func (v *Validation) Min(n int, min int) *ValidationResult {
145         return v.MinFloat(float64(n), float64(min))
146 }
147
148 func (v *Validation) MinFloat(n float64, min float64) *ValidationResult {
149         return v.apply(Min{min}, n)
150 }
151
152 func (v *Validation) Max(n int, max int) *ValidationResult {
153         return v.MaxFloat(float64(n), float64(max))
154 }
155
156 func (v *Validation) MaxFloat(n float64, max float64) *ValidationResult {
157         return v.apply(Max{max}, n)
158 }
159
160 func (v *Validation) Range(n, min, max int) *ValidationResult {
161         return v.RangeFloat(float64(n), float64(min), float64(max))
162 }
163
164 func (v *Validation) RangeFloat(n, min, max float64) *ValidationResult {
165         return v.apply(Range{Min{min}, Max{max}}, n)
166 }
167
168 func (v *Validation) MinSize(obj interface{}, min int) *ValidationResult {
169         return v.apply(MinSize{min}, obj)
170 }
171
172 func (v *Validation) MaxSize(obj interface{}, max int) *ValidationResult {
173         return v.apply(MaxSize{max}, obj)
174 }
175
176 func (v *Validation) Length(obj interface{}, n int) *ValidationResult {
177         return v.apply(Length{n}, obj)
178 }
179
180 func (v *Validation) Match(str string, regex *regexp.Regexp) *ValidationResult {
181         return v.apply(Match{regex}, str)
182 }
183
184 func (v *Validation) Email(str string) *ValidationResult {
185         return v.apply(Email{Match{emailPattern}}, str)
186 }
187
188 func (v *Validation) IPAddr(str string, cktype ...int) *ValidationResult {
189         return v.apply(IPAddr{cktype}, str)
190 }
191
192 func (v *Validation) MacAddr(str string) *ValidationResult {
193         return v.apply(IPAddr{}, str)
194 }
195
196 func (v *Validation) Domain(str string) *ValidationResult {
197         return v.apply(Domain{}, str)
198 }
199
200 func (v *Validation) URL(str string) *ValidationResult {
201         return v.apply(URL{}, str)
202 }
203
204 func (v *Validation) PureText(str string, m int) *ValidationResult {
205         return v.apply(PureText{m}, str)
206 }
207
208 func (v *Validation) FilePath(str string, m int) *ValidationResult {
209         return v.apply(FilePath{m}, str)
210 }
211
212 func (v *Validation) apply(chk Validator, obj interface{}) *ValidationResult {
213         if chk.IsSatisfied(obj) {
214                 return v.ValidationResult(true)
215         }
216
217         // Get the default key.
218         var key string
219         if pc, _, line, ok := runtime.Caller(2); ok {
220                 f := runtime.FuncForPC(pc)
221                 if defaultKeys, ok := DefaultValidationKeys[f.Name()]; ok {
222                         key = defaultKeys[line]
223                 }
224         } else {
225                 utilLog.Error("Validation: Failed to get Caller information to look up Validation key")
226         }
227
228         // Add the error to the validation context.
229         err := &ValidationError{
230                 Message: chk.DefaultMessage(),
231                 Key:     key,
232         }
233         v.Errors = append(v.Errors, err)
234
235         // Also return it in the result.
236         vr := v.ValidationResult(false)
237         vr.Error = err
238         return vr
239 }
240
241 // Check applies a group of validators to a field, in order, and return the
242 // ValidationResult from the first one that fails, or the last one that
243 // succeeds.
244 func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResult {
245         var result *ValidationResult
246         for _, check := range checks {
247                 result = v.apply(check, obj)
248                 if !result.Ok {
249                         return result
250                 }
251         }
252         return result
253 }
254
255 // ValidationFilter revel Filter function to be hooked into the filter chain.
256 func ValidationFilter(c *Controller, fc []Filter) {
257         // If json request, we shall assume json response is intended,
258         // as such no validation cookies should be tied response
259         if c.Params != nil && c.Params.JSON != nil {
260                 c.Validation = &Validation{Request: c.Request, Translator: MessageFunc}
261                 fc[0](c, fc[1:])
262         } else {
263                 errors, err := restoreValidationErrors(c.Request)
264                 c.Validation = &Validation{
265                         Errors:     errors,
266                         keep:       false,
267                         Request:    c.Request,
268                         Translator: MessageFunc,
269                 }
270                 hasCookie := (err != http.ErrNoCookie)
271
272                 fc[0](c, fc[1:])
273
274                 // Add Validation errors to ViewArgs.
275                 c.ViewArgs["errors"] = c.Validation.ErrorMap()
276
277                 // Store the Validation errors
278                 var errorsValue string
279                 if c.Validation.keep {
280                         for _, err := range c.Validation.Errors {
281                                 if err.Message != "" {
282                                         errorsValue += "\x00" + err.Key + ":" + err.Message + "\x00"
283                                 }
284                         }
285                 }
286
287                 // When there are errors from Validation and Keep() has been called, store the
288                 // values in a cookie. If there previously was a cookie but no errors, remove
289                 // the cookie.
290                 if errorsValue != "" {
291                         c.SetCookie(&http.Cookie{
292                                 Name:     CookiePrefix + "_ERRORS",
293                                 Value:    url.QueryEscape(errorsValue),
294                                 Domain:   CookieDomain,
295                                 Path:     "/",
296                                 HttpOnly: true,
297                                 Secure:   CookieSecure,
298                         })
299                 } else if hasCookie {
300                         c.SetCookie(&http.Cookie{
301                                 Name:     CookiePrefix + "_ERRORS",
302                                 MaxAge:   -1,
303                                 Domain:   CookieDomain,
304                                 Path:     "/",
305                                 HttpOnly: true,
306                                 Secure:   CookieSecure,
307                         })
308                 }
309         }
310 }
311
312 // Restore Validation.Errors from a request.
313 func restoreValidationErrors(req *Request) ([]*ValidationError, error) {
314         var (
315                 err    error
316                 cookie ServerCookie
317                 errors = make([]*ValidationError, 0, 5)
318         )
319         if cookie, err = req.Cookie(CookiePrefix + "_ERRORS"); err == nil {
320                 ParseKeyValueCookie(cookie.GetValue(), func(key, val string) {
321                         errors = append(errors, &ValidationError{
322                                 Key:     key,
323                                 Message: val,
324                         })
325                 })
326         }
327         return errors, err
328 }
329
330 // DefaultValidationKeys register default validation keys for all calls to Controller.Validation.Func().
331 // Map from (package).func => (line => name of first arg to Validation func)
332 // E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action"
333 // This is set on initialization in the generated main.go file.
334 var DefaultValidationKeys map[string]map[int]string