1
# See bottom of file for license and copyright information
2
package Foswiki::Configure::UIs::EXTENSIONS;
3
use base 'Foswiki::Configure::UI';
6
use Foswiki::Configure::Type;
8
# THE FOLLOWING MUST BE MAINTAINED CONSISTENT WITH Extensions.FastReport
9
# They describe the format of an extension topic.
11
qw( topic classification description version installedVersion compatibility install );
12
my $VERSION_LINE = qr/\n\|[\s\w-]*\s[Vv]ersion:\s*\|([^|]+)\|/;
16
classification => 'Classification',
17
description => 'Description',
18
version => 'Most Recent Version',
19
installedVersion => 'Installed Version',
20
compatibility => 'Compatible with',
24
my @MNAMES = qw(jan feb mar apr may jun jul aug sep oct nov dec);
25
my $mnamess = join( '|', @MNAMES );
26
my $MNAME = qr/$mnamess/i;
28
foreach ( 0 .. $#MNAMES ) { $N2M{ $MNAMES[$_] } = $_; }
30
# Convert a date in the formats dd mm yyyy or dd Mmm yyyy to a unique integer
32
my ( $d, $m, $y ) = @_;
33
return ( $y * 12 + $m ) * 31 + $d;
36
# Download the report page from the repository, and extract a hash of
37
# available extensions
38
sub _getListOfExtensions {
41
$this->findRepositories();
43
if ( !$this->{list} ) {
46
foreach my $place ( @{ $this->{repositories} } ) {
47
next unless defined $place->{data};
48
$place->{data} =~ s#/*$#/#;
49
print CGI::div("Consulting $place->{name}...");
50
my $url = $place->{data} . 'FastReport?skin=text';
51
my $response = $this->getUrl($url);
52
if ( !$response->is_error() ) {
53
my $page = $response->content();
55
$page =~ s/{(.*?)}/$this->_parseRow($1, $place)/ges;
59
"Error accessing $place->{name}: no content"
66
"Error accessing $place->{name}: " . $response->message()
69
#see if its because LWP isn't installed..
74
"This may be because the LWP CPAN module isn't installed."
84
my ( $this, $row, $place ) = @_;
86
return '' unless defined $row;
87
return '' unless $row =~ s/^ *(\w+): *(.*?) *$/$data{$1} = $2;''/gem;
88
( $data{installedVersion}, $data{namespace} ) =
89
$this->_getInstalledVersion( $data{topic} );
90
$data{repository} = $place->{name};
91
$data{data} = $place->{data};
92
$data{pub} = $place->{pub};
93
die "$row: " . Data::Dumper->Dump( [ \%data ] ) unless $data{topic};
94
$this->{list}->{ $data{topic} } = \%data;
104
my $exts = $this->_getListOfExtensions();
105
foreach my $error ( @{ $this->{errors} } ) {
106
$table .= CGI::Tr( { class => 'foswikiAlert' },
107
CGI::td( { colspan => "7" }, $error ) );
112
map { CGI::th( { valign => 'bottom' }, $headNames{$_} ) }
115
foreach my $key ( sort keys %$exts ) {
116
my $ext = $exts->{$key};
119
foreach my $f (@tableHeads) {
121
if ( $f eq 'install' ) {
122
my @script = File::Spec->splitdir( $ENV{SCRIPT_NAME} );
123
my $scriptName = pop(@script);
124
$scriptName =~ s/.*[\/\\]//; # Fix for Item3511, on Win XP
128
. '?action=InstallExtension'
134
if ( $ext->{installedVersion} ) {
135
if ( $ext->{installedVersion} eq 'HEAD' ) {
137
# Unexpanded, assume pseudo-installed
139
$text = 'pseudo-installed';
140
$ext->{cssclass} = 'pseudoinstalled';
142
elsif ( $ext->{installedVersion} =~
143
/^\s*v?(\d+)\.(\d+)(?:\.(\d+))/ )
146
# X.Y, X.Y.Z, vX.Y, vX.Y.Z
147
# Combine into one number; allows up to 1000
149
my $irev = ( $1 * 1000 + $2 ) * 1000 + $3;
150
$text = 'Re-install';
151
$ext->{cssclass} = 'reinstall';
152
if ( $ext->{version} =~
153
/^\s*v?(\d+)\.(\d+)(?:\.(\d+))?/ ) {
155
# Compatible version number
156
my $arev = ( $1 * 1000 + $2 ) * 1000 + ($3 || 0);
157
if ( $arev > $irev ) {
159
$ext->{cssclass} = 'upgrade';
163
elsif ( $ext->{installedVersion} =~ /^\s*(\d+)\s/ ) {
167
$text = 'Re-install';
168
$ext->{cssclass} = 'reinstall';
169
if ( defined $ext->{version} &&
170
$ext->{version} =~ /^\s*(\d+)\s/ ) {
172
if ( $availrev > $gotrev ) {
174
$ext->{cssclass} = 'upgrade';
178
elsif ( $ext->{installedVersion} =~
179
/(\d{4})-(\d\d)-(\d\d)/ ) {
181
my $idate = d2n( $3, $2, $1 );
182
$text = 'Re-install';
183
$ext->{cssclass} = 'reinstall';
184
if ( defined $ext->{version} &&
185
$ext->{version} =~ /(\d{4})-(\d\d)-(\d\d)/ ) {
186
my $adate = d2n( $3, $2, $1 );
187
if ( $adate > $idate ) {
189
$ext->{cssclass} = 'upgrade';
193
elsif ( $ext->{installedVersion} =~
194
/(\d{1,2}) ($MNAME) (\d{4})/ ) {
197
my $idate = d2n( $1, $N2M{lc($2)}, $3 );
198
$text = 'Re-install';
199
$ext->{cssclass} = 'reinstall';
200
if ( defined $ext->{version} &&
202
/(\d{1,2}) ($MNAME) (\d{4})/ ) {
203
my $adate = d2n( $1, $N2M{lc($2)}, $3 );
204
if ( $adate > $idate ) {
206
$ext->{cssclass} = 'upgrade';
213
$text = CGI::a( { href => $link }, $text );
217
$text = $ext->{$f} || '-';
218
$text =~ s/!(\w+)/$1/go; # remove ! escape syntax from text
219
if ( $f eq 'topic' ) {
220
my $link = $ext->{data} . $ext->{topic};
221
$text = CGI::a( { href => $link }, $text );
226
&& $ext->{namespace} ne 'Foswiki' )
228
$text = "$text ($ext->{namespace})";
232
my %opts = ( valign => 'top' );
233
if ( $ext->{namespace} && $ext->{namespace} ne 'Foswiki' ) {
234
$opts{class} = 'alienExtension';
236
$row .= CGI::td( \%opts, $text );
238
my @classes = ( $rows % 2 ? 'odd' : 'even' );
239
if ( $ext->{installedVersion} ) {
240
push @classes, 'installed';
241
push( @classes, $ext->{cssclass} ) if ($ext->{cssclass});
242
push @classes, 'twikiExtension'
243
if $ext->{installedVersion} =~ /\(TWiki\)/;
245
$table .= CGI::Tr( { class => join( ' ', @classes ) }, $row );
249
{ class => 'patternAccessKeyInfo' },
254
. ( $installed == 1 ? '' : 's' )
257
. ' already installed'
261
<div class="foswikiHelp">Note that the webserver user has to be able to
262
write files everywhere in your Foswiki installation. Otherwise you may see
263
'No permission to write' errors during extension installation.</div>
265
$page .= CGI::table( { class => 'foswikiTable extensionsTable' }, $table );
269
sub _getInstalledVersion {
270
my ( $this, $module ) = @_;
273
return undef unless $module;
275
if ( $module =~ /Plugin$/ ) {
282
# See if we have a compileable module
285
foreach $from qw(Foswiki TWiki) {
286
my $path = $from . '::' . $lib . '::' . $module;
287
eval "require $path";
289
$compileable = 1; # found the module
294
# Now scrape the version information from the .txt
298
my $web ( split( /[, ]+/, $Foswiki::cfg{Plugins}{WebSearchPath} ) )
301
# SMELL: can't use Foswiki store to do this lookup; relying on
302
# directories. Not a problem right now, but in the future.....
303
my $path = "$Foswiki::cfg{DataDir}/$web/$module.txt";
306
if ( -e $path && open( $fh, '<', $path ) ) {
308
if ( defined $ text && $text =~ /$VERSION_LINE/s ) {
310
$release = 'HEAD' if $release =~ /%\$VERSION%/;
318
return ( $release, $from );
324
# Foswiki - The Free and Open Source Wiki, http://foswiki.org/
326
# Copyright (C) 2008 Foswiki Contributors. All Rights Reserved.
327
# Foswiki Contributors are listed in the AUTHORS file in the root
328
# of this distribution. NOTE: Please extend that file, not this notice.
330
# Additional copyrights apply to some or all of the code in this
333
# Copyright (C) 2000-2006 TWiki Contributors. All Rights Reserved.
334
# TWiki Contributors are listed in the AUTHORS file in the root
335
# of this distribution. NOTE: Please extend that file, not this notice.
337
# This program is free software; you can redistribute it and/or
338
# modify it under the terms of the GNU General Public License
339
# as published by the Free Software Foundation; either version 2
340
# of the License, or (at your option) any later version. For
341
# more details read LICENSE in the root of this distribution.
343
# This program is distributed in the hope that it will be useful,
344
# but WITHOUT ANY WARRANTY; without even the implied warranty of
345
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
347
# As per the GPL, removal of this notice is prohibited.