Filename | /var/www/foswikidev/core/lib/Foswiki/Configure/Query.pm |
Statements | Executed 23 statements in 2.21ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 14µs | 27µs | BEGIN@4 | Foswiki::Configure::Query::
1 | 1 | 1 | 12µs | 37µs | BEGIN@7 | Foswiki::Configure::Query::
1 | 1 | 1 | 9µs | 13µs | BEGIN@5 | Foswiki::Configure::Query::
1 | 1 | 1 | 8µs | 39µs | BEGIN@17 | Foswiki::Configure::Query::
1 | 1 | 1 | 8µs | 42µs | BEGIN@16 | Foswiki::Configure::Query::
1 | 1 | 1 | 4µs | 4µs | BEGIN@9 | Foswiki::Configure::Query::
1 | 1 | 1 | 3µs | 3µs | BEGIN@11 | Foswiki::Configure::Query::
1 | 1 | 1 | 3µs | 3µs | BEGIN@12 | Foswiki::Configure::Query::
1 | 1 | 1 | 3µs | 3µs | BEGIN@13 | Foswiki::Configure::Query::
1 | 1 | 1 | 3µs | 3µs | BEGIN@10 | Foswiki::Configure::Query::
1 | 1 | 1 | 3µs | 3µs | BEGIN@14 | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | _getSetParams | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | check_current_value | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | getcfg | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | getspec | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | search | Foswiki::Configure::Query::
0 | 0 | 0 | 0s | 0s | wizard | Foswiki::Configure::Query::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for default license and copyright information | ||||
2 | package Foswiki::Configure::Query; | ||||
3 | |||||
4 | 2 | 27µs | 2 | 40µs | # spent 27µs (14+13) within Foswiki::Configure::Query::BEGIN@4 which was called:
# once (14µs+13µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 4 # spent 27µs making 1 call to Foswiki::Configure::Query::BEGIN@4
# spent 13µs making 1 call to strict::import |
5 | 2 | 24µs | 2 | 18µs | # spent 13µs (9+4) within Foswiki::Configure::Query::BEGIN@5 which was called:
# once (9µs+4µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 5 # spent 13µs making 1 call to Foswiki::Configure::Query::BEGIN@5
# spent 4µs making 1 call to warnings::import |
6 | |||||
7 | 2 | 26µs | 2 | 62µs | # spent 37µs (12+25) within Foswiki::Configure::Query::BEGIN@7 which was called:
# once (12µs+25µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 7 # spent 37µs making 1 call to Foswiki::Configure::Query::BEGIN@7
# spent 25µs making 1 call to Exporter::import |
8 | |||||
9 | 2 | 18µs | 1 | 4µs | # spent 4µs within Foswiki::Configure::Query::BEGIN@9 which was called:
# once (4µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 9 # spent 4µs making 1 call to Foswiki::Configure::Query::BEGIN@9 |
10 | 2 | 26µs | 1 | 3µs | # spent 3µs within Foswiki::Configure::Query::BEGIN@10 which was called:
# once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 10 # spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@10 |
11 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Configure::Query::BEGIN@11 which was called:
# once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 11 # spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@11 |
12 | 2 | 17µs | 1 | 3µs | # spent 3µs within Foswiki::Configure::Query::BEGIN@12 which was called:
# once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 12 # spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@12 |
13 | 2 | 17µs | 1 | 3µs | # spent 3µs within Foswiki::Configure::Query::BEGIN@13 which was called:
# once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 13 # spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@13 |
14 | 2 | 22µs | 1 | 3µs | # spent 3µs within Foswiki::Configure::Query::BEGIN@14 which was called:
# once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 14 # spent 3µs making 1 call to Foswiki::Configure::Query::BEGIN@14 |
15 | |||||
16 | 2 | 32µs | 2 | 75µs | # spent 42µs (8+34) within Foswiki::Configure::Query::BEGIN@16 which was called:
# once (8µs+34µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 16 # spent 42µs making 1 call to Foswiki::Configure::Query::BEGIN@16
# spent 34µs making 1 call to constant::import |
17 | 2 | 1.98ms | 2 | 71µs | # spent 39µs (8+32) within Foswiki::Configure::Query::BEGIN@17 which was called:
# once (8µs+32µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@44 at line 17 # spent 39µs making 1 call to Foswiki::Configure::Query::BEGIN@17
# spent 32µs making 1 call to constant::import |
18 | |||||
19 | =begin TML | ||||
20 | |||||
21 | ---+ package Foswiki::Configure::Query | ||||
22 | |||||
23 | Methods used to query and manipulate the configuration spec. | ||||
24 | |||||
25 | *Contract* | ||||
26 | |||||
27 | All the methods take two parameters; a parameter hash, and a | ||||
28 | reporter. The parameter hash is described for each method, | ||||
29 | as is the return value, which is always a perl reference. | ||||
30 | |||||
31 | All methods return undef if they fail badly. $reporter->ERROR is used to | ||||
32 | describe fatal errors to the caller. | ||||
33 | |||||
34 | The $reporter should be clear before calling any of these methods, | ||||
35 | as existing errors in the reporter will be detected as fatal errors | ||||
36 | and cause the method to fail. | ||||
37 | |||||
38 | =cut | ||||
39 | |||||
40 | # Get =set= parameters and set the values in %Foswiki::cfg | ||||
41 | sub _getSetParams { | ||||
42 | my ( $params, $root, $reporter ) = @_; | ||||
43 | if ( $params->{set} ) { | ||||
44 | while ( my ( $k, $value ) = each %{ $params->{set} } ) { | ||||
45 | my $spec = $root->getValueObject($k); | ||||
46 | unless ($spec) { | ||||
47 | $reporter->ERROR("$k was not found in any Config.spec"); | ||||
48 | next; | ||||
49 | } | ||||
50 | if ( defined $value && !ref($value) ) { | ||||
51 | $value =~ m/^(.*)$/s; # UNTAINT | ||||
52 | $value = $1; | ||||
53 | eval { $value = $spec->decodeValue($value); }; | ||||
54 | if ($@) { | ||||
55 | $reporter->ERROR( | ||||
56 | "The value of $k was unreadable: <verbatim>" | ||||
57 | . Foswiki::Configure::Reporter::stripStacktrace($@) | ||||
58 | . '</verbatim>' ); | ||||
59 | next; | ||||
60 | } | ||||
61 | } | ||||
62 | if ( defined $value ) { | ||||
63 | if ( $spec->isFormattedType() || ref($value) ) { | ||||
64 | print STDERR "GETSET $k=" | ||||
65 | . Data::Dumper->Dump( [$value] ) | ||||
66 | . ", spec " | ||||
67 | . $spec->stringify() . "\n" | ||||
68 | if TRACE_GETSET; | ||||
69 | eval("\$Foswiki::cfg$k=\$value"); | ||||
70 | } | ||||
71 | else { | ||||
72 | print STDERR "GETSET $k=$value, spec " | ||||
73 | . $spec->stringify() . "\n" | ||||
74 | if TRACE_GETSET; | ||||
75 | |||||
76 | # This is needed to prevent expansion of embedded | ||||
77 | # $Foswiki::cfg variables during the eval. | ||||
78 | eval("\$Foswiki::cfg$k=join('',\$value)"); | ||||
79 | } | ||||
80 | } | ||||
81 | else { | ||||
82 | print STDERR "GETSET undef $k\n" if TRACE_GETSET; | ||||
83 | eval("undef \$Foswiki::cfg$k"); | ||||
84 | } | ||||
85 | if ($@) { | ||||
86 | $reporter->ERROR( '<verbatim>' | ||||
87 | . Foswiki::Configure::Reporter::stripStacktrace($@) | ||||
88 | . '</verbatim>' ); | ||||
89 | } | ||||
90 | elsif ( $params->{trace} ) { | ||||
91 | $reporter->NOTE("Set $k"); | ||||
92 | } | ||||
93 | } | ||||
94 | } | ||||
95 | } | ||||
96 | |||||
97 | =begin TML | ||||
98 | |||||
99 | ---++ StaticMethod getcfg(\%params, $reporter) -> \%response | ||||
100 | |||||
101 | Retrieve for the value of one or more keys. \%params may include | ||||
102 | * =keys= - array of key names to recover values for. | ||||
103 | If there isn't at least one key parameter, returns the | ||||
104 | entire configuration hash. Values are returned unexpanded | ||||
105 | (with embedded $Foswiki::cfg references intact.) | ||||
106 | |||||
107 | The result is a hash containing that subsection of %Foswiki::cfg | ||||
108 | that has the keys requested. | ||||
109 | |||||
110 | =cut | ||||
111 | |||||
112 | sub getcfg { | ||||
113 | my ( $params, $reporter ) = @_; | ||||
114 | |||||
115 | # Reload Foswiki::cfg without expansions | ||||
116 | local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} ); | ||||
117 | Foswiki::Configure::Load::readConfig( 1, 1 ); | ||||
118 | |||||
119 | my $keys = $params->{keys}; # expect a list | ||||
120 | my $what; | ||||
121 | my $root; | ||||
122 | if ( defined $keys && scalar(@$keys) ) { | ||||
123 | $what = {}; | ||||
124 | foreach my $key (@$keys) { | ||||
125 | unless ( $key =~ m/^($Foswiki::Configure::Load::ITEMREGEX)$/ ) { | ||||
126 | $reporter->ERROR("Bad key '$key'"); | ||||
127 | return undef; | ||||
128 | } | ||||
129 | |||||
130 | # Implicit untaint for use in eval | ||||
131 | $key = $1; | ||||
132 | |||||
133 | # Avoid loading specs unless we are being asked for a key that's | ||||
134 | # not in LocalSite.cfg | ||||
135 | unless ( eval("exists \$Foswiki::cfg$key") || $root ) { | ||||
136 | $root = Foswiki::Configure::Root->new(); | ||||
137 | Foswiki::Configure::LoadSpec::readSpec( $root, $reporter ); | ||||
138 | if ( $reporter->has_level('errors') ) { | ||||
139 | return undef; | ||||
140 | } | ||||
141 | Foswiki::Configure::LoadSpec::addSpecDefaultsToCfg( $root, | ||||
142 | \%Foswiki::cfg ); | ||||
143 | } | ||||
144 | unless ( eval("exists \$Foswiki::cfg$key") ) { | ||||
145 | $reporter->ERROR("$key not defined"); | ||||
146 | return undef; | ||||
147 | } | ||||
148 | eval("\$what->$key=\$Foswiki::cfg$key"); | ||||
149 | if ($@) { | ||||
150 | $reporter->ERROR( | ||||
151 | Foswiki::Configure::Reporter::stripStacktrace($@) ); | ||||
152 | return undef; | ||||
153 | } | ||||
154 | } | ||||
155 | } | ||||
156 | else { | ||||
157 | $what = \%Foswiki::cfg; | ||||
158 | } | ||||
159 | return $what; | ||||
160 | } | ||||
161 | |||||
162 | =begin TML | ||||
163 | |||||
164 | ---++ StaticMethod search(\%params, $reporter) -> \@response | ||||
165 | |||||
166 | * =search= - text fragment to search for | ||||
167 | |||||
168 | Search headlines and keys for a fragment of text. The response | ||||
169 | gives the path(s) to the item(s) matched in an array of arrays, | ||||
170 | where each entry is a single path. | ||||
171 | |||||
172 | Searches are case-sensitive. | ||||
173 | |||||
174 | =cut | ||||
175 | |||||
176 | sub search { | ||||
177 | my ( $params, $reporter ) = @_; | ||||
178 | my $root = Foswiki::Configure::Root->new(); | ||||
179 | Foswiki::Configure::LoadSpec::readSpec( $root, $reporter ); | ||||
180 | if ( $reporter->has_level('errors') ) { | ||||
181 | return undef; | ||||
182 | } | ||||
183 | |||||
184 | # An empty search isn't fatal, just uninteresting | ||||
185 | return [] | ||||
186 | unless defined $params->{search} | ||||
187 | && $params->{search} =~ m/\S/; | ||||
188 | |||||
189 | my $re = | ||||
190 | join( ".*", map { quotemeta($_) } split( /\s+/, $params->{search} ) ); | ||||
191 | |||||
192 | my %found; | ||||
193 | foreach my $find ( $root->search($re) ) { | ||||
194 | my @path = $find->getPath(); | ||||
195 | $found{ join( '>', @path ) } = \@path; | ||||
196 | } | ||||
197 | my $finds = [ map { $found{$_} } sort keys %found ]; | ||||
198 | |||||
199 | return $finds; | ||||
200 | } | ||||
201 | |||||
202 | =begin TML | ||||
203 | |||||
204 | ---++ StaticMethod getspec(\%params, $reporter) -> \%response | ||||
205 | |||||
206 | Use a search to find a configuration item spec. \%params may include: | ||||
207 | * =get= - specifies the search. The following fields can be | ||||
208 | used in searches: | ||||
209 | * =headline= - title of a section, | ||||
210 | * =typename= - type of a leaf spec entry, | ||||
211 | * =parent= - a structure that will be used to match a parent, | ||||
212 | * =keys= - keys of a spec entry, | ||||
213 | * =desc= - descriptive text of a section or entry. | ||||
214 | * =depth= - matches the depth of a node under the root | ||||
215 | (which is depth 0) | ||||
216 | * =depth= - specifies the depth of the subtree below matched items | ||||
217 | to return. | ||||
218 | Only exact matches are supported. | ||||
219 | |||||
220 | For example, ={ 'get': {'headline':'Store'}}= will retrieve the entire | ||||
221 | spec subtree for the section called 'Store'. | ||||
222 | |||||
223 | ={ 'get' : {'keys' : '{Store}{Implementation}'}}= will retrieve the spec | ||||
224 | for that one entry. You cannot pass a list; if you require the spec for a | ||||
225 | subsection, retrieve the section title. | ||||
226 | |||||
227 | ={ 'get' : { 'parent' : {'headline' : 'Something'}, 'depth' : 0}= will | ||||
228 | return all specs within the section named =Something=. | ||||
229 | |||||
230 | The response is a reference to the spec subtree. Note that this will | ||||
231 | contained blessed hashes. | ||||
232 | |||||
233 | =cut | ||||
234 | |||||
235 | sub getspec { | ||||
236 | my ( $params, $reporter ) = @_; | ||||
237 | |||||
238 | # Reload Foswiki::cfg without expansions so we get the unexpanded | ||||
239 | # values in the spec structure | ||||
240 | my $upper_cfg = \%Foswiki::cfg; | ||||
241 | local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} ); | ||||
242 | if ( $upper_cfg->{isBOOTSTRAPPING} ) { | ||||
243 | |||||
244 | # If we're bootstrapping, retain the values calculated in | ||||
245 | # the bootstrap process. They are almost certainly wrong, | ||||
246 | # but are a better starting point that the .spec defaults. | ||||
247 | %Foswiki::cfg = %$upper_cfg; | ||||
248 | } | ||||
249 | Foswiki::Configure::Load::readConfig( 1, 1 ); | ||||
250 | |||||
251 | my $root = Foswiki::Configure::Root->new(); | ||||
252 | Foswiki::Configure::LoadSpec::readSpec( $root, $reporter ); | ||||
253 | if ( $reporter->has_level('errors') ) { | ||||
254 | return undef; | ||||
255 | } | ||||
256 | Foswiki::Configure::LoadSpec::addCfgValuesToSpec( \%Foswiki::cfg, $root ); | ||||
257 | |||||
258 | my $depth = $params->{depth}; | ||||
259 | my $search = $params->{get}; | ||||
260 | |||||
261 | my @matches = (); | ||||
262 | if ($search) { | ||||
263 | @matches = $root->find(%$search); | ||||
264 | } | ||||
265 | else { | ||||
266 | @matches = ($root); | ||||
267 | } | ||||
268 | |||||
269 | foreach my $m (@matches) { | ||||
270 | $m->unparent(); | ||||
271 | $m->prune($depth) if defined $depth; | ||||
272 | } | ||||
273 | |||||
274 | return \@matches; | ||||
275 | } | ||||
276 | |||||
277 | =begin TML | ||||
278 | |||||
279 | ---++ StaticMethod check_current_value(\%params, $reporter) -> \@response | ||||
280 | |||||
281 | Runs the server-side =check-current_value= checkers on a set of keys. | ||||
282 | The keys to be checked are passed in as key-value pairs. You can also | ||||
283 | pass in candidate values that will be set before any keys are checked. | ||||
284 | * =set= - hash of key-value pairs that maps the names of keys | ||||
285 | to the value to be set. Strings in the values are assumed to be | ||||
286 | unexpanded (i.e. with =$Foswiki::cfg= references intact). | ||||
287 | * =keys= - array of keys to be checked (or the headline(s) of the | ||||
288 | sections(s) to be recursively checked. '' checks the root. All | ||||
289 | keys under the headlined section(s) will be checked). The default | ||||
290 | is to check everything under the root. | ||||
291 | * =check_dependencies= - if true, check everything that depends | ||||
292 | on any of the keys being checked. This include dependencies | ||||
293 | explicitly expressed through CHECK and implicit dependencies found | ||||
294 | from the value of the checked item. | ||||
295 | |||||
296 | The results of the check are reported in an array where each entry is a | ||||
297 | hash with fields =keys= and =reports=. =reports= is an array of reports, | ||||
298 | each being a hash with keys =level= (e.g. =warnings=, =errors=), and | ||||
299 | =message=. | ||||
300 | |||||
301 | *NOTE* check_dependencies will look into the values of other keys for | ||||
302 | $Foswiki::cfg references, for example into the entries in a PERL hash. | ||||
303 | If a dependency is found, the closest checkable entity (i.e. the PERL | ||||
304 | key) will be checked, and *not* the subkey. | ||||
305 | |||||
306 | =cut | ||||
307 | |||||
308 | sub check_current_value { | ||||
309 | my ( $params, $frep ) = @_; | ||||
310 | |||||
311 | local %Foswiki::cfg = %Foswiki::cfg; | ||||
312 | |||||
313 | # Load the spec files | ||||
314 | my $root = Foswiki::Configure::Root->new(); | ||||
315 | Foswiki::Configure::LoadSpec::readSpec( $root, $frep ); | ||||
316 | if ( $frep->has_level('errors') ) { | ||||
317 | return undef; | ||||
318 | } | ||||
319 | |||||
320 | my @report; | ||||
321 | |||||
322 | my $reporter = Foswiki::Configure::Reporter->new(); | ||||
323 | |||||
324 | # Apply "set" values to $Foswiki::cfg | ||||
325 | eval { _getSetParams( $params, $root, $frep ); }; | ||||
326 | if ( $frep->has_level('errors') ) { | ||||
327 | return [ { reports => $frep->messages() } ]; | ||||
328 | } | ||||
329 | |||||
330 | # Because we're running in a plugin, we already have LocalSite.cfg | ||||
331 | # loaded. It's in $Foswiki::cfg! Of course if we're bootstrapping, | ||||
332 | # that config is wishful thinking, but hey, can't have everything. | ||||
333 | |||||
334 | # Determine the set of value keys being checked. We start with | ||||
335 | # the keys passed in as parameters. | ||||
336 | |||||
337 | my @keys; | ||||
338 | foreach my $k ( @{ $params->{keys} } ) { | ||||
339 | if ( $root->getValueObject($k) || $root->getSectionObject($k) ) { | ||||
340 | push( @keys, $k ); | ||||
341 | } | ||||
342 | else { | ||||
343 | $k = "'$k'" unless $k =~ m/^\{.*\}$/; | ||||
344 | push( | ||||
345 | @report, | ||||
346 | { | ||||
347 | keys => $k, | ||||
348 | path => [], | ||||
349 | reports => [ | ||||
350 | { | ||||
351 | text => "$k was not found in any Config.spec", | ||||
352 | level => 'errors' | ||||
353 | } | ||||
354 | ] | ||||
355 | } | ||||
356 | ); | ||||
357 | } | ||||
358 | } | ||||
359 | |||||
360 | if ( scalar(@keys) == 0 ) { | ||||
361 | push( @keys, '' ); | ||||
362 | } | ||||
363 | |||||
364 | my $deps; # forward and reverse dependencies computed from values | ||||
365 | if ( $params->{check_dependencies} ) { | ||||
366 | |||||
367 | # Get reverse dependencies expressed in CHECK_ON_CHANGE | ||||
368 | # and add them as CHECK="also: forward dependencies to the | ||||
369 | # item they depend on. We only do this if check_dependencies | ||||
370 | # is set, as it is quite demanding. | ||||
371 | $root->find_also_dependencies($root); | ||||
372 | |||||
373 | # Reload Foswiki::cfg without expansions so we can find | ||||
374 | # string-embedded dependencies | ||||
375 | local %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} ); | ||||
376 | Foswiki::Configure::Load::readConfig( 1, 0, 1 ); | ||||
377 | if ( $params->{with} ) { | ||||
378 | while ( my ( $k, $v ) = each %{ $params->{with} } ) { | ||||
379 | eval("\$Foswiki::cfg$k=$v"); | ||||
380 | } | ||||
381 | } | ||||
382 | $deps = Foswiki::Configure::Load::findDependencies(); | ||||
383 | } | ||||
384 | |||||
385 | #print STDERR Data::Dumper->Dump([$deps]); | ||||
386 | |||||
387 | my %check; # set of keys to be checked | ||||
388 | my @checko; # list of Value objects for keys to be checked | ||||
389 | while ( defined( my $k = shift(@keys) ) ) { | ||||
390 | print STDERR "Find dependencies for $k\n" if TRACE_CHECK; | ||||
391 | next if $check{$k}; # already done? | ||||
392 | $check{$k} = 1; | ||||
393 | my $v = $root->getValueObject($k); | ||||
394 | if ($v) { | ||||
395 | print STDERR "\t'$k' is a key\n" if TRACE_CHECK; | ||||
396 | push( @checko, $v ); | ||||
397 | if ( $params->{check_dependencies} | ||||
398 | && defined $v->{CHECK}->{also} ) | ||||
399 | { | ||||
400 | |||||
401 | # Look at the CHECK="also:" explicit dependencies | ||||
402 | foreach my $dep ( @{ $v->{CHECK}->{also} } ) { | ||||
403 | next if $check{$dep}; | ||||
404 | print STDERR "\t... has a check:also for $dep\n" | ||||
405 | if TRACE_CHECK; | ||||
406 | push( @keys, $dep ) unless $check{$dep}; | ||||
407 | } | ||||
408 | } | ||||
409 | } | ||||
410 | else { | ||||
411 | $v = $root->getSectionObject($k); | ||||
412 | if ($v) { | ||||
413 | print STDERR "\n'$k' is a section\n" if TRACE_CHECK; | ||||
414 | foreach my $kk ( $v->getAllValueKeys() ) { | ||||
415 | unless ( $check{$kk} ) { | ||||
416 | print STDERR "\tcontains key '$kk'\n" if TRACE_CHECK; | ||||
417 | push( @keys, $kk ); | ||||
418 | } | ||||
419 | } | ||||
420 | } | ||||
421 | else { | ||||
422 | print STDERR "\t'$k' is not a key or a section\n" | ||||
423 | if TRACE_CHECK; | ||||
424 | if ( $k =~ s/{[^{}]+}$// && !$check{$k} ) { | ||||
425 | print STDERR "\tcheck parent '$k' instead\n" if TRACE_CHECK; | ||||
426 | push( @keys, $k ); | ||||
427 | } | ||||
428 | } | ||||
429 | } | ||||
430 | |||||
431 | # Look at forward dependencies i.e. the keys that depend | ||||
432 | # on the value of this key | ||||
433 | if ( $deps && $deps->{forward}->{$k} ) { | ||||
434 | my @more = grep { !$check{$_} } @{ $deps->{forward}->{$k} }; | ||||
435 | map { print STDERR "\t$_ depends on $k\n"; $_ } @more | ||||
436 | if TRACE_CHECK; | ||||
437 | push( @keys, @more ); | ||||
438 | } | ||||
439 | } | ||||
440 | |||||
441 | SPEC: | ||||
442 | foreach my $spec (@checko) { | ||||
443 | my $e = $spec->{CHECK}->{iff}; | ||||
444 | if ( defined $e ) { | ||||
445 | $e = $e->[0]; | ||||
446 | |||||
447 | # Expand {x} as $Foswiki::cfg{x} | ||||
448 | $e =~ s/(({[^}]+})+)/\$Foswiki::cfg$1/g; | ||||
449 | if ( $e =~ m/\S/ ) { | ||||
450 | my $only_if; | ||||
451 | eval("\$only_if=$e"); | ||||
452 | die "Syntax error in $spec->{keys} CHECK='iff:$e' - " | ||||
453 | . Foswiki::Configure::Reporter::stripStacktrace($@) | ||||
454 | if $@; | ||||
455 | next SPEC unless $only_if; | ||||
456 | } | ||||
457 | } | ||||
458 | my $checker = Foswiki::Configure::Checker::loadChecker($spec); | ||||
459 | next unless $checker; | ||||
460 | ASSERT( $spec->{keys} ) if DEBUG; | ||||
461 | $reporter->clear(); | ||||
462 | $reporter->NOTE("Checking $spec->{keys}") if $params->{trace}; | ||||
463 | $checker->check_current_value($reporter); | ||||
464 | my @path = $spec->getPath(); | ||||
465 | pop(@path); # remove keys | ||||
466 | push( | ||||
467 | @report, | ||||
468 | { | ||||
469 | keys => $spec->{keys}, | ||||
470 | path => [@path], | ||||
471 | reports => $reporter->messages() | ||||
472 | } | ||||
473 | ); | ||||
474 | } | ||||
475 | return \@report; | ||||
476 | } | ||||
477 | |||||
478 | =begin TML | ||||
479 | |||||
480 | ---++ StaticMethod wizard(\%params, $reporter) -> \%response | ||||
481 | |||||
482 | Call a configuration wizard. | ||||
483 | |||||
484 | Configuration wizards are modules that support complex operations on | ||||
485 | configuration data; for example, auto-configuration of email and complex | ||||
486 | and time-consuming integrity checks. | ||||
487 | |||||
488 | * =wizard= - name of a wizard class to load | ||||
489 | * =keys= - name of a checker to use if =wizard= is not given | ||||
490 | * =method= - name of the method in the wizard or checker to call | ||||
491 | |||||
492 | If the wizard method returns an object, that will be passed back | ||||
493 | as the result of the call. If the wizard method returns undef, the | ||||
494 | return result is a hash containing the following keys: | ||||
495 | * =report= - Error/Warning etc messages, formatted as HTML. Each | ||||
496 | entry in this array is a hash with keys 'level' (e.g. error, warning) | ||||
497 | and 'message'. | ||||
498 | * =changes= - This is a hash mapping changed keys to their new values | ||||
499 | |||||
500 | =cut | ||||
501 | |||||
502 | sub wizard { | ||||
503 | my ( $params, $reporter ) = @_; | ||||
504 | |||||
505 | my $root = Foswiki::Configure::Root->new(); | ||||
506 | Foswiki::Configure::LoadSpec::readSpec( $root, $reporter ); | ||||
507 | if ( $reporter->has_level('errors') ) { | ||||
508 | return undef; | ||||
509 | } | ||||
510 | |||||
511 | my $target; | ||||
512 | if ( defined $params->{wizard} ) { | ||||
513 | unless ( $params->{wizard} =~ m/^(\w+)$/ ) { # untaint | ||||
514 | $reporter->ERROR("Bad wizard"); | ||||
515 | return undef; | ||||
516 | } | ||||
517 | $target = Foswiki::Configure::Wizard::loadWizard( $1, $params ); | ||||
518 | } | ||||
519 | else { | ||||
520 | unless ( $params->{keys} ) { | ||||
521 | $reporter->ERROR("No wizard and no keys"); | ||||
522 | return undef; | ||||
523 | } | ||||
524 | my $vob = $root->getValueObject( $params->{keys} ); | ||||
525 | $target = Foswiki::Configure::Checker::loadChecker($vob); | ||||
526 | } | ||||
527 | unless ($target) { | ||||
528 | $reporter->ERROR("Bad thing"); | ||||
529 | return undef; | ||||
530 | } | ||||
531 | my $method = $params->{method}; | ||||
532 | unless ( $method =~ m/^(\w+)$/ ) { | ||||
533 | $reporter->ERROR("Bad method"); | ||||
534 | return undef; | ||||
535 | } | ||||
536 | $method = $1; # untaint | ||||
537 | |||||
538 | _getSetParams( $params, $root, $reporter ); | ||||
539 | return { messages => $reporter->messages() } | ||||
540 | if $reporter->has_level('errors'); | ||||
541 | |||||
542 | # Most wizards won't need the $root, only those that actually | ||||
543 | # modify it e.g. installers. | ||||
544 | my $response = $target->$method( $reporter, $root ); | ||||
545 | return $response if $response; | ||||
546 | |||||
547 | # Note: we can't used the value recorded at CHANGED time because that | ||||
548 | # is encoded using Reporter::uneval, which knows nothing about the | ||||
549 | # real type of the value. For the real type we have to use the | ||||
550 | # Value's encoder. | ||||
551 | my %new_values; | ||||
552 | foreach my $k ( keys %{ $reporter->{changes} } ) { | ||||
553 | my $v = $root->getValueObject($k); | ||||
554 | ASSERT( $v, "$k missing from Config.spec $method" ) if DEBUG; | ||||
555 | $new_values{$k} = $v->encodeValue( eval("\$Foswiki::cfg$k") ); | ||||
556 | } | ||||
557 | |||||
558 | return { | ||||
559 | changes => \%new_values, | ||||
560 | messages => $reporter->messages() | ||||
561 | }; | ||||
562 | } | ||||
563 | |||||
564 | 1 | 2µs | 1; | ||
565 | __END__ |