Bug 21395: Fix misc/admin/koha-preferences
[koha-equinox.git] / misc / admin / koha-preferences
1 #!/usr/bin/perl
2 #
3 # Copyright 2010 Jesse Weaver, Koha Dev Team
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19 #
20
21 use Modern::Perl;
22 use Koha::Script;
23 use C4::Boolean;
24 use C4::Context;
25 use C4::Debug;
26 use C4::Log;
27 use Getopt::Long;
28 use Pod::Usage;
29 use YAML::Syck qw();
30 $YAML::Syck::ImplicitTyping = 1;
31 $YAML::Syck::SortKeys = 1;
32 our %NOT_SET_PREFS = map { $_, 1 } qw( Version );
33
34 =head1 NAME
35
36 koha-preferences - Get, set, dump and load Koha system preferences
37
38 =head1 SYNOPSIS
39
40 misc/admin/koha-preferences COMMAND ...
41
42 =cut
43
44 sub print_usage {
45     my ( $annoyed ) = @_;
46
47     if ( $annoyed ) {
48         pod2usage( -verbose => 1, -exitval => 1, -output => \*STDERR );
49     } else {
50         pod2usage( -verbose => 1, -exitval => 0 );
51     }
52 }
53
54 sub _debug {
55     my ( $message ) = @_;
56
57     print STDERR $message . "\n" if ( $C4::Debug::debug );
58 }
59
60 sub _normalize_value {
61     my ($row) = @_;
62
63     # AFAIK, there are no sysprefs where NULL has a different meaning than ''.
64     return ( $row->{'value'} || '' ) unless ( $row->{'type'} eq 'YesNo' );
65
66     #
67     # In theory, YesNo sysprefs should always contain 1/0.
68     # In practice, however, they don't.
69     # - Yogi Berra
70     #
71     my $bool_value = eval {
72         return C4::Boolean::true_p( $row->{'value'} ) ? 1 : 0;
73     };
74     if ( $@ ) {
75         return 0;
76     } else {
77         return $bool_value;
78     }
79 }
80
81 sub _set_preference {
82     my ( $preference, $value ) = @_;
83
84     _debug( "Setting $preference to $value" );
85
86     C4::Context->set_preference( $preference, $value );
87 }
88
89 sub GetPreferences {
90     my $dbh = C4::Context->dbh;
91
92     return {
93         map { $_->{'variable'},  _normalize_value($_) }
94         @{ $dbh->selectall_arrayref( "
95             SELECT
96               variable, value, type
97               FROM systempreferences
98         ", { Slice => {} } ) }
99     };
100 }
101
102 sub SetPreferences {
103     my ( %preferences ) = @_;
104
105     my $dbh = C4::Context->dbh;
106
107     # First, a quick check to make sure all of the system preferences exist
108     my $current_state = $dbh->selectall_arrayref( "
109         SELECT
110           variable, type, value
111           FROM systempreferences
112           WHERE variable IN (" . join( ', ', map( "?", keys %preferences ) ) . ")
113     ", { Slice => {} }, keys %preferences );
114
115     exit 2 if ( scalar( @$current_state ) != scalar( keys %preferences ) );
116
117     foreach my $row ( @$current_state ) {
118         # YAML::Syck encodes no as '', so deal with that
119         $preferences{$row->{'variable'}} = $preferences{$row->{'variable'}} eq '' ? 0 : C4::Boolean::true_p( $preferences{$row->{'variable'}} ) if ( $row->{'type'} eq 'YesNo' );
120     }
121
122     # Iterate through again, now that we've checked all of the YesNo sysprefs
123
124     foreach my $row ( @$current_state ) {
125         next if ( $preferences{$row->{'variable'}} eq $row->{'value'} );
126
127         _set_preference( $row->{'variable'}, $preferences{$row->{'variable'}} );
128     }
129
130     # FIXME This may be not needed
131     C4::Context->clear_syspref_cache();
132 }
133
134 sub _fetch_preference {
135     my ( $preference ) = @_;
136
137     my $dbh = C4::Context->dbh;
138
139     my $row = $dbh->selectrow_hashref( "
140         SELECT
141           variable, value, type
142           FROM systempreferences
143           WHERE variable = ?
144           LIMIT 1
145     ", {}, $preference );
146
147     exit 2 unless ( $row );
148
149     return $row;
150 }
151
152 sub GetPreference {
153     my ( $preference ) = @_;
154
155     my $row = _fetch_preference( $preference );
156
157     return _normalize_value( $row );
158 }
159
160 sub SetPreference {
161     my ( $preference, $value ) = @_;
162
163     my $row = _fetch_preference( $preference );
164
165     $value = C4::Boolean::true_p($value) ? 1 : 0 if ( $row->{'type'} eq 'YesNo' );
166
167     exit 3 if ( $value eq $row->{'value'} );
168
169     _set_preference( $preference, $value );
170 }
171
172 sub ClearPreference {
173     my ( $preference ) = @_;
174
175     my $value = '';
176
177     my $row = _fetch_preference( $preference );
178
179     $value = 0 if ( $row->{'type'} eq 'YesNo' );
180
181     exit 3 if ( $value eq $row->{'value'} );
182
183     _set_preference( $preference, $value );
184 }
185
186 =head1 OPTIONS
187
188 COMMAND can be any of the following:
189
190 =over
191
192 =item B<dump> [ -o I<OUTFILE> ]
193
194 Dump all of Koha's system preferences as a simple YAML mapping into OUTFILE or
195 STDOUT.
196
197 =item B<load> [ -i I<INFILE> ] [ -f|--force ]
198
199 Reads system preferences specified in YAML in INFILE or STDIN.  Will exit with a
200 status of 2, without setting any sysprefs, if any of the sysprefs do not exist.
201 Will also exit if any of the sysprefs are YesNo and have an invalid value.
202
203 If there is a Version syspref in the input, it will not be set in the database,
204 but it will be checked to make sure the running Koha version is equal or higher.
205 The script will exit with a status of 4 if this is not true. Pass the -f option
206 to skip this check.
207
208 =item B<get> I<PREFERENCE>
209
210 Print the value of the system preference PREFERENCE, followed by a newline.  If
211 no such syspref exists, will exit with a status of 2.
212
213 =item B<set> I<PREFERENCE> I<VALUE>
214
215 Set the system preference PREFERENCE to the value VALUE. If no such syspref
216 exists, will exit with a status of 2. If the syspref already has that value,
217 will exit with a status of 3.
218
219 If the syspref is YesNo, will accept only a boolean value, but the syntax for
220 these is fairly lax (yes/no, on/off, 1/0, n/y, true/false are all accepted).
221
222 =item B<clear> I<PREFERENCE>
223
224 Clears the value of the system preference PREFERENCE. If no such syspref exists,
225 will exit with a status of 2. Will set YesNo sysprefs to 'false'.
226
227 =item B<manual>
228
229 Print a longer, more detailed manual.
230
231 =cut
232
233 my %commands = (
234     dump => sub{
235         my ( $outfile );
236
237         GetOptions(
238             'o:s' => \$outfile
239         ) || _print_usage( 1 );
240
241         if ( $outfile ) {
242             YAML::Syck::DumpFile( $outfile, GetPreferences() );
243         } else {
244             print YAML::Syck::Dump( GetPreferences() );
245         }
246     },
247     load => sub {
248         my ( $infile, $force_version );
249
250         GetOptions(
251             'i:s' => \$infile,
252             'f' => \$force_version,
253         );
254
255         my $preferences = YAML::Syck::LoadFile($infile || \*STDIN);
256
257         die "Expected a YAML mapping" if ( ref($preferences) ne 'HASH' );
258
259         die "Tried to load preferences for version " . $preferences->{'Version'} . ", we are " . C4::Context->preference( 'Version' ) if ( $preferences->{'Version'} && C4::Context->preference( 'Version' ) < $preferences->{'Version'} );
260
261         my %prefs_to_set = (
262             map { $_, $preferences->{$_} }
263             grep { !$NOT_SET_PREFS{$_} }
264             keys %$preferences
265         );
266
267         SetPreferences( %prefs_to_set );
268     },
269     get => sub {
270         my ( $preference ) = @_;
271
272         print_usage() unless ( $preference );
273
274         print GetPreference( $preference ) . "\n";
275     },
276     set => sub {
277         my ( $preference, $value ) = @_;
278
279         print_usage() unless ( $preference && defined($value) );
280
281         SetPreference( $preference, $value );
282     },
283     clear => sub {
284         my ( $preference ) = @_;
285
286         print_usage() unless ( $preference );
287
288         ClearPreference( $preference );
289     },
290     manual => sub {
291         pod2usage( -verbose => 2 );
292     }
293 );
294
295 print_usage() if ( $ARGV[0] =~ /^(-h|--help|-help|help)$/ );
296
297 print_usage( 1 ) if ( !$ARGV[0] || ref($commands{$ARGV[0]}) ne 'CODE' );
298
299 my $command = $commands{$ARGV[0]};
300 shift @ARGV;
301 $command->(@ARGV);
302
303 =item B<help>
304
305 Print a short usage message.
306
307 =back
308
309 =head1 EXAMPLES
310
311   $ export KOHA_DEBUG=1 # Used here to show what is being stored
312   $ misc/admin/koha-preferences get viewISBD
313   0
314   $ misc/admin/koha-preferences set viewISBD on
315   Setting viewISBD to 1
316   $ misc/admin/koha-preferences dump -o preferences.yaml
317   $ [ edit preferences.yaml ]
318   $ misc/admin/koha-preferences load -i preferences.yaml
319   $ misc/admin/koha-preferences load -i preferences-too-new.yaml
320   Tried to load preferences for version 3.0500012, we are 3.0300009 at misc/admin/koha-preferences line 255
321   $ misc/admin/koha-preferences load # Can also work from STDIN
322   XISBN: false
323   viewMARC: y
324   [ Control-D ]
325   Setting viewMARC to 1
326   Setting XISBN to 0
327
328 =cut