summaryrefslogtreecommitdiff
path: root/lib/TUWF/Misc.pod
blob: 8500b8a08265baf55c02a68b8c2b1fae1aba7bb2 (plain)
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
=head1 NAME

TUWF::Misc - Miscellaneous utility functions and methods for TUWF

=head1 DESCRIPTION

This module provides a few methods for the main TUWF object and optionally
exports a few handy functions. The methods can be used from within any TUWF
website without requiring any additional magic, TUWF automatically loads this
module at initialization.

The exported functions can be imported through TUWF (C<use TUWF 'uri_escape';>)
or by using this module directly (C<use TUWF::Misc 'uri_escape';>). The latter
form is useful if you wish to use a function outside of the TUWF framework.

=head1 METHODS

=head2 formValidate(@fields)

Shorthand for calling C<kv_validate()> with the following sources:

  Name    TUWF Method
   post    reqPosts()
   get     reqGets()
   param   reqParams()
   cookie  reqCookie()

The L<validate_templates|TUWF/validate_templates> configuration setting is
passed as the I<templates> option.

=head2 compile($schema)

I<Experimental.> Short-hand for calling
L<TUWF::Validate::compile|TUWF::Validate/compile> with the
L<custom_validations|TUWF/custom_validations> option. For example:

  TUWF::set('custom_validations')->{username} = { length => [ 3, 32 ] };

  my $val = tuwf->compile({ username => 1 })->validate('this is a username')->data;

=head2 validate(what, @args)

I<Experimental replacement for formValidate().> Validate and return request
data using L<TUWF::Validate>. The first argument must be the source of the data
to validate, the following sources are supported:

  Argument  Source
    post     reqPosts()
    get      reqGets()
    param    reqParams()
    json     reqJSON()

This method takes several different forms for the further arguments (the
examples below will perhaps make this more clear):

=over

=item Single value (C<< tuwf->validate($source, $param_name, $schema)> >>)

In this form, only a single value is validated and returned.

=item Hash value (C<< tuwf->validate($source, $schema) >>)

In this form, the source is converted into a hash table and then validated. The
C<$schema> then validates the entire hash.

=item Multiple values (C<< tuwf->validate($source, $param1, $schema1, $param2, $schema2, ..) >>)

This form uses the syntax of the I<Single value> form to validate multiple
values. It is a convenient short-hand for the I<Hash value> form.

=back

The C<$schema> argument in the above description can be either a bare or
compiled schema. A bare schema will be compiled with the
L<custom_validations|TUWF/custom_validations> setting.

The C<json> source only supports the I<Hash value> form. Using the C<param>
source is discouraged, as it is slower than using the C<get> or C<post> source
directly. And in almost all cases you only need to accept the data from either
the query string or the POST data, not both.

If a get/post/param parameter has multiple values, it is represented in the
source data as an array. To handle parameters that may occur any number of
times, use a C<< {type => 'array', scalar => 1} >> schema.

Some examples:

  # JSON, with custom error handling
  my $validation_result = tuwf->validate(json => {
    type => 'hash',
    keys => {
      username => { length => [ 3, 32 ] },
      password => { },
    }
  });
  if($validation_result) {
    my $data = $validation_result->data;
    check_login($data->{username}, $data->{password});
  } else {
    show_error();
  }

  # Single value
  my $page_number = tuwf->validate(get => page => { uint => 1, max => 1000 })->data;

  # Same, with a pre-compiled scheme (this is faster)
  state $c = tuwf->compile({ uint => 1, max => 1000 });
  my $page_number = tuwf->validate(get => page => $c)->data;

  # Multiple values
  my $data = tuwf->validate(post =>
    username => { length => [ 3, 32 ] },
    password => { },
  )->data;

  # Same, using the "Hash value" form
  my $data = tuwf->validate(post => {
    type => 'hash',
    keys => {
      username => { length => [ 3, 32 ] },
      password => { },
    }
  })->data;

