← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:16 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Form.pm
StatementsExecuted 28 statements in 3.11ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.61ms1.71msFoswiki::Form::::BEGIN@42Foswiki::Form::BEGIN@42
111761µs854µsFoswiki::Form::::BEGIN@43Foswiki::Form::BEGIN@43
11123µs48µsFoswiki::Form::::BEGIN@32Foswiki::Form::BEGIN@32
11118µs29µsFoswiki::Form::::BEGIN@33Foswiki::Form::BEGIN@33
11115µs67µsFoswiki::Form::::BEGIN@38Foswiki::Form::BEGIN@38
11113µs198µsFoswiki::Form::::BEGIN@39Foswiki::Form::BEGIN@39
1116µs6µsFoswiki::Form::::BEGIN@35Foswiki::Form::BEGIN@35
1115µs5µsFoswiki::Form::::BEGIN@44Foswiki::Form::BEGIN@44
1115µs5µsFoswiki::Form::::BEGIN@41Foswiki::Form::BEGIN@41
1114µs4µsFoswiki::Form::::BEGIN@48Foswiki::Form::BEGIN@48
1114µs4µsFoswiki::Form::::BEGIN@45Foswiki::Form::BEGIN@45
1114µs4µsFoswiki::Form::::BEGIN@46Foswiki::Form::BEGIN@46
0000s0sFoswiki::Form::::__ANON__[:373]Foswiki::Form::__ANON__[:373]
0000s0sFoswiki::Form::::__ANON__[:377]Foswiki::Form::__ANON__[:377]
0000s0sFoswiki::Form::::__ANON__[:381]Foswiki::Form::__ANON__[:381]
0000s0sFoswiki::Form::::__ANON__[:402]Foswiki::Form::__ANON__[:402]
0000s0sFoswiki::Form::::_extractPseudoFieldDefsFoswiki::Form::_extractPseudoFieldDefs
0000s0sFoswiki::Form::::_linkFoswiki::Form::_link
0000s0sFoswiki::Form::::_parseFormDefinitionFoswiki::Form::_parseFormDefinition
0000s0sFoswiki::Form::::createFieldFoswiki::Form::createField
0000s0sFoswiki::Form::::fieldTitle2FieldNameFoswiki::Form::fieldTitle2FieldName
0000s0sFoswiki::Form::::finishFoswiki::Form::finish
0000s0sFoswiki::Form::::getAvailableFormsFoswiki::Form::getAvailableForms
0000s0sFoswiki::Form::::getFieldFoswiki::Form::getField
0000s0sFoswiki::Form::::getFieldValuesFromQueryFoswiki::Form::getFieldValuesFromQuery
0000s0sFoswiki::Form::::getFieldsFoswiki::Form::getFields
0000s0sFoswiki::Form::::isTextMergeableFoswiki::Form::isTextMergeable
0000s0sFoswiki::Form::::newFoswiki::Form::new
0000s0sFoswiki::Form::::renderForDisplayFoswiki::Form::renderForDisplay
0000s0sFoswiki::Form::::renderForEditFoswiki::Form::renderForEdit
0000s0sFoswiki::Form::::renderHiddenFoswiki::Form::renderHidden
0000s0sFoswiki::Form::::stringifyFoswiki::Form::stringify
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::Form
6
7Object representing a single form definition.
8
9Form definitions are mainly used to control rendering of a form for
10editing, though there is some application login there that handles
11transferring values between edits and saves.
12
13A form definition consists of a Foswiki::Form object, which has a list
14of field definitions. Each field definition is an object of a type
15derived from Foswiki::Form::FieldDefinition. These objects are responsible
16for the actual syntax and semantics of the field type. Form definitions
17are parsed from Foswiki tables, and the types are mapped by name to a
18class declared in Foswiki::Form::* - for example, the =text= type is mapped
19to =Foswiki::Form::Text= and the =checkbox= type to =Foswiki::Form::Checkbox=.
20
21The =Foswiki::Form::FieldDefinition= class declares default behaviours for
22types that accept a single value in their definitions. The
23=Foswiki::Form::ListFieldDefinition= extends this for types that have lists
24of possible values.
25
26=cut
27
28# The bulk of this object is a parser for form definitions. All the
29# intelligence is in the individual field types.
30
31package Foswiki::Form;
32242µs274µs
# spent 48µs (23+25) within Foswiki::Form::BEGIN@32 which was called: # once (23µs+25µs) by Foswiki::Meta::renderFormForDisplay at line 32
use strict;
# spent 48µs making 1 call to Foswiki::Form::BEGIN@32 # spent 26µs making 1 call to strict::import
33234µs240µs
# spent 29µs (18+11) within Foswiki::Form::BEGIN@33 which was called: # once (18µs+11µs) by Foswiki::Meta::renderFormForDisplay at line 33
use warnings;
# spent 29µs making 1 call to Foswiki::Form::BEGIN@33 # spent 11µs making 1 call to warnings::import
34
35246µs16µs
# spent 6µs within Foswiki::Form::BEGIN@35 which was called: # once (6µs+0s) by Foswiki::Meta::renderFormForDisplay at line 35
use Foswiki::Meta ();
# spent 6µs making 1 call to Foswiki::Form::BEGIN@35
3618µsour @ISA = ('Foswiki::Meta');
37
38240µs2120µs
# spent 67µs (15+53) within Foswiki::Form::BEGIN@38 which was called: # once (15µs+53µs) by Foswiki::Meta::renderFormForDisplay at line 38
use Assert;
# spent 67µs making 1 call to Foswiki::Form::BEGIN@38 # spent 53µs making 1 call to Exporter::import
39236µs2383µs
# spent 198µs (13+185) within Foswiki::Form::BEGIN@39 which was called: # once (13µs+185µs) by Foswiki::Meta::renderFormForDisplay at line 39
use Error qw( :try );
# spent 198µs making 1 call to Foswiki::Form::BEGIN@39 # spent 185µs making 1 call to Error::import
40
41221µs15µs
# spent 5µs within Foswiki::Form::BEGIN@41 which was called: # once (5µs+0s) by Foswiki::Meta::renderFormForDisplay at line 41
use Foswiki::Sandbox ();
# spent 5µs making 1 call to Foswiki::Form::BEGIN@41
422114µs11.71ms
# spent 1.71ms (1.61+100µs) within Foswiki::Form::BEGIN@42 which was called: # once (1.61ms+100µs) by Foswiki::Meta::renderFormForDisplay at line 42
use Foswiki::Form::FieldDefinition ();
# spent 1.71ms making 1 call to Foswiki::Form::BEGIN@42
432104µs1854µs
# spent 854µs (761+94) within Foswiki::Form::BEGIN@43 which was called: # once (761µs+94µs) by Foswiki::Meta::renderFormForDisplay at line 43
use Foswiki::Form::ListFieldDefinition ();
# spent 854µs making 1 call to Foswiki::Form::BEGIN@43
44221µs15µs
# spent 5µs within Foswiki::Form::BEGIN@44 which was called: # once (5µs+0s) by Foswiki::Meta::renderFormForDisplay at line 44
use Foswiki::AccessControlException ();
# spent 5µs making 1 call to Foswiki::Form::BEGIN@44
45219µs14µs
# spent 4µs within Foswiki::Form::BEGIN@45 which was called: # once (4µs+0s) by Foswiki::Meta::renderFormForDisplay at line 45
use Foswiki::OopsException ();
# spent 4µs making 1 call to Foswiki::Form::BEGIN@45
46240µs14µs
# spent 4µs within Foswiki::Form::BEGIN@46 which was called: # once (4µs+0s) by Foswiki::Meta::renderFormForDisplay at line 46
use Foswiki::Func ();
# spent 4µs making 1 call to Foswiki::Form::BEGIN@46
47
48
# spent 4µs within Foswiki::Form::BEGIN@48 which was called: # once (4µs+0s) by Foswiki::Meta::renderFormForDisplay at line 53
BEGIN {
4915µs if ( $Foswiki::cfg{UseLocale} ) {
50 require locale;
51 import locale();
52 }
5312.54ms14µs}
# spent 4µs making 1 call to Foswiki::Form::BEGIN@48
54
55# The following are reserved as URL parameters to scripts and may not be
56# used as field names in forms.
57120µsmy %reservedFieldNames = map { $_ => 1 }
58 qw( action breaklock contenttype cover dontnotify editaction
59 forcenewrevision formtemplate onlynewtopic onlywikiname
60 originalrev skin templatetopic text topic topicparent user );
61
6212µsmy @default_columns = qw/name type size value description attributes default/;
6314µsmy %valid_columns = map { $_ => 1 } @default_columns;
64
65=begin TML
66
67---++ ClassMethod new ( $session, $web, $topic, \@def )
68
69Looks up a form in the session object or, if it hasn't been read yet,
70reads it from the form definition topic on disc.
71 * =$web= - default web to recover form from, if =$form= doesn't
72 specify a web
73 * =$topic= - name of the topic that contains the form definition
74 * =\@def= - optional. A reference to a list of field definitions.
75 If present, these definitions will be used, rather than any read from
76 the form definition topic.
77
78May throw Foswiki::OopsException if the web and form are not valid for use as a
79form name, or if \@def is not given and the form does not exist in the
80database. May throw Foswiki::AccessControlException if the form schema
81in the database is protected against view.
82
83=cut
84
85sub new {
86 my ( $class, $session, $web, $form, $def ) = @_;
87
88 my ( $vweb, $vtopic ) = $session->normalizeWebTopicName( $web, $form );
89 my $this = $session->{forms}->{"$vweb.$vtopic"};
90
91 unless ($this) {
92
93 # A form name has to be a valid topic name after normalisation
94 $vweb = Foswiki::Sandbox::untaint( $vweb,
95 \&Foswiki::Sandbox::validateWebName );
96 $vtopic = Foswiki::Sandbox::untaint( $vtopic,
97 \&Foswiki::Sandbox::validateTopicName );
98 unless ( $vweb && $vtopic ) {
99 throw Foswiki::OopsException(
100 'attention',
101 def => 'invalid_form_name',
102 web => $session->{webName},
103 topic => $session->{topicName},
104 params => [ $web, $form ]
105 );
106 }
107
108 # Got to have either a def or a topic
109 unless ( $def || $session->topicExists( $vweb, $vtopic ) ) {
110 throw Foswiki::OopsException(
111 'attention',
112 def => 'no_form_def',
113 web => $session->{webName},
114 topic => $session->{topicName},
115 params => [ $vweb, $vtopic ]
116 );
117 }
118
119 $this = $class->SUPER::new( $session, $vweb, $vtopic );
120
121 unless ( $def || $this->haveAccess('VIEW') ) {
122 throw Foswiki::AccessControlException( 'VIEW', $session->{user},
123 $vweb, $vtopic, $Foswiki::Meta::reason );
124 }
125
126 if ( ref($this) ne 'Foswiki::Form' ) {
127
128 #recast if we have to - allowing the cache to work its magic
129 $this = bless( $this, 'Foswiki::Form' );
130 }
131
132 # cache the object before we've parsed it to prevent recursion
133 #when there are SEARCH / INCLUDE macros in the form definition
134 $session->{forms}->{"$vweb.$vtopic"} = $this;
135
136 unless ($def) {
137 $this->{fields} = $this->_parseFormDefinition();
138 }
139 elsif ( ref($def) eq 'ARRAY' ) {
140 $this->{fields} = $def;
141 }
142 else {
143
144 # Foswiki::Meta object
145 $this->{fields} = $this->_extractPseudoFieldDefs($def);
146 }
147 }
148
149 return $this;
150}
151
152=begin TML
153
154---++ ObjectMethod finish()
155Break circular references.
156
157=cut
158
159# Note to developers; please undef *all* fields in the object explicitly,
160# whether they are references or not. That way this method is "golden
161# documentation" of the live fields in the object.
162sub finish {
163 my $this = shift;
164 foreach ( @{ $this->{fields} } ) {
165 $_->finish();
166 }
167 undef $this->{fields};
168 $this->SUPER::finish();
169}
170
171=begin TML
172
173---++ StaticMethod getAvailableForms( $metaObject ) -> @forms
174
175Get a list of the names of forms that are available for use in the
176given topic. $metaObject can be a topic or a web.
177
178=cut
179
180sub getAvailableForms {
181 my $contextObject = shift;
182 my $legalForms;
183 my $metaObject;
184
185 # SMELL: Item11527, why aren't we using Foswiki::Func::getPreferencesValue?
186 # AKA: Why can't we inherit WEBFORMS from parent webs?
187 if ( defined $contextObject->topic ) {
188 $metaObject =
189 Foswiki::Meta->new( $contextObject->session, $contextObject->web() );
190 $legalForms = $metaObject->getPreference('WEBFORMS') || '';
191 $legalForms =
192 Foswiki::Func::expandCommonVariables( $legalForms,
193 $contextObject->topic(), $contextObject->web(), $contextObject );
194 }
195 else {
196 $legalForms = $contextObject->getPreference('WEBFORMS') || '';
197 }
198 $legalForms =~ s/^\s+//;
199 $legalForms =~ s/\s+$//;
200 my @forms = split( /[,\s]+/, $legalForms );
201
202 # This is where we could %SEARCH for *Form topics
203 return @forms;
204}
205
206=begin TML
207
208---++ StaticMethod fieldTitle2FieldName($title) -> $name
209Chop out all except \w. from a field name to create a
210valid "name" for storing in meta-data
211
212=cut
213
214sub fieldTitle2FieldName {
215 my ($text) = @_;
216 return '' unless defined($text);
217 $text =~ s/!//g;
218 $text =~ s/<nop>//g; # support <nop> character in title
219 $text =~ s/[^\w\.]//g;
220 return $text;
221}
222
223# Get definition from supplied topic text
224# Returns array of arrays
225# 1st - list fields
226# 2nd - name, title, type, size, vals, description, attributes
227# Possible attributes are "M" (mandatory field) and "H" (hidden)
228sub _parseFormDefinition {
229 my $this = shift;
230
231 require Foswiki::Tables::Parser;
232 ASSERT( !$@ ) if DEBUG;
233
234 my @fields = ();
235 my $text = $this->text();
236 $text = '' unless defined $text;
237
238 {
239
240 package Foswiki::Form::ParseFinished;
241 our @ISA = ('Error');
242 }
243
244 # Valid column titles, in default order.
245 my @columns = @default_columns;
246 my $col = 0;
247 my $have_head = 0;
248 my $seen_head = 0;
249
250 # my @field = ();
251 my %field = ();
252
253 my $handler = sub {
254 my $event = shift;
255 if ( $event eq 'th' ) {
256 if ($have_head) {
257
258 # A header on any row but the first is treated as data
259 $event = 'td';
260 $_[1] = "*$_[1]*";
261
262 # Drop-through to 'td'
263 }
264 else {
265 # Header row declaring column titles.
266
267 # This could re-order the columns based on the title,
268 # but is disabled due to compatibility issues.
269 #my ( $pre, $data, $post ) = @_;
270 #$data = lc($data);
271 #$data =~ s/[\s:]//g;
272 #$data =~ s/s$//g; # singularise
273 #$data = 'description' if $data =~ m/^tooltip(message)?$/;
274 #$columns[ $col ] = $data if $valid_columns{$data};
275
276 $col++;
277 $seen_head = 1;
278 return;
279 }
280 }
281
282 if ( $event eq 'td' ) {
283 my ( $pre, $data, $post ) = @_;
284
285 $data = '' unless defined $data;
286
287 $field{ $columns[$col] } = $data if $columns[$col];
288 $col++;
289 return;
290 }
291
292 if ( $event eq 'close_table' ) {
293
294 # Abort the parse after the first table has been read
295 throw Foswiki::Form::ParseFinished;
296 }
297
298 if ( $event eq 'close_tr' ) {
299 $col = 0;
300 if ( !$have_head && $seen_head ) {
301
302 # Closing the header row
303 $have_head = 1;
304 return;
305
306 # if !$seen_head it's a conventional data row
307 }
308 $have_head = 1; # Subsequent th cells treated as data
309
310 if ( $field{type} ) {
311 $field{type} = lc( $field{type} );
312 }
313 else {
314 $field{type} = 'text';
315 }
316
317 my $vals = $field{value} || '';
318 if ( $vals =~ m/%/ ) {
319 $vals = $this->expandMacros($vals);
320 }
321 $vals =~ s/<\/?(!|nop|noautolink)\/?>//g;
322
323 # Trim again in case macro expansion has added spaces
324 $vals =~ s/^\s+//g;
325 $vals =~ s/\s+$//g;
326 $field{value} = $vals;
327
328 #repeat same again with {default}
329 if ( exists( $field{default} ) ) {
330 my $vals = $field{default} || '';
331 if ( $vals =~ m/%/ ) {
332 $vals = $this->expandMacros($vals);
333 }
334 $vals =~ s/<\/?(!|nop|noautolink)\/?>//g;
335
336 # Trim again in case macro expansion has added spaces
337 $vals =~ s/^\s+//g;
338 $vals =~ s/\s+$//g;
339 $field{default} = $vals;
340 }
341
342 $field{attributes} =~ s/\s*//g if ( exists( $field{attributes} ) );
343
344 $field{title} = $field{name};
345 $field{definingTopic} = '';
346 if ( $field{title} =~ m/\[\[(.+)\]\[(.+)\]\]/ ) {
347
348 # use common defining topics with different field titles
349 $field{definingTopic} =
350 fieldTitle2FieldName(
351 Foswiki::Func::expandCommonVariables($1) );
352 $field{title} = $2;
353 }
354
355 $field{name} = fieldTitle2FieldName( $field{title} );
356
357 # Rename fields with reserved names
358 if ( $reservedFieldNames{ $field{name} } ) {
359 $field{name} .= '_';
360 }
361 my $fieldDef = $this->createField(
362 $field{type},
363 web => $this->web(),
364 topic => $this->topic(),
365 %field
366 );
367
368 push( @fields, $fieldDef );
369 %field = ();
370
371 $this->{mandatoryFieldsPresent} ||= $fieldDef->isMandatory();
372 }
373 };
374
375 try {
376 Foswiki::Tables::Parser::parse( $text, $handler );
377 }
378 catch Foswiki::Form::ParseFinished with {
379
380 # clean exit, fired when first table has been parsed
381 };
382
383 return \@fields;
384}
385
386# PROTECTED
387# Create a field object. Done like this so that this method can be
388# overridden by subclasses to extend the range of field types.
389sub createField {
390 my $this = shift;
391 my $type = shift;
392
393 # The untaint is required for the validation *and* the ucfirst, which
394 # retaints when use locale is in force (hence we do the validation *after*
395 # the ucfirst)
396 my $class = Foswiki::Sandbox::untaint(
397 $type,
398 sub {
399 my $class = ucfirst(shift);
400 $class =~ m/^([a-zA-Z0-9_]*)/; # cut off +buttons etc
401 return "Foswiki::Form::$1";
402 }
403 );
404 eval 'require ' . $class;
405 if ($@) {
406 $this->session->logger->log( 'error',
407 "error compiling class $class: $@" );
408
409 # Type not available; use base type
410 require Foswiki::Form::FieldDefinition;
411 $class = 'Foswiki::Form::FieldDefinition';
412 }
413 return $class->new( session => $this->session(), type => $type, @_ );
414}
415
416# Generate a link to the given topic, so we can bring up details in a
417# separate window.
418sub _link {
419 my ( $this, $string, $tooltip, $topic ) = @_;
420
421 $string =~ s/[\[\]]//g;
422
423 $topic ||= $string;
424 my $defaultToolTip =
425 $this->session->i18n->maketext('Details in separate window');
426 $tooltip ||= $defaultToolTip;
427
428 ( my $web, $topic ) =
429 $this->session->normalizeWebTopicName( $this->web(), $topic );
430
431 $web =
432 Foswiki::Sandbox::untaint( $web, \&Foswiki::Sandbox::validateWebName );
433
434 $web ||= $this->web();
435
436 $topic = Foswiki::Sandbox::untaint( $topic,
437 \&Foswiki::Sandbox::validateTopicName );
438
439 my $link;
440
441 if ( $this->session->topicExists( $web, $topic ) ) {
442 $link = CGI::a(
443 {
444 target => $topic,
445 title => $tooltip,
446 href => $this->session->getScriptUrl( 0, 'view', $web, $topic ),
447 rel => 'nofollow'
448 },
449 $string
450 );
451 }
452 else {
453 my $that =
454 Foswiki::Meta->new( $this->session, $web,
455 $topic || $Foswiki::cfg{HomeTopicName} );
456 my $expanded = $that->expandMacros($string);
457 if ( $tooltip ne $defaultToolTip ) {
458 $link = CGI::span( { title => $tooltip }, $expanded );
459 }
460 else {
461 $link = $expanded;
462 }
463 }
464
465 return $link;
466}
467
468sub stringify {
469 my $this = shift;
470 my $fs = "| *Name* | *Type* | *Size* | *Attributes* |\n";
471 foreach my $fieldDef ( @{ $this->{fields} } ) {
472 $fs .= $fieldDef->stringify();
473 }
474 return $fs;
475}
476
477=begin TML
478
479---++ ObjectMethod renderForEdit( $topicObject ) -> $html
480
481 * =$topicObject= the topic being rendered
482
483Render the form fields for entry during an edit session, using data values
484from $meta
485
486=cut
487
488sub renderForEdit {
489 my ( $this, $topicObject ) = @_;
490 ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG;
491 require CGI;
492 my $session = $this->session;
493
494 if ( $this->{mandatoryFieldsPresent} ) {
495 $session->enterContext('mandatoryfields');
496 }
497 my $tmpl = $session->templates->readTemplate('form');
498 $tmpl = $topicObject->expandMacros($tmpl);
499
500 $tmpl =~ s/%FORMTITLE%/$this->_link( $this->web.'.'.$this->topic )/ge;
501 my ( $text, $repeatTitledText, $repeatUntitledText, $afterText ) =
502 split( /%REPEAT%/, $tmpl );
503
504 foreach my $fieldDef ( @{ $this->{fields} } ) {
505
506 my $value;
507 my $tooltip = $fieldDef->{description};
508 my $definingTopic = $fieldDef->{definingTopic};
509 my $title = $fieldDef->{title};
510 my $tmp = '';
511 if ( !$title && !$fieldDef->isEditable() ) {
512
513 # Special handling for untitled labels.
514 # SMELL: Assumes that uneditable fields are not multi-valued
515 $tmp = $repeatUntitledText;
516 $value =
517 $topicObject->renderTML(
518 $topicObject->expandMacros( $fieldDef->{value} ) );
519 }
520 else {
521 $tmp = $repeatTitledText;
522
523 if ( defined( $fieldDef->{name} ) ) {
524 my $field = $topicObject->get( 'FIELD', $fieldDef->{name} );
525 $value = $field->{value};
526 }
527 my $extra = ''; # extras on col 0
528
529 unless ( defined($value) ) {
530
531 my $dv = $fieldDef->getDefaultValue($value);
532 if ( defined($dv) ) {
533 $dv = $topicObject->expandMacros($dv);
534 $value = Foswiki::expandStandardEscapes($dv); # Item2837
535 }
536 }
537
538 # Give plugin field types a chance first (but no chance to add to
539 # col 0 :-(
540 # SMELL: assumes that the field value is a string
541 my $output = $session->{plugins}->dispatch(
542 'renderFormFieldForEditHandler', $fieldDef->{name},
543 $fieldDef->{type}, $fieldDef->{size},
544 $value, $fieldDef->{attributes},
545 $fieldDef->{value}
546 );
547
548 if ($output) {
549 $value = $output;
550 }
551 else {
552 ( $extra, $value ) =
553 $fieldDef->renderForEdit( $topicObject, $value );
554 }
555
556 if ( $fieldDef->isMandatory() ) {
557 $extra .= CGI::span( { class => 'foswikiAlert' }, ' *' );
558 }
559
560 $tmp =~ s/%ROWTITLE%/
561 $this->_link( $title, $tooltip, $definingTopic )/ge;
562 $tmp =~ s/%ROWEXTRA%/$extra/g;
563 }
564 $tmp =~ s/%ROWVALUE%/$value/g;
565 $text .= $tmp;
566 }
567
568 $text .= $afterText;
569 return $text;
570}
571
572=begin TML
573
574---++ ObjectMethod renderHidden( $topicObject ) -> $html
575
576Render form fields found in the meta as hidden inputs, so they pass
577through edits untouched.
578
579=cut
580
581sub renderHidden {
582 my ( $this, $topicObject ) = @_;
583 ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG;
584
585 my $text = '';
586
587 foreach my $field ( @{ $this->{fields} } ) {
588 $text .= $field->renderHidden($topicObject);
589 }
590
591 return $text;
592}
593
594=begin TML
595
596---++ ObjectMethod getFieldValuesFromQuery($query, $topicObject) -> ( $seen, \@missing )
597
598Extract new values for form fields from a query.
599
600 * =$query= - the query
601 * =$topicObject= - the meta object that is storing the form values
602
603For each field, if there is a value in the query, use it.
604Otherwise if there is already entry for the field in the meta, keep it.
605
606Returns the number of fields which had values provided by the query,
607and a references to an array of the names of mandatory fields that were
608missing from the query.
609
610=cut
611
612sub getFieldValuesFromQuery {
613 my ( $this, $query, $topicObject ) = @_;
614 ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG;
615 my @missing;
616 my $seen = 0;
617
618 # Remove the old defs so we apply the
619 # order in the form definition, and not the
620 # order in the previous meta object. See Item1982.
621 my @old = $topicObject->find('FIELD');
622 $topicObject->remove('FIELD');
623 foreach my $fieldDef ( @{ $this->{fields} } ) {
624 my ( $set, $present ) =
625 $fieldDef->populateMetaFromQueryData( $query, $topicObject, \@old );
626 if ($present) {
627 $seen++;
628 }
629 if ( !$set && $fieldDef->isMandatory() ) {
630
631 # Remember missing mandatory fields
632 push( @missing, $fieldDef->{title} || "unnamed field" );
633 }
634 }
635 return ( $seen, \@missing );
636}
637
638=begin TML
639
640---++ ObjectMethod isTextMergeable( $name ) -> $boolean
641
642 * =$name= - name of a form field (value of the =name= attribute)
643
644Returns true if the type of the named field allows it to be text-merged.
645
646If the form does not define the field, it is assumed to be mergeable.
647
648=cut
649
650sub isTextMergeable {
651 my ( $this, $name ) = @_;
652
653 my $fieldDef = $this->getField($name);
654 if ($fieldDef) {
655 return $fieldDef->isTextMergeable();
656 }
657
658 # Field not found - assume it is mergeable
659 return 1;
660}
661
662=begin TML
663
664---++ ObjectMethod getField( $name ) -> $fieldDefinition
665
666 * =$name= - name of a form field (value of the =name= attribute)
667
668Returns a =Foswiki::Form::FieldDefinition=, or undef if the form does not
669define the field.
670
671=cut
672
673sub getField {
674 my ( $this, $name ) = @_;
675 foreach my $fieldDef ( @{ $this->{fields} } ) {
676 return $fieldDef if ( $fieldDef->{name} && $fieldDef->{name} eq $name );
677 }
678 return;
679}
680
681=begin TML
682
683---++ ObjectMethod getFields() -> \@fields
684
685Return a list containing references to field name/value pairs.
686Each entry in the list has a {name} field and a {value} field. It may
687have other fields as well, which caller should ignore. The
688returned list should be treated as *read only* (must not be written to).
689
690=cut
691
692sub getFields {
693 my $this = shift;
694 return $this->{fields};
695}
696
697sub renderForDisplay {
698 my ( $this, $topicObject ) = @_;
699
700 my $templates = $this->session->templates;
701 $templates->readTemplate('formtables');
702
703 my $text = $templates->expandTemplate('FORM:display:header');
704 $text =~ s/\$title/$this->getPath()/ge;
705
706 my $rowTemplate = $templates->expandTemplate('FORM:display:row');
707 my $hasAllFieldsHidden = 1;
708 foreach my $fieldDef ( @{ $this->{fields} } ) {
709 my $fm = $topicObject->get( 'FIELD', $fieldDef->{name} );
710 next unless $fm;
711 my $fa = $fieldDef->{attributes} || '';
712 unless ( $fa =~ m/H/ ) {
713 $hasAllFieldsHidden = 0;
714 my $row = $rowTemplate;
715
716 # Legacy; handle special macros
717 $row =~ s/%A_TITLE%/\$title/g;
718 $row =~ s/%A_VALUE%/\$value(display)/g; # Legacy
719
720 $text .= $fieldDef->renderForDisplay( $row, $fm->{value} );
721 }
722 }
723 return '' if $hasAllFieldsHidden;
724
725 my $footer = $templates->expandTemplate('FORM:display:footer');
726 $footer =~ s/\$title/$this->getPath()/ge;
727 $text .= $footer;
728
729 # Legacy: substitute remaining placeholders in header and footer
730 $text =~ s/%A_TITLE%/$this->getPath()/ge;
731
732 return $text;
733}
734
735# extractPseudoFieldDefs( $meta ) -> $fieldDefs
736# Examine the FIELDs in $meta and reverse-engineer a set of field
737# definitions that can be used to construct a new "pseudo-form". This
738# fake form can be used to support editing of topics that have an attached
739# form that has no definition topic.
740sub _extractPseudoFieldDefs {
741 my ( $this, $meta ) = @_;
742 my @fields = $meta->find('FIELD');
743 my @fieldDefs;
744 require Foswiki::Form::FieldDefinition;
745 foreach my $field (@fields) {
746
747 # Fields are name, value, title, but there is no other type
748 # information so we have to treat them all as "text" :-(
749 my $fieldDef = new Foswiki::Form::FieldDefinition(
750 session => $this->session,
751 name => $field->{name},
752 title => $field->{title} || $field->{name}
753 );
754 push( @fieldDefs, $fieldDef );
755 }
756 return \@fieldDefs;
757}
758
75918µs1;
760__END__