root/branches/release-35/lib/MT/ConfigMgr.pm @ 1910

Revision 1910, 11.1 kB (checked in by auno, 20 months ago)

Not to change set_internal return value. BugzID:79302
If the SecretToken doesn't set, MT->config->SecretToken returns wrong value.

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    $mgr->set_dirty() if defined($_[2]) && $_[2];
134    my $set = $db_flag ? '__dbvar' : '__var';
135    if (defined(my $alias = $mgr->{__settings}{$var}{alias})) {
136        if ($max_depth < $depth) {
137            die MT->translate('Alias for [_1] is looping in the configuration.', $alias);
138        }
139        local $depth = $depth + 1;
140        $mgr->set($alias, $val, $db_flag);
141    } elsif (my $type = $mgr->{__settings}{$var}{type}) {
142        if ($type eq 'ARRAY') {
143            if (ref $val eq 'ARRAY') {
144                $mgr->{$set}{$var} = $val;
145            } else {
146                $mgr->{$set}{$var} ||= [];
147                push @{ $mgr->{$set}{$var} }, $val if defined $val;
148            }
149        } elsif ($type eq 'HASH') {
150            my $hash = $mgr->{$set}{$var};
151            $hash = $mgr->default($var) unless defined $hash;
152            if (ref $val eq 'HASH') {
153                $mgr->{$set}{$var} = $val;
154            } else {
155                $hash ||= {};
156                (my($key), $val) = split /=/, $val;
157                $mgr->{$set}{$var}{$key} = $val;
158            }
159        } else {
160            $mgr->{$set}{$var} = $val;
161        }
162    } else {
163        $mgr->{$set}{$var} = $val;
164    }
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.