root/branches/release-33/lib/MT/ConfigMgr.pm @ 1769

Revision 1769, 11.1 kB (checked in by fumiakiy, 20 months ago)

Stopped saving something in the database while database driver is being initialized. Let us see if this fixes the occasional "Time to Upgrade!" bug. BugId:58199

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 => [], __dirty => 0 }, $_[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    $mgr->set_dirty() if defined($_[2]) && $_[2];
165}
166
167sub set {
168    my $mgr = shift;
169    my($var, $val, $db_flag) = @_;
170    $var = lc $var;
171    if (my $h = $mgr->{__settings}{$var}{handler}) {
172        $h = MT->handler_to_coderef($h) unless ref $h;
173        return $h->($mgr, $val, $db_flag);
174    }
175    return $mgr->set_internal(@_);
176}
177
178sub is_readonly {
179    my $class = shift;
180    my ($var) = @_;
181    defined $class->instance->{__var}{lc $var} ? 1 : 0;
182}
183
184sub read_config {
185    my $class = shift;
186    $class->read_config_file(@_);
187}
188
189sub set_dirty {
190    my $mgr = shift;
191    $mgr = $mgr->instance unless ref($mgr);
192    $mgr->{__dirty} = 1;
193}
194
195sub clear_dirty {
196    my $mgr = shift;
197    $mgr = $mgr->instance unless ref($mgr);
198    $mgr->{__dirty} = 0;
199}
200
201sub is_dirty {
202    my $mgr = shift;
203    $mgr = $mgr->instance unless ref($mgr);
204    $mgr->{__dirty};
205}
206
207sub save_config {
208    my $class = shift;
209    my $mgr = $class->instance;
210    return 0 unless $mgr->is_dirty();
211    my $data = '';
212    my $settings = $mgr->{__dbvar};
213    foreach (sort keys %$settings) {
214        my $type = ($mgr->{__settings}{$_}{type}||'');
215        if ($type eq 'HASH') {
216            my $h = $settings->{$_};
217            foreach my $k (keys %$h) {
218                $data .= $mgr->{__settings}{$_}{key} . ' ' . $k . '=' . $h->{$k} . "\n";
219            }
220        } elsif ($type eq 'ARRAY') {
221            my $a = $settings->{$_};
222            foreach my $v (@$a) {
223                $data .= $mgr->{__settings}{$_}{key} . ' ' . $v . "\n";
224            }
225        } else {
226            $data .= $mgr->{__settings}{$_}{key} . ' ' . $settings->{$_} . "\n";
227        }
228    }
229    require MT::Config;
230    my ($config) = MT::Config->load() || new MT::Config;
231
232    if ($data !~ m/schemaversion/i) {
233        if ($config->id && (($config->data || '') =~ m/schemaversion/i)) {
234            require Carp;
235            MT->log(Carp::longmess("Caught attempt to clear SchemaVersion setting. New config settings were:\n$data"));
236            return;
237        }
238    }
239
240    $config->data($data);
241    $config->save or die $config->errstr;
242    $mgr->clear_dirty;
243    1;
244}
245
246sub read_config_file {
247    my $class = shift;
248    my($cfg_file) = @_;
249    my $mgr = $class->instance;
250    $mgr->{__var} = {};
251    local(*FH, $_, $/);
252    $/ = "\n";
253    die "Can't read config without config file name" if !$cfg_file;
254    open FH, $cfg_file or
255        return $class->error(MT->translate(
256            "Error opening file '[_1]': [_2]", $cfg_file, "$!" ));
257    my $line;
258    while (<FH>) {
259        chomp; $line++;
260        next if !/\S/ || /^#/;
261        my($var, $val) = $_ =~ /^\s*(\S+)\s+(.*)$/;
262        return $class->error(MT->translate("Config directive [_1] without value at [_2] line [_3]", $var, $cfg_file, $line))
263            unless defined($val) && $val ne '';
264        $val =~ s/\s*$// if defined($val);
265        next unless $var && defined($val);
266        #return $class->error(MT->translate(
267        #    "[_1]:[_2]: variable '[_3]' not defined", $cfg_file, $., $var
268        #    )) unless exists $mgr->{__settings}->{$var};
269        # next unless exists $mgr->{__settings}->{$var};
270        $mgr->set($var, $val);
271    }
272    close FH;
273    1;
274}
275
276sub read_config_db {
277    my $class = shift;
278    my $mgr = $class->instance;
279    require MT::Config;
280    my ($config) = eval { MT::Config->search };
281    if ($config) {
282        my $data = $config->data;
283        my @data = split /[\r?\n]/, $data;
284        my $line = 0;
285        foreach (@data) {
286            $line++;
287            chomp;
288            next if !/\S/ || /^#/;
289            my($var, $val) = $_ =~ /^\s*(\S+)\s+(.+)$/;
290            $val =~ s/\s*$// if defined($val);
291            next unless $var && defined($val);
292            #return $class->error(MT->translate(
293            #    "[_1]:[_2]: variable '[_3]' not defined", "database", $line, $var
294            #)) unless exists $mgr->{__settings}->{$var};
295
296            # ignore setting if it isn't defined...
297            # next unless exists $mgr->{__settings}->{$var};
298            $mgr->set($var, $val, 1);
299        }
300    }
301    1;
302}
303
304sub DESTROY { }
305
306use vars qw( $AUTOLOAD );
307sub AUTOLOAD {
308    my $mgr = $_[0];
309    (my $var = $AUTOLOAD) =~ s!.+::!!;
310    $var = lc $var;
311    return $mgr->error(MT->translate("No such config variable '[_1]'", $var))
312        unless exists $mgr->{__settings}->{$var};
313    no strict 'refs';
314    *$AUTOLOAD = sub {
315        my $mgr = shift;
316        @_ ? $mgr->set($var, @_) : $mgr->get($var);
317    };
318    goto &$AUTOLOAD;
319}
320
3211;
322__END__
323
324=head1 NAME
325
326MT::ConfigMgr - Movable Type configuration manager
327
328=head1 SYNOPSIS
329
330    use MT::ConfigMgr;
331    my $cfg = MT::ConfigMgr->instance;
332
333    $cfg->read_config('/path/to/mt.cfg')
334        or die $cfg->errstr;
335
336=head1 DESCRIPTION
337
338I<MT::ConfigMgr> is a singleton class that manages the Movable Type
339configuration file (F<mt.cfg>), allowing access to the config directives
340contained therin.
341
342=head1 USAGE
343
344=head2 MT::ConfigMgr->instance
345
346Returns the singleton I<MT::ConfigMgr> object. Note that when you want the
347object, you should always call I<instance>, never I<new>; I<new> will construct
348a B<new> I<MT::ConfigMgr> object, and that isn't what you want. You want the
349object that has already been initialized with the contents of F<mt.cfg>. This
350initialization is done by I<MT::new>.
351
352=head2 $cfg->read_config($file)
353
354Reads the config file at the path I<$file> and initializes the I<$cfg> object
355with the directives in that file. Returns true on success, C<undef> otherwise;
356if an error occurs you can obtain the error message with C<$cfg-E<gt>errstr>.
357
358=head2 $cfg->define($directive [, %arg ])
359
360Defines the directive I<$directive> as a valid configuration directive; you
361must define new configuration directives B<before> you read the configuration
362file, or else the read will fail.
363
364=head2 $cfg->ExternalUserManagement()
365
366Returns boolean value indicating whether the configuration is set so that
367external users management feature in Movable Type Enterprise is turned on.
368
369=head1 CONFIGURATION DIRECTIVES
370
371The following configuration directives are allowed in F<mt.cfg>. To get the
372value of a directive, treat it as a method that you are calling on the
373I<$cfg> object. For example:
374
375    $cfg->CGIPath
376
377To set the value of a directive, do the same as the above, but pass in a value
378to the method:
379
380    $cfg->CGIPath('http://www.foo.com/mt/');
381
382A list of valid configuration directives can be found in the
383I<CONFIGURATION SETTINGS> section of the manual.
384
385=head1 AUTHOR & COPYRIGHT
386
387Please see the I<MT> manpage for author, copyright, and license information.
388
389=cut
Note: See TracBrowser for help on using the browser.