Merge remote-tracking branch 'kc/new/enh/bug_5922' into kcmaster
[wip/koha-chris_n.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     C4::Context->clear_syspref_cache();
130 }
131
132 sub _fetch_preference {
133     my ( $preference ) = @_;
134
135     my $dbh = C4::Context->dbh;
136
137     my $row = $dbh->selectrow_hashref( "
138         SELECT
139           variable, value, type
140           FROM systempreferences
141           WHERE variable = ?
142           LIMIT 1
143     ", {}, $preference );
144
145     exit 2 unless ( $row );
146
147     return $row;
148 }
149
150 sub GetPreference {
151     my ( $preference ) = @_;
152
153     my $row = _fetch_preference( $preference );
154
155     return _normalize_value( $row );
156 }
157
158 sub SetPreference {
159     my ( $preference, $value ) = @_;
160
161     my $row = _fetch_preference( $preference );
162
163     $value = C4::Boolean::true_p($value) ? 1 : 0 if ( $row->{'type'} eq 'YesNo' );
164
165     exit 3 if ( $value eq $row->{'value'} );
166
167     _set_preference( $preference, $value );
168 }
169
170 sub ClearPreference {
171     my ( $preference ) = @_;
172
173     my $value = '';
174
175     my $row = _fetch_preference( $preference );
176
177     $value = 0 if ( $row->{'type'} eq 'YesNo' );
178
179     exit 3 if ( $value eq $row->{'value'} );
180
181     _set_preference( $preference, $value );
182 }
183
184 =head1 OPTIONS
185
186 COMMAND can be any of the following:
187
188 =over
189
190 =item B<dump> [ -o I<OUTFILE> ]
191
192 Dump all of Koha's system preferences as a simple YAML mapping into OUTFILE or
193 STDOUT.
194
195 =item B<load> [ -i I<INFILE> ] [ -f|--force ]
196
197 Reads system preferences specified in YAML in INFILE or STDIN.  Will exit with a
198 status of 2, without setting any sysprefs, if any of the sysprefs do not exist.
199 Will also exit if any of the sysprefs are YesNo and have an invalid value.
200
201 If there is a Version syspref in the input, it will not be set in the database,
202 but it will be checked to make sure the running Koha version is equal or higher.
203 The script will exit with a status of 4 if this is not true. Pass the -f option
204 to skip this check.
205
206 =item B<get> I<PREFERENCE>
207
208 Print the value of the system preference PREFERENCE, followed by a newline.  If
209 no such syspref exists, will exit with a status of 2.
210
211 =item B<set> I<PREFERENCE> I<VALUE>
212
213 Set the system preference PREFERENCE to the value VALUE. If no such syspref
214 exists, will exit with a status of 2. If the syspref already has that value,
215 will exit with a status of 3.
216
217 If the syspref is YesNo, will accept only a boolean value, but the syntax for
218 these is fairly lax (yes/no, on/off, 1/0, n/y, true/false are all accepted).
219
220 =item B<clear> I<PREFERENCE>
221
222 Clears the value of the system preference PREFERENCE. If no such syspref exists,
223 will exit with a status of 2. Will set YesNo sysprefs to 'false'.
224
225 =item B<manual>
226
227 Print a longer, more detailed manual.
228
229 =cut
230
231 my %commands = (
232     dump => sub{
233         my ( $outfile );
234
235         GetOptions(
236             'o:s' => \$outfile
237         ) || _print_usage( 1 );
238
239         if ( $outfile ) {
240             YAML::Syck::DumpFile( $outfile, GetPreferences() );
241         } else {
242             print YAML::Syck::Dump( GetPreferences() );
243         }
244     },
245     load => sub {
246         my ( $infile, $force_version );
247
248         GetOptions(
249             'i:s' => \$infile,
250             'f' => \$force_version,
251         );
252
253         my $preferences = YAML::Syck::LoadFile($infile || \*STDIN);
254
255         die "Expected a YAML mapping" if ( ref($preferences) ne 'HASH' );
256
257         die "Tried to load preferences for version " . $preferences->{'Version'} . ", we are " . C4::Context->preference( 'Version' ) if ( $preferences->{'Version'} && C4::Context->preference( 'Version' ) < $preferences->{'Version'} );
258
259         my %prefs_to_set = (
260             map { $_, $preferences->{$_} }
261             grep { !$NOT_SET_PREFS{$_} }
262             keys %$preferences
263         );
264
265         SetPreferences( %prefs_to_set );
266     },
267     get => sub {
268         my ( $preference ) = @_;
269
270         print_usage() unless ( $preference );
271
272         print GetPreference( $preference ) . "\n";
273     },
274     set => sub {
275         my ( $preference, $value ) = @_;
276
277         print_usage() unless ( $preference && defined($value) );
278
279         SetPreference( $preference, $value );
280     },
281     clear => sub {
282         my ( $preference ) = @_;
283
284         print_usage() unless ( $preference );
285
286         ClearPreference( $preference );
287     },
288     manual => sub {
289         pod2usage( -verbose => 2 );
290     }
291 );
292
293 print_usage() if ( $ARGV[0] =~ /^(-h|--help|-help|help)$/ );
294
295 print_usage( 1 ) if ( !$ARGV[0] || ref($commands{$ARGV[0]}) ne 'CODE' );
296
297 $command = $commands{$ARGV[0]};
298 shift @ARGV;
299 $command->(@ARGV);
300
301 =item B<help>
302
303 Print a short usage message.
304
305 =back
306
307 =head1 EXAMPLES
308
309   $ export KOHA_DEBUG=1 # Used here to show what is being stored
310   $ misc/admin/koha-preferences get viewISBD
311   0
312   $ misc/admin/koha-preferences set viewISBD on
313   Setting viewISBD to 1
314   $ misc/admin/koha-preferences dump -o preferences.yaml
315   $ [ edit preferences.yaml ]
316   $ misc/admin/koha-preferences load -i preferences.yaml
317   $ misc/admin/koha-preferences load -i preferences-too-new.yaml
318   Tried to load preferences for version 3.0500012, we are 3.0300009 at misc/admin/koha-preferences line 255
319   $ misc/admin/koha-preferences load # Can also work from STDIN
320   XISBN: false
321   viewMARC: y
322   [ Control-D ]
323   Setting viewMARC to 1
324   Setting XISBN to 0
325
326 =cut