line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package OpenInteract::Package; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# $Id: Package.pm,v 1.40 2003/01/25 16:16:07 lachoy Exp $ |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# This module manipulates information from individual packages to |
6
|
|
|
|
|
|
|
# perform some action in the package files. |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
34
|
|
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
1227
|
use Archive::Tar (); |
|
1
|
|
|
|
|
380322
|
|
|
1
|
|
|
|
|
32
|
|
11
|
1
|
|
|
1
|
|
14
|
use Cwd qw( cwd ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
74
|
|
12
|
1
|
|
|
1
|
|
6
|
use Data::Dumper qw( Dumper ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
13
|
1
|
|
|
1
|
|
3264
|
use ExtUtils::Manifest (); |
|
1
|
|
|
|
|
17006
|
|
|
1
|
|
|
|
|
30
|
|
14
|
1
|
|
|
1
|
|
11
|
use File::Basename (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
20
|
|
15
|
1
|
|
|
1
|
|
5
|
use File::Copy qw( cp ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
63
|
|
16
|
1
|
|
|
1
|
|
5
|
use File::Path (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
15
|
|
17
|
1
|
|
|
1
|
|
541
|
use SPOPS::HashFile (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use SPOPS::Utility (); |
19
|
|
|
|
|
|
|
require Exporter; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
@OpenInteract::Package::ISA = qw( Exporter ); |
22
|
|
|
|
|
|
|
$OpenInteract::Package::VERSION = sprintf("%d.%02d", q$Revision: 1.40 $ =~ /(\d+)\.(\d+)/); |
23
|
|
|
|
|
|
|
@OpenInteract::Package::EXPORT_OK = qw( READONLY_FILE ); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
use constant READONLY_FILE => '.no_overwrite'; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Define the subdirectories present in a package |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my @PKG_SUBDIR = qw( conf data doc struct template script html html/images ); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# Fields in our package/configuration |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my @PKG_FIELDS = qw( name version author url description notes |
34
|
|
|
|
|
|
|
module template_plugin template_block filter |
35
|
|
|
|
|
|
|
base_dir website_dir package_dir website_name |
36
|
|
|
|
|
|
|
dependency script_install script_upgrade |
37
|
|
|
|
|
|
|
script_uninstall sql_installer installed_on |
38
|
|
|
|
|
|
|
installed_by last_updated_on last_updated_by ); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Name of the package configuration file, always found in the |
42
|
|
|
|
|
|
|
# package's root directory |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $DEFAULT_CONF_FILE = 'package.conf'; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Define the keys in 'package.conf' that can be a list, meaning you |
47
|
|
|
|
|
|
|
# can have multiple items defined: |
48
|
|
|
|
|
|
|
# |
49
|
|
|
|
|
|
|
# author Larry Wall |
50
|
|
|
|
|
|
|
# author Chris Winters |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my %CONF_LIST_KEYS = map { $_ => 1 } |
53
|
|
|
|
|
|
|
qw( author script_install script_upgrade script_uninstall module ); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Define the keys in 'package.conf' that can be a hash, meaning that |
56
|
|
|
|
|
|
|
# you can have items defined as multiple key-value pairs |
57
|
|
|
|
|
|
|
# (space-separated): |
58
|
|
|
|
|
|
|
# |
59
|
|
|
|
|
|
|
# dependency base_linked 1.09 |
60
|
|
|
|
|
|
|
# dependency static_page 1.18 |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my %CONF_HASH_KEYS = map { $_ => 1 } qw( dependency template_plugin template_block filter ); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# For exporting a package, the following variables are required in |
65
|
|
|
|
|
|
|
# 'package.conf' |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my @EXPORT_REQUIRED = qw( name version ); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Global for holding Archive::Tar errors |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
my $ARCHIVE_ERROR = undef; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Fields NOT to copy over in conf/spops.perl when creating package in |
74
|
|
|
|
|
|
|
# website from base installation (the first three are ones we |
75
|
|
|
|
|
|
|
# manipulate by hand) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
my %SPOPS_CONF_KEEP = map { $_ => 1 } qw( class has_a links_to ); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# These are the default public and site admin group IDs; we use them |
80
|
|
|
|
|
|
|
# when copying over the SPOPS configuration files (see |
81
|
|
|
|
|
|
|
# _copy_spops_config_file()) |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
use constant PUBLIC_GROUP_ID => 2; |
84
|
|
|
|
|
|
|
use constant SITE_ADMIN_GROUP_ID => 3; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
use constant DEBUG => 0; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# Create subdirectories for a package. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub create_subdirectories { |
92
|
|
|
|
|
|
|
my ( $class, $dir, $main_class ) = @_; |
93
|
|
|
|
|
|
|
$main_class ||= 'OpenInteract'; |
94
|
|
|
|
|
|
|
return undef unless ( -d $dir ); |
95
|
|
|
|
|
|
|
foreach my $sub_dir ( @PKG_SUBDIR, $main_class, |
96
|
|
|
|
|
|
|
"$main_class/Handler", |
97
|
|
|
|
|
|
|
"$main_class/SQLInstall" ) { |
98
|
|
|
|
|
|
|
mkdir( "$dir/$sub_dir", 0775 ) |
99
|
|
|
|
|
|
|
|| die "Cannot create package subdirectory $dir/$sub_dir: $!"; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
return 1; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Creates a package directories using our base subdirectories |
106
|
|
|
|
|
|
|
# along with a package.conf file and some other goodies (?) |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub create_skeleton { |
109
|
|
|
|
|
|
|
my ( $class, $repository, $name ) = @_; |
110
|
|
|
|
|
|
|
my $pwd = cwd; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $cleaned_pkg = $class->_clean_package_name( $name ); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Check directories |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
unless ( $repository ) { |
117
|
|
|
|
|
|
|
die "Cannot create package skeleton: no existing base ", |
118
|
|
|
|
|
|
|
"installation repository specified!\n"; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $base_dir = $repository->{META_INF}{base_dir}; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
if ( -d $cleaned_pkg ) { |
124
|
|
|
|
|
|
|
die "Cannot create package skeleton: directory ($cleaned_pkg) already exists!\n"; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
mkdir( $cleaned_pkg, 0775 ) || die "Cannot create package directory $cleaned_pkg: $!\n"; |
127
|
|
|
|
|
|
|
chdir( $cleaned_pkg ); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# Then create the subdirectories for the package |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
$class->create_subdirectories( '.' ); |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# This does a replacement so that 'static_page' becomes StaticPage |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
my $uc_first_name = ucfirst $cleaned_pkg; |
136
|
|
|
|
|
|
|
$uc_first_name =~ s/_(\w)/\U$1\U/g; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Copy over files from the samples (located in the base OpenInteract |
139
|
|
|
|
|
|
|
# directory), doing replacements as necessary |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
$class->replace_and_copy({ from_file => "$base_dir/conf/sample-package.conf", |
142
|
|
|
|
|
|
|
to_file => "package.conf", |
143
|
|
|
|
|
|
|
from_text => [ '%%NAME%%', '%%UC_FIRST_NAME%%' ], |
144
|
|
|
|
|
|
|
to_text => [ $cleaned_pkg, $uc_first_name ] }); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
$class->replace_and_copy({ from_file => "$base_dir/conf/sample-package.pod", |
147
|
|
|
|
|
|
|
to_file => "doc/$cleaned_pkg.pod", |
148
|
|
|
|
|
|
|
from_text => [ '%%NAME%%' ], |
149
|
|
|
|
|
|
|
to_text => [ $cleaned_pkg ] }); |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
$class->replace_and_copy({ from_file => "$base_dir/conf/sample-doc-titles", |
152
|
|
|
|
|
|
|
to_file => "doc/titles", |
153
|
|
|
|
|
|
|
from_text => [ '%%NAME%%' ], |
154
|
|
|
|
|
|
|
to_text => [ $cleaned_pkg ] }); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
$class->replace_and_copy({ from_file => "$base_dir/conf/sample-SQLInstall.pm", |
157
|
|
|
|
|
|
|
to_file => "OpenInteract/SQLInstall/$uc_first_name.pm", |
158
|
|
|
|
|
|
|
from_text => [ '%%NAME%%', '%%UC_FIRST_NAME%%' ], |
159
|
|
|
|
|
|
|
to_text => [ $cleaned_pkg, $uc_first_name ] }); |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
$class->replace_and_copy({ from_file => "$base_dir/conf/sample-Handler.pm", |
162
|
|
|
|
|
|
|
to_file => "OpenInteract/Handler/$uc_first_name.pm", |
163
|
|
|
|
|
|
|
from_text => [ '%%NAME%%', '%%UC_FIRST_NAME%%' ], |
164
|
|
|
|
|
|
|
to_text => [ $cleaned_pkg, $uc_first_name ] }); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
cp( "$base_dir/conf/sample-spops.perl", "conf/spops.perl" ) |
167
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy sample (conf/spops.perl): $!" ); |
168
|
|
|
|
|
|
|
cp( "$base_dir/conf/sample-action.perl", "conf/action.perl" ) |
169
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy sample (conf/action.perl): $!" ); |
170
|
|
|
|
|
|
|
cp( "$base_dir/conf/sample-MANIFEST.SKIP", "MANIFEST.SKIP" ) |
171
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy sample (MANIFEST.SKIP): $!" ); |
172
|
|
|
|
|
|
|
cp( "$base_dir/conf/sample-dummy-template.meta", "template/dummy.meta" ) |
173
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy sample (template/dummy.meta): $!" ); |
174
|
|
|
|
|
|
|
cp( "$base_dir/conf/sample-dummy-template.tmpl", "template/dummy.tmpl" ) |
175
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy sample (template/dummy.tmpl): $!" ); |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# Create a 'Changes' file |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
eval { open( CHANGES, "> Changes" ) || die $! }; |
180
|
|
|
|
|
|
|
if ( $@ ) { |
181
|
|
|
|
|
|
|
_w( 0, "Cannot open 'Changes' file ($!). Please create your ", |
182
|
|
|
|
|
|
|
"own so people can follow your progress." ); |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
else { |
185
|
|
|
|
|
|
|
my $time_stamp = scalar localtime; |
186
|
|
|
|
|
|
|
print CHANGES <
|
187
|
|
|
|
|
|
|
Revision history for OpenInteract package $cleaned_pkg. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
0.01 $time_stamp |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Package skeleton created by oi_manage |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
INIT |
194
|
|
|
|
|
|
|
close( CHANGES ); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Create a MANIFEST from the pwd |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
$class->_create_manifest(); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Go back to the original dir and return the name |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
chdir( $pwd ); |
204
|
|
|
|
|
|
|
return $cleaned_pkg; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Rules for a clean package name: |
209
|
|
|
|
|
|
|
# - Package name cannot have spaces (s/ /_/) |
210
|
|
|
|
|
|
|
# - Package name cannot have dashes (s/-/_/) |
211
|
|
|
|
|
|
|
# - Package name cannot start with a number (die) |
212
|
|
|
|
|
|
|
# - Package name cannot have nonword characters except '_' |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub _clean_package_name { |
215
|
|
|
|
|
|
|
my ( $class, $name ) = @_; |
216
|
|
|
|
|
|
|
my ( @clean_actions, @die_actions ); |
217
|
|
|
|
|
|
|
$name =~ s/ /_/g && push @clean_actions, "Name must not have spaces"; |
218
|
|
|
|
|
|
|
$name =~ s/\-/_/g && push @clean_actions, "Name must not have dashes"; |
219
|
|
|
|
|
|
|
$name =~ /^\d/ && push @die_actions, "Name must not start with a number"; |
220
|
|
|
|
|
|
|
$name =~ /\W/ && push @die_actions, "Name must not have non-word characters"; |
221
|
|
|
|
|
|
|
if ( scalar @die_actions ) { |
222
|
|
|
|
|
|
|
die "Package name unacceptable: \n", |
223
|
|
|
|
|
|
|
join( "\n", @die_actions, @clean_actions ), "\n"; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
return $name; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# Takes a package file and installs the package to the base |
230
|
|
|
|
|
|
|
# OpenInteract directory. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub install_distribution { |
233
|
|
|
|
|
|
|
my ( $class, $p ) = @_; |
234
|
|
|
|
|
|
|
my $old_pwd = cwd; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# ------------------------------ |
237
|
|
|
|
|
|
|
# Taken from CGI.pm |
238
|
|
|
|
|
|
|
# FIGURE OUT THE OS WE'RE RUNNING UNDER |
239
|
|
|
|
|
|
|
# Some systems support the $^O variable. If not |
240
|
|
|
|
|
|
|
# available then require() the Config library |
241
|
|
|
|
|
|
|
my $OS = undef; |
242
|
|
|
|
|
|
|
unless ( $OS = $^O ) { |
243
|
|
|
|
|
|
|
require Config; |
244
|
|
|
|
|
|
|
$OS = $Config::Config{'osname'}; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
# ------------------------------ |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
unless ( -f $p->{package_file} ) { |
249
|
|
|
|
|
|
|
die "Package file for installation ($p->{package_file}) does not exist\n"; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# TODO: Use File::Spec for this? |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# Note that this should NOT be just 'win' since 'Darwin' gives a |
255
|
|
|
|
|
|
|
# (very) false positive |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
if ( $OS =~ /Win32/i ) { |
258
|
|
|
|
|
|
|
unless ( $p->{package_file} =~ /^\w:\// ) { |
259
|
|
|
|
|
|
|
$p->{package_file} = join( '/', $old_pwd, $p->{package_file} ); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
else { |
263
|
|
|
|
|
|
|
unless ( $p->{package_file} =~ /^\// ) { |
264
|
|
|
|
|
|
|
$p->{package_file} = join( '/', $old_pwd, $p->{package_file} ); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
DEBUG && _w( 1, "Package file used for distribution: ($p->{package_file}" ); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# This is the repository we'll be using |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
my $repos = $p->{repository} || |
272
|
|
|
|
|
|
|
eval { OpenInteract::PackageRepository->fetch( |
273
|
|
|
|
|
|
|
undef, { directory => $p->{base_dir}, |
274
|
|
|
|
|
|
|
perm => 'write' } ) }; |
275
|
|
|
|
|
|
|
unless ( $repos ) { die "Cannot open repository: $@\n" } |
276
|
|
|
|
|
|
|
my $base_dir = $repos->{META_INF}{base_dir}; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
my $base_package_file = File::Basename::basename( $p->{package_file} ); |
279
|
|
|
|
|
|
|
my ( $package_base ) = $base_package_file =~ /^(.*)\.tar\.gz$/; |
280
|
|
|
|
|
|
|
DEBUG && _w( 1, "Package base: $package_base" ); |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my $rv = $class->_extract_archive( $p->{package_file} ); |
283
|
|
|
|
|
|
|
unless ( $rv ) { |
284
|
|
|
|
|
|
|
my $msg = "Error found trying to unpack the distribution! " . |
285
|
|
|
|
|
|
|
"Error: " . $ARCHIVE_ERROR; |
286
|
|
|
|
|
|
|
my $removed_files = $class->_remove_directory_tree( $package_base ); |
287
|
|
|
|
|
|
|
die $msg; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Read in the package config and grab the name/version |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
chdir( $package_base ); |
293
|
|
|
|
|
|
|
DEBUG && _w( 1, "Trying to find config file in ($package_base/)" ); |
294
|
|
|
|
|
|
|
my $conf_file = $p->{package_conf_file} || $DEFAULT_CONF_FILE; |
295
|
|
|
|
|
|
|
my $conf = $class->read_config({ file => $conf_file }); |
296
|
|
|
|
|
|
|
die "No valid package config read!\n" unless ( scalar keys %{ $conf } ); |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $name = $conf->{name}; |
299
|
|
|
|
|
|
|
my $version = $conf->{version}; |
300
|
|
|
|
|
|
|
chdir( $old_pwd ); |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# We're all done with the temp stuff, so get rid of it. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
my $removed_files = $class->_remove_directory_tree( $package_base ); |
305
|
|
|
|
|
|
|
DEBUG && _w( 2, "Removed extracted tree, config file found ok." ); |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Check to see if the package/version already exists |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
my $error_msg = undef; |
310
|
|
|
|
|
|
|
my $exist_info = $repos->fetch_package_by_name({ name => $name, |
311
|
|
|
|
|
|
|
version => $version }); |
312
|
|
|
|
|
|
|
if ( $exist_info ) { |
313
|
|
|
|
|
|
|
die "Cannot install since package $name-$version already " . |
314
|
|
|
|
|
|
|
"exists in the base installation repository. (It was installed on " . |
315
|
|
|
|
|
|
|
"$exist_info->{installed_on}).\n\nAborting package installation.\n"; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
DEBUG && _w( 1, "Package does not currently exist in repository." ); |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Now see if the package has specified any modules that are |
320
|
|
|
|
|
|
|
# necessary for its operation. For now, we will refuse to install |
321
|
|
|
|
|
|
|
# a package that does not have supporting modules. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
if ( ref $conf->{module} eq 'ARRAY' ) { |
324
|
|
|
|
|
|
|
my @failed_modules = $class->_check_module_install( @{ $conf->{module} } ); |
325
|
|
|
|
|
|
|
if ( scalar @failed_modules ) { |
326
|
|
|
|
|
|
|
die "Package $name-$version requires the following modules " . |
327
|
|
|
|
|
|
|
"that are not currently installed: " . join( ', ', @failed_modules ) . |
328
|
|
|
|
|
|
|
". Please install them and try again.\n"; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# Create some directory names and move to the base package directory |
333
|
|
|
|
|
|
|
# -- the directory that holds all of the package definitions |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
my $new_pkg_dir = join( '/', 'pkg', "$name-$version" ); |
336
|
|
|
|
|
|
|
my $full_pkg_dir = join( '/', $base_dir, $new_pkg_dir ); |
337
|
|
|
|
|
|
|
if ( -d $full_pkg_dir ) { |
338
|
|
|
|
|
|
|
die "The directory into which the distribution should be unpacked ", |
339
|
|
|
|
|
|
|
"($full_pkg_dir) already exists. Please remove it and try again.\n"; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
chdir( join( '/', $base_dir, 'pkg' ) ); |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# Unarchive the package; note that since the archive creates a |
344
|
|
|
|
|
|
|
# directory name-version/blah we don't need to create the directory |
345
|
|
|
|
|
|
|
# ourselves and then chdir() to it. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
my $extract_rv = $class->_extract_archive( $p->{package_file} ); |
348
|
|
|
|
|
|
|
unless ( $extract_rv ) { |
349
|
|
|
|
|
|
|
chdir( $base_dir ); |
350
|
|
|
|
|
|
|
$class->_remove_directory_tree( $full_pkg_dir ); |
351
|
|
|
|
|
|
|
die "Cannot unpack the distribution into its final " . |
352
|
|
|
|
|
|
|
"directory ($full_pkg_dir)! Error: " . $ARCHIVE_ERROR; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
DEBUG && _w( 1, "Unpackaged package into $base_dir/pkg ok" ); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# Create the package info and try to save; if we're successful, return the |
357
|
|
|
|
|
|
|
# package info. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
my $info = { |
360
|
|
|
|
|
|
|
base_dir => $base_dir, |
361
|
|
|
|
|
|
|
package_dir => $new_pkg_dir, |
362
|
|
|
|
|
|
|
installed_on => $repos->now }; |
363
|
|
|
|
|
|
|
foreach my $conf_field ( keys %{ $conf } ) { |
364
|
|
|
|
|
|
|
$info->{ $conf_field } = $conf->{ $conf_field }; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
DEBUG && _w( 1, "Trying to save package info: ", Dumper( $info ) ); |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
$repos->save_package( $info ); |
369
|
|
|
|
|
|
|
eval { $repos->save() }; |
370
|
|
|
|
|
|
|
if ( $@ ) { |
371
|
|
|
|
|
|
|
chdir( $base_dir ); |
372
|
|
|
|
|
|
|
$class->_remove_directory_tree( $full_pkg_dir ); |
373
|
|
|
|
|
|
|
die "Could not save data to installed package database. " . |
374
|
|
|
|
|
|
|
"Error returned: $@ " . |
375
|
|
|
|
|
|
|
"Aborting package installation."; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
DEBUG && _w( 1, "Saved repository ok." ); |
378
|
|
|
|
|
|
|
chdir( $old_pwd ); |
379
|
|
|
|
|
|
|
return $info; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# Install a package from the base OpenInteract directory to a website |
384
|
|
|
|
|
|
|
# directory. This is known in 'oi_manage' terms as 'applying' a |
385
|
|
|
|
|
|
|
# package. Note that if you're upgrading the app calling this module |
386
|
|
|
|
|
|
|
# must first get rid of the old package. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub install_to_website { |
389
|
|
|
|
|
|
|
my ( $class, $base_repository, $website_repository, $info, $CONFIG ) = @_; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Be sure to have the website directory, website name, and package |
392
|
|
|
|
|
|
|
# directory set |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
unless ( $info->{website_name} ) { |
395
|
|
|
|
|
|
|
die "Website name not set in package object.\n"; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
my $package_name_version = "$info->{name}-$info->{version}"; |
398
|
|
|
|
|
|
|
$info->{website_dir} ||= $website_repository->{META_INF}{base_dir}; |
399
|
|
|
|
|
|
|
$info->{package_dir} ||= join( '/', 'pkg', $package_name_version ); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Then create package directory within the website directory |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my $pkg_dir = join( '/', $info->{website_dir}, $info->{package_dir} ); |
404
|
|
|
|
|
|
|
if ( -d $pkg_dir ) { die "Package directory $pkg_dir already exists.\n" } |
405
|
|
|
|
|
|
|
mkdir( $pkg_dir, 0775 ) || die "Cannot create $pkg_dir : $!"; |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Next move to the base package directory (we return to the original |
408
|
|
|
|
|
|
|
# directory just before the routine exits) |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
my $pwd = cwd; |
411
|
|
|
|
|
|
|
chdir( "$info->{base_dir}/pkg/$package_name_version" ); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
# ...then ensure that it has all its files |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my @missing = ExtUtils::Manifest::manicheck; |
416
|
|
|
|
|
|
|
if ( scalar @missing ) { |
417
|
|
|
|
|
|
|
die "Cannot install package $info->{name}-$info->{version} to website ", |
418
|
|
|
|
|
|
|
"-- the base package has files that are specified in MANIFEST missing ", |
419
|
|
|
|
|
|
|
"from the filesystem: @missing. Please fix the situation.\n"; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# ...and get all the filenames from MANIFEST |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
my $BASE_FILES = ExtUtils::Manifest::maniread; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# Now create the subdirectories and copy the configs |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
$class->create_subdirectories( $pkg_dir, $info->{website_name} ); |
429
|
|
|
|
|
|
|
$class->_copy_spops_config_file( $info, $CONFIG, 'spops.perl' ); |
430
|
|
|
|
|
|
|
$class->_copy_spops_config_file( $info, $CONFIG, 'spops.perl.ldap' ); |
431
|
|
|
|
|
|
|
$class->_copy_action_config_file( $info, $CONFIG ); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Now copy over the struct/, script/, data/, template/, html/, |
434
|
|
|
|
|
|
|
# html/images/ and doc/ files -- intact with no translations, as |
435
|
|
|
|
|
|
|
# long as they appear in the MANIFEST file (read in earlier) |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# The value of the subdir key is the root where the files will be |
438
|
|
|
|
|
|
|
# copied -- so files in the 'widget' directory of the package will |
439
|
|
|
|
|
|
|
# be copied to the 'template/' subdirectory of the website, while |
440
|
|
|
|
|
|
|
# the files in the 'data' directory of the package will be copied |
441
|
|
|
|
|
|
|
# to the 'data' directory of the *package* in the website. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
my %subdir_match = ( |
444
|
|
|
|
|
|
|
struct => "$pkg_dir/struct", |
445
|
|
|
|
|
|
|
data => "$pkg_dir/data", |
446
|
|
|
|
|
|
|
template => "$pkg_dir/template", |
447
|
|
|
|
|
|
|
widget => "$info->{website_dir}/template", |
448
|
|
|
|
|
|
|
doc => "$pkg_dir/doc", |
449
|
|
|
|
|
|
|
script => "$pkg_dir/script", |
450
|
|
|
|
|
|
|
html => "$info->{website_dir}/html" ); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
my $pkg_file_list = [ keys %{ $BASE_FILES } ]; |
453
|
|
|
|
|
|
|
foreach my $sub_dir ( sort keys %subdir_match ) { |
454
|
|
|
|
|
|
|
$class->_copy_package_files( $subdir_match{ $sub_dir }, |
455
|
|
|
|
|
|
|
$sub_dir, |
456
|
|
|
|
|
|
|
$pkg_file_list ); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
######################################## |
460
|
|
|
|
|
|
|
# TODO: For each file copied over to the /html directory, create a |
461
|
|
|
|
|
|
|
# 'page' object in the system for it. Note that we might have to |
462
|
|
|
|
|
|
|
# hook this up with the system that ensures we don't overwrite |
463
|
|
|
|
|
|
|
# certain files. So we might need to either remove it from the |
464
|
|
|
|
|
|
|
# _copy_package_files() routine, or add an argument to that |
465
|
|
|
|
|
|
|
# routine that lets us pass in a coderef to execute with every |
466
|
|
|
|
|
|
|
# item copied over. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# ACK -- here's a problem. We don't know if we can even create an |
469
|
|
|
|
|
|
|
# $R yet, because (1) the base_page package might not have even |
470
|
|
|
|
|
|
|
# been installed yet (when creating a website) and (2) the user |
471
|
|
|
|
|
|
|
# hasn't yet configured the database (etc.) |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# We can get around this whenever we rewrite |
474
|
|
|
|
|
|
|
# Package/PackageRepository/oi_manage, but until then we will tell |
475
|
|
|
|
|
|
|
# people to include the relevant data inserts with packages that |
476
|
|
|
|
|
|
|
# include HTML documents. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# Until then, here's what this might look like :-) |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# # Now do the HTML files, but also create records for each of the |
481
|
|
|
|
|
|
|
# # HTML files in the 'page' table |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# my $copied = $class->_copy_package_files( "$info->{website_dir}/html", |
484
|
|
|
|
|
|
|
# 'html', |
485
|
|
|
|
|
|
|
# $pkg_file_list ); |
486
|
|
|
|
|
|
|
# my @html_locations = map { s/^html//; $_ } @{ $copied }; |
487
|
|
|
|
|
|
|
# foreach my $location ( @html_locations ) { |
488
|
|
|
|
|
|
|
# my $page = $R->page->fetch( $location, { skip_security => 1 } ); |
489
|
|
|
|
|
|
|
# next if ( $page ); |
490
|
|
|
|
|
|
|
# eval { |
491
|
|
|
|
|
|
|
# $R->page->new({ location => $location, |
492
|
|
|
|
|
|
|
# ... }) |
493
|
|
|
|
|
|
|
# ->save({ skip_security => 1 }); |
494
|
|
|
|
|
|
|
# }; |
495
|
|
|
|
|
|
|
# } |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# Now copy the MANIFEST.SKIP file and package.conf, so we can run |
498
|
|
|
|
|
|
|
# 'check_package' on the package directory (once complete) as well as |
499
|
|
|
|
|
|
|
# generate a MANIFEST once we're done copying files |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
foreach my $root_file ( 'MANIFEST.SKIP', 'package.conf' ) { |
502
|
|
|
|
|
|
|
cp( $root_file, "$pkg_dir/$root_file" ) |
503
|
|
|
|
|
|
|
|| _w( 0, "Cannot copy $root_file to $pkg_dir/$root_file : $!" ); |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
$class->_copy_handler_files( $info, $BASE_FILES ); |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# Now go to our package directory and create a new MANIFEST file |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
chdir( $pkg_dir ); |
511
|
|
|
|
|
|
|
$class->_create_manifest(); |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# Finally, save this package information to the site |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
$website_repository->save_package( $info ); |
516
|
|
|
|
|
|
|
$website_repository->save(); |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
chdir( $pwd ); |
519
|
|
|
|
|
|
|
return $pkg_dir; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
# Dump the package from the current directory (or the directory |
525
|
|
|
|
|
|
|
# specified in $p->{directory} into a tar.gz distribution file |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub export { |
528
|
|
|
|
|
|
|
my ( $class, $p ) = @_; |
529
|
|
|
|
|
|
|
$p ||= {}; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
my $old_pwd = cwd; |
532
|
|
|
|
|
|
|
chdir( $p->{directory} ) if ( -d $p->{directory} ); |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
my $cwd = cwd; |
535
|
|
|
|
|
|
|
DEBUG && _w( 1, "Current directory exporting from: [$cwd]" ); |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# If necessary, Read in the config and ensure that it has all the |
538
|
|
|
|
|
|
|
# right information |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
my $config_file = $p->{config_file} || $DEFAULT_CONF_FILE; |
541
|
|
|
|
|
|
|
my $config = $p->{config} || |
542
|
|
|
|
|
|
|
eval { $class->read_config( { file => $config_file } ) }; |
543
|
|
|
|
|
|
|
if ( $@ ) { |
544
|
|
|
|
|
|
|
die "Package configuration file cannot be opened -- \n" , |
545
|
|
|
|
|
|
|
"are you chdir'd to the package directory? (Reported reason \n", |
546
|
|
|
|
|
|
|
"for failure: $@)\n"; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
DEBUG && _w( 2, "Package config read in: ", Dumper( $config ) ); |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# Check to ensure that all required fields have something in them; we |
551
|
|
|
|
|
|
|
# might do a 'version' check in the future, but not until it proves |
552
|
|
|
|
|
|
|
# necessary |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
my @missing_fields = (); |
555
|
|
|
|
|
|
|
foreach my $required_field ( @EXPORT_REQUIRED ) { |
556
|
|
|
|
|
|
|
unless ( $config->{ $required_field } ) { |
557
|
|
|
|
|
|
|
push @missing_fields, $required_field; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
if ( scalar @missing_fields ) { |
561
|
|
|
|
|
|
|
die "Configuration file exists [$cwd/$DEFAULT_CONF_FILE] ", |
562
|
|
|
|
|
|
|
"but is missing the following fields: (", |
563
|
|
|
|
|
|
|
join( ', ', @missing_fields ), "). Please add these fields and try again.\n"; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
DEBUG && _w( 1, "Required fields ok in package configuration file." ); |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# Now, do a check on this package's MANIFEST - are there files in |
568
|
|
|
|
|
|
|
# MANIFEST that don't exist? |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
warn "Package $config->{name}: checking MANIFEST for discrepancies\n"; |
571
|
|
|
|
|
|
|
my @missing = ExtUtils::Manifest::manicheck(); |
572
|
|
|
|
|
|
|
if ( scalar @missing ) { |
573
|
|
|
|
|
|
|
warn "\nIf the files specified do not need to be in MANIFEST any longer,\n", |
574
|
|
|
|
|
|
|
"please remove them from MANIFEST and re-export the package. Otherwise\n", |
575
|
|
|
|
|
|
|
"users installing the package will get a warning.\n"; |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
else { |
578
|
|
|
|
|
|
|
warn "Looks good\n\n"; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
# Next see if there are files NOT in the MANIFEST |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
warn "Package $config->{name}: checking filesystem for files not in MANIFEST\n"; |
584
|
|
|
|
|
|
|
my @extra = ExtUtils::Manifest::filecheck(); |
585
|
|
|
|
|
|
|
if ( scalar @extra ) { |
586
|
|
|
|
|
|
|
warn "\nBuilding a package without these files is OK, but you can also\n", |
587
|
|
|
|
|
|
|
"add them as necessary to the MANIFEST and re-export the package.\n"; |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
else { |
590
|
|
|
|
|
|
|
warn "Looks good\n\n"; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# Read in the MANIFEST |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
my $package_files = ExtUtils::Manifest::maniread(); |
596
|
|
|
|
|
|
|
DEBUG && _w( 2, "Package info read in:\n", Dumper( $package_files ) ); |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# Now, create a directory of this name-version and copy the files |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my $package_id = join( '-', $config->{name}, $config->{version} ); |
601
|
|
|
|
|
|
|
if ( -d $package_id ) { |
602
|
|
|
|
|
|
|
die "Cannot create directory [$cwd/$package_id] to ", |
603
|
|
|
|
|
|
|
"archive the package because it already exists.\n"; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
mkdir( $package_id, 0777 ) |
606
|
|
|
|
|
|
|
|| die "Cannot create directory [$cwd/$package_id] to ", |
607
|
|
|
|
|
|
|
"archive the package! Error: $!"; |
608
|
|
|
|
|
|
|
{ |
609
|
|
|
|
|
|
|
local $ExtUtils::Manifest::Quiet = 1; |
610
|
|
|
|
|
|
|
ExtUtils::Manifest::manicopy( $package_files, "$cwd/$package_id" ); |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
# And prepend the directory name to all the files so they get |
614
|
|
|
|
|
|
|
# un-archived in the right way |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
my @archive_files = map { "$package_id/$_" } keys %{ $package_files }; |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
# Create the tardist |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
my $filename = "$cwd/$package_id.tar.gz"; |
621
|
|
|
|
|
|
|
if ( -f $filename ) { |
622
|
|
|
|
|
|
|
$class->_remove_directory_tree( "$cwd/$package_id" ); |
623
|
|
|
|
|
|
|
die "Cannot create archive [$filename] - file already exists.\n"; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
my $rv = eval { $class->_create_archive( $filename, @archive_files ) }; |
626
|
|
|
|
|
|
|
die "Error creating archive: $@\n" if ( $@ ); |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# And remove the directory we just created |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
$class->_remove_directory_tree( "$cwd/$package_id" ); |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# Return the filename and the name/version information for the |
633
|
|
|
|
|
|
|
# package distribution we just created |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
chdir( $old_pwd ); |
636
|
|
|
|
|
|
|
if ( $rv ) { |
637
|
|
|
|
|
|
|
warn "\n"; |
638
|
|
|
|
|
|
|
return { name => $config->{name}, |
639
|
|
|
|
|
|
|
version => $config->{version}, |
640
|
|
|
|
|
|
|
file => "$filename" }; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
die "Cannot create distribution [$filename]. Error: ", Archive::Tar->error(), "\n"; |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# |
647
|
|
|
|
|
|
|
# check_package |
648
|
|
|
|
|
|
|
# |
649
|
|
|
|
|
|
|
# What we check for: |
650
|
|
|
|
|
|
|
# package.conf -- has name, version and author defined; all modules defined exist |
651
|
|
|
|
|
|
|
# conf/*.perl -- pass an 'eval' test (through SPOPS::HashFile) |
652
|
|
|
|
|
|
|
# OpenInteract/*.pm -- pass a 'require' test |
653
|
|
|
|
|
|
|
# MyApp/*.pm -- pass a 'require' test |
654
|
|
|
|
|
|
|
# |
655
|
|
|
|
|
|
|
# Parameters: |
656
|
|
|
|
|
|
|
# package_dir |
657
|
|
|
|
|
|
|
# package_name |
658
|
|
|
|
|
|
|
# website_name (optional) |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub check { |
661
|
|
|
|
|
|
|
my ( $class, $p ) = @_; |
662
|
|
|
|
|
|
|
my $status = { ok => 0 }; |
663
|
|
|
|
|
|
|
if ( ! $p->{package_dir} and $p->{info} ) { |
664
|
|
|
|
|
|
|
my $main_dir = $p->{info}{website_dir} || $p->{info}{base_dir}; |
665
|
|
|
|
|
|
|
$p->{package_dir} = join( '/', $main_dir, $p->{info}{package_dir} ); |
666
|
|
|
|
|
|
|
$p->{website_name} = $p->{info}{website_name}; |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
unless ( -d $p->{package_dir} ) { |
669
|
|
|
|
|
|
|
die "No valid package dir to check! (Given: $p->{package_dir})"; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
my $pwd = cwd; |
672
|
|
|
|
|
|
|
chdir( $p->{package_dir} ); |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
# First ensure the package config exists |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
unless ( -f "package.conf" ) { |
677
|
|
|
|
|
|
|
$status->{msg} .= "\n-- Package config (package.conf) does not " . |
678
|
|
|
|
|
|
|
"exist in package!\n"; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
if ( $p->{website_name} and ! -d "$p->{website_name}/" ) { |
681
|
|
|
|
|
|
|
$status->{msg} .= "\n-- Website directory ($p->{website_name}/) " . |
682
|
|
|
|
|
|
|
"does not exist in package!\n"; |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
return $status if ( $status->{msg} ); |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
DEBUG && _w( 1, " - package.conf and website_name directory (if app.) ok" ); |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# Set this after we do the initial sanity checks |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
$status->{ok}++; |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# This is just a warning |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
if ( -f 'Changes' ) { |
695
|
|
|
|
|
|
|
$status->{msg} .= "\n++ File (Changes) to show package Changelog: ok" ; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
else { |
698
|
|
|
|
|
|
|
$status->{msg} .= "\n-- File (Changes) to show package Changelog: DOES NOT EXIST\n" ; |
699
|
|
|
|
|
|
|
} |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
DEBUG && _w( 1, " - Changes file exists" ); |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
my $pkg_files = ExtUtils::Manifest::maniread(); |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# Now, first go through the config perl files |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
my @perl_files = grep /^conf.*\.perl$/, keys %{ $pkg_files }; |
708
|
|
|
|
|
|
|
foreach my $perl_file ( sort @perl_files ) { |
709
|
|
|
|
|
|
|
DEBUG && _w( 1, " checking perl file ($perl_file)" ); |
710
|
|
|
|
|
|
|
my $filestatus = 'ok'; |
711
|
|
|
|
|
|
|
my $sig = '++'; |
712
|
|
|
|
|
|
|
my $obj = eval { SPOPS::HashFile->new({ filename => $perl_file }) }; |
713
|
|
|
|
|
|
|
if ( $@ ) { |
714
|
|
|
|
|
|
|
$status->{ok} = 0; |
715
|
|
|
|
|
|
|
$filestatus = "cannot be read in. $@\n"; |
716
|
|
|
|
|
|
|
$sig = '--'; |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
elsif ( $perl_file =~ /spops/ ) { |
719
|
|
|
|
|
|
|
foreach my $spops_key ( keys %{ $obj } ) { |
720
|
|
|
|
|
|
|
my $typeof = ref $obj->{ $spops_key } || 'not a reference'; |
721
|
|
|
|
|
|
|
unless ( $typeof eq 'HASH' ) { |
722
|
|
|
|
|
|
|
$status->{ok} = 0; |
723
|
|
|
|
|
|
|
$filestatus = "invalid SPOPS configuration: value of each key must be " . |
724
|
|
|
|
|
|
|
"a hashref and the value [$spops_key] is [$typeof]\n"; |
725
|
|
|
|
|
|
|
$sig = '--'; |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
$status->{msg} .= "\n$sig File ($perl_file) $filestatus"; |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# Next all the .pm files - stick the package directory (cwd) into |
733
|
|
|
|
|
|
|
# @INC so we don't have any ambiguity about where the modules |
734
|
|
|
|
|
|
|
# being tested come from |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
unshift @INC, cwd; |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# We suppress warnings within this block so all the interesting |
739
|
|
|
|
|
|
|
# stuff goes into the status |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
{ |
742
|
|
|
|
|
|
|
local $SIG{__WARN__} = sub { return undef }; |
743
|
|
|
|
|
|
|
my @pm_files = grep /\.pm$/, keys %{ $pkg_files }; |
744
|
|
|
|
|
|
|
foreach my $pm_file ( sort @pm_files ) { |
745
|
|
|
|
|
|
|
DEBUG && _w( 1, " checking module file ($pm_file)" ); |
746
|
|
|
|
|
|
|
my $filestatus = 'ok'; |
747
|
|
|
|
|
|
|
my $sig = '++'; |
748
|
|
|
|
|
|
|
eval { require "$pm_file" }; |
749
|
|
|
|
|
|
|
if ( $@ ) { |
750
|
|
|
|
|
|
|
$status->{ok} = 0; |
751
|
|
|
|
|
|
|
$filestatus = "cannot be require'd.\n$@\n"; |
752
|
|
|
|
|
|
|
$sig = '--'; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
$status->{msg} .= "\n$sig File ($pm_file) $filestatus"; |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
} |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
# Check all the .dat files in data/ -- they should be valid perl files. |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
my @data_files = grep /^data\/.*\.dat$/, keys %{ $pkg_files }; |
761
|
|
|
|
|
|
|
foreach my $data_file ( sort @data_files ) { |
762
|
|
|
|
|
|
|
DEBUG && _w( 1, " checking data file ($data_file)" ); |
763
|
|
|
|
|
|
|
my $filestatus = 'ok'; |
764
|
|
|
|
|
|
|
my $sig = '++'; |
765
|
|
|
|
|
|
|
eval { $class->read_data_file( $data_file ) }; |
766
|
|
|
|
|
|
|
if ( $@ ) { |
767
|
|
|
|
|
|
|
$status->{ok} = 0; |
768
|
|
|
|
|
|
|
$filestatus = "is not a valid Perl structure.\n$@\n"; |
769
|
|
|
|
|
|
|
$sig = '--'; |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
$status->{msg} .= "\n$sig File ($data_file) $filestatus"; |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
# See if all the templates pass a basic syntax test -- do not log |
776
|
|
|
|
|
|
|
# 'plugin not found' or 'no providers for template prefix' errors, |
777
|
|
|
|
|
|
|
# since we assume those will be ok when it runs in the environment |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
require Template; |
780
|
|
|
|
|
|
|
my $template = Template->new(); |
781
|
|
|
|
|
|
|
my @template_files = grep ! /(\.meta|~|\.bak)$/, |
782
|
|
|
|
|
|
|
grep /^(template|widget)/, |
783
|
|
|
|
|
|
|
keys %{ $pkg_files }; |
784
|
|
|
|
|
|
|
my ( $out ); |
785
|
|
|
|
|
|
|
my @template_errors_ok = ( 'plugin not found', 'no providers for template prefix', 'file error' ); |
786
|
|
|
|
|
|
|
my $template_errors_re = '(' . join( '|', @template_errors_ok ) . ')'; |
787
|
|
|
|
|
|
|
foreach my $template_file ( sort @template_files ) { |
788
|
|
|
|
|
|
|
DEBUG && _w( 1, " checking template ($template_file)" ); |
789
|
|
|
|
|
|
|
my $filestatus = 'ok'; |
790
|
|
|
|
|
|
|
my $sig = '++'; |
791
|
|
|
|
|
|
|
eval { $template->process( $template_file, undef, \$out ) |
792
|
|
|
|
|
|
|
|| die $template->error(), "\n" }; |
793
|
|
|
|
|
|
|
if ( $@ ) { |
794
|
|
|
|
|
|
|
unless ( $@ =~ /$template_errors_re/ ) { |
795
|
|
|
|
|
|
|
$status->{ok} = 0; |
796
|
|
|
|
|
|
|
$filestatus = "is not a valid Template Toolkit template.\n$@\n"; |
797
|
|
|
|
|
|
|
$sig = '--'; |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
$status->{msg} .= "\n$sig File ($template_file) $filestatus"; |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
# Now open up the package.conf and check to see that name, version |
804
|
|
|
|
|
|
|
# and author exist |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
DEBUG && _w( 1, " checking package.conf validity" ); |
807
|
|
|
|
|
|
|
my $config = $class->read_config({ directory => '.' }); |
808
|
|
|
|
|
|
|
$status->{name} = $config->{name}; |
809
|
|
|
|
|
|
|
my $conf_msg = ''; |
810
|
|
|
|
|
|
|
unless ( $config->{name} ) { |
811
|
|
|
|
|
|
|
$conf_msg .= "\n-- package.conf: required field 'name' is not defined."; |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
unless ( $config->{version} ) { |
814
|
|
|
|
|
|
|
$conf_msg .= "\n-- package.conf: required field 'version' is not defined."; |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
unless ( $config->{author} ) { |
817
|
|
|
|
|
|
|
$conf_msg .= "\n-- package.conf: required field 'author' is not defined."; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
if ( ref $config->{module} eq 'ARRAY' ) { |
820
|
|
|
|
|
|
|
my @failed_modules = $class->_check_module_install( @{ $config->{module} } ); |
821
|
|
|
|
|
|
|
if ( scalar @failed_modules ) { |
822
|
|
|
|
|
|
|
$conf_msg .= "\n-- package.conf: the following modules are used by " . |
823
|
|
|
|
|
|
|
"package but not installed: " . |
824
|
|
|
|
|
|
|
"(" . join( ', ', @failed_modules ) . ") " . |
825
|
|
|
|
|
|
|
"INSTALL THESE PACKAGES BEFORE CONTINUING." |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
if ( $conf_msg ) { |
829
|
|
|
|
|
|
|
$status->{msg} .= "$conf_msg\n"; |
830
|
|
|
|
|
|
|
$status->{ok} = 0; |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
else { |
833
|
|
|
|
|
|
|
$status->{msg} .= "\n++ package.conf: ok"; |
834
|
|
|
|
|
|
|
} |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
# While we have the package.conf open, see if there are any |
837
|
|
|
|
|
|
|
# modules and whether they're available |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
# Now do the check to ensure that all files in the MANIFEST exist |
842
|
|
|
|
|
|
|
# -- just get feedback from the manifest module, don't let it |
843
|
|
|
|
|
|
|
# print out results of its findings (Quiet) |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
DEBUG && _w( " checking MANIFEST against files" ); |
846
|
|
|
|
|
|
|
$ExtUtils::Manifest::Quiet = 1; |
847
|
|
|
|
|
|
|
my @missing = ExtUtils::Manifest::manicheck(); |
848
|
|
|
|
|
|
|
if ( scalar @missing ) { |
849
|
|
|
|
|
|
|
$status->{msg} .= "\n-- MANIFEST files not all in package. " . |
850
|
|
|
|
|
|
|
"Following not found: \n " . |
851
|
|
|
|
|
|
|
join( "\n ", @missing ) . "\n"; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
else { |
854
|
|
|
|
|
|
|
$status->{msg} .= "\n++ MANIFEST files all exist in package: ok"; |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
# Now do the check to see if any extra files exist than are in the MANIFEST |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
my @extra = ExtUtils::Manifest::filecheck(); |
860
|
|
|
|
|
|
|
if ( scalar @extra ) { |
861
|
|
|
|
|
|
|
$status->{msg} .= "\n-- Files in package not in MANIFEST:\n " . |
862
|
|
|
|
|
|
|
join( "\n ", @extra ) . "\n"; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
else { |
865
|
|
|
|
|
|
|
$status->{msg} .= "\n++ All files in package also in MANIFEST: ok"; |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
$status->{msg} .= "\n"; |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
chdir( $pwd ); |
871
|
|
|
|
|
|
|
return $status; |
872
|
|
|
|
|
|
|
} |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# Copy all modules from a particular package (site directory AND base |
875
|
|
|
|
|
|
|
# directory) to another directory |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
sub copy_modules { |
878
|
|
|
|
|
|
|
my ( $class, $info, $to_dir ) = @_; |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
my $site_pkg_dir = join( '/', $info->{website_dir}, $info->{package_dir} ); |
881
|
|
|
|
|
|
|
my $site_modules = $class->_copy_module_files( $site_pkg_dir, $to_dir ); |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
my $base_pkg_dir = join( '/', $info->{base_dir}, $info->{package_dir} ); |
884
|
|
|
|
|
|
|
my $base_modules = $class->_copy_module_files( $base_pkg_dir, $to_dir ); |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
return [ sort @{ $base_modules }, @{ $site_modules } ]; |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
sub _copy_module_files { |
891
|
|
|
|
|
|
|
my ( $class, $pkg_dir, $to_dir ) = @_; |
892
|
|
|
|
|
|
|
unless ( -d $pkg_dir ) { |
893
|
|
|
|
|
|
|
die "Package directory ($pkg_dir) does not exist -- cannot copy files.\n"; |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
unless ( -d $to_dir ) { |
896
|
|
|
|
|
|
|
die "Destination for package modules ($to_dir) does not exist -- cannot copy files.\n"; |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
my $current_dir = cwd; |
899
|
|
|
|
|
|
|
chdir( $pkg_dir ); |
900
|
|
|
|
|
|
|
$to_dir =~ s|/$||; |
901
|
|
|
|
|
|
|
my $pkg_files = ExtUtils::Manifest::maniread; |
902
|
|
|
|
|
|
|
my @module_files = grep /\.pm$/, keys %{ $pkg_files }; |
903
|
|
|
|
|
|
|
my @module_files_full = (); |
904
|
|
|
|
|
|
|
my ( %dir_ok ); |
905
|
|
|
|
|
|
|
foreach my $filename ( @module_files ) { |
906
|
|
|
|
|
|
|
my $full_dest_file = join( '/', $to_dir, $filename ); |
907
|
|
|
|
|
|
|
#warn "Trying to copy file ($filename) to ($full_dest_file)\n"; |
908
|
|
|
|
|
|
|
next if ( -f $full_dest_file ); |
909
|
|
|
|
|
|
|
my $full_dest_dir = File::Basename::dirname( $full_dest_file ); |
910
|
|
|
|
|
|
|
unless ( $dir_ok{ $full_dest_dir } ) { |
911
|
|
|
|
|
|
|
File::Path::mkpath( $full_dest_dir ); |
912
|
|
|
|
|
|
|
$dir_ok{ $full_dest_dir }++; |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
cp( $filename, $full_dest_file ); |
915
|
|
|
|
|
|
|
push @module_files_full, $full_dest_file; |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
chdir( $current_dir ); |
918
|
|
|
|
|
|
|
return \@module_files_full; |
919
|
|
|
|
|
|
|
} |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
sub read_data_file { |
923
|
|
|
|
|
|
|
my ( $class, $filename ) = @_; |
924
|
|
|
|
|
|
|
open( D, $filename ) || die "Cannot open: $@"; |
925
|
|
|
|
|
|
|
local $/ = undef; |
926
|
|
|
|
|
|
|
my $raw = ; |
927
|
|
|
|
|
|
|
close( D ); |
928
|
|
|
|
|
|
|
my ( $dat ); |
929
|
|
|
|
|
|
|
{ |
930
|
|
|
|
|
|
|
no strict 'vars'; |
931
|
|
|
|
|
|
|
$dat = eval $raw; |
932
|
|
|
|
|
|
|
die $@ if ( $@ ); |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
return $dat; |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
sub remove { |
938
|
|
|
|
|
|
|
my ( $class, $repository, $info, $opt ) = @_; |
939
|
|
|
|
|
|
|
$repository->remove_package( $info ); |
940
|
|
|
|
|
|
|
$repository->save(); |
941
|
|
|
|
|
|
|
my $base_dir = $info->{website_dir} || $info->{base_dir}; |
942
|
|
|
|
|
|
|
my $full_dir = join( '/', $base_dir, $info->{package_dir} ); |
943
|
|
|
|
|
|
|
if ( $opt eq 'directory' ) { |
944
|
|
|
|
|
|
|
return $class->_remove_directory_tree( $full_dir ); |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
return 1; |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
sub read_config { |
951
|
|
|
|
|
|
|
my ( $class, $p ) = @_; |
952
|
|
|
|
|
|
|
if ( ( $p->{info} or $p->{directory} ) and ! $p->{file} ) { |
953
|
|
|
|
|
|
|
my $dir = $p->{directory}; |
954
|
|
|
|
|
|
|
unless ( -d $dir ) { |
955
|
|
|
|
|
|
|
$dir = $p->{info}{website_dir} || $p->{info}{base_dir}; |
956
|
|
|
|
|
|
|
$dir = join( '/', $dir, $p->{info}{package_dir} ); |
957
|
|
|
|
|
|
|
} |
958
|
|
|
|
|
|
|
$p->{file} = join( '/', $dir, $DEFAULT_CONF_FILE ); |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
unless ( -f $p->{file} ) { |
961
|
|
|
|
|
|
|
die "Package configuration file ($p->{file}) does not exist.\n"; |
962
|
|
|
|
|
|
|
} |
963
|
|
|
|
|
|
|
open( CONF, $p->{file} ) || die "Error opening $p->{file}: $!"; |
964
|
|
|
|
|
|
|
my $config = {}; |
965
|
|
|
|
|
|
|
while ( ) { |
966
|
|
|
|
|
|
|
next if ( /^\s*\#/ ); |
967
|
|
|
|
|
|
|
next if ( /^\s*$/ ); |
968
|
|
|
|
|
|
|
chomp; |
969
|
|
|
|
|
|
|
s/\r//g; |
970
|
|
|
|
|
|
|
s/^\s+//; |
971
|
|
|
|
|
|
|
s/\s+$//; |
972
|
|
|
|
|
|
|
my ( $k, $v ) = split /\s+/, $_, 2; |
973
|
|
|
|
|
|
|
last if ( $k eq 'description' ); |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
# If there are multiple values possible, make a list |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
if ( $CONF_LIST_KEYS{ $k } ) { |
978
|
|
|
|
|
|
|
push @{ $config->{ $k } }, $v; |
979
|
|
|
|
|
|
|
} |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
# Otherwise, if it's a key -> key -> value set; add to list |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
elsif ( $CONF_HASH_KEYS{ $k } ) { |
984
|
|
|
|
|
|
|
my ( $sub_key, $sub_value ) = split /\s+/, $v, 2; |
985
|
|
|
|
|
|
|
$config->{ $k }{ $sub_key } = $sub_value; |
986
|
|
|
|
|
|
|
} |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# If not all that, then simple key -> value |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
else { |
991
|
|
|
|
|
|
|
$config->{ $k } = $v; |
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
# Once all that is done, read the description in all at once |
996
|
|
|
|
|
|
|
{ |
997
|
|
|
|
|
|
|
local $/ = undef; |
998
|
|
|
|
|
|
|
$config->{description} = ; |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
chomp $config->{description}; |
1001
|
|
|
|
|
|
|
close( CONF ); |
1002
|
|
|
|
|
|
|
return $config; |
1003
|
|
|
|
|
|
|
} |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
# Read in a file (parameter 'from_file') and write it to a file |
1007
|
|
|
|
|
|
|
# (parameter 'to_file'), doing replacements on keys along the way. The |
1008
|
|
|
|
|
|
|
# keys are found in the list 'from_text' and the replacements are |
1009
|
|
|
|
|
|
|
# found in the list 'to_text'. |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
sub replace_and_copy { |
1012
|
|
|
|
|
|
|
my ( $class, $p ) = @_; |
1013
|
|
|
|
|
|
|
unless ( $p->{from_text} and $p->{to_text} |
1014
|
|
|
|
|
|
|
and $p->{from_file} and $p->{to_file} ) { |
1015
|
|
|
|
|
|
|
die "Not enough params for copy/replace! ", Dumper( $p ), "\n"; |
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
cp( $p->{from_file}, "$p->{to_file}.old" ) |
1018
|
|
|
|
|
|
|
|| die "No copy $p->{from_file} -> $p->{to_file}.old: $!"; |
1019
|
|
|
|
|
|
|
open( OLD, "$p->{to_file}.old" ) |
1020
|
|
|
|
|
|
|
|| die "Cannot open copied file: $!"; |
1021
|
|
|
|
|
|
|
open( NEW, "> $p->{to_file}" ) |
1022
|
|
|
|
|
|
|
|| die "Cannot open new file: $!"; |
1023
|
|
|
|
|
|
|
while ( ) { |
1024
|
|
|
|
|
|
|
my $line = $_; |
1025
|
|
|
|
|
|
|
for ( my $i = 0; $i < scalar @{ $p->{from_text} }; $i++ ) { |
1026
|
|
|
|
|
|
|
$line =~ s/$p->{from_text}->[ $i ]/$p->{to_text}->[ $i ]/g; |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
print NEW $line; |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
close( NEW ); |
1031
|
|
|
|
|
|
|
close( OLD ); |
1032
|
|
|
|
|
|
|
unlink( "$p->{to_file}.old" ) |
1033
|
|
|
|
|
|
|
|| warn qq/Cannot erase temp file (you should do a /, |
1034
|
|
|
|
|
|
|
qq/'rm -f `find . -name "*.old"`' after this is done): $!\n/; |
1035
|
|
|
|
|
|
|
} |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
# Find a file that exists in either the website directory or the base |
1039
|
|
|
|
|
|
|
# installation directory. @file_list defines a number of choices |
1040
|
|
|
|
|
|
|
# available for the file to be named. |
1041
|
|
|
|
|
|
|
# |
1042
|
|
|
|
|
|
|
# Returns: the full path and filename of the first match |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
sub find_file { |
1045
|
|
|
|
|
|
|
my ( $class, $info, @file_list ) = @_; |
1046
|
|
|
|
|
|
|
return undef unless ( scalar @file_list ); |
1047
|
|
|
|
|
|
|
foreach my $base_file ( @file_list ) { |
1048
|
|
|
|
|
|
|
if ( $info->{website_dir} ) { |
1049
|
|
|
|
|
|
|
my $filename = join( '/', $info->{website_dir}, $info->{package_dir}, $base_file ); |
1050
|
|
|
|
|
|
|
DEBUG && _w( 1, "Created filename <<$filename>> using the website directory" ); |
1051
|
|
|
|
|
|
|
return $filename if ( -f $filename ); |
1052
|
|
|
|
|
|
|
} |
1053
|
|
|
|
|
|
|
my $filename = join( '/', $info->{base_dir}, $info->{package_dir}, $base_file ); |
1054
|
|
|
|
|
|
|
DEBUG && _w( 1, "Created filename <<$filename>> using the base installation directory" ); |
1055
|
|
|
|
|
|
|
return $filename if ( -f $filename ); |
1056
|
|
|
|
|
|
|
} |
1057
|
|
|
|
|
|
|
DEBUG && _w( 1, "No existing filename found matching @file_list" ); |
1058
|
|
|
|
|
|
|
return undef; |
1059
|
|
|
|
|
|
|
} |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
# Put the base and website package directories into @INC |
1063
|
|
|
|
|
|
|
# |
1064
|
|
|
|
|
|
|
# NOTE: THIS WILL PROBABLY BE REMOVED |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
sub add_to_inc { |
1067
|
|
|
|
|
|
|
my ( $class, $info ) = @_; |
1068
|
|
|
|
|
|
|
my @my_inc = (); |
1069
|
|
|
|
|
|
|
my $base_package_dir = join( '/', $info->{base_dir}, $info->{package_dir} ); |
1070
|
|
|
|
|
|
|
unshift @my_inc, $base_package_dir if ( -d $base_package_dir ); |
1071
|
|
|
|
|
|
|
if ( $info->{website_dir} ) { |
1072
|
|
|
|
|
|
|
my $app_package_dir = join( '/', $info->{website_dir}, $info->{package_dir} ); |
1073
|
|
|
|
|
|
|
unshift @my_inc, $app_package_dir if ( -d $app_package_dir ); |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
#unshift @INC, @my_inc; |
1076
|
|
|
|
|
|
|
return @my_inc; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
sub _check_module_install { |
1081
|
|
|
|
|
|
|
my ( $class, @modules ) = @_; |
1082
|
|
|
|
|
|
|
my ( @failed_modules ); |
1083
|
|
|
|
|
|
|
MODULE: |
1084
|
|
|
|
|
|
|
foreach my $module ( @modules ) { |
1085
|
|
|
|
|
|
|
next unless ( $module ); |
1086
|
|
|
|
|
|
|
if ( $module =~ /\|\|/ ) { |
1087
|
|
|
|
|
|
|
my @alt_modules = split /\s*\|\|\s*/, $module; |
1088
|
|
|
|
|
|
|
foreach my $alt_module ( @alt_modules ) { |
1089
|
|
|
|
|
|
|
eval "require $alt_module"; |
1090
|
|
|
|
|
|
|
next MODULE unless ( $@ ); |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
push @failed_modules, join( ' or ', @alt_modules ); |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
else { |
1095
|
|
|
|
|
|
|
eval "require $module"; |
1096
|
|
|
|
|
|
|
push @failed_modules, $module if ( $@ ); |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
return @failed_modules; |
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
sub _create_archive { |
1104
|
|
|
|
|
|
|
my ( $class, $filename, @files ) = @_; |
1105
|
|
|
|
|
|
|
return undef unless ( $filename and scalar @files ); |
1106
|
|
|
|
|
|
|
DEBUG && _w( 2, "Creating archive ($filename) with files:\n", join( ' -- ', @files ) ); |
1107
|
|
|
|
|
|
|
die "file exits" if ( -f $filename ); |
1108
|
|
|
|
|
|
|
my $rv = undef; |
1109
|
|
|
|
|
|
|
if ( Archive::Tar->VERSION >= 0.20 ) { |
1110
|
|
|
|
|
|
|
DEBUG && _w( 1, "Creating archive using NEW Archive::Tar syntax." ); |
1111
|
|
|
|
|
|
|
$rv = Archive::Tar->create_archive( $filename, 9, @files ); |
1112
|
|
|
|
|
|
|
unless ( $rv ) { $ARCHIVE_ERROR = Archive::Tar->error() } |
1113
|
|
|
|
|
|
|
} |
1114
|
|
|
|
|
|
|
else { |
1115
|
|
|
|
|
|
|
DEBUG && _w( 1, "Creating archive using OLD Archive::Tar syntax." ); |
1116
|
|
|
|
|
|
|
my $tar = Archive::Tar->new(); |
1117
|
|
|
|
|
|
|
$tar->add_files( @files ); |
1118
|
|
|
|
|
|
|
$tar->write( $filename, 1 ); |
1119
|
|
|
|
|
|
|
if ( $Archive::Tar::error ) { |
1120
|
|
|
|
|
|
|
$ARCHIVE_ERROR = "Possible errors: $Archive::Tar::error / $@ / $!"; |
1121
|
|
|
|
|
|
|
} |
1122
|
|
|
|
|
|
|
else { |
1123
|
|
|
|
|
|
|
$rv++; |
1124
|
|
|
|
|
|
|
} |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
return $rv; |
1127
|
|
|
|
|
|
|
} |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
# Used to accommodate earlier versions of Archive::Tar (such as those |
1130
|
|
|
|
|
|
|
# shipped with ActivePerl, sigh) |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# * You should already be chdir'd to the directory where this will be |
1133
|
|
|
|
|
|
|
# unpacked |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
# * I'm not sure if the version reference below is correct -- I |
1136
|
|
|
|
|
|
|
# *think* it might be 0.20, but I'm not entirely sure. |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
sub _extract_archive { |
1139
|
|
|
|
|
|
|
my ( $class, $filename ) = @_; |
1140
|
|
|
|
|
|
|
return undef unless ( -f $filename ); |
1141
|
|
|
|
|
|
|
my $rv = undef; |
1142
|
|
|
|
|
|
|
if ( $Archive::Tar::VERSION >= 0.20 ) { |
1143
|
|
|
|
|
|
|
$rv = Archive::Tar->extract_archive( $filename ); |
1144
|
|
|
|
|
|
|
unless ( $rv ) { $ARCHIVE_ERROR = Archive::Tar->error() } |
1145
|
|
|
|
|
|
|
} |
1146
|
|
|
|
|
|
|
else { |
1147
|
|
|
|
|
|
|
my $tar = Archive::Tar->new(); |
1148
|
|
|
|
|
|
|
$tar->read( $filename, 1 ); |
1149
|
|
|
|
|
|
|
my @files = $tar->list_files(); |
1150
|
|
|
|
|
|
|
$tar->extract( @files ); |
1151
|
|
|
|
|
|
|
if ( $Archive::Tar::error ) { |
1152
|
|
|
|
|
|
|
$ARCHIVE_ERROR = "Possible errors: $Archive::Tar::error / $@ / $!"; |
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
else { |
1155
|
|
|
|
|
|
|
$rv++; |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
} |
1158
|
|
|
|
|
|
|
return $rv; |
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
# Copy the spops.perl file from the base install package directory to |
1163
|
|
|
|
|
|
|
# the website package directory Note that we have changed this |
1164
|
|
|
|
|
|
|
# recently (Jan 01) to keep only certain configuration variables |
1165
|
|
|
|
|
|
|
# *behind* -- all others are copied over to the website |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
# Also, this works with spops.perl AND spops.perl.IMPL, where 'IMPL' |
1168
|
|
|
|
|
|
|
# right now is generally 'ldap' |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
sub _copy_spops_config_file { |
1171
|
|
|
|
|
|
|
my ( $class, $info, $CONFIG, $filename ) = @_; |
1172
|
|
|
|
|
|
|
my $interact_pkg_dir = join( '/', $info->{base_dir}, $info->{package_dir} ); |
1173
|
|
|
|
|
|
|
my $website_pkg_dir = join( '/', $info->{website_dir}, $info->{package_dir} ); |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
my $spops_conf = "conf/$filename"; |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
unless ( -f "$interact_pkg_dir/$spops_conf" ) { |
1178
|
|
|
|
|
|
|
return undef; |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
my $spops_base = eval { SPOPS::HashFile->new({ |
1181
|
|
|
|
|
|
|
filename => "$interact_pkg_dir/$spops_conf" }) }; |
1182
|
|
|
|
|
|
|
if ( $@ ) { |
1183
|
|
|
|
|
|
|
_w( 0, "Cannot eval $spops_conf in ($info->{name}-$info->{version}): $@" ); |
1184
|
|
|
|
|
|
|
return undef; |
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
my $new_config_file = "$website_pkg_dir/$spops_conf"; |
1187
|
|
|
|
|
|
|
my $spops_pkg = SPOPS::HashFile->new({ |
1188
|
|
|
|
|
|
|
filename => $new_config_file, |
1189
|
|
|
|
|
|
|
perm => 'new' }); |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
foreach my $spops_key ( keys %{ $spops_base } ) { |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
# Change the class to reflect the website name |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
if ( my $old_class = $spops_base->{ $spops_key }{class} ) { |
1196
|
|
|
|
|
|
|
$spops_pkg->{ $spops_key }{class} = $class->_change_class_name( $info, $old_class ); |
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
# Both the has_a and links_to use class names as keys to link |
1200
|
|
|
|
|
|
|
# objects; change the class names from 'OpenInteract' to the |
1201
|
|
|
|
|
|
|
# website name |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
if ( my $old_has_a = $spops_base->{ $spops_key }{has_a} ) { |
1204
|
|
|
|
|
|
|
foreach my $old_class ( keys %{ $old_has_a } ) { |
1205
|
|
|
|
|
|
|
my $new_class = $class->_change_class_name( $info, $old_class ); |
1206
|
|
|
|
|
|
|
$spops_pkg->{ $spops_key }{has_a}{ $new_class } = $old_has_a->{ $old_class }; |
1207
|
|
|
|
|
|
|
} |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
if ( my $old_links_to = $spops_base->{ $spops_key }{links_to} ) { |
1211
|
|
|
|
|
|
|
foreach my $old_class ( keys %{ $old_links_to } ) { |
1212
|
|
|
|
|
|
|
my $new_class = $class->_change_class_name( $info, $old_class ); |
1213
|
|
|
|
|
|
|
$spops_pkg->{ $spops_key }{links_to}{ $new_class } = $old_links_to->{ $old_class }; |
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
# Copy over all the fields verbatim except those specified in the |
1218
|
|
|
|
|
|
|
# global %SPOPS_CONF_KEEP. Note that it's ok we're copying |
1219
|
|
|
|
|
|
|
# references here since we're going to dump the information to a |
1220
|
|
|
|
|
|
|
# file anyway |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
foreach my $to_copy ( keys %{ $spops_base->{ $spops_key } } ) { |
1223
|
|
|
|
|
|
|
next if ( $SPOPS_CONF_KEEP{ $to_copy } ); |
1224
|
|
|
|
|
|
|
next if ( ref $spops_base->{ $spops_key }{ $to_copy } eq 'CODE' ); |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
# For the 'creation_security', we want to check to see if |
1227
|
|
|
|
|
|
|
# we need to modify the group IDs to match what the server |
1228
|
|
|
|
|
|
|
# has configured |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
if ( $to_copy eq 'creation_security' ) { |
1231
|
|
|
|
|
|
|
my ( %new_security ); |
1232
|
|
|
|
|
|
|
my $orig = $spops_base->{ $spops_key }{ $to_copy }; # alias to save typing... |
1233
|
|
|
|
|
|
|
foreach my $scope ( keys %{ $orig } ) { |
1234
|
|
|
|
|
|
|
unless ( $scope eq 'g' ) { |
1235
|
|
|
|
|
|
|
$new_security{ $scope } = $orig->{ $scope }; |
1236
|
|
|
|
|
|
|
next; |
1237
|
|
|
|
|
|
|
} |
1238
|
|
|
|
|
|
|
next unless ( ref $orig->{g} eq 'HASH' and keys %{ $orig->{g} } ); |
1239
|
|
|
|
|
|
|
foreach my $scope_id ( keys %{ $orig->{g} } ) { |
1240
|
|
|
|
|
|
|
my $new_scope = $scope_id; |
1241
|
|
|
|
|
|
|
if ( $scope_id == PUBLIC_GROUP_ID ) { |
1242
|
|
|
|
|
|
|
$new_scope = $CONFIG->{default_objects}{public_group} || PUBLIC_GROUP_ID; |
1243
|
|
|
|
|
|
|
} |
1244
|
|
|
|
|
|
|
elsif ( $scope_id == SITE_ADMIN_GROUP_ID ) { |
1245
|
|
|
|
|
|
|
$new_scope = $CONFIG->{default_objects}{site_admin_group} || SITE_ADMIN_GROUP_ID; |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
$new_security{g}->{ $new_scope } = $orig->{g}{ $scope_id }; |
1248
|
|
|
|
|
|
|
} |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
$spops_pkg->{ $spops_key }{ $to_copy } = \%new_security; |
1251
|
|
|
|
|
|
|
} |
1252
|
|
|
|
|
|
|
else { |
1253
|
|
|
|
|
|
|
$spops_pkg->{ $spops_key }{ $to_copy } = $spops_base->{ $spops_key }{ $to_copy }; |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
} |
1256
|
|
|
|
|
|
|
} |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
eval { $spops_pkg->save({ dumper_level => 1 }) }; |
1259
|
|
|
|
|
|
|
die "Cannot save package spops file: $@\n" if ( $@ ); |
1260
|
|
|
|
|
|
|
return $new_config_file; |
1261
|
|
|
|
|
|
|
} |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
# Copy the conf/action.perl file over from the base installation to |
1265
|
|
|
|
|
|
|
# the website. This is somewhat easier because there are no nested |
1266
|
|
|
|
|
|
|
# classes we need to modify |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub _copy_action_config_file { |
1269
|
|
|
|
|
|
|
my ( $class, $info, $CONFIG ) = @_; |
1270
|
|
|
|
|
|
|
my $interact_pkg_dir = join( '/', $info->{base_dir}, |
1271
|
|
|
|
|
|
|
$info->{package_dir} ); |
1272
|
|
|
|
|
|
|
my $website_pkg_dir = join( '/', $info->{website_dir}, |
1273
|
|
|
|
|
|
|
$info->{package_dir} ); |
1274
|
|
|
|
|
|
|
DEBUG && _w( 1, "Coping action info from ($interact_pkg_dir)", |
1275
|
|
|
|
|
|
|
"to ($website_pkg_dir)" ); |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
my $action_conf = 'conf/action.perl'; |
1278
|
|
|
|
|
|
|
my $base_config_file = "$interact_pkg_dir/$action_conf"; |
1279
|
|
|
|
|
|
|
my $action_base = eval { SPOPS::HashFile->new({ |
1280
|
|
|
|
|
|
|
filename => $base_config_file }) }; |
1281
|
|
|
|
|
|
|
if ( $@ ) { |
1282
|
|
|
|
|
|
|
DEBUG && _w( 1, "No action info for $info->{name}-$info->{version}", |
1283
|
|
|
|
|
|
|
"(generally ok: $@)" ); |
1284
|
|
|
|
|
|
|
return undef; |
1285
|
|
|
|
|
|
|
} |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
my $new_config_file = "$website_pkg_dir/$action_conf"; |
1288
|
|
|
|
|
|
|
my $action_pkg = eval { SPOPS::HashFile->new({ |
1289
|
|
|
|
|
|
|
filename => $new_config_file, |
1290
|
|
|
|
|
|
|
perm => 'new' }) }; |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
# Go through all of the actions and all of the keys and copy them |
1293
|
|
|
|
|
|
|
# over to the new file. The only modification we make is to a field |
1294
|
|
|
|
|
|
|
# named 'class': if it exists, we modify it to fit in the website's |
1295
|
|
|
|
|
|
|
# namespace. |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
foreach my $action_key ( keys %{ $action_base } ) { |
1298
|
|
|
|
|
|
|
foreach my $action_item_key ( keys %{ $action_base->{ $action_key } } ) { |
1299
|
|
|
|
|
|
|
next if ( ref $action_base->{ $action_key }{ $action_item_key } eq 'CODE' ); |
1300
|
|
|
|
|
|
|
my $value = $action_base->{ $action_key }{ $action_item_key }; |
1301
|
|
|
|
|
|
|
if ( $action_item_key eq 'class' ) { |
1302
|
|
|
|
|
|
|
if ( $value =~ /^OpenInteract::Handler/ ) { |
1303
|
|
|
|
|
|
|
$value = $class->_change_class_name( $info, $value ); |
1304
|
|
|
|
|
|
|
} |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
$action_pkg->{ $action_key }{ $action_item_key } = $value; |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
eval { $action_pkg->save({ dumper_level => 1 }) }; |
1311
|
|
|
|
|
|
|
die "Cannot save package action file: $@\n" if ( $@ ); |
1312
|
|
|
|
|
|
|
return $new_config_file; |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
# Copy files from the current (package) directory into a website's |
1317
|
|
|
|
|
|
|
# directory and package |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
sub _copy_package_files { |
1320
|
|
|
|
|
|
|
my ( $class, $root_dir, $sub_dir, $file_list ) = @_; |
1321
|
|
|
|
|
|
|
my @copy_file_list = grep /^$sub_dir/, @{ $file_list }; |
1322
|
|
|
|
|
|
|
my %no_copy = map { $_ => 1 } $class->read_readonly_file( $root_dir ); |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
foreach my $sub_dir_file ( @copy_file_list ) { |
1325
|
|
|
|
|
|
|
my $just_filename = $sub_dir_file; |
1326
|
|
|
|
|
|
|
$just_filename =~ s|^$sub_dir/||; |
1327
|
|
|
|
|
|
|
my $new_name = join( '/', $root_dir, $just_filename ); |
1328
|
|
|
|
|
|
|
next if ( $no_copy{ $just_filename } ); |
1329
|
|
|
|
|
|
|
eval { $class->_create_full_path( $new_name ) }; |
1330
|
|
|
|
|
|
|
if ( $@ ) { die "Cannot create path to file ($new_name): $@" } |
1331
|
|
|
|
|
|
|
eval { cp( $sub_dir_file, "$new_name" ) || die $! }; |
1332
|
|
|
|
|
|
|
if ( $@ ) { |
1333
|
|
|
|
|
|
|
_w( 0, "Cannot copy ($sub_dir_file) to ($new_name) : $@" ); |
1334
|
|
|
|
|
|
|
} |
1335
|
|
|
|
|
|
|
else { |
1336
|
|
|
|
|
|
|
chmod( 0775, $new_name ); |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
} |
1339
|
|
|
|
|
|
|
return \@copy_file_list; |
1340
|
|
|
|
|
|
|
} |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
sub read_readonly_file { |
1344
|
|
|
|
|
|
|
my ( $class, $dir ) = @_; |
1345
|
|
|
|
|
|
|
my $overwrite_check_file = join( '/', $dir, READONLY_FILE ); |
1346
|
|
|
|
|
|
|
return () unless ( -f $overwrite_check_file ); |
1347
|
|
|
|
|
|
|
my ( @no_write ); |
1348
|
|
|
|
|
|
|
if ( open( NOWRITE, $overwrite_check_file ) ) { |
1349
|
|
|
|
|
|
|
while ( ) { |
1350
|
|
|
|
|
|
|
chomp; |
1351
|
|
|
|
|
|
|
next if ( /^\s*$/ ); |
1352
|
|
|
|
|
|
|
next if ( /^\s*\#/ ); |
1353
|
|
|
|
|
|
|
s/^\s+//; |
1354
|
|
|
|
|
|
|
s/\s+$//; |
1355
|
|
|
|
|
|
|
push @no_write, $_; |
1356
|
|
|
|
|
|
|
} |
1357
|
|
|
|
|
|
|
close( NOWRITE ); |
1358
|
|
|
|
|
|
|
} |
1359
|
|
|
|
|
|
|
return @no_write; |
1360
|
|
|
|
|
|
|
} |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
# Copy handlers from the base installation to the website directory, |
1364
|
|
|
|
|
|
|
# putting class names into the namespace of the website |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
sub _copy_handler_files { |
1367
|
|
|
|
|
|
|
my ( $class, $info, $base_files ) = @_; |
1368
|
|
|
|
|
|
|
my $website_pkg_dir = join( '/', $info->{website_dir}, |
1369
|
|
|
|
|
|
|
$info->{package_dir} ); |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
# We're only operating on the files that begin with |
1372
|
|
|
|
|
|
|
# 'OpenInteract/Handler'... |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
my @handler_file_list = grep /^OpenInteract\/Handler/, |
1375
|
|
|
|
|
|
|
keys %{ $base_files }; |
1376
|
|
|
|
|
|
|
foreach my $handler_filename ( @handler_file_list ) { |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
# First create the old/new class names... |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
my $handler_class = $handler_filename; |
1381
|
|
|
|
|
|
|
$handler_class =~ s|/|::|g; |
1382
|
|
|
|
|
|
|
$handler_class =~ s/\.pm$//; |
1383
|
|
|
|
|
|
|
my $new_handler_class = $class->_change_class_name( $info, $handler_class ); |
1384
|
|
|
|
|
|
|
DEBUG && _w( 1, "Old name: $handler_class; New name: $new_handler_class" ); |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
# ... then the new filename |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
my $new_filename = "$website_pkg_dir/$handler_filename"; |
1389
|
|
|
|
|
|
|
$new_filename =~ s|OpenInteract/Handler|$info->{website_name}/Handler|; |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
# Now read in the old handler and write out the new one, replacing |
1392
|
|
|
|
|
|
|
# the 'OpenInteract::Handler::xx' with '$WEBSITE_NAME::Handler::xx' |
1393
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
open( OLDHANDLER, $handler_filename ) |
1395
|
|
|
|
|
|
|
|| die "Cannot read handler ($handler_filename): $!"; |
1396
|
|
|
|
|
|
|
eval { $class->_create_full_path( $new_filename ) }; |
1397
|
|
|
|
|
|
|
if ( $@ ) { |
1398
|
|
|
|
|
|
|
die "Cannot create a dir tree to handler ($new_filename): $@"; |
1399
|
|
|
|
|
|
|
} |
1400
|
|
|
|
|
|
|
open( NEWHANDLER, "> $new_filename" ) |
1401
|
|
|
|
|
|
|
|| die "Cannot write to handler ($new_filename): $!"; |
1402
|
|
|
|
|
|
|
while ( ) { |
1403
|
|
|
|
|
|
|
s/$handler_class/$new_handler_class/g; |
1404
|
|
|
|
|
|
|
print NEWHANDLER; |
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
close( OLDHANDLER ); |
1407
|
|
|
|
|
|
|
close( NEWHANDLER ); |
1408
|
|
|
|
|
|
|
} |
1409
|
|
|
|
|
|
|
return \@handler_file_list; |
1410
|
|
|
|
|
|
|
} |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
# auxiliary routine to create necessary directories for a file, given |
1414
|
|
|
|
|
|
|
# the file; die on error, otherwise return a true value |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
sub _create_full_path { |
1417
|
|
|
|
|
|
|
my ( $class, $filename ) = @_; |
1418
|
|
|
|
|
|
|
my $dirname = File::Basename::dirname( $filename ); |
1419
|
|
|
|
|
|
|
return 1 if ( -d $dirname ); |
1420
|
|
|
|
|
|
|
eval { File::Path::mkpath( $dirname, undef, 0755 ) }; |
1421
|
|
|
|
|
|
|
return 1 unless ( $@ ); |
1422
|
|
|
|
|
|
|
_w( 0, "Cannot create path ($dirname): $@" ); |
1423
|
|
|
|
|
|
|
die $@; |
1424
|
|
|
|
|
|
|
} |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
# Create a manifest file in the current directory. (Note that the |
1428
|
|
|
|
|
|
|
# 'Quiet' and 'Verbose' parameters won't work properly until |
1429
|
|
|
|
|
|
|
# ExtUtils::Manifest is patched which won't likely be until 5.6.1) |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
sub _create_manifest { |
1432
|
|
|
|
|
|
|
my ( $class ) = @_; |
1433
|
|
|
|
|
|
|
local $SIG{__WARN__} = sub { return undef }; |
1434
|
|
|
|
|
|
|
$ExtUtils::Manifest::Quiet = 1; |
1435
|
|
|
|
|
|
|
$ExtUtils::Manifest::Verbose = 0; |
1436
|
|
|
|
|
|
|
ExtUtils::Manifest::mkmanifest(); |
1437
|
|
|
|
|
|
|
} |
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
# Remove a directory and all files/directories beneath it. Return the |
1441
|
|
|
|
|
|
|
# number of removed files. |
1442
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
sub _remove_directory_tree { |
1444
|
|
|
|
|
|
|
my ( $class, $dir ) = @_; |
1445
|
|
|
|
|
|
|
my $removed_files = File::Path::rmtree( $dir, undef, undef ); |
1446
|
|
|
|
|
|
|
DEBUG && _w( 1, "Removed ($removed_files) files/directories from ($dir)" ); |
1447
|
|
|
|
|
|
|
return $removed_files; |
1448
|
|
|
|
|
|
|
} |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
# Modify the first argument by replacing 'OpenInteract' with either |
1452
|
|
|
|
|
|
|
# the second argument or the property 'website_name' of the zeroth |
1453
|
|
|
|
|
|
|
# argument. |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
sub _change_class_name { |
1456
|
|
|
|
|
|
|
my ( $class, $info, $old_class, $new_name ) = @_; |
1457
|
|
|
|
|
|
|
if ( ref $info and ! $new_name ) { |
1458
|
|
|
|
|
|
|
$new_name = $info->{website_name}; |
1459
|
|
|
|
|
|
|
} |
1460
|
|
|
|
|
|
|
$old_class =~ s/OpenInteract/$new_name/g; |
1461
|
|
|
|
|
|
|
return $old_class; |
1462
|
|
|
|
|
|
|
} |
1463
|
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
sub _w { |
1467
|
|
|
|
|
|
|
my $lev = shift; |
1468
|
|
|
|
|
|
|
return unless ( DEBUG >= $lev ); |
1469
|
|
|
|
|
|
|
my ( $pkg, $file, $line ) = caller; |
1470
|
|
|
|
|
|
|
my @ci = caller(1); |
1471
|
|
|
|
|
|
|
warn "$ci[3] ($line) >> ", join( ' ', @_ ), "\n"; |
1472
|
|
|
|
|
|
|
} |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
1; |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
__END__ |