#!/usr/bin/perl -wT # See bottom of file for license and copyright information =begin TML Configuration script for Foswiki. Once you have a basic webserver configuration that lets you access this script, the rest of the configuration process is done from here. The script works from the top down, by checking features of the environment before moving on. The sequence is: 1. Check the version of perl 2. Check we have the modules to run this script 3. Check the environment 4. Check we have the modules to load the rest of configure ... and so on. At any stage, the script reports any errors in the best way it can given the environment established so far. When the basic checks are complete, the script moves into the real configuration steps; setting configuration variables. This phase of the configure environment follows a Model-View- Controller pattern. ---++ Controller This script is the controller; it handles communication with the browser (and thus the user). Communication is very simple; this script is re-invoked with different 'action' parameters to determine what it does. ---++ Model The Model consists of a simple node tree, where each node represents a structural element in the *presentation* of the configuration (this may not be consistent with the structure of $Foswiki:cfg, so beware). Each leaf node has an associated Type (in the Types subdirectory) that has collected model and view behaviours for the basic types. Class hierarchy * Foswiki::Configure::Item * Foswiki::Configure::Value - a leaf value * Foswiki::Configure::Section - a running node * Foswiki::Configure::Root - a root section * Foswiki::Configure::Pluggable - a plug-in subsection * Foswiki::Configure::Pluggables::FINDEXTENSIONS - etc * Foswiki::Configure::Checkers::Introduction - should be a Pluggable * Foswiki::Configure::Checkers::Welcome - should be a Pluggable * Foswiki::Configure::Checkers::MSWin32 - should be a Pluggable The Model is independent of the language used to represent the configuration. There is one parser/generator provided, FoswikiCfg, but it would be trivial to add others. ---++ View The View is a DOM document, generated as HTML by a set of UI classes, all subclasses of Foswiki::Configure::UI. The UI classes visit the model in order to generate the HTML. ---+++ UIs Each class in the model (Root, Section, Value, Item) has a corresponding UI decorator object, which renders HTML for the model. There are also a number of bespoke UIs, some of which assist =configure= in the generation of full screens (Introduction, Welcome, AUTH, UPDATE, EXTEND, EXTENSIONS, UPDATE) and others which relate to the Pluggables (FINDEXTENSIONS, LANGUAGES, PLUGINS). The special UI CGISetup is a specialised Section focused on groping the CGI configuration. Several of the bespoke UIs (CGISetup, Introduction, Welcome) have corresponding Checkers, which act as placeholders in the model for these sections. Class hierarchy * Foswiki::Configure::UI * Foswiki::Configure::Checker * Foswiki::Configure::Checkers::* - see below * Foswiki::Configure::UIs::* - components used in building screens * Foswiki::Configure::UIs::Item * Foswiki::Configure::UIs::Section * Foswiki::Configure::UIs::Root * Foswiki::Configure::UIs::Introduction * Foswiki::Configure::UIs::Welcome * Foswiki::Configure::UIs::MSWin32 * Foswiki::Configure::UIs::* - other UIs for Pluggables and screens * Foswiki::Configure::UIs::Value ---+++ Checkers Checkers give checking and guessing support for configuration values. Checkers are all subclasses of Foswiki::Configure::Checker, and inhabit a class hierarchy under it that mirrors the organisation of configuration keys in Foswiki.spec. Checkers include read-only checking UI used for checking environment sanity (BasicSanity) Note that when configure is run for the first time (before LocalSite.cfg has been created) then only the first section of Foswiki.spec is loaded, and thus checkers only for that subset will be created and run. This means that on some platforms, initial configuration is a two-phase process, as the initial path checks are performed on the first run, and only on the second run, when LocalSite.cfg exists, are the other checkers built and invoked. This needs improving on. ---+++ Types Types provide some UI support in the form of type-specific prompters. This is really an abuse of the Model, but it saves creating decorator classes for all the Model types. HTML is generated for the model using Visitor pattern. Each node in the tree is visited in depth-first order. Class hierarchy * Foswiki::Configure::Type - base * Foswiki::Configure::Types::NUMBER - numerical type (perl float values) * Foswiki::Configure::Types::OCTAL - octal (permissions) * Foswiki::Configure::Types::BOOLEAN - boolean type * Foswiki::Configure::Types::LANGUAGE * Foswiki::Configure::Types::PERL - perl structure * Foswiki::Configure::Types::SELECT - select from a list of values * Foswiki::Configure::Types::SELECTCLASS - select a class from a path * Foswiki::Configure::Types::STRING - string type * Foswiki::Configure::Types::REGEX - regular expression * Foswiki::Configure::Types::COMMAND - shell command * Foswiki::Configure::Types::PASSWORD - hidden password * Foswiki::Configure::Types::PATH - file path (/) * Foswiki::Configure::Types::URL - absolute url path (/) * Foswiki::Configure::Types::URLPATH - relative url path (/) * Foswiki::Configure::Types::UNKNOWN - unknown type TODO: The type classes are the obvious place to attach client-side javascript validators, thus releasing the server-side checkers to consider the "deeper" issues. =cut use strict; use warnings; my $SESSIONEXP = 3600; # Lifetime of configure session my $SAVEEXP = 300; # Lifetime of cached save password # This is absolutely essential for error reporting. We load it using # an eval so we can report the problem. eval "use CGI::Carp qw(fatalsToBrowser)"; if ($@) { print <<"REPORT"; Content-type: text/plain Could not load CGI::Carp. Please install this module before continuing. It can be downloaded from http://www.cpan.org The error seen was: $@ REPORT exit 1; } ########################################################### # VERY basic stuff required for configure to work. Any errors # during this phase will throw a die, which will be picked up # using CGI::Carp fatalsToBrowser # Warnings are fatal $SIG{'__WARN__'} = sub { die @_ }; eval 'require 5.00503'; die $@ if $@; # We warn against running Foswiki on an older Perl version then 5.8.8 # but we will not let configure die in this situation. The user # may have updated many libraries and tweaked Foswiki so let us give # him a chance. my $perlversion = $]; if ( $perlversion < 5.008008 ) { print STDERR <) || ''; }; my $localLibFailure; sub _loadBasicModule { my ($module) = @_; eval "use $module"; if ($@) { my $reason = "Failed to load the perl module $module. The module "; # See if we can find the .pm on @INC my $foundAt = "could not be found. "; my $modpath = $module; if ( $modpath =~ /^([\w:]+)/ ) { $modpath =~ s#::#/#g; $modpath .= '.pm'; foreach my $path (@INC) { if ( -e "$path/$modpath" ) { $foundAt = "was found at $path/$modpath"; if ( !-r "$path/$modpath" ) { $foundAt .= ", but I don't have permission to read it."; } last; } } } $reason .= $foundAt; $reason .= <splitdir($1); my $setlib = File::Spec->catfile( @root, 'setlib.cfg' ); pop(@root); my $scriptName = Foswiki::getScriptName(); # Try to load the LocalLib.cfg optional overload eval "require '$setlib';"; if ($@) { # No joy. Remember the failure so we can report it later. $localLibFailure = $@; # Stick the root/lib on the path; there's a high probability we'll be # able to find the bits of Foswiki::Configure that way. We will report # the setlib error later. unshift( @INC, File::Spec->catfile( @root, 'lib' ) ); } ::_loadBasicModule('CGI qw(:any)'); $| = 1; # no buffering on STDOUT # We are configuring $Foswiki::cfg, so we need to be in package Foswiki from # now on. package Foswiki; # We keep the actual config, and the default from Foswiki.spec, separate my ( %cfg, $defaultCfg ); # Declared in Foswiki to support checkers our $query = new CGI; our $session; # 'constants' used in Foswiki.spec my $TRUE = 1; my $FALSE = 0; our $badLSC; our $insane; our $time = time(); my $url = $query->url(); my $action = $query->param('action') || 'Configure'; our $DEFAULT_FIELD_WIDTH_NO_CSS = '70'; # Handle serving an resource embedded in the configure page, before generating # any other output ::_loadBasicModule('Foswiki::Configure::Util'); ::_loadBasicModule('Foswiki::Configure::TemplateParser'); if ( $action eq 'resource' ) { my $resource = $query->param('resource'); $resource =~ /^([-\w]+\.\w+)$/; # filter-in and untaint $resource = $1; if ( defined($resource) ) { #ignore $query->param('type') and set it using the extension my $type = 'image/gif'; if ( $resource =~ /\.ico$/ ) { $type = 'resource/x-icon'; } elsif ( $resource =~ /\.js$/ ) { $type = 'text/javascript'; } my $parser = Foswiki::Configure::TemplateParser->new; my $text = $parser->getResource($resource); # SMELL: this call is correct, but causes a perl error # on some versions of CGI.pm # print $query->header(-type => $query->param('type')); # So use this instead: print 'Content-type: ' . $type . "\n\n"; print $text; } exit 0; } # Get web server's group info $::WebServer_gid = ''; eval { $::WebServer_gid = join( ',', map { lc( getgrgid($_) ) } split( ' ', $( ) ); }; if ($@) { # Try to use Cygwin's 'id' command - may be on the path, since Cygwin # is probably installed to supply ls, egrep, etc - if it isn't, give # up. # Run command without stderr output, to avoid CGI giving error. # Get names of primary and other groups. # This is down here because it takes 30s to execute on Strawberry perl $::WebServer_gid = lc(qx(sh -c '( id -un ; id -gn) 2>/dev/null' 2>nul )); } ########################################################### # From this point on we shouldn't have any more "fatal" (to configure) # errors, so we can report errors in the browser (i.e. without using die) # Remember what we detected previously, for use by Checkers if ( $scriptName =~ /(\.\w+)$/ ) { $Foswiki::cfg{DETECTED}{ScriptExtension} = $1; } # duplicated here, should use the one in Util.pm sub getScriptName { my @script = File::Spec->splitdir( $ENV{SCRIPT_NAME} || 'THISSCRIPT' ); my $scriptName = pop(@script); $scriptName =~ s/.*[\/\\]//; # Fix for Item3511, on Win XP return $scriptName; } ########################################################### # Grope the OS. This duplicates a bit of code in Foswiki.pm, # but it has to be duplicated because we don't want to deal # with loading Foswiki just yet. unless ( $Foswiki::cfg{DetailedOS} ) { $Foswiki::cfg{DetailedOS} = $^O; unless ( $Foswiki::cfg{DetailedOS} ) { require Config; $Foswiki::cfg{DetailedOS} = $Config::Config{osname}; } } unless ( $Foswiki::cfg{OS} ) { if ( $Foswiki::cfg{DetailedOS} =~ /darwin/i ) { # MacOS X $Foswiki::cfg{OS} = 'UNIX'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /Win/i ) { $Foswiki::cfg{OS} = 'WINDOWS'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /vms/i ) { $Foswiki::cfg{OS} = 'VMS'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /bsdos/i ) { $Foswiki::cfg{OS} = 'UNIX'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /dos/i ) { $Foswiki::cfg{OS} = 'DOS'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /^MacOS$/i ) { # MacOS 9 or earlier $Foswiki::cfg{OS} = 'MACINTOSH'; } elsif ( $Foswiki::cfg{DetailedOS} =~ /os2/i ) { $Foswiki::cfg{OS} = 'OS2'; } else { $Foswiki::cfg{OS} = 'UNIX'; } } sub log { my ($message) = @_; $message ||= ''; my $log = $Foswiki::cfg{DebugFileName} || 'ConfigureError.log'; my $file; if ( open( $file, '>>', $log ) ) { print $file "$message\n"; close($file); } } # Load all the bits of the configure module that we explicitly use # The loadBasicModule does some extra analysis on errors. foreach my $module ( 'Cwd', 'Data::Dumper', 'File::Copy', 'File::Temp', 'CGI::Session', 'CGI::Cookie', 'Foswiki::Configure::Checker', 'Foswiki::Configure::Item', 'Foswiki::Configure::Load', 'Foswiki::Configure::Pluggable', 'Foswiki::Configure::Root', 'Foswiki::Configure::Section', 'Foswiki::Configure::Type', 'Foswiki::Configure::Types::BOOLEAN', 'Foswiki::Configure::Types::NUMBER', 'Foswiki::Configure::Types::SELECT', 'Foswiki::Configure::Types::STRING', 'Foswiki::Configure::FoswikiCfg', 'Foswiki::Configure::UI', 'Foswiki::Configure::UIs::Section', 'Foswiki::Configure::Value', 'Foswiki::Configure::Valuer', 'Foswiki::Configure::GlobalControls', ) { ::_loadBasicModule($module); } _dispatchContents(); ########################################################### # End of the main program; the rest is all subs sub _dispatchContents { my $stub = new Foswiki::Configure::Item(); # This call will define $Foswiki::defaultCfg by loading .spec files my $sanityUI = Foswiki::Configure::UI::loadChecker( 'BasicSanity', $stub ); # Perform the check my $sanityStatement = $sanityUI->check(); $badLSC = $sanityUI->lscIsBad(); $insane = $sanityUI->insane(); $action =~ s/\W//g; # if LSC is bad, can't check authorization, so just print a header if ($badLSC) { print $query->header( -type => 'text/html' ); } else { # _checkSessionAuth prints the header. return unless ( _checkSessionAuth() ); } # This is the dispatcher; $action is the name of the action to perform, # this is concatenated to _action to determine the name of the procedure. # Dispatcher methods return a boolean to indicate whether to generate a # link back to the main page at the end. if ( $insane && $query->param('abort') ) { print $sanityStatement; } else { my $method = '_action' . $action; die "Undefined action $action" unless defined(&$method); no strict 'refs'; &$method($sanityStatement); use strict 'refs'; } } sub _checkSessionAuth { my $sid = $query->cookie("FOSWIKICFGSID") || undef; my $validateAction = 0; $session = CGI::Session->new( "driver:File", $sid, { Directory => File::Spec->tmpdir } ); my $cookie = $query->cookie( FOSWIKICFGSID => $session->id ); print $query->header( -cookie => $cookie, -type => 'text/html' ); #print STDERR "--- Validating $action --- \n"; if ( $session->param( -name => '_SAVE_OK' ) && ( $session->atime() - $session->ctime() ) > $SAVEEXP ) { $session->clear('_SAVE_OK'); #print STDERR "SAVE OKAY CLEARED\n"; } # Require validation when an update is requested and update session expired $validateAction = 1 if ( $action ne 'Configure' && !$session->param( -name => '_SAVE_OK' ) ); # If the browser has authenticated, we don't need to check password for basic access. # unless password needs to be set or change is requested. if ( $Foswiki::query->auth_type() # Browser has authenticated with server && !$validateAction # and it's not an update && $Foswiki::cfg{Password} # and a password is set && !$query->param('changePassword') # and change not requested ) { _resetExpires(); return 1; } if ( $validateAction # An update is requested || !$Foswiki::cfg{Password} # Or no password set || $query->param('changePassword') # Or change is requested || $session->is_expired() # Or session is expired || !defined $sid # Or there is no session || $sid ne $session->id() # Or it's a new session ) { my ( $authorised, $messageType ) = Foswiki::Configure::UI::authorised(); #print STDERR "MESSAGE TYPE $messageType\n"; unless ($authorised) { _screenAuthorize( $action, $messageType, 0 ); $session->delete(); return 0; } else { _resetExpires(1); # Reset session and also set the save allowed flag # messageType 4 = PW Changed, 8 = no PW, 16 = incorrect PW, 32 = PW Mismatch, 64 = Enter new PW if ( $messageType == 4 && $action eq 'Configure' ) { _screenFeedback($messageType); return 0; } } } _resetExpires(); return 1; } sub _resetExpires { my $saveOK = shift; $session->expire($SESSIONEXP); # Save password caching has a smaller lifetime than the overall configure session. As long as configure # has been accessed successfully in the past $SESSIONEXP seconds, the general access session is valid. However the ability # to make changes is only cached for $SAVEEXP seconds without password validation. if ($saveOK) { $session->param( -name => '_SAVE_OK', -value => 1 ); } $session->flush(); } sub _checkLoadUI { my ( $uiname, $root ) = @_; my $ui = Foswiki::Configure::UI::loadUI( $uiname, $root ); unless ($ui) { print "Could not load $uiname UI. Error was:
$@
"; if ( $@ =~ /Can't locate (\S+)/ ) { print <param('confirmChanges') ) { $query->delete('confirmChanges'); _screenFeedback($messageType); } else { _screenAuthorize( $action, $messageType, $confirm ); } } # Screen that prompts for a password sub _screenAuthorize { my ( $transact, $messageType, $confirm ) = @_; my $contents = ''; #print STDERR # "_screenAuthorize entered $transact, $messageType, $confirm \n"; # create the root of the UI my $root = new Foswiki::Configure::Root(); my $ui; my @items = (); my $params = ''; my $hasPassword = ( $Foswiki::cfg{Password} ne '' ) || 0; $ui = _checkLoadUI( 'AUTH', $root ); return '' unless $ui; $query->delete( 'time', 'cfgAccess', 'formAction' ); $params = join( "\n", $ui->params() ); my $contentTemplate; if ($confirm) { $contentTemplate = Foswiki::Configure::UI::getTemplateParser()->readTemplate('confirm'); } else { $contentTemplate = Foswiki::Configure::UI::getTemplateParser() ->readTemplate('authorize'); } my $changePassword = $Foswiki::query->param('changePassword') || undef; # displayStatus - 1 = No Changes, 2 = Changes, 4 = No Extensions, 8 = Extensions, 16 = Email Test, 32 = Login my @dummy = (); if ( $transact eq 'Savechanges' ) { my $valuer = new Foswiki::Configure::Valuer( $Foswiki::defaultCfg, \%Foswiki::cfg ); my %updated; my $modified = $valuer->loadCGIParams( $Foswiki::query, \%updated ); my $changesList = (); foreach my $key ( sort keys %updated ) { my $valueString = $query->param($key); push( @{$changesList}, { key => $key, value => $valueString } ); } @items = sort keys %updated if $modified; my $displayStatus = ( $modified || $Foswiki::query->param('changePassword') ) ? 2 : 1; Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'main' => $contents, 'hasPassword' => $hasPassword, 'formAction' => $scriptName, 'displayStatus' => $displayStatus, 'modifiedCount' => $modified, 'items' => \@items, 'changesList' => $changesList, 'params' => $params, 'messageType' => $messageType, 'configureUrl' => $url, 'changePassword' => $changePassword, 'extAction' => '', 'extAddCount' => 0, 'extRemoveCount' => 0, 'extAddItems' => \@dummy, 'extRemoveItems' => \@dummy, } ); } elsif ( $transact eq 'ManageExtensions' ) { my $processExt = $query->param('processExt') || 'all'; my @remove = (); my @add = (); foreach my $ext ( $query->param('remove') ) { my ( $repo, $extn ) = split( /\//, $ext ); push( @remove, "$extn" ); } foreach my $ext ( $query->param('add') ) { my ( $repo, $extn ) = split( /\//, $ext ); push( @add, "$extnfrom $repo" ); } my $action = $processExt eq 'dep' ? 'run a dependency report' : $processExt eq 'sim' ? 'simulate the following actions' : $processExt eq 'nodep' ? 'install without dependencies' : $processExt eq 'all' ? 'perform the following actions' : $processExt; my $addCount = scalar @add; my $removeCount = scalar @remove; my $displayStatus = ( $addCount || $removeCount ) ? 8 : 4; Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'main' => $contents, 'hasPassword' => $hasPassword, 'formAction' => $scriptName, 'displayStatus' => $displayStatus, 'changesList' => \@dummy, 'modifiedCount' => 0, 'items' => \@dummy, 'extAddCount' => $addCount, 'extRemoveCount' => $removeCount, 'extAddItems' => \@add, 'extRemoveItems' => \@remove, 'params' => $params, 'messageType' => $messageType, 'extAction' => $action, 'configureUrl' => $url, 'changePassword' => $changePassword, } ); } elsif ( $transact eq 'TestEmail' ) { Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'main' => $contents, 'hasPassword' => $hasPassword, 'formAction' => $scriptName, 'displayStatus' => 16, 'changesList' => \@dummy, 'modifiedCount' => 0, 'items' => \@dummy, 'extAddCount' => 0, 'extRemoveCount' => 0, 'extAddItems' => \@dummy, 'extRemoveItems' => \@dummy, 'params' => $params, 'messageType' => $messageType, 'extAction' => $action, 'configureUrl' => $url, 'changePassword' => $changePassword, } ); } elsif ( $transact eq 'Configure' || $transact eq 'FindMoreExtensions' ) { Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'main' => $contents, 'hasPassword' => $hasPassword, 'formAction' => $scriptName, 'displayStatus' => 32, 'changesList' => \@dummy, 'modifiedCount' => 0, 'items' => \@dummy, 'extAddCount' => 0, 'extRemoveCount' => 0, 'extAddItems' => \@dummy, 'extRemoveItems' => \@dummy, 'params' => $params, 'messageType' => $messageType, 'extAction' => $action, 'configureUrl' => $url, 'changePassword' => $changePassword, } ); } my $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('pagebegin'); $html .= $contentTemplate; $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('pageend'); Foswiki::Configure::UI::getTemplateParser()->parse( $html, { 'time' => $time, 'formAction' => $scriptName, 'extAddCount' => 0, 'extRemoveCount' => 0, } ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; } # After authentication, the screen that shows the changes. sub _screenFeedback { my ($messageType) = @_; #print STDERR "ScreenFeedback entered - type $messageType\n"; my $valuer = new Foswiki::Configure::Valuer( $Foswiki::defaultCfg, \%Foswiki::cfg ); my %updated; my $modified = $valuer->loadCGIParams( $Foswiki::query, \%updated ); # create the root of the UI my $root = new Foswiki::Configure::Root(); # Load the specs from the .spec files and generate the UI template Foswiki::Configure::FoswikiCfg::load( $root, 1 ); my $ui = _checkLoadUI( 'UPDATE', $root ); return '' unless $ui; $ui->setInsane() if $insane; $ui->commitChanges( $root, $valuer, \%updated ); undef $ui; # SMELL: why is this list built again? It's already been built # once in commitChanges() my $changesList = (); foreach my $key ( sort keys %updated ) { my $valueString = $query->param($key); push( @{$changesList}, { key => $key, value => $valueString } ); } my $contentTemplate = Foswiki::Configure::UI::getTemplateParser()->readTemplate('feedback'); Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'modifiedCount' => scalar keys %updated, 'changesList' => $changesList, 'formAction' => $scriptName, 'messageType' => $messageType, } ); Foswiki::Configure::UI::getTemplateParser() ->cleanupTemplateResidues($contentTemplate); # If this pass created the LocalSite.cfg, need to rerun the sanity check # to re-establish that the configuration is valid my $sanityStatement = ''; if ($badLSC) { my $stub = new Foswiki::Configure::Item(); # This call will define $Foswiki::defaultCfg by loading .spec files my $sanityUI = Foswiki::Configure::UI::loadChecker( 'BasicSanity', $stub ); $sanityStatement = $sanityUI->check(); $badLSC = $sanityUI->lscIsBad(); $insane = $sanityUI->insane(); } # Pass control to the mainline configure screen, passing along the # results of the save _actionConfigure( $sanityStatement . $contentTemplate ); } # Invoked by "find more extensions" button in the Extensions section sub _actionFindMoreExtensions { my $root = new Foswiki::Configure::Root(); my $ui = _checkLoadUI( 'EXTENSIONS', $root ); return unless $ui; my ( $consultedLocations, $table, $errors, $installedCount, $allCount ) = $ui->getExtensions(); my @script = File::Spec->splitdir( $ENV{SCRIPT_NAME} ); my $scriptName = pop(@script); $scriptName =~ s/.*[\/\\]//; # Fix for Item3511, on Win XP my $contentTemplate = Foswiki::Configure::UI::getTemplateParser()->readTemplate('extensions'); Foswiki::Configure::UI::getTemplateParser()->parse( $contentTemplate, { 'formAction' => $scriptName, 'table' => $table, 'errors' => $errors, 'consultedLocations' => $consultedLocations, 'installedCount' => $installedCount, 'allCount' => $allCount, } ); my $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('pagebegin'); $html .= $contentTemplate; $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('pageend'); Foswiki::Configure::UI::getTemplateParser()->parse( $html, { 'time' => '', 'formAction' => $scriptName, } ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; } # Action invoked by 'Next' button on the main screen sub _actionManageExtensions { my ( $authorised, $messageType ) = Foswiki::Configure::UI::authorised(); if ( $query->param('confirmChanges') ) { $query->delete('confirmChanges'); _runManageExtensions($messageType); } else { _screenAuthorize( $action, $messageType, 1 ); } } # Invoked when extensions are to be (un)installed sub _runManageExtensions { my $root = new Foswiki::Configure::Root(); my $ui; # Reload the configuration - expanding variables delete $Foswiki::cfg{ConfigurationFinished}; Foswiki::Configure::Load::readConfig(); my $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('pagebegin'); Foswiki::Configure::UI::getTemplateParser()->parse( $html, {} ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; $ui = _checkLoadUI( 'EXTEND', $root ); return 1 unless $ui; # Warning: the 'install' method uses print for rapid feedback print $ui->install(); $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('installed'); $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('pageend'); my $frontpageUrl = "$Foswiki::cfg{DefaultUrlHost}$Foswiki::cfg{ScriptUrlPath}/view$Foswiki::cfg{ScriptSuffix}/"; Foswiki::Configure::UI::getTemplateParser()->parse( $html, { 'frontpageUrl' => $frontpageUrl, 'configureUrl' => $url, } ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; } # Invoked to test email configuration sub _actionTestEmail { my $root = new Foswiki::Configure::Root(); my $ui; my $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('pagebegin'); Foswiki::Configure::UI::getTemplateParser()->parse( $html, {} ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; ::_loadBasicModule('Foswiki::Net'); my $charset = $Foswiki::cfg{Site}{CharSet} || CGI::charset(); Foswiki::Configure::Load::expandValue( $Foswiki::cfg{Email}{SmimeCertificateFile} ); Foswiki::Configure::Load::expandValue( $Foswiki::cfg{Email}{SmimeKeyFile} ); my $msg = <Email is globally disabled - temporarily enabling email for this test"; $Foswiki::cfg{EnableEmail} = 1; } unless ( $Foswiki::cfg{Email}{MailMethod} ) { $Foswiki::cfg{Email}{MailMethod} = ( $Foswiki::cfg{SMTP}{MAILHOST} ) ? 'Net::SMTP' : 'MailProgram'; print "
Incomplete config - guessed MailMethod = $Foswiki::cfg{Email}{MailMethod}
"; } my $sendmethod = ( $Foswiki::cfg{Email}{MailMethod} eq 'MailProgram' ) ? $Foswiki::cfg{MailProgram} : $Foswiki::cfg{Email}{MailMethod}; # Warning: the 'install' method uses print for rapid feedback print "

Attempting to send the following message using $sendmethod

\n"; print "
$msg
"; print "
Please wait ... connecting to server ...
"; $Foswiki::cfg{SMTP}{Debug} = 1; my $net = Foswiki::Net->new(); my $stderr = ''; my $error = ''; eval { local *STDERR; open STDERR, '>', \$stderr; $error = $net->sendEmail("$msg"); close STDERR; } or do { $error .= $@; }; print "

Results:

\n"; my $emsg = ($error) ? "
Net::sendEmail() returned the following error:
$error
" : "
No errors returned
"; print $emsg; print "
Debug log messages:
$stderr
" if ($stderr); } else { print "
Impossible to send message: No WebMasterEmail address is configured.
"; } $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('installed'); $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('pageend'); my $frontpageUrl = "$Foswiki::cfg{DefaultUrlHost}$Foswiki::cfg{ScriptUrlPath}/view$Foswiki::cfg{ScriptSuffix}/"; Foswiki::Configure::UI::getTemplateParser()->parse( $html, { 'frontpageUrl' => $frontpageUrl, 'configureUrl' => $url, } ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; } # This is the default screen sub _actionConfigure { my $messages; # If coming from the save action, or insane, pick up the messages if ($insane) { $messages = "

Internal error - proceed with caution

"; } $messages .= shift; my $contents = ''; my $isFirstTime = $badLSC; #allow debugging of checker's guesses by showing the entire UI $isFirstTime = 0 if ( $query->param('DEBUG') ); Foswiki::Configure::UI::reset($isFirstTime); my $valuer = new Foswiki::Configure::Valuer( $Foswiki::defaultCfg, \%Foswiki::cfg ); # This is the root of the model my $root = new Foswiki::Configure::Root(); # Load special sections used as placeholders my $intro = 'Foswiki::Configure::Checkers::' . ( $isFirstTime ? 'Welcome' : 'Introduction' ); eval "require $intro"; Carp::confess $@ if $@; my $intro_checker = $intro->new($root); $root->addChild($intro_checker); my $oscfg = $Config::Config{osname}; if ($oscfg) { # See if this platform has special detection or checking requirements my $osospecial = "Foswiki::Configure::Checkers::$oscfg"; eval "require $osospecial"; unless ($@) { my $os_checker = $osospecial->new($root); $root->addChild($os_checker) if $os_checker; } } my $cgienv = 'Foswiki::Configure::Checkers::CGISetup'; eval "require $cgienv"; Carp::confess $@ if $@; my $cgi_checker = $cgienv->new($root); $root->addChild($cgi_checker); # Load the config structures. # If $isFirstTime is true, only Foswiki.spec will be loaded # (extension Config.spec files will *not* be loaded) and # only the first section of Foswiki.spec will be processed # i.e. all other sections will be skipped. Foswiki::Configure::FoswikiCfg::load( $root, !$isFirstTime ); # Now generate the UI # Load the UI for the root; this UI is simply a visitor over # the model my $ui = _checkLoadUI( 'Root', $root ); return '' unless $ui; my $uiMessages = ( !$isFirstTime && $messages ) ? $messages : undef; # Visit the model and generate $ui->{controls} = new Foswiki::Configure::GlobalControls(); $contents .= $ui->createUI( $root, $valuer ); my $showSanityStatement = ( !$isFirstTime && !$session && !$Foswiki::query->auth_type() ) ? 1 : undef; my $html = Foswiki::Configure::UI::getTemplateParser()->readTemplate('pagebegin'); if ($showSanityStatement) { $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('sanity'); } $html .= $contents; $html .= Foswiki::Configure::UI::getTemplateParser()->readTemplate('pageend'); Foswiki::Configure::UI::getTemplateParser()->parse( $html, { 'time' => $time, # use time to make sure we never allow cacheing 'formAction' => $scriptName, 'messages' => $uiMessages, 'style' => ( $badLSC || $insane ) ? 'Bad' : 'Good', } ); Foswiki::Configure::UI::getTemplateParser()->cleanupTemplateResidues($html); print $html; } 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-2007 TWiki Contributors. All Rights Reserved. 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.