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