=head2 mail(body, header => value, ..)

Very simple email sending function. The C<Content-Type> header defaults to
C<text/plain; charset='UTF-8'>, and the C<From> header to the
L<mail_from|TUWF/mail_from> configuration setting. The mail is sent using the
sendmail program configured with L<mail_sendmail|TUWF/mail_sendmail>, you
should make sure both that option is correct and the server is configured to
allow using the sendmail program to actually send mail.

=head1 FUNCTIONS

=head2 uri_escape(string)

Percent-encodes the given string and returns a string suitable for use as a
parameter value in a URI. The given string is assumed to be in Perls native
unicode format, and the escaped string will have UTF-8 encoded
percent-escaping.

This function is equivalent to C<uri_escape_utf8()> provided by
L<URI::Escape|URI::Escape>, and the C<encodeURIComponent()> function in
JavaScript.

=head2 kv_validate(sources, templates, fields)

Validates a set of key/value pairs against a list of constraints and data
definitions. Returns the (optionally modified) key/value pairs and, if a field
did not validate, an error indication. This function was designed to validate
form input data, and can as such also validate keys (or I<fields>) which
represent multiple values. This function is rarely used directly,
C<formValidate()> does everything you need when validating common input data.

I<sources> is a hashref explaining where the values should be fetched from.
Each key in the hash represents the I<name> of the source, and its value is a
subroutine reference. This subroutine should accept one argument: the name of
the field, and is expected return a list of values, or an empty list if there
are no values with that key. The following example defines a source by the name
"param", and tells C<kv_validate()> to fetch the values using
L<reqParams()|TUWF::Request>.

  kv_validate(
    { param => sub { $TUWF::OBJ->reqParams(shift) } },
    ..
  );

The I<templates> argument should be reference to a (possibly empty) hash
providing templates for commonly used constraints and data definitions.

The I<fields> argument should be an arrayref, where each item in the array
represents a single field. Each field should be a hashref with options. The
following options are accepted:

=over

=item <source name> => <field name>

String, indicates from which source the field should be fetched, and which name
to use. Note that even though C<kv_validate()> accepts validating data from
multiple sources, each field should only have one source option, and there
should not be a field from a different source with the same name.

Using the I<sources> example above, specifying C<param =E<gt> 'foo'> as field
option tells C<kv_validate()> to fetch the value(s) for this field from
C<reqParams('foo')>.

=item required

0/1. Indicates whether the field is required or not. Default: 1.

=item default

Specifies the default value to return when the field is not present or left
empty. Only makes sense for fields that are not required.

=item rmwhitespace

0/1. Removes any whitespace before and after the value before doing any other
validation. All occurences of C<\r> are also removed from the value. Default:
1.

=item maxlength

Number. Maximum length (in number of characters) of the value.

=item minlength

Number. Minimum length of the value.

=item enum

Arrayref. The value must be equal to any of the strings in the array. Note that
even though a string comparison is used, this works fine numbers as well.

=item regex

Validate the value against a regular expression. For identification, this
option can also be set to an arrayref, of which the first item contains the
regular expression. All other elements in the array are ignored by
C<kv_validate()>, but are still returned in the error structure and can
therefore be used in your code.

=item func

Subroutine reference. Validate the value against a function. The subroutine is
passed two arguments: The value, and a hash reference with the I<fields> in the
scope that the I<func> is defined. The subroutine should return false if the
value is invalid, true otherwise. The value argument is passed as a reference,
and may be modified in-place to perform normalization. This option can also be
set to an arrayref, which works the same as with the I<regex> option. This
constraint is only executed after the other constraints have been validated. So
the subroutine is not called if, for example, the value is empty and the
I<required> flag is set.

The extra I<fields> argument can be used to read additional information for
validation. For example,

  { func => sub { $_[0] =~ /^$_[1]{prefix}/ },
    prefix => 'hello' }

The subroutine verifies that the value is prefixed by the value of the
I<prefix> field, which is passed to the subroutine as second argument.

