Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm |
Statements | Executed 1762 statements in 16.2ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
28 | 1 | 1 | 7.03ms | 15.5ms | createField | Foswiki::Form::
1 | 1 | 1 | 4.94ms | 1.34s | _parseFormDefinition | Foswiki::Form::
1 | 1 | 1 | 2.23ms | 2.35ms | BEGIN@42 | Foswiki::Form::
455 | 16 | 1 | 1.62ms | 1.62ms | CORE:subst (opcode) | Foswiki::Form::
1 | 1 | 1 | 894µs | 10.4ms | renderForDisplay | Foswiki::Form::
1 | 1 | 1 | 876µs | 1.02ms | BEGIN@43 | Foswiki::Form::
28 | 1 | 1 | 634µs | 792µs | fieldTitle2FieldName | Foswiki::Form::
28 | 1 | 1 | 438µs | 526µs | __ANON__[:302] | Foswiki::Form::
1 | 1 | 1 | 436µs | 1.25ms | finish | Foswiki::Form::
128 | 4 | 1 | 309µs | 309µs | CORE:match (opcode) | Foswiki::Form::
1 | 1 | 1 | 133µs | 1.34s | new | Foswiki::Form::
1 | 1 | 1 | 29µs | 37µs | BEGIN@32 | Foswiki::Form::
1 | 1 | 1 | 24µs | 540µs | BEGIN@39 | Foswiki::Form::
1 | 1 | 1 | 22µs | 47µs | BEGIN@33 | Foswiki::Form::
3 | 1 | 1 | 19µs | 19µs | CORE:substcont (opcode) | Foswiki::Form::
1 | 1 | 1 | 18µs | 68µs | BEGIN@38 | Foswiki::Form::
1 | 1 | 1 | 11µs | 11µs | BEGIN@44 | Foswiki::Form::
1 | 1 | 1 | 10µs | 10µs | BEGIN@41 | Foswiki::Form::
1 | 1 | 1 | 10µs | 10µs | BEGIN@35 | Foswiki::Form::
1 | 1 | 1 | 9µs | 9µs | BEGIN@45 | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | _extractPseudoFieldDefs | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | _link | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | getAvailableForms | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | getField | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | getFieldValuesFromQuery | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | getFields | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | isTextMergeable | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | renderForEdit | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | renderHidden | Foswiki::Form::
0 | 0 | 0 | 0s | 0s | stringify | Foswiki::Form::
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 | |||||
7 | Object representing a single form definition. | ||||
8 | |||||
9 | Form definitions are mainly used to control rendering of a form for | ||||
10 | editing, though there is some application login there that handles | ||||
11 | transferring values between edits and saves. | ||||
12 | |||||
13 | A form definition consists of a Foswiki::Form object, which has a list | ||||
14 | of field definitions. Each field definition is an object of a type | ||||
15 | derived from Foswiki::Form::FieldDefinition. These objects are responsible | ||||
16 | for the actual syntax and semantics of the field type. Form definitions | ||||
17 | are parsed from Foswiki tables, and the types are mapped by name to a | ||||
18 | class declared in Foswiki::Form::* - for example, the =text= type is mapped | ||||
19 | to =Foswiki::Form::Text= and the =checkbox= type to =Foswiki::Form::Checkbox=. | ||||
20 | |||||
21 | The =Foswiki::Form::FieldDefinition= class declares default behaviours for | ||||
22 | types that accept a single value in their definitions. The | ||||
23 | =Foswiki::Form::ListFieldDefinition= extends this for types that have lists | ||||
24 | of 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 | |||||
31 | package Foswiki::Form; | ||||
32 | 2 | 49µs | 2 | 46µs | # spent 37µs (29+8) within Foswiki::Form::BEGIN@32 which was called:
# once (29µs+8µs) by Foswiki::Meta::renderFormForDisplay at line 32 # spent 37µs making 1 call to Foswiki::Form::BEGIN@32
# spent 8µs making 1 call to strict::import |
33 | 2 | 53µs | 2 | 72µs | # spent 47µs (22+25) within Foswiki::Form::BEGIN@33 which was called:
# once (22µs+25µs) by Foswiki::Meta::renderFormForDisplay at line 33 # spent 47µs making 1 call to Foswiki::Form::BEGIN@33
# spent 25µs making 1 call to warnings::import |
34 | |||||
35 | 2 | 75µs | 1 | 10µs | # spent 10µs within Foswiki::Form::BEGIN@35 which was called:
# once (10µs+0s) by Foswiki::Meta::renderFormForDisplay at line 35 # spent 10µs making 1 call to Foswiki::Form::BEGIN@35 |
36 | 1 | 11µs | our @ISA = ('Foswiki::Meta'); | ||
37 | |||||
38 | 2 | 50µs | 2 | 117µs | # spent 68µs (18+49) within Foswiki::Form::BEGIN@38 which was called:
# once (18µs+49µs) by Foswiki::Meta::renderFormForDisplay at line 38 # spent 68µs making 1 call to Foswiki::Form::BEGIN@38
# spent 49µs making 1 call to Assert::import |
39 | 2 | 59µs | 2 | 1.05ms | # spent 540µs (24+515) within Foswiki::Form::BEGIN@39 which was called:
# once (24µs+515µs) by Foswiki::Meta::renderFormForDisplay at line 39 # spent 540µs making 1 call to Foswiki::Form::BEGIN@39
# spent 515µs making 1 call to Error::import |
40 | |||||
41 | 2 | 40µs | 1 | 10µs | # spent 10µs within Foswiki::Form::BEGIN@41 which was called:
# once (10µs+0s) by Foswiki::Meta::renderFormForDisplay at line 41 # spent 10µs making 1 call to Foswiki::Form::BEGIN@41 |
42 | 2 | 165µs | 1 | 2.35ms | # spent 2.35ms (2.23+123µs) within Foswiki::Form::BEGIN@42 which was called:
# once (2.23ms+123µs) by Foswiki::Meta::renderFormForDisplay at line 42 # spent 2.35ms making 1 call to Foswiki::Form::BEGIN@42 |
43 | 2 | 159µs | 1 | 1.02ms | # spent 1.02ms (876µs+139µs) within Foswiki::Form::BEGIN@43 which was called:
# once (876µs+139µs) by Foswiki::Meta::renderFormForDisplay at line 43 # spent 1.02ms making 1 call to Foswiki::Form::BEGIN@43 |
44 | 2 | 40µs | 1 | 11µs | # spent 11µs within Foswiki::Form::BEGIN@44 which was called:
# once (11µs+0s) by Foswiki::Meta::renderFormForDisplay at line 44 # spent 11µs making 1 call to Foswiki::Form::BEGIN@44 |
45 | 2 | 3.95ms | 1 | 9µs | # spent 9µs within Foswiki::Form::BEGIN@45 which was called:
# once (9µs+0s) by Foswiki::Meta::renderFormForDisplay at line 45 # spent 9µs making 1 call to Foswiki::Form::BEGIN@45 |
46 | |||||
47 | # The following are reserved as URL parameters to scripts and may not be | ||||
48 | # used as field names in forms. | ||||
49 | 18 | 65µs | my %reservedFieldNames = map { $_ => 1 } | ||
50 | qw( action breaklock contenttype cover dontnotify editaction | ||||
51 | forcenewrevision formtemplate onlynewtopic onlywikiname | ||||
52 | originalrev skin templatetopic text topic topicparent user ); | ||||
53 | |||||
54 | =begin TML | ||||
55 | |||||
56 | ---++ ClassMethod new ( $session, $web, $topic, \@def ) | ||||
57 | |||||
58 | Looks up a form in the session object or, if it hasn't been read yet, | ||||
59 | reads it from the form definition topic on disc. | ||||
60 | * =$web= - default web to recover form from, if =$form= doesn't | ||||
61 | specify a web | ||||
62 | * =$topic= - name of the topic that contains the form definition | ||||
63 | * =\@def= - optional. A reference to a list of field definitions. | ||||
64 | If present, these definitions will be used, rather than any read from | ||||
65 | the form definition topic. | ||||
66 | |||||
67 | May throw Foswiki::OopsException if the web and form are not valid for use as a | ||||
68 | form name, or if \@def is not given and the form does not exist in the | ||||
69 | database. May throw Foswiki::AccessControlException if the form schema | ||||
70 | in the database is protected against view. | ||||
71 | |||||
72 | =cut | ||||
73 | |||||
74 | # spent 1.34s (133µs+1.34) within Foswiki::Form::new which was called:
# once (133µs+1.34s) by Foswiki::Meta::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm:1739] at line 1737 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm | ||||
75 | 15 | 108µs | my ( $class, $session, $web, $form, $def ) = @_; | ||
76 | |||||
77 | my $this = $session->{forms}->{"$web.$form"}; | ||||
78 | unless ($this) { | ||||
79 | |||||
80 | # A form name has to be a valid topic name after normalisation | ||||
81 | 1 | 50µs | my ( $vweb, $vtopic ) = $session->normalizeWebTopicName( $web, $form ); # spent 50µs making 1 call to Foswiki::normalizeWebTopicName | ||
82 | 1 | 95µs | $vweb = Foswiki::Sandbox::untaint( $vweb, # spent 95µs making 1 call to Foswiki::Sandbox::untaint | ||
83 | \&Foswiki::Sandbox::validateWebName ); | ||||
84 | 1 | 67µs | $vtopic = Foswiki::Sandbox::untaint( $vtopic, # spent 67µs making 1 call to Foswiki::Sandbox::untaint | ||
85 | \&Foswiki::Sandbox::validateTopicName ); | ||||
86 | unless ( $vweb && $vtopic ) { | ||||
87 | throw Foswiki::OopsException( | ||||
88 | 'attention', | ||||
89 | def => 'invalid_form_name', | ||||
90 | web => $session->{webName}, | ||||
91 | topic => $session->{topicName}, | ||||
92 | params => [ $web, $form ] | ||||
93 | ); | ||||
94 | } | ||||
95 | |||||
96 | # Got to have either a def or a topic | ||||
97 | 1 | 200µs | unless ( $def || $session->topicExists( $vweb, $vtopic ) ) { # spent 200µs making 1 call to Foswiki::topicExists | ||
98 | throw Foswiki::OopsException( | ||||
99 | 'attention', | ||||
100 | def => 'no_form_def', | ||||
101 | web => $session->{webName}, | ||||
102 | topic => $session->{topicName}, | ||||
103 | params => [ $web, $form ] | ||||
104 | ); | ||||
105 | } | ||||
106 | |||||
107 | 1 | 73µs | $this = $class->SUPER::new( $session, $vweb, $vtopic ); # spent 73µs making 1 call to Foswiki::Meta::new | ||
108 | $session->{forms}->{"$web.$form"} = $this; | ||||
109 | |||||
110 | 1 | 3.17ms | unless ( $def || $this->haveAccess('VIEW') ) { # spent 3.17ms making 1 call to Foswiki::Meta::haveAccess | ||
111 | throw Foswiki::AccessControlException( 'VIEW', $session->{user}, | ||||
112 | $web, $form, $Foswiki::Meta::reason ); | ||||
113 | } | ||||
114 | |||||
115 | if ( ref($this) ne 'Foswiki::Form' ) { | ||||
116 | |||||
117 | #recast if we have to - allowing the cache to work its magic | ||||
118 | $this = bless( $this, 'Foswiki::Form' ); | ||||
119 | $session->{forms}->{"$web.$form"} = $this; | ||||
120 | } | ||||
121 | |||||
122 | unless ($def) { | ||||
123 | 1 | 1.34s | $this->{fields} = $this->_parseFormDefinition(); # spent 1.34s making 1 call to Foswiki::Form::_parseFormDefinition | ||
124 | } | ||||
125 | elsif ( ref($def) eq 'ARRAY' ) { | ||||
126 | $this->{fields} = $def; | ||||
127 | } | ||||
128 | else { | ||||
129 | |||||
130 | # Foswiki::Meta object | ||||
131 | $this->{fields} = $this->_extractPseudoFieldDefs($def); | ||||
132 | } | ||||
133 | } | ||||
134 | |||||
135 | return $this; | ||||
136 | } | ||||
137 | |||||
138 | =begin TML | ||||
139 | |||||
140 | ---++ ObjectMethod finish() | ||||
141 | Break circular references. | ||||
142 | |||||
143 | =cut | ||||
144 | |||||
145 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
146 | # whether they are references or not. That way this method is "golden | ||||
147 | # documentation" of the live fields in the object. | ||||
148 | # spent 1.25ms (436µs+810µs) within Foswiki::Form::finish which was called:
# once (436µs+810µs) by Foswiki::finish at line 2091 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm | ||||
149 | 33 | 400µs | my $this = shift; | ||
150 | foreach ( @{ $this->{fields} } ) { | ||||
151 | 28 | 680µs | $_->finish(); # spent 295µs making 20 calls to Foswiki::Form::FieldDefinition::finish, avg 15µs/call
# spent 227µs making 4 calls to Foswiki::Form::Select::finish, avg 57µs/call
# spent 158µs making 4 calls to Foswiki::Form::ListFieldDefinition::finish, avg 40µs/call | ||
152 | } | ||||
153 | undef $this->{fields}; | ||||
154 | 1 | 131µs | $this->SUPER::finish(); # spent 131µs making 1 call to Foswiki::Meta::finish | ||
155 | } | ||||
156 | |||||
157 | =begin TML | ||||
158 | |||||
159 | ---++ StaticMethod getAvailableForms( $metaObject ) -> @forms | ||||
160 | |||||
161 | Get a list of the names of forms that are available for use in the | ||||
162 | given topic. $metaObject can be a topic or a web. | ||||
163 | |||||
164 | =cut | ||||
165 | |||||
166 | sub getAvailableForms { | ||||
167 | my $metaObject = shift; | ||||
168 | if ( defined $metaObject->topic ) { | ||||
169 | $metaObject = | ||||
170 | Foswiki::Meta->new( $metaObject->session, $metaObject->web ); | ||||
171 | } | ||||
172 | my $legalForms = $metaObject->getPreference('WEBFORMS') || ''; | ||||
173 | $legalForms =~ s/^\s+//; | ||||
174 | $legalForms =~ s/\s+$//; | ||||
175 | my @forms = split( /[,\s]+/, $legalForms ); | ||||
176 | |||||
177 | # This is where we could %SEARCH for *Form topics | ||||
178 | return @forms; | ||||
179 | } | ||||
180 | |||||
181 | =begin TML | ||||
182 | |||||
183 | ---++ StaticMethod fieldTitle2FieldName($title) -> $name | ||||
184 | Chop out all except A-Za-z0-9_. from a field name to create a | ||||
185 | valid "name" for storing in meta-data | ||||
186 | |||||
187 | =cut | ||||
188 | |||||
189 | # spent 792µs (634+158) within Foswiki::Form::fieldTitle2FieldName which was called 28 times, avg 28µs/call:
# 28 times (634µs+158µs) by Foswiki::Form::_parseFormDefinition at line 257, avg 28µs/call | ||||
190 | 168 | 805µs | my ($text) = @_; | ||
191 | return '' unless defined($text); | ||||
192 | 28 | 51µs | $text =~ s/!//g; # spent 51µs making 28 calls to Foswiki::Form::CORE:subst, avg 2µs/call | ||
193 | 28 | 42µs | $text =~ s/<nop>//g; # support <nop> character in title # spent 42µs making 28 calls to Foswiki::Form::CORE:subst, avg 1µs/call | ||
194 | 28 | 65µs | $text =~ s/[^A-Za-z0-9_\.]//g; # spent 65µs making 28 calls to Foswiki::Form::CORE:subst, avg 2µs/call | ||
195 | return $text; | ||||
196 | } | ||||
197 | |||||
198 | # Get definition from supplied topic text | ||||
199 | # Returns array of arrays | ||||
200 | # 1st - list fields | ||||
201 | # 2nd - name, title, type, size, vals, tooltip, attributes | ||||
202 | # Possible attributes are "M" (mandatory field) | ||||
203 | # spent 1.34s (4.94ms+1.33) within Foswiki::Form::_parseFormDefinition which was called:
# once (4.94ms+1.33s) by Foswiki::Form::new at line 123 | ||||
204 | 1086 | 6.36ms | my $this = shift; | ||
205 | |||||
206 | my @fields = (); | ||||
207 | my $inBlock = 0; | ||||
208 | 1 | 28µs | my $text = $this->text(); # spent 28µs making 1 call to Foswiki::Meta::text | ||
209 | $text = '' unless defined $text; | ||||
210 | |||||
211 | 1 | 6µs | $text =~ s/\\\n//g; # remove trailing '\' and join continuation lines # spent 6µs making 1 call to Foswiki::Form::CORE:subst | ||
212 | |||||
213 | # | *Name:* | *Type:* | *Size:* | *Value:* | *Tooltip message:* | *Attributes:* | | ||||
214 | # Tooltip and attributes are optional | ||||
215 | foreach my $line ( split( /\n/, $text ) ) { | ||||
216 | 58 | 138µs | if ( $line =~ /^\s*\|.*Name[^|]*\|.*Type[^|]*\|.*Size[^|]*\|/ ) { # spent 138µs making 58 calls to Foswiki::Form::CORE:match, avg 2µs/call | ||
217 | $inBlock = 1; | ||||
218 | next; | ||||
219 | } | ||||
220 | |||||
221 | # Only insist on first field being present FIXME - use oops page instead? | ||||
222 | 29 | 167µs | if ( $inBlock && $line =~ s/^\s*\|\s*// ) { # spent 167µs making 29 calls to Foswiki::Form::CORE:subst, avg 6µs/call | ||
223 | 28 | 56µs | $line =~ s/\\\|/\007/g; # protect \| from split # spent 56µs making 28 calls to Foswiki::Form::CORE:subst, avg 2µs/call | ||
224 | 116 | 205µs | my ( $title, $type, $size, $vals, $tooltip, $attributes ) = # spent 205µs making 116 calls to Foswiki::Form::CORE:subst, avg 2µs/call | ||
225 | map { s/\007/|/g; $_ } split( /\s*\|\s*/, $line ); | ||||
226 | |||||
227 | $title ||= ''; | ||||
228 | |||||
229 | $type ||= ''; | ||||
230 | $type = lc($type); | ||||
231 | 28 | 130µs | $type =~ s/^\s*//go; # spent 130µs making 28 calls to Foswiki::Form::CORE:subst, avg 5µs/call | ||
232 | 28 | 160µs | $type =~ s/\s*$//go; # spent 160µs making 28 calls to Foswiki::Form::CORE:subst, avg 6µs/call | ||
233 | $type = 'text' if ( !$type ); | ||||
234 | |||||
235 | $size ||= ''; | ||||
236 | |||||
237 | $vals ||= ''; | ||||
238 | 28 | 0s | $vals = $this->expandMacros($vals); # spent 1.32s making 28 calls to Foswiki::Meta::expandMacros, avg 47.0ms/call, recursion: max depth 1, sum of overlapping time 1.32s | ||
239 | 28 | 155µs | $vals =~ s/<\/?(!|nop|noautolink)\/?>//go; # spent 155µs making 28 calls to Foswiki::Form::CORE:subst, avg 6µs/call | ||
240 | 28 | 71µs | $vals =~ s/^\s+//g; # spent 71µs making 28 calls to Foswiki::Form::CORE:subst, avg 3µs/call | ||
241 | 28 | 292µs | $vals =~ s/\s+$//g; # spent 292µs making 28 calls to Foswiki::Form::CORE:subst, avg 10µs/call | ||
242 | |||||
243 | $tooltip ||= ''; | ||||
244 | |||||
245 | $attributes ||= ''; | ||||
246 | 28 | 115µs | $attributes =~ s/\s*//go; # spent 115µs making 28 calls to Foswiki::Form::CORE:subst, avg 4µs/call | ||
247 | $attributes = '' if ( !$attributes ); | ||||
248 | |||||
249 | my $definingTopic = ""; | ||||
250 | 28 | 58µs | if ( $title =~ /\[\[(.+)\]\[(.+)\]\]/ ) { # spent 58µs making 28 calls to Foswiki::Form::CORE:match, avg 2µs/call | ||
251 | |||||
252 | # use common defining topics with different field titles | ||||
253 | $definingTopic = fieldTitle2FieldName($1); | ||||
254 | $title = $2; | ||||
255 | } | ||||
256 | |||||
257 | 28 | 792µs | my $name = fieldTitle2FieldName($title); # spent 792µs making 28 calls to Foswiki::Form::fieldTitle2FieldName, avg 28µs/call | ||
258 | |||||
259 | # Rename fields with reserved names | ||||
260 | if ( $reservedFieldNames{$name} ) { | ||||
261 | $name .= '_'; | ||||
262 | } | ||||
263 | 84 | 15.9ms | my $fieldDef = $this->createField( # spent 15.5ms making 28 calls to Foswiki::Form::createField, avg 552µs/call
# spent 220µs making 28 calls to Foswiki::Meta::web, avg 8µs/call
# spent 195µs making 28 calls to Foswiki::Meta::topic, avg 7µs/call | ||
264 | $type, | ||||
265 | name => $name, | ||||
266 | title => $title, | ||||
267 | size => $size, | ||||
268 | value => $vals, | ||||
269 | tooltip => $tooltip, | ||||
270 | attributes => $attributes, | ||||
271 | definingTopic => $definingTopic, | ||||
272 | web => $this->web(), | ||||
273 | topic => $this->topic() | ||||
274 | ); | ||||
275 | push( @fields, $fieldDef ); | ||||
276 | |||||
277 | 2 | 26µs | $this->{mandatoryFieldsPresent} ||= $fieldDef->isMandatory(); # spent 26µs making 2 calls to Foswiki::Form::FieldDefinition::isMandatory, avg 13µs/call | ||
278 | } | ||||
279 | else { | ||||
280 | $inBlock = 0; | ||||
281 | } | ||||
282 | } | ||||
283 | |||||
284 | return \@fields; | ||||
285 | } | ||||
286 | |||||
287 | # PROTECTED | ||||
288 | # Create a field object. Done like this so that this method can be | ||||
289 | # overridden by subclasses to extend the range of field types. | ||||
290 | # spent 15.5ms (7.03+8.43) within Foswiki::Form::createField which was called 28 times, avg 552µs/call:
# 28 times (7.03ms+8.43ms) by Foswiki::Form::_parseFormDefinition at line 263, avg 552µs/call | ||||
291 | 168 | 2.22ms | my $this = shift; | ||
292 | my $type = shift; | ||||
293 | |||||
294 | # The untaint is required for the validation *and* the ucfirst, which | ||||
295 | # retaints when use locale is in force | ||||
296 | my $class = Foswiki::Sandbox::untaint( | ||||
297 | $type, | ||||
298 | # spent 526µs (438+88) within Foswiki::Form::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm:302] which was called 28 times, avg 19µs/call:
# 28 times (438µs+88µs) by Foswiki::Sandbox::untaint at line 133 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Sandbox.pm, avg 19µs/call | ||||
299 | 84 | 563µs | my $class = shift; | ||
300 | 28 | 88µs | $class =~ /^(\w*)/; # cut off +buttons etc # spent 88µs making 28 calls to Foswiki::Form::CORE:match, avg 3µs/call | ||
301 | return 'Foswiki::Form::' . ucfirst($1); | ||||
302 | } | ||||
303 | 28 | 1.53ms | ); # spent 1.53ms making 28 calls to Foswiki::Sandbox::untaint, avg 55µs/call | ||
304 | eval 'require ' . $class; # spent 191µs executing statements in 14 string evals (merged)
# spent 152µs executing statements in 4 string evals (merged)
# spent 142µs executing statements in string eval
# spent 120µs executing statements in 6 string evals (merged)
# spent 116µs executing statements in 2 string evals (merged)
# spent 113µs executing statements in string eval | ||||
305 | if ($@) { | ||||
306 | |||||
307 | # Type not available; use base type | ||||
308 | require Foswiki::Form::FieldDefinition; | ||||
309 | $class = 'Foswiki::Form::FieldDefinition'; | ||||
310 | } | ||||
311 | 56 | 6.15ms | return $class->new( session => $this->session(), type => $type, @_ ); # spent 3.83ms making 2 calls to Foswiki::Form::Textboxlist::new, avg 1.92ms/call
# spent 862µs making 14 calls to Foswiki::Form::FieldDefinition::new, avg 62µs/call
# spent 575µs making 6 calls to Foswiki::Form::Text::new, avg 96µs/call
# spent 428µs making 4 calls to Foswiki::Form::Select::new, avg 107µs/call
# spent 169µs making 28 calls to Foswiki::Meta::session, avg 6µs/call
# spent 164µs making 1 call to Foswiki::Form::Checkbox::new
# spent 118µs making 1 call to Foswiki::Form::Radio::new | ||
312 | } | ||||
313 | |||||
314 | # Generate a link to the given topic, so we can bring up details in a | ||||
315 | # separate window. | ||||
316 | sub _link { | ||||
317 | my ( $this, $string, $tooltip, $topic ) = @_; | ||||
318 | |||||
319 | $string =~ s/[\[\]]//go; | ||||
320 | |||||
321 | $topic ||= $string; | ||||
322 | my $defaultToolTip = | ||||
323 | $this->session->i18n->maketext('Details in separate window'); | ||||
324 | $tooltip ||= $defaultToolTip; | ||||
325 | |||||
326 | ( my $web, $topic ) = | ||||
327 | $this->session->normalizeWebTopicName( $this->{web}, $topic ); | ||||
328 | |||||
329 | $web = | ||||
330 | Foswiki::Sandbox::untaint( $web, \&Foswiki::Sandbox::validateWebName ); | ||||
331 | |||||
332 | $topic = Foswiki::Sandbox::untaint( $topic, | ||||
333 | \&Foswiki::Sandbox::validateTopicName ); | ||||
334 | |||||
335 | my $link; | ||||
336 | |||||
337 | if ( $this->session->topicExists( $web, $topic ) ) { | ||||
338 | $link = CGI::a( | ||||
339 | { | ||||
340 | target => $topic, | ||||
341 | title => $tooltip, | ||||
342 | href => $this->session->getScriptUrl( 0, 'view', $web, $topic ), | ||||
343 | rel => 'nofollow' | ||||
344 | }, | ||||
345 | $string | ||||
346 | ); | ||||
347 | } | ||||
348 | else { | ||||
349 | my $that = | ||||
350 | Foswiki::Meta->new( $this->session, $web, | ||||
351 | $topic || $Foswiki::cfg{HomeTopicName} ); | ||||
352 | my $expanded = $that->expandMacros($string); | ||||
353 | if ( $tooltip ne $defaultToolTip ) { | ||||
354 | $link = CGI::span( { title => $tooltip }, $expanded ); | ||||
355 | } | ||||
356 | else { | ||||
357 | $link = $expanded; | ||||
358 | } | ||||
359 | } | ||||
360 | |||||
361 | return $link; | ||||
362 | } | ||||
363 | |||||
364 | sub stringify { | ||||
365 | my $this = shift; | ||||
366 | my $fs = "| *Name* | *Type* | *Size* | *Attributes* |\n"; | ||||
367 | foreach my $fieldDef ( @{ $this->{fields} } ) { | ||||
368 | $fs .= $fieldDef->stringify(); | ||||
369 | } | ||||
370 | return $fs; | ||||
371 | } | ||||
372 | |||||
373 | =begin TML | ||||
374 | |||||
375 | ---++ ObjectMethod renderForEdit( $topicObject ) -> $html | ||||
376 | |||||
377 | * =$topicObject= the topic being rendered | ||||
378 | |||||
379 | Render the form fields for entry during an edit session, using data values | ||||
380 | from $meta | ||||
381 | |||||
382 | =cut | ||||
383 | |||||
384 | sub renderForEdit { | ||||
385 | my ( $this, $topicObject ) = @_; | ||||
386 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
387 | require CGI; | ||||
388 | my $session = $this->session; | ||||
389 | |||||
390 | if ( $this->{mandatoryFieldsPresent} ) { | ||||
391 | $session->enterContext('mandatoryfields'); | ||||
392 | } | ||||
393 | my $tmpl = $session->templates->readTemplate('form'); | ||||
394 | $tmpl = $topicObject->expandMacros($tmpl); | ||||
395 | |||||
396 | $tmpl =~ s/%FORMTITLE%/$this->_link( $this->web.'.'.$this->topic )/ge; | ||||
397 | my ( $text, $repeatTitledText, $repeatUntitledText, $afterText ) = | ||||
398 | split( /%REPEAT%/, $tmpl ); | ||||
399 | |||||
400 | foreach my $fieldDef ( @{ $this->{fields} } ) { | ||||
401 | |||||
402 | my $value; | ||||
403 | my $tooltip = $fieldDef->{tooltip}; | ||||
404 | my $definingTopic = $fieldDef->{definingTopic}; | ||||
405 | my $title = $fieldDef->{title}; | ||||
406 | my $tmp = ''; | ||||
407 | if ( !$title && !$fieldDef->isEditable() ) { | ||||
408 | |||||
409 | # Special handling for untitled labels. | ||||
410 | # SMELL: Assumes that uneditable fields are not multi-valued | ||||
411 | $tmp = $repeatUntitledText; | ||||
412 | $value = | ||||
413 | $topicObject->renderTML( | ||||
414 | $topicObject->expandMacros( $fieldDef->{value} ) ); | ||||
415 | } | ||||
416 | else { | ||||
417 | $tmp = $repeatTitledText; | ||||
418 | |||||
419 | if ( defined( $fieldDef->{name} ) ) { | ||||
420 | my $field = $topicObject->get( 'FIELD', $fieldDef->{name} ); | ||||
421 | $value = $field->{value}; | ||||
422 | } | ||||
423 | my $extra = ''; # extras on col 0 | ||||
424 | |||||
425 | unless ( defined($value) ) { | ||||
426 | my $dv = $fieldDef->getDefaultValue($value); | ||||
427 | if ( defined($dv) ) { | ||||
428 | $dv = $topicObject->expandMacros($dv); | ||||
429 | $value = Foswiki::expandStandardEscapes($dv); # Item2837 | ||||
430 | } | ||||
431 | } | ||||
432 | |||||
433 | # Give plugin field types a chance first (but no chance to add to | ||||
434 | # col 0 :-( | ||||
435 | # SMELL: assumes that the field value is a string | ||||
436 | my $output = $session->{plugins}->dispatch( | ||||
437 | 'renderFormFieldForEditHandler', $fieldDef->{name}, | ||||
438 | $fieldDef->{type}, $fieldDef->{size}, | ||||
439 | $value, $fieldDef->{attributes}, | ||||
440 | $fieldDef->{value} | ||||
441 | ); | ||||
442 | |||||
443 | if ($output) { | ||||
444 | $value = $output; | ||||
445 | } | ||||
446 | else { | ||||
447 | ( $extra, $value ) = | ||||
448 | $fieldDef->renderForEdit( $topicObject, $value ); | ||||
449 | } | ||||
450 | |||||
451 | if ( $fieldDef->isMandatory() ) { | ||||
452 | $extra .= CGI::span( { class => 'foswikiAlert' }, ' *' ); | ||||
453 | } | ||||
454 | |||||
455 | $tmp =~ s/%ROWTITLE%/ | ||||
456 | $this->_link( $title, $tooltip, $definingTopic )/ge; | ||||
457 | $tmp =~ s/%ROWEXTRA%/$extra/g; | ||||
458 | } | ||||
459 | $tmp =~ s/%ROWVALUE%/$value/g; | ||||
460 | $text .= $tmp; | ||||
461 | } | ||||
462 | |||||
463 | $text .= $afterText; | ||||
464 | return $text; | ||||
465 | } | ||||
466 | |||||
467 | =begin TML | ||||
468 | |||||
469 | ---++ ObjectMethod renderHidden( $topicObject ) -> $html | ||||
470 | |||||
471 | Render form fields found in the meta as hidden inputs, so they pass | ||||
472 | through edits untouched. | ||||
473 | |||||
474 | =cut | ||||
475 | |||||
476 | sub renderHidden { | ||||
477 | my ( $this, $topicObject ) = @_; | ||||
478 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
479 | |||||
480 | my $text = ''; | ||||
481 | |||||
482 | foreach my $field ( @{ $this->{fields} } ) { | ||||
483 | $text .= $field->renderHidden($topicObject); | ||||
484 | } | ||||
485 | |||||
486 | return $text; | ||||
487 | } | ||||
488 | |||||
489 | =begin TML | ||||
490 | |||||
491 | ---++ ObjectMethod getFieldValuesFromQuery($query, $topicObject) -> ( $seen, \@missing ) | ||||
492 | |||||
493 | Extract new values for form fields from a query. | ||||
494 | |||||
495 | * =$query= - the query | ||||
496 | * =$topicObject= - the meta object that is storing the form values | ||||
497 | |||||
498 | For each field, if there is a value in the query, use it. | ||||
499 | Otherwise if there is already entry for the field in the meta, keep it. | ||||
500 | |||||
501 | Returns the number of fields which had values provided by the query, | ||||
502 | and a references to an array of the names of mandatory fields that were | ||||
503 | missing from the query. | ||||
504 | |||||
505 | =cut | ||||
506 | |||||
507 | sub getFieldValuesFromQuery { | ||||
508 | my ( $this, $query, $topicObject ) = @_; | ||||
509 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
510 | my @missing; | ||||
511 | my $seen = 0; | ||||
512 | |||||
513 | # Remove the old defs so we apply the | ||||
514 | # order in the form definition, and not the | ||||
515 | # order in the previous meta object. See Item1982. | ||||
516 | my @old = $topicObject->find('FIELD'); | ||||
517 | $topicObject->remove('FIELD'); | ||||
518 | foreach my $fieldDef ( @{ $this->{fields} } ) { | ||||
519 | my ( $set, $present ) = | ||||
520 | $fieldDef->populateMetaFromQueryData( $query, $topicObject, \@old ); | ||||
521 | if ($present) { | ||||
522 | $seen++; | ||||
523 | } | ||||
524 | if ( !$set && $fieldDef->isMandatory() ) { | ||||
525 | |||||
526 | # Remember missing mandatory fields | ||||
527 | push( @missing, $fieldDef->{title} || "unnamed field" ); | ||||
528 | } | ||||
529 | } | ||||
530 | return ( $seen, \@missing ); | ||||
531 | } | ||||
532 | |||||
533 | =begin TML | ||||
534 | |||||
535 | ---++ ObjectMethod isTextMergeable( $name ) -> $boolean | ||||
536 | |||||
537 | * =$name= - name of a form field (value of the =name= attribute) | ||||
538 | |||||
539 | Returns true if the type of the named field allows it to be text-merged. | ||||
540 | |||||
541 | If the form does not define the field, it is assumed to be mergeable. | ||||
542 | |||||
543 | =cut | ||||
544 | |||||
545 | sub isTextMergeable { | ||||
546 | my ( $this, $name ) = @_; | ||||
547 | |||||
548 | my $fieldDef = $this->getField($name); | ||||
549 | if ($fieldDef) { | ||||
550 | return $fieldDef->isTextMergeable(); | ||||
551 | } | ||||
552 | |||||
553 | # Field not found - assume it is mergeable | ||||
554 | return 1; | ||||
555 | } | ||||
556 | |||||
557 | =begin TML | ||||
558 | |||||
559 | ---++ ObjectMethod getField( $name ) -> $fieldDefinition | ||||
560 | |||||
561 | * =$name= - name of a form field (value of the =name= attribute) | ||||
562 | |||||
563 | Returns a =Foswiki::Form::FieldDefinition=, or undef if the form does not | ||||
564 | define the field. | ||||
565 | |||||
566 | =cut | ||||
567 | |||||
568 | sub getField { | ||||
569 | my ( $this, $name ) = @_; | ||||
570 | foreach my $fieldDef ( @{ $this->{fields} } ) { | ||||
571 | return $fieldDef if ( $fieldDef->{name} && $fieldDef->{name} eq $name ); | ||||
572 | } | ||||
573 | return; | ||||
574 | } | ||||
575 | |||||
576 | =begin TML | ||||
577 | |||||
578 | ---++ ObjectMethod getFields() -> \@fields | ||||
579 | |||||
580 | Return a list containing references to field name/value pairs. | ||||
581 | Each entry in the list has a {name} field and a {value} field. It may | ||||
582 | have other fields as well, which caller should ignore. The | ||||
583 | returned list should be treated as *read only* (must not be written to). | ||||
584 | |||||
585 | =cut | ||||
586 | |||||
587 | sub getFields { | ||||
588 | my $this = shift; | ||||
589 | return $this->{fields}; | ||||
590 | } | ||||
591 | |||||
592 | # spent 10.4ms (894µs+9.53) within Foswiki::Form::renderForDisplay which was called:
# once (894µs+9.53ms) by Foswiki::Meta::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm:1739] at line 1738 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm | ||||
593 | 168 | 989µs | my ( $this, $topicObject ) = @_; | ||
594 | |||||
595 | 2 | 17µs | my $templates = $this->session->templates; # spent 13µs making 1 call to Foswiki::templates
# spent 4µs making 1 call to Foswiki::Meta::session | ||
596 | 1 | 4.85ms | $templates->readTemplate('formtables'); # spent 4.85ms making 1 call to Foswiki::Templates::readTemplate | ||
597 | |||||
598 | 1 | 1.30ms | my $text = $templates->expandTemplate('FORM:display:header'); # spent 1.30ms making 1 call to Foswiki::Templates::expandTemplate | ||
599 | |||||
600 | 1 | 220µs | my $rowTemplate = $templates->expandTemplate('FORM:display:row'); # spent 220µs making 1 call to Foswiki::Templates::expandTemplate | ||
601 | my $hasAllFieldsHidden = 1; | ||||
602 | foreach my $fieldDef ( @{ $this->{fields} } ) { | ||||
603 | 28 | 988µs | my $fm = $topicObject->get( 'FIELD', $fieldDef->{name} ); # spent 988µs making 28 calls to Foswiki::Meta::get, avg 35µs/call | ||
604 | next unless $fm; | ||||
605 | my $fa = $fm->{attributes} || ''; | ||||
606 | 14 | 25µs | unless ( $fa =~ /H/ ) { # spent 25µs making 14 calls to Foswiki::Form::CORE:match, avg 2µs/call | ||
607 | $hasAllFieldsHidden = 0; | ||||
608 | my $row = $rowTemplate; | ||||
609 | |||||
610 | # Legacy; was %A_TITLE% before it was $title | ||||
611 | 14 | 59µs | $row =~ s/%A_TITLE%/\$title/g; # spent 59µs making 14 calls to Foswiki::Form::CORE:subst, avg 4µs/call | ||
612 | 14 | 42µs | $row =~ s/%A_VALUE%/\$value/g; # Legacy # spent 42µs making 14 calls to Foswiki::Form::CORE:subst, avg 3µs/call | ||
613 | 14 | 1.75ms | $text .= $fieldDef->renderForDisplay( $row, $fm->{value} ); # spent 1.75ms making 14 calls to Foswiki::Form::FieldDefinition::renderForDisplay, avg 125µs/call | ||
614 | } | ||||
615 | } | ||||
616 | return '' if $hasAllFieldsHidden; | ||||
617 | |||||
618 | 1 | 231µs | $text .= $templates->expandTemplate('FORM:display:footer'); # spent 231µs making 1 call to Foswiki::Templates::expandTemplate | ||
619 | |||||
620 | # substitute remaining placeholders in footer and header | ||||
621 | 6 | 48µs | $text =~ s/%A_TITLE%/$this->getPath()/ge; # spent 25µs making 2 calls to Foswiki::Meta::getPath, avg 12µs/call
# spent 19µs making 3 calls to Foswiki::Form::CORE:substcont, avg 6µs/call
# spent 4µs making 1 call to Foswiki::Form::CORE:subst | ||
622 | |||||
623 | return $text; | ||||
624 | } | ||||
625 | |||||
626 | # extractPseudoFieldDefs( $meta ) -> $fieldDefs | ||||
627 | # Examine the FIELDs in $meta and reverse-engineer a set of field | ||||
628 | # definitions that can be used to construct a new "pseudo-form". This | ||||
629 | # fake form can be used to support editing of topics that have an attached | ||||
630 | # form that has no definition topic. | ||||
631 | sub _extractPseudoFieldDefs { | ||||
632 | my ( $this, $meta ) = @_; | ||||
633 | my @fields = $meta->find('FIELD'); | ||||
634 | my @fieldDefs; | ||||
635 | require Foswiki::Form::FieldDefinition; | ||||
636 | foreach my $field (@fields) { | ||||
637 | |||||
638 | # Fields are name, value, title, but there is no other type | ||||
639 | # information so we have to treat them all as "text" :-( | ||||
640 | my $fieldDef = new Foswiki::Form::FieldDefinition( | ||||
641 | session => $this->session, | ||||
642 | name => $field->{name}, | ||||
643 | title => $field->{title} || $field->{name}, | ||||
644 | attributes => $field->{attributes} || '' | ||||
645 | ); | ||||
646 | push( @fieldDefs, $fieldDef ); | ||||
647 | } | ||||
648 | return \@fieldDefs; | ||||
649 | } | ||||
650 | |||||
651 | 1 | 10µs | 1; | ||
652 | __END__ | ||||
# spent 309µs within Foswiki::Form::CORE:match which was called 128 times, avg 2µs/call:
# 58 times (138µs+0s) by Foswiki::Form::_parseFormDefinition at line 216, avg 2µs/call
# 28 times (88µs+0s) by Foswiki::Form::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm:302] at line 300, avg 3µs/call
# 28 times (58µs+0s) by Foswiki::Form::_parseFormDefinition at line 250, avg 2µs/call
# 14 times (25µs+0s) by Foswiki::Form::renderForDisplay at line 606, avg 2µs/call | |||||
# spent 1.62ms within Foswiki::Form::CORE:subst which was called 455 times, avg 4µs/call:
# 116 times (205µs+0s) by Foswiki::Form::_parseFormDefinition at line 224, avg 2µs/call
# 29 times (167µs+0s) by Foswiki::Form::_parseFormDefinition at line 222, avg 6µs/call
# 28 times (292µs+0s) by Foswiki::Form::_parseFormDefinition at line 241, avg 10µs/call
# 28 times (160µs+0s) by Foswiki::Form::_parseFormDefinition at line 232, avg 6µs/call
# 28 times (155µs+0s) by Foswiki::Form::_parseFormDefinition at line 239, avg 6µs/call
# 28 times (130µs+0s) by Foswiki::Form::_parseFormDefinition at line 231, avg 5µs/call
# 28 times (115µs+0s) by Foswiki::Form::_parseFormDefinition at line 246, avg 4µs/call
# 28 times (71µs+0s) by Foswiki::Form::_parseFormDefinition at line 240, avg 3µs/call
# 28 times (65µs+0s) by Foswiki::Form::fieldTitle2FieldName at line 194, avg 2µs/call
# 28 times (56µs+0s) by Foswiki::Form::_parseFormDefinition at line 223, avg 2µs/call
# 28 times (51µs+0s) by Foswiki::Form::fieldTitle2FieldName at line 192, avg 2µs/call
# 28 times (42µs+0s) by Foswiki::Form::fieldTitle2FieldName at line 193, avg 1µs/call
# 14 times (59µs+0s) by Foswiki::Form::renderForDisplay at line 611, avg 4µs/call
# 14 times (42µs+0s) by Foswiki::Form::renderForDisplay at line 612, avg 3µs/call
# once (6µs+0s) by Foswiki::Form::_parseFormDefinition at line 211
# once (4µs+0s) by Foswiki::Form::renderForDisplay at line 621 | |||||
# spent 19µs within Foswiki::Form::CORE:substcont which was called 3 times, avg 6µs/call:
# 3 times (19µs+0s) by Foswiki::Form::renderForDisplay at line 621, avg 6µs/call |