root/branches/release-26/lib/MT/ConfigMgr.pm @ 1174

Revision 1174, 10.6 kB (checked in by bchoate, 23 months ago)

Updated copyright year for source.

Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::ConfigMgr;
8
9use strict;
10use base qw( MT::ErrorHandler );
11
12use vars qw( $cfg );
13sub instance {
14    return $cfg if $cfg;
15    $cfg = __PACKAGE__->new;
16}
17
18sub new {
19    my $mgr = bless { __var => { }, __dbvar => { }, __paths => [] }, $_[0];
20    $mgr->init;
21    $mgr;
22}
23
24sub init {
25}
26
27sub define {
28    my $mgr = shift;
29    my($vars);
30    if (ref $_[0] eq 'ARRAY') {
31        $vars = shift;
32    } elsif (ref $_[0] eq 'HASH') {
33        $vars = shift;
34    } else {
35        my($var, %param) = @_;
36        $vars = [ [ $var, \%param ] ];
37    }
38    if (ref($vars) eq 'ARRAY') {
39        foreach my $def (@$vars) {
40            my($var, $param) = @$def;
41            my $lcvar = lc $var;
42            $mgr->{__var}{$lcvar} = undef;
43            $mgr->{__settings}{$lcvar} = keys %$param ? { %$param } : {};
44            $mgr->{__settings}{$lcvar}{key} = $var;
45            if ($mgr->{__settings}{$lcvar}{path}) {
46                push @{$mgr->{__paths}}, $var;
47            }
48        }
49    } elsif (ref($vars) eq 'HASH') {
50        foreach my $var (keys %$vars) {
51            my $param = $vars->{$var};
52            my $lcvar = lc $var;
53            $mgr->{__settings}{$lcvar} = $param;
54            if (ref $param eq 'ARRAY') {
55                $mgr->{__settings}{$lcvar} = $param->[0];
56            }
57            $mgr->{__settings}{$lcvar}{key} = $var;
58            if ($mgr->{__settings}{$lcvar}{path}) {
59                push @{$mgr->{__paths}}, $var;
60            }
61        }
62    }
63}
64
65sub paths {
66    my $mgr = shift;
67    wantarray ? @{$mgr->{__paths}} : $mgr->{__paths};
68}
69
70our $depth = 0;
71my $max_depth = 5;
72sub get_internal {
73    my $mgr = shift;
74    my $var = lc shift;
75    my $val;
76    if (defined(my $alias = $mgr->{__settings}{$var}{alias})) {
77        if ($max_depth < $depth) {
78            die MT->translate('Alias for [_1] is looping in the configuration.', $alias);
79        }
80        local $depth = $depth + 1;
81        $mgr->get($alias);
82    } elsif (defined($val = $mgr->{__var}{$var})) {
83        $val = $val->() if ref($val) eq 'CODE';
84        wantarray && ($mgr->{__settings}{$var}{type}||'') eq 'ARRAY' ?
85            @$val : ((ref $val) eq 'ARRAY' && @$val ? $val->[0] : $val);
86    } elsif (defined($val = $mgr->{__dbvar}{$var})) {
87        wantarray && ($mgr->{__settings}{$var}{type}||'') eq 'ARRAY' ?
88            @$val : ((ref $val) eq 'ARRAY' && @$val ? $val->[0] : $val);
89    } else {
90        $mgr->default($var);
91    }
92}
93
94sub get {
95    my $mgr = shift;
96    my $var = lc shift;
97    if (my $h = $mgr->{__settings}{$var}{handler}) {
98        $h = MT->handler_to_coderef($h) unless ref $h;
99        return $h->($mgr);
100    }
101    return $mgr->get_internal($var, @_);
102}
103
104sub type {
105    my $mgr = shift;
106    my $var = lc shift;
107    $mgr->{__settings}{$var}{type} || 'SCALAR';
108}
109
110sub default {
111    my $mgr = shift;
112    my $var = lc shift;
113    my $def = $mgr->{__settings}{$var}{default};
114    return wantarray ? () : undef unless defined $def;
115    if (my $type = $mgr->{__settings}{$var}{type}) {
116        if ($type eq 'ARRAY') {
117            return wantarray ? ( $def ) : $def;
118        } elsif ($type eq 'HASH') {
119            if (ref $def ne 'HASH') {
120                (my($key), my($val)) = split /=/, $def;
121                return { $key => $val };
122            }
123        }
124    }
125    $def;
126}
127
128sub set_internal {
129    my $mgr = shift;
130    my($var, $val, $db_flag) = @_;
131    $var = lc $var;
132    $db_flag ||= exists $mgr->{__dbvar}{$var};
133    my $set = $db_flag ? '__dbvar' : '__var';
134    if (defined(my $alias = $mgr->{__settings}{$var}{alias})) {
135        if ($max_depth < $depth) {
136            die MT->translate('Alias for [_1] is looping in the configuration.', $alias);
137        }
138        local $depth = $depth + 1;
139        $mgr->set($alias, $val, $db_flag);
140    } elsif (my $type = $mgr->{__settings}{$var}{type}) {
141        if ($type eq 'ARRAY') {
142            if (ref $val eq 'ARRAY') {
143                $mgr->{$set}{$var} = $val;
144            } else {
145                $mgr->{$set}{$var} ||= [];
146                push @{ $mgr->{$set}{$var} }, $val if defined $val;
147            }
148        } elsif ($type eq 'HASH') {
149            my $hash = $mgr->{$set}{$var};
150            $hash = $mgr->default($var) unless defined $hash;
151            if (ref $val eq 'HASH') {
152                $mgr->{$set}{$var} = $val;
153            } else {
154                $hash ||= {};
155                (my($key), $val) = split /=/, $val;
156                $mgr->{$set}{$var}{$key} = $val;
157            }
158        } else {
159            $mgr->{$set}{$var} = $val;
160        }
161    } else {
162        $mgr->{$set}{$var} = $val;
163    }
164}
165
166sub set {
167    my $mgr = shift;
168    my($var, $val, $db_flag) = @_;
169    $var = lc $var;
170    if (my $h = $mgr->{__settings}{$var}{handler}) {
171        $h = MT->handler_to_coderef($h) unless ref $h;
172        return $h->($mgr, $val, $db_flag);
173    }
174    return $mgr->set_internal(@_);
175}
176
177sub is_readonly {
178    my $class = shift;
179    my ($var) = @_;
180    defined $class->instance->{__var}{lc $var} ? 1 : 0;
181}
182
183sub read_config {
184    my $class = shift;
185    $class->read_config_file(@_);
186}
187
188sub save_config {
189    my $class = shift;
190    my $mgr = $class->instance;
191    my $data = '';
192    my $settings = $mgr->{__dbvar};
193    foreach (sort keys %$settings) {
194        my $type = ($mgr->{__settings}{$_}{type}||'');
195        if ($type eq 'HASH') {
196            my $h = $settings->{$_};
197            foreach my $k (keys %$h) {
198                $data .= $mgr->{__settings}{$_}{key} . ' ' . $k . '=' . $h->{$k} . "\n";
199            }
200        } elsif ($type eq 'ARRAY') {
201            my $a = $settings->{$_};
202            foreach my $v (@$a) {
203                $data .= $mgr->{__settings}{$_}{key} . ' ' . $v . "\n";
204            }
205        } else {
206            $data .= $mgr->{__settings}{$_}{key} . ' ' . $settings->{$_} . "\n";
207        }
208    }
209    require MT::Config;
210    my ($config) = MT::Config->load() || new MT::Config;
211
212    if ($data !~ m/schemaversion/i) {
213        if ($config->id && (($config->data || '') =~ m/schemaversion/i)) {
214            require Carp;
215            MT->log(Carp::longmess("Caught attempt to clear SchemaVersion setting. New config settings were:\n$data"));
216            return;
217        }
218    }
219
220    $config->data($data);
221    $config->save or die $config->errstr;
222}
223
224sub read_config_file {
225    my $class = shift;
226    my($cfg_file) = @_;
227    my $mgr = $class->instance;
228    $mgr->{__var} = {};
229    local(*FH, $_, $/);
230    $/ = "\n";
231    die "Can't read config without config file name" if !$cfg_file;
232    open FH, $cfg_file or
233        return $class->error(MT->translate(
234            "Error opening file '[_1]': [_2]", $cfg_file, "$!" ));
235    my $line;
236    while (<FH>) {
237        chomp; $line++;
238        next if !/\S/ || /^#/;
239        my($var, $val) = $_ =~ /^\s*(\S+)\s+(.*)$/;
240        return $class->error(MT->translate("Config directive [_1] without value at [_2] line [_3]", $var, $cfg_file, $line))
241            unless defined($val) && $val ne '';
242        $val =~ s/\s*$// if defined($val);
243        next unless $var && defined($val);
244        #return $class->error(MT->translate(
245        #    "[_1]:[_2]: variable '[_3]' not defined", $cfg_file, $., $var
246        #    )) unless exists $mgr->{__settings}->{$var};
247        # next unless exists $mgr->{__settings}->{$var};
248        $mgr->set($var, $val);
249    }
250    close FH;
251    1;
252}
253
254sub read_config_db {
255    my $class = shift;
256    my $mgr = $class->instance;
257    require MT::Config;
258    my ($config) = eval { MT::Config->search };
259    if ($config) {
260        my $data = $config->data;
261        my @data = split /[\r?\n]/, $data;
262        my $line = 0;
263        foreach (@data) {
264            $line++;
265            chomp;
266            next if !/\S/ || /^#/;
267            my($var, $val) = $_ =~ /^\s*(\S+)\s+(.+)$/;
268            $val =~ s/\s*$// if defined($val);
269            next unless $var && defined($val);
270            #return $class->error(MT->translate(
271            #    "[_1]:[_2]: variable '[_3]' not defined", "database", $line, $var
272            #)) unless exists $mgr->{__settings}->{$var};
273
274            # ignore setting if it isn't defined...
275            # next unless exists $mgr->{__settings}->{$var};
276            $mgr->set($var, $val, 1);
277        }
278    }
279    1;
280}
281
282sub DESTROY { }
283
284use vars qw( $AUTOLOAD );
285sub AUTOLOAD {
286    my $mgr = $_[0];
287    (my $var = $AUTOLOAD) =~ s!.+::!!;
288    $var = lc $var;
289    return $mgr->error(MT->translate("No such config variable '[_1]'", $var))
290        unless exists $mgr->{__settings}->{$var};
291    no strict 'refs';
292    *$AUTOLOAD = sub {
293        my $mgr = shift;
294        @_ ? $mgr->set($var, @_) : $mgr->get($var);
295    };
296    goto &$AUTOLOAD;
297}
298
2991;
300__END__
301
302=head1 NAME
303
304MT::ConfigMgr - Movable Type configuration manager
305
306=head1 SYNOPSIS
307
308    use MT::ConfigMgr;
309    my $cfg = MT::ConfigMgr->instance;
310
311    $cfg->read_config('/path/to/mt.cfg')
312        or die $cfg->errstr;
313
314=head1 DESCRIPTION
315
316I<MT::ConfigMgr> is a singleton class that manages the Movable Type
317configuration file (F<mt.cfg>), allowing access to the config directives
318contained therin.
319
320=head1 USAGE
321
322=head2 MT::ConfigMgr->instance
323
324Returns the singleton I<MT::ConfigMgr> object. Note that when you want the
325object, you should always call I<instance>, never I<new>; I<new> will construct
326a B<new> I<MT::ConfigMgr> object, and that isn't what you want. You want the
327object that has already been initialized with the contents of F<mt.cfg>. This
328initialization is done by I<MT::new>.
329
330=head2 $cfg->read_config($file)
331
332Reads the config file at the path I<$file> and initializes the I<$cfg> object
333with the directives in that file. Returns true on success, C<undef> otherwise;
334if an error occurs you can obtain the error message with C<$cfg-E<gt>errstr>.
335
336=head2 $cfg->define($directive [, %arg ])
337
338Defines the directive I<$directive> as a valid configuration directive; you
339must define new configuration directives B<before> you read the configuration
340file, or else the read will fail.
341
342=head2 $cfg->ExternalUserManagement()
343
344Returns boolean value indicating whether the configuration is set so that
345external users management feature in Movable Type Enterprise is turned on.
346
347=head1 CONFIGURATION DIRECTIVES
348
349The following configuration directives are allowed in F<mt.cfg>. To get the
350value of a directive, treat it as a method that you are calling on the
351I<$cfg> object. For example:
352
353    $cfg->CGIPath
354
355To set the value of a directive, do the same as the above, but pass in a value
356to the method:
357
358    $cfg->CGIPath('http://www.foo.com/mt/');
359
360A list of valid configuration directives can be found in the
361I<CONFIGURATION SETTINGS> section of the manual.
362
363=head1 AUTHOR & COPYRIGHT
364
365Please see the I<MT> manpage for author, copyright, and license information.
366
367=cut
Note: See TracBrowser for help on using the browser.