diff --git a/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin.pm b/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin.pm index 0003ba1e11..46218ac403 100644 --- a/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin.pm +++ b/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin.pm @@ -79,7 +79,7 @@ sub initPlugin { # Register each of the RPC methods with JsonRpcContrib foreach my $method ( - qw(getcfg getspec search check_current_value changecfg deletecfg purgecfg wizard) + qw(getcfg getspec search check_current_value changecfg deletecfg wizard) ) { Foswiki::Contrib::JsonRpcContrib::registerMethod( 'configure', $method, @@ -356,32 +356,14 @@ sub getspec { @matches = ($root); } - if ( defined $depth ) { - - # Children to a fixed depth only; prune - foreach my $m (@matches) { - _prune( $m, $depth ); - } + foreach my $m (@matches) { + $m->unparent(); + $m->prune($depth) if defined $depth; } return \@matches; } -# 0 will prune children -# 1 will prune children-of-children -sub _prune { - my ( $node, $level ) = @_; - - if ( $level == 0 ) { - delete $node->{children}; - } - elsif ( $node->{children} ) { - foreach my $c ( @{ $node->{children} } ) { - _prune( $c, $level - 1 ); - } - } -} - # Recursive locate references to other keys in the values of keys # Returns a =forward= hash mapping keys to a list of the keys that depend # on their value, and a =reverse= hash mapping keys to a list of keys @@ -460,7 +442,6 @@ each being a hash with keys =level= (e.g. =warnings=, =errors=), and sub _getSetParams { my ( $params, $root ) = @_; - if ( $params->{set} ) { while ( my ( $k, $v ) = each %{ $params->{set} } ) { if ( defined $v && $v ne '' ) { @@ -562,7 +543,8 @@ sub check_current_value { next if $done{$dep}; $check{$dep} = 1; $done{$dep} = 1; - push( @dep_keys, @{ $deps{forward}->{$dep} } ); + push( @dep_keys, @{ $deps{forward}->{$dep} } ) + if $deps{forward}->{$dep}; } } @@ -620,7 +602,7 @@ sub _purgecfg { my $root = shift; # All specced keys are OK - my %ok = map { $_ => 1 } $root->getAllKeys(); + my %ok = map { $_ => 1 } $root->getAllValueKeys(); # See if there are any dependencies between OK keys and other # keys @@ -692,8 +674,7 @@ sub changecfg { if ( defined $deletions ) { foreach my $key (@$deletions) { die "Bad key '$key'" - unless $key =~ -/^($Foswiki::Plugins::ConfigurePlugin::SpecEntry::configItemRegex)$/; + unless $key =~ /^($Foswiki::Configure::Load::ITEMREGEX)$/; # Implicit untaint $key = _safeKeys($1); @@ -707,8 +688,7 @@ sub changecfg { if ( defined $changes ) { while ( my ( $key, $value ) = each %$changes ) { die "Bad key '$key'" - unless $key =~ -/^($Foswiki::Plugins::ConfigurePlugin::SpecEntry::configItemRegex)$/; + unless $key =~ /^($Foswiki::Configure::Load::ITEMREGEX)$/; # Implicit untaint $key = _safeKeys($1); @@ -741,8 +721,12 @@ sub changecfg { $Foswiki::cfg{ConfigurationFinished} = 0; Foswiki::Configure::Load::readConfig( 0, 1 ); - return - "Added: $added; Changed: $changed; Cleared: $cleared; Purged: $purged"; + return { + added => $added, + changed => $changed, + cleared => $cleared, + purged => $purged + }; } # Traverse LSC generating LSC format output @@ -753,7 +737,7 @@ sub _lscify { if ( scalar(@path) ) { my $keypath = '{' . join( '}{', @path ) . '}'; - my $spec = ( $specs->_keyCache->{$keypath} ); + my $spec = ( $specs->getValueObject($keypath) ); if ( $spec && defined $spec->{keys} ) { # This is a specced level; we will take the entire data @@ -801,8 +785,7 @@ sub _value2string { } sub _save { - my $lsc = Foswiki::Plugins::ConfigurePlugin::SpecEntry::findFileOnPath( - 'Foswiki.spec') + my $lsc = Foswiki::Configure::FileUtil::findFileOnPath('Foswiki.spec') || ''; $lsc =~ s/Foswiki\.spec/LocalSite.cfg/; print STDERR "LSC is at $lsc\n" if TRACE; @@ -982,7 +965,7 @@ while POSTing a request encoded according to the JSON-RPC 2.0 specification: jsonrpc: "2.0", method: "getspec", params: { - get : { keys: [ "{DataDir}", "Store" ] }, + get : { keys: "{DataDir}" }, depth : 0 }, id: "caller's id" diff --git a/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin/Config.spec b/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin/Config.spec index be9c214c97..5868707d19 100644 --- a/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin/Config.spec +++ b/ConfigurePlugin/lib/Foswiki/Plugins/ConfigurePlugin/Config.spec @@ -3,7 +3,9 @@ # ---+++ Testing # The following key specs are only used in testing the ConfigurePlugin # and do nothing. -# **STRING FEEDBACK="label='Test';wizard='Test';method='test';auth=1"** +# **STRING FEEDBACK="label='Test one';wizard='Test';method='test';auth=1" \ +# FEEDBACK="label='Test two';wizard='Gandalf';method='wand'" \ +# CHECK="min:3 max:20" ** # When you press the Test button, expect the value to change to "ROPE" and # there to be one each of the different levels of report. $Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{STRING} = 'STRING'; @@ -34,8 +36,8 @@ $Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{PATH} = 'PATH'; # **PERL** $Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{PERL} = '\'PERL\';'; # **REGEX** -# Should be /^regex$/ -$Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{REGEX} = qr/^regex$/; +# Should be '^regex$' +$Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{REGEX} = '^regex$'; # **SELECTCLASS none,Foswiki::Confi* ** # Should be Foswiki::Configure $Foswiki::cfg{Plugins}{ConfigurePlugin}{Test}{SELECTCLASS} = 'Foswiki::Configure'; diff --git a/ConfigurePlugin/test/unit/ConfigurePlugin/ConfigurePluginTests.pm b/ConfigurePlugin/test/unit/ConfigurePlugin/ConfigurePluginTests.pm index 574787d429..0f1fb921e8 100644 --- a/ConfigurePlugin/test/unit/ConfigurePlugin/ConfigurePluginTests.pm +++ b/ConfigurePlugin/test/unit/ConfigurePlugin/ConfigurePluginTests.pm @@ -4,8 +4,8 @@ use warnings; package ConfigurePluginTests; -use FoswikiTestCase; -our @ISA = qw( FoswikiTestCase ); +use FoswikiFnTestCase; +our @ISA = qw( FoswikiFnTestCase ); use strict; use warnings; @@ -51,12 +51,12 @@ sub test_getcfg { ); } -sub test_getcfg_all { - my $this = shift; - my $params = {}; - my $result = Foswiki::Plugins::ConfigurePlugin::getcfg($params); - $this->assert_deep_equals( \%Foswiki::cfg, $result ); -} +#sub test_getcfg_all { +# my $this = shift; +# my $params = {}; +# my $result = Foswiki::Plugins::ConfigurePlugin::getcfg($params); +# $this->assert_deep_equals( \%Foswiki::cfg, $result ); +#} sub test_getcfg_badkey { my $this = shift; @@ -107,23 +107,70 @@ sub unparent { return $what; } -sub test_getspec { +sub test_getspec_headline { my $this = shift; - my %params = ( keys => '{Plugins}{ConfigurePlugin}{Test}{STRING}' ); + my %params = ( get => { headline => 'ConfigurePlugin' } ); my $spec = Foswiki::Plugins::ConfigurePlugin::getspec( \%params ); $this->assert_num_equals( 1, scalar @$spec ); - $spec = $spec->[0]; - $this->assert_str_equals( 'STRING', $spec->{type} ); - $this->assert_str_equals( $params{keys}, $spec->{keys} ); - $this->assert_str_equals( 'STRING', $spec->{spec_value} ); + $this->assert_str_equals( 'ConfigurePlugin', $spec->[0]->{headline} ); + $params{depth} = 1; + $spec = Foswiki::Plugins::ConfigurePlugin::getspec( \%params ); + $this->assert_num_equals( 1, scalar @$spec ); + $this->assert_str_equals( 'ConfigurePlugin', $spec->[0]->{headline} ); + $this->assert_num_equals( 1, scalar @{ $spec->[0]->{children} } ); + $this->assert_str_equals( 'Testing', + $spec->[0]->{children}->[0]->{headline} ); + $this->assert_null( $spec->[0]->{children}->[0]->{children} ); +} - $params{keys} = '{Plugins}{ConfigurePlugin}{Test}{empty}'; +sub test_getspec_parent { + my $this = shift; + my %params = ( get => { parent => { headline => 'ConfigurePlugin' } } ); + my $spec = Foswiki::Plugins::ConfigurePlugin::getspec( \%params ); + $this->assert_num_equals( 1, scalar @$spec ); + $this->assert_str_equals( 'Testing', $spec->[0]->{headline} ); +} + +sub test_getspec { + my $this = shift; + my %params = + ( get => { keys => '{Plugins}{ConfigurePlugin}{Test}{STRING}' } ); + my $spec = Foswiki::Plugins::ConfigurePlugin::getspec( \%params ); + $this->assert_num_equals( 1, scalar @$spec ); + $spec = $spec->[0]; + $this->assert_str_equals( 'STRING', $spec->{typename} ); + $this->assert_str_equals( 'STRING', $spec->{default} ); + $this->assert_str_equals( '{Plugins}{ConfigurePlugin}{Test}{STRING}', + $spec->{keys} ); + $this->assert_str_equals( 'STRING', $spec->{current_value} ); + $this->assert_matches( qr/^When you press the.*of report.$/s, + $spec->{desc} ); + $this->assert_num_equals( 4, $spec->{depth} ); + $this->assert_num_equals( 2, scalar @{ $spec->{defined_at} } ); + $this->assert_num_equals( 2, scalar @{ $spec->{FEEDBACK} } ); + my $fb = $spec->{FEEDBACK}->[0]; + $this->assert( $fb->{auth} ); + $this->assert_str_equals( 'Test', $fb->{wizard} ); + $this->assert_str_equals( 'test', $fb->{method} ); + $this->assert_str_equals( 'Test one', $fb->{label} ); + $fb = $spec->{FEEDBACK}->[1]; + $this->assert( !$fb->{auth} ); + $this->assert_str_equals( 'Gandalf', $fb->{wizard} ); + $this->assert_str_equals( 'wand', $fb->{method} ); + $this->assert_str_equals( 'Test two', $fb->{label} ); + + my $ch = $spec->{CHECK}; + $this->assert_num_equals( 1, scalar @$ch ); + $this->assert_num_equals( 3, $ch->[0]->{min}->[0] ); + $this->assert_num_equals( 20, $ch->[0]->{max}->[0] ); + + $params{get}->{keys} = '{Plugins}{ConfigurePlugin}{Test}{empty}'; $spec = Foswiki::Plugins::ConfigurePlugin::getspec( \%params ); $this->assert_num_equals( 1, scalar @$spec ); $spec = $spec->[0]; - $this->assert_str_equals( 'PATH', $spec->{type} ); - $this->assert_str_equals( $params{keys}, $spec->{keys} ); - $this->assert_str_equals( 'empty', $spec->{spec_value} ); + $this->assert_str_equals( $params{get}->{keys}, $spec->{keys} ); + $this->assert_str_equals( 'PATH', $spec->{typename} ); + $this->assert_str_equals( 'empty', $spec->{default} ); } sub test_getspec_no_LSC { @@ -137,44 +184,7 @@ sub test_getspec_no_LSC { my $spec = Foswiki::Plugins::ConfigurePlugin::getspec( {} ); $this->assert_num_equals( 1, scalar @$spec ); $spec = $spec->[0]; - $this->assert_str_equals( 'ROOT', $spec->{type} ); -} - -sub test_getspec_children { - my $this = shift; - my $use_section; - my $params = { children => 1 }; - my $ss = Foswiki::Plugins::ConfigurePlugin::getspec($params); - $this->assert_num_equals( 1, scalar(@$ss) ); - $this->assert_equals( "ROOT", $ss->[0]->{type} ); - $this->assert_null( $ss->[0]->{title} ); - $this->assert( scalar( @{ $ss->[0]->{children} } ) ); - - foreach my $spec ( @{ $ss->[0]->{children} } ) { - $this->assert( $spec->{type} eq 'SECTION', $spec->{type} ); - $this->assert_null( $spec->{children} ); - if ( !$use_section ) { - $use_section = $spec->{title}; - } - } - - $params = { parent => { title => $use_section }, children => 0 }; - $ss = Foswiki::Plugins::ConfigurePlugin::getspec($params); - foreach my $spec (@$ss) { - $this->assert_equals( $use_section, $spec->{parent}->{title} ); - $this->assert_null( $spec->{children} ); - } - - $params = { title => $use_section, children => 1 }; - $ss = Foswiki::Plugins::ConfigurePlugin::getspec($params); - foreach my $spec (@$ss) { - $this->assert_not_null( $spec->{children} ); - foreach my $subspec ( @{ $spec->{children} } ) { - $this->assert_null( $subspec->{children} ); - } - } - - # Check pluggables + $this->assert_str_equals( 'SECTION', $spec->{typename} ); } sub test_getspec_badkey { @@ -209,20 +219,25 @@ use Foswiki::Configure::Checker; } sub test_check { - my $this = shift; - my $params = { "{Plugins}{ConfigurePlugin}{Test}{STRING}" => 'Theory' }; - my $report = Foswiki::Plugins::ConfigurePlugin::check($params); + my $this = shift; + + # force an error - STRING length + my $params = { + keys => ["{Plugins}{ConfigurePlugin}{Test}{STRING}"], + set => { "{Plugins}{ConfigurePlugin}{Test}{STRING}" => 'no' } + }; + my $report = + Foswiki::Plugins::ConfigurePlugin::check_current_value($params); $this->assert_num_equals( 1, scalar @$report ); $report = $report->[0]; $this->assert_str_equals( '{Plugins}{ConfigurePlugin}{Test}{STRING}', $report->{keys} ); - $this->assert_str_equals( 'errors', $report->{level} ); - $this->assert_str_equals( 'Extensions', $report->{sections}->[0] ); - $this->assert_str_equals( 'ConfigurePlugin', $report->{sections}->[1] ); - $this->assert_str_equals( 'Testing', $report->{sections}->[2] ); - $this->assert_matches( qr/Error/, $report->{message} ); - $this->assert_matches( qr/Warning/, $report->{message} ); - $this->assert_matches( qr/Note/, $report->{message} ); + $this->assert_str_equals( 'Extensions', $report->{path}->[0] ); + $this->assert_str_equals( 'ConfigurePlugin', $report->{path}->[1] ); + $this->assert_str_equals( 'Testing', $report->{path}->[2] ); + $this->assert_num_equals( 1, scalar( @{ $report->{reports} } ) ); + $this->assert_str_equals( 'errors', $report->{reports}->[0]->{level} ); + $this->assert_matches( qr/3/, $report->{reports}->[0]->{text} ); } sub test_check_dependencies { @@ -230,10 +245,12 @@ sub test_check_dependencies { # DEPENDS depends on H and EXPERT my $params = { - '{Plugins}{ConfigurePlugin}{Test}{H}' => 'fruitbat', - 'check_dependent' => 1 + keys => ['{Plugins}{ConfigurePlugin}{Test}{H}'], + set => { '{Plugins}{ConfigurePlugin}{Test}{H}' => 'fruitbat' }, + check_dependencies => 1 }; - my $report = Foswiki::Plugins::ConfigurePlugin::check($params); + my $report = + Foswiki::Plugins::ConfigurePlugin::check_current_value($params); $this->assert_num_equals( 2, scalar @$report ); my ( $first, $second ); if ( $report->[0]->{keys} =~ /DEPENDS/ ) { diff --git a/ConfigurePlugin/test/unit/ConfigurePlugin/SaveTests.pm b/ConfigurePlugin/test/unit/ConfigurePlugin/SaveTests.pm index beeb305797..2e829ca7f6 100644 --- a/ConfigurePlugin/test/unit/ConfigurePlugin/SaveTests.pm +++ b/ConfigurePlugin/test/unit/ConfigurePlugin/SaveTests.pm @@ -25,8 +25,7 @@ sub set_up { $this->SUPER::set_up(); $this->{lscpath} = - Foswiki::Plugins::ConfigurePlugin::SpecEntry::findFileOnPath( - 'Foswiki.spec'); + Foswiki::Configure::FileUtil::findFileOnPath('Foswiki.spec'); $this->{lscpath} =~ s/Foswiki\.spec/LocalSite.cfg/; $this->{test_work_dir} = $Foswiki::cfg{WorkingDir}; if ( open( F, '<', $this->{lscpath} ) ) { @@ -90,15 +89,17 @@ sub test_changecfg { set => { '{TestA}' => 'Shingle', '{TestB}{Ruin}' => 'Ribbed', - '{Test-Key}' => 'newtestkey', + '{"Test-Key"}' => 'newtestkey', '{TestKey}' => 'newval', '{Plugins}{ConfigurePlugin}{Test}{SELECT}' => 'choice' }, purge => 1, }; my $result = Foswiki::Plugins::ConfigurePlugin::changecfg($params); - $this->assert_str_equals( 'Added: 4; Changed: 1; Cleared: 2; Purged: 19', - $result ); + $this->assert_num_equals( 4, $result->{added} ); + $this->assert_num_equals( 1, $result->{changed} ); + $this->assert_num_equals( 2, $result->{cleared} ); + $this->assert_num_equals( 19, $result->{purged} ); $this->assert_str_equals( 'newtestkey', $Foswiki::cfg{'Test-Key'} ); $this->assert( !exists $Foswiki::cfg{Test}{Key} ); $this->assert( !exists $Foswiki::cfg{TestDontCountMe} ); @@ -108,9 +109,8 @@ sub test_changecfg { # Check it was written correctly delete $Foswiki::cfg{Test}; open( F, '<', - Foswiki::Plugins::ConfigurePlugin::SpecEntry::findFileOnPath( - 'LocalSite.cfg') - ) || die $@; + Foswiki::Configure::FileUtil::findFileOnPath('LocalSite.cfg') ) + || die $@; local $/ = undef; my $c = ; close F; diff --git a/core/lib/Foswiki/Configure/Checker.pm b/core/lib/Foswiki/Configure/Checker.pm index 4b82ff23a5..4c342380f4 100755 --- a/core/lib/Foswiki/Configure/Checker.pm +++ b/core/lib/Foswiki/Configure/Checker.pm @@ -249,7 +249,6 @@ sub NOTE { sub WARN { my $this = shift; ASSERT( $this->{reporter} ) if DEBUG; - $this->{item}->inc('warningcount'); $this->{reporter}->WARN(@_); return join( ' ', @_ ); } @@ -259,7 +258,6 @@ sub WARN { sub ERROR { my $this = shift; ASSERT( $this->{reporter} ) if DEBUG; - $this->{item}->inc('errorcount'); $this->{reporter}->ERROR(@_); return join( ' ', @_ ); } diff --git a/core/lib/Foswiki/Configure/Checkers/BasicSanity.pm b/core/lib/Foswiki/Configure/Checkers/BasicSanity.pm deleted file mode 100644 index b4d3d33f5c..0000000000 --- a/core/lib/Foswiki/Configure/Checkers/BasicSanity.pm +++ /dev/null @@ -1,239 +0,0 @@ -# See bottom of file for license and copyright information - -=begin TML - ----+ package Foswiki::Configure::Checkers::BasicSanity - -Checker that implements the basic sanity checks that configure performs. - -=cut - -package Foswiki::Configure::Checkers::BasicSanity; - -use strict; -use warnings; - -use Foswiki::Configure::Checker (); -use Foswiki::Configure::Util (); -our @ISA = ('Foswiki::Configure::Checker'); - -sub new { - my ( $class, $item ) = @_; - my $this = $class->SUPER::new($item); - $this->{LocalSiteDotCfg} = undef; - $this->{errorcount} = 0; - $this->{badLSC} = 0; - - return $this; -} - -=begin TML - ----++ ObjectMethod insane() -> $boolean - -Return true if we have fatal errors - -=cut - -sub insane() { - my $this = shift; - return $this->{errorcount}; -} - -=begin TML - ----++ ObjectMethod lscIsBad() -> $boolean - -Return true if LocalSite.cfg was found to be bad. - -=cut - -sub lscIsBad() { - my $this = shift; - return $this->{badLSC}; -} - -# Perform basic sanity checks, returning a HTML condition statement. - -sub checkSanity { - my ($this) = @_; - my $result = ''; - $this->{badLSC} = 0; - - $this->{LocalSiteDotCfg} = - Foswiki::Configure::FileUtil::findFileOnPath('LocalSite.cfg'); - unless ( $this->{LocalSiteDotCfg} ) { - $this->{LocalSiteDotCfg} = - Foswiki::Configure::FileUtil::findFileOnPath('Foswiki.spec') - || ''; - $this->{LocalSiteDotCfg} =~ s/Foswiki\.spec/LocalSite.cfg/; - } - - # Get default settings by reading .spec files - require Foswiki::Configure::Load; - Foswiki::Configure::Load::readDefaults(); #Foswiki.spec + plugins & contribs - - $Foswiki::Configure::defaultCfg = _copy( \%Foswiki::cfg ); - - if ( !$this->{LocalSiteDotCfg} ) { - $this->{errorcount}++; - $result .= <Edit =LocalLib.cfg= and set =\$twikiLibPath= to point to the 'lib' directory of your installation. -

Please correct this error before continuing. -HERE - } - elsif ( -e $this->{LocalSiteDotCfg} ) { - eval { Foswiki::Configure::Load::readConfig( 1, 1 ); }; # Don't expand or re-read Foswiki.spec. - if ($@) { - $this->{errorcount}++; - $result .= <{LocalSiteDotCfg}= has a problem -that is causing a Perl error. The following message(s) was generated: -

$@
-

You can continue, but configure will not pick up any of the existing -settings from this file and your previous configuration will be lost. -

Manually edit and correct =$this->{LocalSiteDotCfg}= file if you -wish to preserve the prior configuration. -HERE - $this->{badLSC} = 1; - } - elsif ( !-w $this->{LocalSiteDotCfg} ) { - $this->{errorcount}++; - $result .= <{LocalSiteDotCfg}= is not writable.
-You can view the configuration, but you will not be able to save changes. -Please check the file and directory permissions. -HERE - } - elsif ( ( my $mess = $this->_checkCfg( \%Foswiki::cfg ) ) ) { - $this->{errorcount}++; - $result .= <{LocalSiteDotCfg}= doesn't seem to contain a good configuration for Foswiki. The following problems were found:
-$mess -

You can continue, but configure will not pick up any of the existing -settings from this file and the previous configuration will be lost. -

Manually edit and correct =$this->{LocalSiteDotCfg}= file if you -wish to preserve the prior configuration. -HERE - } - - } - else { - - # Doesn't exist (yet) - my $errs = Foswiki::Configure::FileUtil::checkCanCreateFile( - $this->{LocalSiteDotCfg} ); - - if ($errs) { - $this->{errorcount}++; - $result .= <{LocalSiteDotCfg}= does not exist, and I cannot write a new configuration file due to these errors: -

$errs
-

You can view the default configuration, but you will not be able to save changes. -HERE - $this->{badLSC} = 1; - } - else { - $result .= <{LocalSiteDotCfg}=. -HERE - $this->{badLSC} = 1; - } - } - - # Make %ENV safer for CGI - Assign a safe default for SafeEnvPath - $Foswiki::cfg{DETECTED}{originalPath} = $ENV{PATH} || ''; - - unless ( defined $Foswiki::cfg{SafeEnvPath} ) { - - # SMELL: Untaint to get past the first run. It will be - # Overridden to the SafeEnvPath after first save - ( $ENV{PATH} ) = ( $ENV{PATH} || '' ) =~ m/^(.*)$/; - } - else { - $ENV{PATH} = $Foswiki::cfg{SafeEnvPath}; - } - - delete @ENV{qw( IFS CDPATH ENV BASH_ENV )}; - - return $result; -} - -sub _copy { - my $n = shift; - - return unless defined($n); - - if ( UNIVERSAL::isa( $n, 'ARRAY' ) ) { - my @new; - for ( 0 .. $#$n ) { - push( @new, _copy( $n->[$_] ) ); - } - return \@new; - } - elsif ( UNIVERSAL::isa( $n, 'HASH' ) ) { - my %new; - for ( keys %$n ) { - $new{$_} = _copy( $n->{$_} ); - } - return \%new; - } - elsif ( UNIVERSAL::isa( $n, 'Regexp' ) ) { - return qr/$n/; - } - elsif ( UNIVERSAL::isa( $n, 'REF' ) || UNIVERSAL::isa( $n, 'SCALAR' ) ) { - $n = _copy($$n); - return \$n; - } - else { - return $n; - } -} - -# Check that an existing LocalSite.cfg doesn't contain crap. - -sub _checkCfg { - my ( $this, $entry, $keys ) = @_; - $keys ||= ''; - my $mess = ''; - - if ( ref($entry) eq 'HASH' ) { - foreach my $el ( keys %$entry ) { - $mess .= $this->_checkCfg( $entry->{$el}, "$keys\{$el}" ); - } - } - elsif ( ref($entry) eq 'ARRAY' ) { - foreach my $i ( 0 .. scalar(@$entry) ) { - $mess .= $this->_checkCfg( $entry->[$i], "$keys\[$i]" ); - } - } - return $mess; -} - -1; -__END__ -Foswiki - The Free and Open Source Wiki, http://foswiki.org/ - -Copyright (C) 2008-2010 Foswiki Contributors. Foswiki Contributors -are listed in the AUTHORS file in the root of this distribution. -NOTE: Please extend that file, not this notice. - -Additional copyrights apply to some or all of the code in this -file as follows: - -Copyright (C) 2000-2006 TWiki Contributors. All Rights Reserved. -TWiki Contributors are listed in the AUTHORS file in the root -of this distribution. NOTE: Please extend that file, not this notice. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. For -more details read LICENSE in the root of this distribution. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -As per the GPL, removal of this notice is prohibited. diff --git a/core/lib/Foswiki/Configure/Checkers/STRING.pm b/core/lib/Foswiki/Configure/Checkers/STRING.pm index 63ad360354..d2664f5123 100644 --- a/core/lib/Foswiki/Configure/Checkers/STRING.pm +++ b/core/lib/Foswiki/Configure/Checkers/STRING.pm @@ -27,7 +27,6 @@ sub check_current_value { my $len = length($value); my $check = $this->{item}->{CHECK}->[0] || {}; - return unless ($check); my $min = $check->{min}[0]; diff --git a/core/lib/Foswiki/Configure/Item.pm b/core/lib/Foswiki/Configure/Item.pm index 444f83ccff..0d208006f1 100644 --- a/core/lib/Foswiki/Configure/Item.pm +++ b/core/lib/Foswiki/Configure/Item.pm @@ -38,13 +38,11 @@ sub new { my $this = bless( { _parent => undef, - _depth => 0, + depth => 0, # Serialisable attribtes - desc => '', - errorcount => 0, - warningcount => 0, - defined_at => undef # where it is defined [ "file", line ] + desc => '', + defined_at => undef # where it is defined [ "file", line ] }, $class ); @@ -68,20 +66,6 @@ sub DESTROY { =begin TML ----++ ObjectMethod getDepth() -> $integer - -Get the depth of the item in the item tree, where the root is at depth 0, -it's children at depth 1, etc. - -=cut - -sub getDepth { - my $this = shift; - return $this->{_depth}; -} - -=begin TML - ---++ ObjectMethod set(@what) / set(%what) * =@what= array of key-value pairs for attributes to set e.g. set(typename=> 'BLAH'). The array may be interpreted as a hash, @@ -250,29 +234,6 @@ sub append { =begin TML ----++ ObjectMethod inc($key) - -Increment a numeric value identified by $key, recursing up the tree to the -root. - -Assumptions - * All item levels have $key defined and initialized - * Parents of items are items (or more precisely: can inc()) - -This is used for counting the numbers of warnings, errors etc found in -subtrees of the configuration structure. - -=cut - -sub inc { - my ( $this, $key ) = @_; - - $this->{$key}++; - $this->{_parent}->inc($key) if $this->{_parent}; -} - -=begin TML - ---++ ObjectMethod hasDeep($attrname) -> $boolean Determine if this item (or any sub-item if this is a collection) @@ -322,10 +283,50 @@ sub getPath { =begin TML +---++ ObjectMethod unparent() + +Unparent a configuration item. This only clears the parent of the node, +it does not remove the node from the parent. After removing parents +only the top-down structure remains, and methods that use the parent, +such as getPath, will not work any more, so use with great caution. + +The main purpose of this method is to prepare a spec node for isolated +use (e.g. serialisation). + +=cut + +sub unparent { + my $this = shift; + delete $this->{_parent}; + delete $this->{_vobCache}; +} + +=begin TML + +---++ ObjectMethod prune($depth) + +Prunes the subtree under $this to a maximum depth of $depth, discarding +children under that point. + +$depth = 0 will prune immediate children +$depth = 1 will prune children-of-children + +etc. + +=cut + +sub prune { + my ( $this, $depth ) = @_; + + # NOP +} + +=begin TML + ---++ ObjectMethod getSectionObject($head, $depth) -> $item This gets the section object that has the heading $head and -getDepth() == $depth below this item. If $depth is not given, +$this->{depth} == $depth below this item. If $depth is not given, will return the first headline that matches. Subclasses must provide an implementation. @@ -355,51 +356,55 @@ All search terms must be matched. # True if the given configuration item matches the given search sub _matches { my ( $this, %search ) = @_; - my $match = 1; while ( my ( $k, $e ) = each %search ) { - if ( $k eq 'parent' ) { - unless ( $this->{_parent} - && $this->{_parent}->_matches(%$e) ) - { - $match = 0; - last; - } + if ( ref($e) ) { + return 0 + unless ( ref( $this->{"_$k"} ) + && $this->{"_$k"}->isa('Foswiki::Configure::Item') + && $this->{"_$k"}->_matches(%$e) ); } - elsif ( $k eq 'depth' ) { - unless ( $this->getDepth() == $e ) { - $match = 0; - last; - } + elsif ( !defined $e ) { + return 0 if defined $this->{$k}; } elsif ( !defined $this->{$k} || $this->{$k} ne $e ) { - $match = 0; - last; + return 0; } } - return $match; + return 1; } +=begin TML + +---++ ObjectMethod find(\%match) -> @nodes + +Get a list of nodes that match the given search template. The template +is a node structure with a subset of fields filled in that must be +matched in a node for it to be returned. + +Any fields can be used in searches and will match using eq, for example: + * =headline= - title of a section, + * =typename= - type of a leaf spec entry, + * =keys= - keys of a spec entry, + * =desc= - descriptive text of a section or entry. + * =depth= - matches the depth of a node under the root + (which is depth 0) +Fields starting with _ are assumed to refer to another Foswiki::Configure::Item + * =parent= - a structure that will be used to match a parent (the value + should be another match hash that will match the parent), + +=cut + sub find { my $this = shift; my %search = @_; my $match = $this->_matches(%search); - # Return without searching the subtree if this node matches if ($match) { return ($this); } - - return () unless $this->{children}; - - # Search children - my @result = (); - foreach my $child ( @{ $this->{children} } ) { - push( @result, $child->find(@_) ); - } - - return @result; + return (); } =begin TML @@ -407,7 +412,7 @@ sub find { ---++ ObjectMethod search($re) -> @nodes Get a list of nodes that match the given RE. Sections match on the headline, -Valuse on the keys. +Values on the keys. =cut diff --git a/core/lib/Foswiki/Configure/Load.pm b/core/lib/Foswiki/Configure/Load.pm index 1e46336f95..cafd85fc2e 100644 --- a/core/lib/Foswiki/Configure/Load.pm +++ b/core/lib/Foswiki/Configure/Load.pm @@ -256,122 +256,6 @@ sub _handleExpand { =begin TML ----++ StaticMethod readDefaults() -> \@errors - -This is only called by =configure= to initialise the Foswiki config hash with -default values from the .spec files. - -Normally all configuration values come from LocalSite.cfg. However when -=configure= runs it has to get default values for config vars that have not -yet been saved to =LocalSite.cfg=. - -Returns a reference to a list of the errors it saw. - -SEE ALSO: Foswiki::Configure::LoadSpec - -=cut - -sub readDefaults { - my %read = (); - my @errors; - - eval { - do 'Foswiki.spec'; - $read{'Foswiki.spec'} = $INC{'Foswiki.spec'}; - }; - push( @errors, $@ ) if ($@); - foreach my $dir (@INC) { - my $root; # SMELL: Not used - _loadDefaultsFrom( "$dir/Foswiki/Plugins", $root, \%read, \@errors ); - _loadDefaultsFrom( "$dir/Foswiki/Contrib", $root, \%read, \@errors ); - _loadDefaultsFrom( "$dir/TWiki/Plugins", $root, \%read, \@errors ); - _loadDefaultsFrom( "$dir/TWiki/Contrib", $root, \%read, \@errors ); - } - - # SMELL: This will create the %TWiki::cfg - # But as it ought to be aliased to %Foswiki::cfg, it's not a big deal - # XXX: Do we still need this code? - if ( %TWiki::cfg && \%TWiki::cfg != \%Foswiki::cfg ) { - - # We had some TWiki plugins, need to map their config to Foswiki - sub mergeHash { - - # Merges the keys in the right hashref to the ones in the - # left hashref - my ( $left, $right, $errors ) = @_; - while ( my ( $key, $value ) = each %$right ) { - if ( exists $left->{$key} ) { - if ( ref($value) ne ref( $left->{$key} ) ) { - push @$errors, - 'Trying to overwrite $Foswiki::cfg{' - . $key - . '} with its $TWiki::cfg version (' - . $value . ')'; - } - elsif ( ref($value) eq 'SCALAR' ) { - $left->{$key} = $value; - } - elsif ( ref($value) eq 'HASH' ) { - $left->{$key} = - mergeHash( $left->{$key}, $value, $errors ); - } - elsif ( ref($value) eq 'ARRAY' ) { - - # It's an array. try to be smart - # SMELL: Ideally, it should keep order too - foreach my $item (@$value) { - unless ( grep /^$item$/, @{ $left->{$key} } ) { - - # The item isn't in the current list, - # add it at the end - unshift @{ $left->{$key} }, $item; - } - } - } - else { - - # It's something else (GLOB, coderef, ...) - push @$errors, - '$TWiki::cfg{' - . $key - . '} is a reference to a' - . ref($value) - . '. No idea how to merge that, sorry.'; - } - } - else { - - # We don't already have such a key in the Foswiki scope - $left->{$key} = $value; - } - } - return $left; - } - mergeHash \%Foswiki::cfg, \%TWiki::cfg, \@errors; - } - - return \@errors; -} - -sub _loadDefaultsFrom { - my ( $dir, $root, $read, $errors ) = @_; - - return unless opendir( D, $dir ); - foreach my $extension ( grep { !/^\./ } readdir D ) { - $extension =~ /(.*)/; - $extension = $1; # untaint - next if $read->{$extension}; - my $file = "$dir/$extension/Config.spec"; - next unless -e $file; - eval { do $file; }; - push( @$errors, $@ ) if ($@); - $read->{$extension} = $file; - } - closedir(D); -} - -=begin TML - ---++ StaticMethod bootstrapConfig() This routine is called from Foswiki.pm BEGIN block to discover the mandatory diff --git a/core/lib/Foswiki/Configure/LoadSpec.pm b/core/lib/Foswiki/Configure/LoadSpec.pm index a8b1340647..38094ceaeb 100644 --- a/core/lib/Foswiki/Configure/LoadSpec.pm +++ b/core/lib/Foswiki/Configure/LoadSpec.pm @@ -104,8 +104,7 @@ Foswiki.spec, which must be found on the @INC path and is always loaded first. Then find all settings for extensions in their .spec files. This *only* reads type specifications, it *does not* read values. For that, -use Foswiki::Configure::Load::readConfig (or -Foswiki::Configure::Load::readDefaults if you are after *default* values) +use Foswiki::Configure::Load::readConfig. * =$root= - Foswiki::Configure::Root of the model diff --git a/core/lib/Foswiki/Configure/Section.pm b/core/lib/Foswiki/Configure/Section.pm index 918a53d2da..92d56fddf8 100644 --- a/core/lib/Foswiki/Configure/Section.pm +++ b/core/lib/Foswiki/Configure/Section.pm @@ -42,7 +42,6 @@ sub new { children => [], headline => 'UNKNOWN', typename => 'SECTION', - _depth => 0, _vobCache => {}, # Do not serialise @opts ); @@ -63,7 +62,7 @@ sub addChild { die "Subnode already present; cannot add again" if $child eq $kid; } $child->{_parent} = $this; - $child->{_depth} = $this->{_depth} + 1; + $child->{depth} = $this->{depth} + 1; push( @{ $this->{children} }, $child ); @@ -95,6 +94,32 @@ sub hasDeep { return 0; } +# See Foswiki::Configure::Item +sub unparent { + my $this = shift; + + if ( $this->{children} ) { + foreach my $c ( @{ $this->{children} } ) { + $c->unparent(); + } + } + $this->SUPER::unparent(); +} + +# See Foswiki::Configure::Item +sub prune { + my ( $this, $depth ) = @_; + + if ( $depth == 0 ) { + delete $this->{children}; + } + elsif ( $this->{children} ) { + foreach my $c ( @{ $this->{children} } ) { + $c->prune( $depth - 1 ); + } + } +} + # See Foswiki::Configure::Item # Visit each of the children of this node in turn. sub visit { @@ -117,7 +142,7 @@ sub visit { sub getSectionObject { my ( $this, $head, $depth ) = @_; if ( $this->{headline} eq $head - && ( !defined $depth || $this->getDepth() == $depth ) ) + && ( !defined $depth || $this->{depth} == $depth ) ) { return $this; } @@ -184,6 +209,29 @@ sub search { return @result; } +# Implements Foswiki::Configure::Item +sub find { + my $this = shift; + my %search = @_; + + my $match = $this->_matches(%search); + + # Return without searching the subtree if this node matches + if ($match) { + return ($this); + } + + return () unless $this->{children}; + + # Search children + my @result = (); + foreach my $child ( @{ $this->{children} } ) { + push( @result, $child->find(@_) ); + } + + return @result; +} + 1; __END__ Foswiki - The Free and Open Source Wiki, http://foswiki.org/ diff --git a/core/lib/Foswiki/Contrib/core/MANIFEST b/core/lib/Foswiki/Contrib/core/MANIFEST index faf7afa9df..d94e795c57 100644 --- a/core/lib/Foswiki/Contrib/core/MANIFEST +++ b/core/lib/Foswiki/Contrib/core/MANIFEST @@ -426,7 +426,6 @@ lib/Foswiki/Configure/Checker.pm 0444 lib/Foswiki/Configure/Checkers/AccessibleCFG.pm 0444 lib/Foswiki/Configure/Checkers/AuthRealm.pm 0444 lib/Foswiki/Configure/Checkers/AuthScripts.pm 0444 -lib/Foswiki/Configure/Checkers/BasicSanity.pm 0444 lib/Foswiki/Configure/Checkers/Cache/Debug.pm 0444 lib/Foswiki/Configure/Checkers/Cache/DependencyFilter.pm 0444 lib/Foswiki/Configure/Checkers/Cache/Implementation.pm 0444