=item template

String, refers to a key in the I<templates> hash. Validates the value against
the options in I<%{$templates{$string}}>, which may contain any of the above
mentioned options (except <source name>).

When a template contains a I<func> field, the fields that are passed to the
subroutine only include the fields that are specified in the template options.
Using the example above, the following will not work as expected:

  $templates{prefix} = { func => sub { $_[0] =~ /^$_[1]{prefix} } };
  # In the field definitions
  { template => 'prefix', prefix => 'hello' }

In this example, the only field that is passed to the subroutine is the 'func'
field that is in the template definition itself. The subroutine has no access
to 'prefix' field that is defined outside of the template. To work around this
issue, a special I<inherit> option exists to allow the template to inherit
certain fields from the parent scope. The template definition can be changed as
follows:

  $templates{prefix} = {
    func => sub { $_[0] =~ /^$_[1]{prefix} },
    inherit => ['prefix'],
  };

This causes the 'prefix' field from the user of the template to be passed into
the context of the template itself, so the subroutine can access it.

=item multi

0/1, indicates whether there should be only one value or multiple values. If
this option is disabled (as is the default), only the first value will be
validated and returned.  Otherwise, each value is validated separately against
the above options and an arrayref with values is returned. Validating this
field is stopped when one value does not match. The I<required> and I<default>
options are also evaluated per-value: if required is true, each value should be
non-empty. Empty values are set to the default.

=item maxcount

Number. When I<multi> is true, specifies the maximum number of values to be
present. Whether they are empty or not is ignored in this count.

=item mincount

Number. See I<mincount>.

=back

C<kv_validate()> returns a hashref with field name to value mappings. When one
or more fields did not validate, a special C<_err> field is added. Its value is
an arrayref where each item represents an invalid field. Each invalid field is
represented again by an arrayref containing three items: the name of the field,
the option that caused it to fail and the value of that option.

The following templates are provided by default. These templates can be safely
overridden with the I<templates> argument to C<kv_validate> or removed
completely by setting the template to C<undef>.

=over

=item num

The value must be a (JSON-like) number. Two extra fields are available when
this template is used: I<min> to specify the lower bound on the number, and
I<max> the upper bound. This template automatically coorces the value into a
Perl number.

=item int

Similar to the I<num> template, except the value must be a whole number, no
fractions or exponents are allowed.

=item uint

Similar to the I<int> template, but the number must be positive.

=item ascii

The value must consist entirely of printable ASCII characters.

=item email

The value must be a valid e-mail address. Note that this is just simple
validation using a regular expression. A valid e-mail address in this context
does not imply that the email exists, or that all mail clients or servers will
accept it. For more precise validation, there's always L<Data::Validate::Email>
and L<Email::Address>.

=item weburl

The value must be a valid HTTP or HTTPS URL.

=back

Usage example:

  my $r = kv_validate(
    # sources
    { param => sub { $TUWF::OBJ->reqParams(shift) } },
    # templates
    { crc32_hex => {
      regex => qr/^[0-9a-f]+$/i,
      maxlength => 8,
      minlength => 8,
    } },
    # field definitions
    [
      { param => 'name', maxlength => 100 },
      { param => 'age',  min => 18, max => 120 },
      { param => 'crc',  required => 0, template => 'crc32_hex' },
    ],
  );

  # Depending on the input, $r may look something like:
  {
    name => 'John Doe',
    age => 28,
    crc => 'This does not look like a CRC32 string',
    _err => [
      [ 'crc', 'template', 'crc32_hex' ]
    ]
  }

=head1 SEE ALSO

L<TUWF>, L<URI::Escape>.

=head1 COPYRIGHT

Copyright (c) 2008-2019 Yoran Heling.

This module is part of the TUWF framework and is free software available under
the liberal MIT license. See the COPYING file in the TUWF distribution for the
details.


=head1 AUTHOR

Yoran Heling <projects@yorhel.nl>

=cut