Filename | /var/www/foswikidev/core/lib/Foswiki/Form/FieldDefinition.pm |
Statements | Executed 10 statements in 1.57ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 25µs | 38µs | BEGIN@18 | Foswiki::Form::FieldDefinition::
1 | 1 | 1 | 11µs | 35µs | BEGIN@20 | Foswiki::Form::FieldDefinition::
1 | 1 | 1 | 10µs | 15µs | BEGIN@19 | Foswiki::Form::FieldDefinition::
1 | 1 | 1 | 6µs | 6µs | BEGIN@21 | Foswiki::Form::FieldDefinition::
1 | 1 | 1 | 5µs | 5µs | BEGIN@23 | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | createMetaKeyValues | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | cssClasses | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | finish | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | getDefaultValue | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | getDisplayValue | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | isEditable | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | isMandatory | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | isMultiValued | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | isTextMergeable | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | new | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | populateMetaFromQueryData | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | renderForDisplay | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | renderForEdit | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | renderHidden | Foswiki::Form::FieldDefinition::
0 | 0 | 0 | 0s | 0s | stringify | Foswiki::Form::FieldDefinition::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | # base class for all form field types | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki::Form::FieldDefinition | ||||
7 | |||||
8 | Base class of all field definition classes. | ||||
9 | |||||
10 | Type-specific classes are derived from this class to define specific | ||||
11 | per-type behaviours. This class also provides default behaviours for when | ||||
12 | a specific type cannot be loaded. | ||||
13 | |||||
14 | =cut | ||||
15 | |||||
16 | package Foswiki::Form::FieldDefinition; | ||||
17 | |||||
18 | 2 | 31µs | 2 | 52µs | # spent 38µs (25+13) within Foswiki::Form::FieldDefinition::BEGIN@18 which was called:
# once (25µs+13µs) by Foswiki::Form::BEGIN@42 at line 18 # spent 38µs making 1 call to Foswiki::Form::FieldDefinition::BEGIN@18
# spent 13µs making 1 call to strict::import |
19 | 2 | 27µs | 2 | 19µs | # spent 15µs (10+4) within Foswiki::Form::FieldDefinition::BEGIN@19 which was called:
# once (10µs+4µs) by Foswiki::Form::BEGIN@42 at line 19 # spent 15µs making 1 call to Foswiki::Form::FieldDefinition::BEGIN@19
# spent 4µs making 1 call to warnings::import |
20 | 2 | 29µs | 2 | 60µs | # spent 35µs (11+25) within Foswiki::Form::FieldDefinition::BEGIN@20 which was called:
# once (11µs+25µs) by Foswiki::Form::BEGIN@42 at line 20 # spent 35µs making 1 call to Foswiki::Form::FieldDefinition::BEGIN@20
# spent 25µs making 1 call to Exporter::import |
21 | 2 | 60µs | 1 | 6µs | # spent 6µs within Foswiki::Form::FieldDefinition::BEGIN@21 which was called:
# once (6µs+0s) by Foswiki::Form::BEGIN@42 at line 21 # spent 6µs making 1 call to Foswiki::Form::FieldDefinition::BEGIN@21 |
22 | |||||
23 | # spent 5µs within Foswiki::Form::FieldDefinition::BEGIN@23 which was called:
# once (5µs+0s) by Foswiki::Form::BEGIN@42 at line 28 | ||||
24 | 1 | 5µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
25 | require locale; | ||||
26 | import locale(); | ||||
27 | } | ||||
28 | 1 | 1.42ms | 1 | 5µs | } # spent 5µs making 1 call to Foswiki::Form::FieldDefinition::BEGIN@23 |
29 | |||||
30 | =begin TML | ||||
31 | |||||
32 | ---++ ClassMethod new(%...) | ||||
33 | |||||
34 | Construct a new FieldDefinition. Parameters are passed in a hash. See | ||||
35 | Form.pm for how it is called. Subclasses should pass @_ on to this class. | ||||
36 | |||||
37 | =cut | ||||
38 | |||||
39 | sub new { | ||||
40 | my $class = shift; | ||||
41 | my %attrs = @_; | ||||
42 | ASSERT( $attrs{session} ) if DEBUG; | ||||
43 | |||||
44 | $attrs{name} ||= ''; | ||||
45 | $attrs{attributes} ||= ''; | ||||
46 | $attrs{description} ||= ''; | ||||
47 | $attrs{type} ||= ''; # default | ||||
48 | $attrs{size} ||= ''; | ||||
49 | $attrs{size} =~ s/^\s*//; | ||||
50 | $attrs{size} =~ s/\s*$//; | ||||
51 | $attrs{validModifiers} ||= []; | ||||
52 | |||||
53 | return bless( \%attrs, $class ); | ||||
54 | } | ||||
55 | |||||
56 | =begin TML | ||||
57 | |||||
58 | ---++ ObjectMethod finish() | ||||
59 | Break circular references. | ||||
60 | |||||
61 | =cut | ||||
62 | |||||
63 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
64 | # whether they are references or not. That way this method is "golden | ||||
65 | # documentation" of the live fields in the object. | ||||
66 | sub finish { | ||||
67 | my $this = shift; | ||||
68 | |||||
69 | undef $this->{name}; | ||||
70 | undef $this->{type}; | ||||
71 | undef $this->{size}; | ||||
72 | undef $this->{value}; | ||||
73 | undef $this->{description}; | ||||
74 | undef $this->{attributes}; | ||||
75 | undef $this->{default}; | ||||
76 | |||||
77 | undef $this->{session}; | ||||
78 | } | ||||
79 | |||||
80 | =begin TML | ||||
81 | |||||
82 | ---++ isEditable() -> $boolean | ||||
83 | |||||
84 | Is the field type editable? Labels aren't, for example. Subclasses may need | ||||
85 | to redefine this. | ||||
86 | |||||
87 | =cut | ||||
88 | |||||
89 | sub isEditable { 1 } | ||||
90 | |||||
91 | =begin TML | ||||
92 | |||||
93 | ---++ isMultiValued() -> $boolean | ||||
94 | |||||
95 | Is the field type multi-valued (i.e. does it store multiple values)? | ||||
96 | Subclasses may need to redefine this. | ||||
97 | |||||
98 | =cut | ||||
99 | |||||
100 | sub isMultiValued { 0 } | ||||
101 | |||||
102 | =begin TML | ||||
103 | |||||
104 | ---++ isTextMergeable() -> $boolean | ||||
105 | |||||
106 | Is this field type mergeable using a conventional text merge? | ||||
107 | |||||
108 | =cut | ||||
109 | |||||
110 | # can't merge multi-valued fields (select+multi, checkbox) | ||||
111 | sub isTextMergeable { return !shift->isMultiValued() } | ||||
112 | |||||
113 | =begin TML | ||||
114 | |||||
115 | ---++ isMandatory() -> $boolean | ||||
116 | |||||
117 | Is this field mandatory (required)? | ||||
118 | |||||
119 | =cut | ||||
120 | |||||
121 | sub isMandatory { return shift->{attributes} =~ m/M/ } | ||||
122 | |||||
123 | =begin TML | ||||
124 | |||||
125 | ---++ renderForEdit( $topicObject, $value ) -> ($col0html, $col1html) | ||||
126 | =$topicObject= - the topic being edited | ||||
127 | Render the field for editing. Returns two chunks of HTML; the | ||||
128 | =$col0html= is appended to the HTML for the first column in the | ||||
129 | form table, and the =$col1html= is used as the content of the second column. | ||||
130 | |||||
131 | =cut | ||||
132 | |||||
133 | sub renderForEdit { | ||||
134 | my ( $this, $topicObject, $value ) = @_; | ||||
135 | |||||
136 | # Treat like text, make it reasonably long, add a warning | ||||
137 | return ( | ||||
138 | '<br /><span class="foswikiAlert">MISSING TYPE ' | ||||
139 | . $this->{type} | ||||
140 | . '</span>', | ||||
141 | CGI::textfield( | ||||
142 | -class => $this->cssClasses('foswikiAlert foswikiInputField'), | ||||
143 | -name => $this->{name}, | ||||
144 | -size => 80, | ||||
145 | -override => 1, | ||||
146 | -value => $value, | ||||
147 | ) | ||||
148 | ); | ||||
149 | } | ||||
150 | |||||
151 | =begin TML | ||||
152 | |||||
153 | ---++ cssClasses(@classes) -> $classes | ||||
154 | Construct a list of the CSS classes for the form field. Adds additional | ||||
155 | class specifiers related to the attributes of the field e.g mandatory. | ||||
156 | Pass it a list of the other classnames you want on the field. | ||||
157 | |||||
158 | =cut | ||||
159 | |||||
160 | sub cssClasses { | ||||
161 | my $this = shift; | ||||
162 | if ( $this->isMandatory() ) { | ||||
163 | push( @_, 'foswikiMandatory' ); | ||||
164 | } | ||||
165 | return join( ' ', @_ ); | ||||
166 | } | ||||
167 | |||||
168 | =begin TML | ||||
169 | |||||
170 | ---++ getDefaultValue() -> $value | ||||
171 | Try and get a sensible default value for the field from the | ||||
172 | values stored in the form definition. The result should be | ||||
173 | a value string. | ||||
174 | |||||
175 | Some subclasses may not support the definition of defaults in | ||||
176 | the form definition. In that case this method should return =undef=. | ||||
177 | |||||
178 | =cut | ||||
179 | |||||
180 | sub getDefaultValue { | ||||
181 | my $this = shift; | ||||
182 | |||||
183 | my $value = | ||||
184 | ( exists( $this->{default} ) ? $this->{default} : $this->{value} ); | ||||
185 | $value = '' unless defined $value; # allow 0 values | ||||
186 | |||||
187 | return $value; | ||||
188 | } | ||||
189 | |||||
190 | =begin TML | ||||
191 | |||||
192 | ---++ renderHidden($meta) -> $html | ||||
193 | Render the form in =$meta= as a set of hidden fields. | ||||
194 | |||||
195 | =cut | ||||
196 | |||||
197 | sub renderHidden { | ||||
198 | my ( $this, $meta ) = @_; | ||||
199 | |||||
200 | my $value; | ||||
201 | if ( $this->{name} ) { | ||||
202 | my $field = $meta->get( 'FIELD', $this->{name} ); | ||||
203 | $value = $field->{value}; | ||||
204 | } | ||||
205 | |||||
206 | my @values; | ||||
207 | |||||
208 | if ( defined($value) ) { | ||||
209 | if ( $this->isMultiValued() ) { | ||||
210 | push( @values, split( /\s*,\s*/, $value ) ); | ||||
211 | } | ||||
212 | else { | ||||
213 | push( @values, $value ); | ||||
214 | } | ||||
215 | } | ||||
216 | else { | ||||
217 | $value = $this->getDefaultValue(); | ||||
218 | push( @values, $this->getDefaultValue() ) if $value; | ||||
219 | } | ||||
220 | |||||
221 | return '' unless scalar(@values); | ||||
222 | |||||
223 | return CGI::hidden( -name => $this->{name}, -default => \@values ); | ||||
224 | } | ||||
225 | |||||
226 | =begin TML | ||||
227 | |||||
228 | ---++ ObjectMethod populateMetaDataFromQuery( $query, $meta, $old ) -> ($bValid, $bPresent) | ||||
229 | |||||
230 | Given a CGI =$query=, a =$meta= object, and an array of =$old= field entries, | ||||
231 | then populate the $meta with a row for this field definition, taking the | ||||
232 | content from the query if it's there, otherwise from $old or failing that, | ||||
233 | from the default defined for the type. Refuses to update mandatory fields | ||||
234 | that have an empty value. | ||||
235 | |||||
236 | Return $bValid true if the value in $meta was updated (either from the | ||||
237 | query or from a default in the form. | ||||
238 | Return $bPresent true if a value was present in the query (even it was undef) | ||||
239 | |||||
240 | =cut | ||||
241 | |||||
242 | sub populateMetaFromQueryData { | ||||
243 | my ( $this, $query, $meta, $old ) = @_; | ||||
244 | my $value; | ||||
245 | my $bPresent = 0; | ||||
246 | |||||
247 | return unless $this->{name}; | ||||
248 | |||||
249 | my %names = map { $_ => 1 } $query->multi_param; | ||||
250 | |||||
251 | if ( $names{ $this->{name} } ) { | ||||
252 | |||||
253 | # Field is present in the request | ||||
254 | $bPresent = 1; | ||||
255 | if ( $this->isMultiValued() ) { | ||||
256 | my @values = $query->multi_param( $this->{name} ); | ||||
257 | |||||
258 | if ( scalar(@values) == 1 && defined $values[0] ) { | ||||
259 | @values = split( /,|%2C/, $values[0] ); | ||||
260 | } | ||||
261 | my %vset = (); | ||||
262 | foreach my $val (@values) { | ||||
263 | $val ||= ''; | ||||
264 | $val =~ s/^\s*//; | ||||
265 | $val =~ s/\s*$//; | ||||
266 | |||||
267 | # skip empty values | ||||
268 | $vset{$val} = ( defined $val && $val =~ m/\S/ ); | ||||
269 | } | ||||
270 | $value = ''; | ||||
271 | my $isValues = ( $this->{type} =~ m/\+values/ ); | ||||
272 | |||||
273 | foreach my $option ( @{ $this->getOptions() } ) { | ||||
274 | $option =~ s/^.*?[^\\]=(.*)$/$1/ if $isValues; | ||||
275 | |||||
276 | # Maintain order of definition | ||||
277 | if ( $vset{$option} ) { | ||||
278 | $value .= ', ' if length($value); | ||||
279 | $value .= $option; | ||||
280 | } | ||||
281 | } | ||||
282 | } | ||||
283 | else { | ||||
284 | |||||
285 | # Default the value to the empty string (undef would result | ||||
286 | # in the old value being restored) | ||||
287 | # Note: we test for 'defined' because value can also be 0 (zero) | ||||
288 | $value = $query->param( $this->{name} ); | ||||
289 | $value = '' unless defined $value; | ||||
290 | if ( $this->{session}->inContext('edit') ) { | ||||
291 | $value = Foswiki::expandStandardEscapes($value); | ||||
292 | } | ||||
293 | } | ||||
294 | } | ||||
295 | |||||
296 | # Find the old value of this field | ||||
297 | my $preDef; | ||||
298 | foreach my $item (@$old) { | ||||
299 | if ( $item->{name} eq $this->{name} ) { | ||||
300 | $preDef = $item; | ||||
301 | last; | ||||
302 | } | ||||
303 | } | ||||
304 | my $def; | ||||
305 | |||||
306 | if ( defined($value) ) { | ||||
307 | |||||
308 | # mandatory fields must have length > 0 | ||||
309 | if ( $this->isMandatory() && length($value) == 0 ) { | ||||
310 | return ( 0, $bPresent ); | ||||
311 | } | ||||
312 | |||||
313 | # NOTE: title and name are stored in the topic so that it can be | ||||
314 | # viewed without reading in the form definition | ||||
315 | my $title = $this->{title}; | ||||
316 | if ( $this->{definingTopic} ) { | ||||
317 | $title = '[[' . $this->{definingTopic} . '][' . $title . ']]'; | ||||
318 | } | ||||
319 | $def = $this->createMetaKeyValues( | ||||
320 | $query, $meta, | ||||
321 | { | ||||
322 | name => $this->{name}, | ||||
323 | title => $title, | ||||
324 | value => $value | ||||
325 | } | ||||
326 | ); | ||||
327 | } | ||||
328 | elsif ($preDef) { | ||||
329 | $def = $preDef; | ||||
330 | } | ||||
331 | else { | ||||
332 | return ( 0, $bPresent ); | ||||
333 | } | ||||
334 | |||||
335 | $meta->putKeyed( 'FIELD', $def ) if $def; | ||||
336 | |||||
337 | return ( 1, $bPresent ); | ||||
338 | } | ||||
339 | |||||
340 | =begin TML | ||||
341 | |||||
342 | ---++ ObjectMethod createMetaKeyValues( $query, $meta, $keyvalues ) -> $keyvalues | ||||
343 | |||||
344 | Create meta key/value pairs hash, to be overridden by subclasses. | ||||
345 | Default implementation passes all inputs unchanged. | ||||
346 | |||||
347 | =cut | ||||
348 | |||||
349 | sub createMetaKeyValues { | ||||
350 | |||||
351 | #my ( $this, $query, $meta, $keyvalues ) = @_; | ||||
352 | |||||
353 | return $_[3]; | ||||
354 | } | ||||
355 | |||||
356 | =begin TML | ||||
357 | |||||
358 | ---++ ObjectMethod renderForDisplay($format, $value, $attrs) -> $html | ||||
359 | |||||
360 | Render the field for display, under the control of $attrs. | ||||
361 | |||||
362 | (protected) means the resulting string is run through | ||||
363 | Foswiki::Render::protectFormFieldValue. | ||||
364 | |||||
365 | * =format= - the format to be expanded. The following tokens are available: | ||||
366 | * =$title= - title of the form field. if this is not available | ||||
367 | from the value, then the default title is taken from the form | ||||
368 | field definition. | ||||
369 | * =$value= - expanded to the (protected) value of the form field | ||||
370 | *before mapping* | ||||
371 | * =$value(display) - expanded to the (protected) value of the form | ||||
372 | field *after* mapping | ||||
373 | * =$attributes= - from the field definition | ||||
374 | * =$type= - from the field definition | ||||
375 | * =$size= - from the field definition | ||||
376 | * =$definingTopic= - topic in which the field is defined | ||||
377 | * =$value= - the scalar value of the field | ||||
378 | * =$attrs= - attributes. Fields used are: | ||||
379 | * =showhidden= - set to override H attribute | ||||
380 | * =newline= - replace newlines with this (default <br>) | ||||
381 | * =bar= - replace vbar with this (default &#124) | ||||
382 | * =break= - boolean, set to hyphenate | ||||
383 | * =protectdollar= - set to escape $ | ||||
384 | * =usetitle= - if set, use this for the title rather than the title | ||||
385 | from the form definition | ||||
386 | |||||
387 | =cut | ||||
388 | |||||
389 | sub renderForDisplay { | ||||
390 | my ( $this, $format, $value, $attrs ) = @_; | ||||
391 | |||||
392 | if ( !$attrs->{showhidden} ) { | ||||
393 | my $fa = $this->{attributes} || ''; | ||||
394 | if ( $fa =~ m/H/ ) { | ||||
395 | return ''; | ||||
396 | } | ||||
397 | } | ||||
398 | |||||
399 | my $title = $this->{title}; # default | ||||
400 | $title = $attrs->{usetitle} if defined $attrs->{usetitle}; | ||||
401 | |||||
402 | require Foswiki::Render; | ||||
403 | |||||
404 | $format =~ s/\$title/$title/g; | ||||
405 | if ( $format =~ m/\$value\(display\)/ ) { | ||||
406 | my $vd = Foswiki::Render::protectFormFieldValue( | ||||
407 | $this->getDisplayValue($value), $attrs ); | ||||
408 | $format =~ s/\$value\(display\)/$vd/g; | ||||
409 | } | ||||
410 | if ( $format =~ m/\$value/ ) { | ||||
411 | my $v = Foswiki::Render::protectFormFieldValue( $value, $attrs ); | ||||
412 | $format =~ s/\$value/$v/g; | ||||
413 | } | ||||
414 | $format =~ s/\$name/$this->{name}/g; | ||||
415 | $format =~ s/\$attributes/$this->{attributes}/g; | ||||
416 | $format =~ s/\$type/$this->{type}/g; | ||||
417 | $format =~ s/\$size/$this->{size}/g; | ||||
418 | my $definingTopic = $this->{definingTopic} || 'FIELD'; | ||||
419 | $format =~ s/\$definingTopic/$definingTopic/g; | ||||
420 | |||||
421 | # remove nop exclamation marks from form field value before it is put | ||||
422 | # inside a format like [[$topic][$formfield()]] that prevents it being | ||||
423 | # detected | ||||
424 | $format =~ s/!($Foswiki::regex{wikiWordRegex})/<nop>$1/gs; | ||||
425 | |||||
426 | return $format; | ||||
427 | } | ||||
428 | |||||
429 | =begin TML | ||||
430 | |||||
431 | ---++ ObjectMethod getDisplayValue($value) -> $html | ||||
432 | |||||
433 | Given a value for this form field, return the *mapped* value suitable for | ||||
434 | display. This is used when a form field must be displayed using a different | ||||
435 | format to the way the value is stored. | ||||
436 | |||||
437 | The default does nothing. | ||||
438 | |||||
439 | =cut | ||||
440 | |||||
441 | sub getDisplayValue { | ||||
442 | my ( $this, $value ) = @_; | ||||
443 | |||||
444 | return $value; | ||||
445 | } | ||||
446 | |||||
447 | # Debug | ||||
448 | sub stringify { | ||||
449 | my $this = shift; | ||||
450 | my $s = '| ' | ||||
451 | . $this->{name} . ' | ' | ||||
452 | . $this->{type} . ' | ' | ||||
453 | . $this->{size} . ' | ' | ||||
454 | . $this->{attributes} . " |\n"; | ||||
455 | return $s; | ||||
456 | } | ||||
457 | |||||
458 | 1 | 3µs | 1; | ||
459 | __END__ |