2 # TWiki Enterprise Collaboration Platform, http://TWiki.org/
4 # Copyright (C) 2000-2006 TWiki Contributors.
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version. For
10 # more details read LICENSE in the root of this distribution.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 # As per the GPL, removal of this notice is prohibited.
18 # This is both the factory for UIs and the base class of UI constructors.
19 # A UI is the V part of the MVC model used in configure.
21 # Each structural entity in a configure screen has a UI type, either
22 # stored directly in the entity or indirectly in the type associated
23 # with a value. The UI type is used to guide a visitor which is run
24 # over the structure to generate the UI.
26 package TWiki::Configure::UI;
32 use vars qw ($totwarnings $toterrors);
35 my ($class, $item) = @_;
37 Carp::confess unless $item;
39 my $this = bless( { item => $item }, $class);
40 $this->{item} = $item;
42 $this->{bin} = $FindBin::Bin;
43 my @root = File::Spec->splitdir($this->{bin});
45 $this->{root} = File::Spec->catfile(@root, '');
50 sub findRepositories {
52 unless (defined($this->{repositories})) {
54 $replist .= $TWiki::cfg{ExtensionsRepositories}
55 if defined $TWiki::cfg{ExtensionsRepositories};
56 $replist .= "$ENV{TWIKI_REPOSITORIES};" # DEPRECATED
57 if defined $ENV{TWIKI_REPOSITORIES}; # DEPRECATED
58 $replist = ";$replist;";
59 while ($replist =~ s/[;\s]+(.*?)=\((.*?),(.*?)(?:,(.*?),(.*?))?\)\s*;/;/) {
60 push(@{$this->{repositories}},
61 { name => $1, data => $2, pub => $3});
67 my ($this, $reponame) = @_;
68 foreach my $place (@{$this->{repositories}}) {
69 return $place if $place->{name} eq $reponame;
78 $id = 'TWiki::Configure::UIs::'.$id;
81 eval "use $id; \$ui = new $id(\$item);";
83 return undef if (!$ui && $@);
88 # Static checker factory
89 # Checkers *need not* exist
96 my $checkClass = 'TWiki::Configure::Checkers::'.$id;
99 eval "use $checkClass; \$checker = new $checkClass(\$item);";
100 # Can't locate errors are OK
101 die $@ if ($@ && $@ !~ /Can't locate /);
106 # Returns a response object as described in TWiki::Net
108 my ($this, $url) = @_;
111 my $tn = new TWiki::Net();
112 my $response = $tn->getExternalResource($url);
117 # STATIC Used by a whole bunch of things that just need to show a key-value row
118 # (called as a method, i.e. with class as first parameter)
122 return CGI::Tr(CGI::td({class=>'firstCol'}, $key).
123 CGI::td({class=>'secondCol'}, join(' ', @_)))."\n";
126 # Generate a foldable block (twisty). This is a DIV with a table in it
127 # that contains the settings and doc rows.
129 my( $this, $head, $attr, $body ) = @_;
130 my $headText = $head . CGI::span({ class => 'blockLinkAttribute' }, $attr);
131 $body = CGI::start_table({width => '100%', -border => 0, -cellspacing => 0, -cellpadding => 0}).$body.CGI::end_table();
132 my $mess = $this->collectMessages($this->{item});
134 my $anchor = $this->_makeAnchor( $head );
137 my $linkId = 'blockLink'.$id;
138 my $linkAnchor = $anchor.'link';
139 return CGI::a({ name => $linkAnchor }).
140 CGI::a({id => $linkId,
141 class => 'blockLink blockLinkOff',
142 href => '#'.$linkAnchor,
144 onclick => 'foldBlock("' . $id . '"); return false;'
147 CGI::div( {id => $blockId,
148 class=> 'foldableBlock foldableBlockClosed'
153 # encode a string to make an HTML anchor
155 my ($this, $str) = @_;
157 $str =~ s/\s(\w)/uc($1)/ge;
164 return CGI::p({class=>"info"}, join("\n",@_));
170 $this->{item}->inc('warnings');
172 return CGI::div(CGI::span({class=>'warn'},
173 CGI::strong('Warning: ').join("\n",@_)));
179 $this->{item}->inc('errors');
181 return CGI::div(CGI::span({class=>'error'},
182 CGI::strong('Error: ').join("\n",@_)));
185 # Used in place of CGI::hidden, which is broken in some versions.
186 # Assumes $name does not need to be encoded
187 # HTML encodes the value
189 my ($this, $name, $value) = @_;
191 $value =~ s/([[\x01-\x09\x0b\x0c\x0e-\x1f"%&'*<=>@[_\|])/'&#'.ord($1).';'/ge;
192 return '<input type="hidden" name="'.$name.'" value="'.$value.'" />';
195 # Invoked to confirm authorisation, and handle password changes. The password
196 # is changed in $TWiki::cfg, a change which is then detected and written when
197 # the configuration file is actually saved.
199 my $pass = $TWiki::query->param('cfgAccess');
201 # The first time we get here is after the "next" button is hit. A password
202 # won't have been defined yet; so the authorisation must fail to force
204 if (!defined($pass)) {
208 # If we get this far, a password has been given. Check it.
209 if (!$TWiki::cfg{Password} && !$TWiki::query->param('confCfgP')) {
210 # No password passed in, and TWiki::cfg doesn't contain a password
211 print CGI::div({class=>'error'}, <<'HERE');
212 WARNING: You have not defined a password. You must define a password before
218 # If a password has been defined, check that it has been used
219 if ($TWiki::cfg{Password} &&
220 crypt($pass, $TWiki::cfg{Password}) ne $TWiki::cfg{Password}) {
221 print CGI::div({class=>'error'}, "Password incorrect");
225 # Password is correct, or no password defined
226 # Change the password if so requested
227 my $newPass = $TWiki::query->param('newCfgP');
230 my $confPass = $TWiki::query->param('confCfgP') || '';
231 if ($newPass ne $confPass) {
232 print CGI::div({class=>'error'},
233 'New password and confirmation do not match');
236 $TWiki::cfg{Password} = _encode($newPass);
237 print CGI::div({class=>'error'}, 'Password changed');
244 sub collectMessages {
248 my $warnings = $item->{warnings} || 0;
249 my $errors = $item->{errors} || 0;
250 my $errorsMess = "$errors error" . (($errors > 1) ? 's' : '');
251 my $warningsMess = "$warnings warning" . (($warnings > 1) ? 's' : '');
253 $mess .= ' ' . CGI::span({class=>'error'}, $errorsMess) if $errors;
254 $mess .= ' ' . CGI::span({class=>'warn'}, $warningsMess) if $warnings;
261 my @saltchars = ( 'a'..'z', 'A'..'Z', '0'..'9', '.', '/' );
262 my $salt = $saltchars[int(rand($#saltchars+1))] .
263 $saltchars[int(rand($#saltchars+1)) ];
264 return crypt($pass, $salt);