bugfix : report only the "tagtoreport" field
[koha.git] / C4 / Context.pm
1 # Copyright 2002 Katipo Communications
2 #
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
16 # Suite 330, Boston, MA  02111-1307 USA
17
18 # $Id$
19 # Revision History:
20 # 2004-08-11 A. Tarallo: Added the function db_escheme2dbi, tested my bugfixes,
21 # further  details about them in the code.
22
23 package C4::Context;
24 use strict;
25 use DBI;
26 use C4::Boolean;
27
28 use vars qw($VERSION $AUTOLOAD),
29         qw($context),
30         qw(@context_stack);
31
32 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
33                 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
34
35 =head1 NAME
36
37 C4::Context - Maintain and manipulate the context of a Koha script
38
39 =head1 SYNOPSIS
40
41   use C4::Context;
42
43   use C4::Context("/path/to/koha.conf");
44
45   $config_value = C4::Context->config("config_variable");
46   $db_handle = C4::Context->dbh;
47   $stopwordhash = C4::Context->stopwords;
48
49 =head1 DESCRIPTION
50
51 When a Koha script runs, it makes use of a certain number of things:
52 configuration settings in F</etc/koha.conf>, a connection to the Koha
53 database, and so forth. These things make up the I<context> in which
54 the script runs.
55
56 This module takes care of setting up the context for a script:
57 figuring out which configuration file to load, and loading it, opening
58 a connection to the right database, and so forth.
59
60 Most scripts will only use one context. They can simply have
61
62   use C4::Context;
63
64 at the top.
65
66 Other scripts may need to use several contexts. For instance, if a
67 library has two databases, one for a certain collection, and the other
68 for everything else, it might be necessary for a script to use two
69 different contexts to search both databases. Such scripts should use
70 the C<&set_context> and C<&restore_context> functions, below.
71
72 By default, C4::Context reads the configuration from
73 F</etc/koha.conf>. This may be overridden by setting the C<$KOHA_CONF>
74 environment variable to the pathname of a configuration file to use.
75
76 =head1 METHODS
77
78 =over 2
79
80 =cut
81 #'
82 # In addition to what is said in the POD above, a Context object is a
83 # reference-to-hash with the following fields:
84 #
85 # config
86 #       A reference-to-hash whose keys and values are the
87 #       configuration variables and values specified in the config
88 #       file (/etc/koha.conf).
89 # dbh
90 #       A handle to the appropriate database for this context.
91 # dbh_stack
92 #       Used by &set_dbh and &restore_dbh to hold other database
93 #       handles for this context.
94
95 use constant CONFIG_FNAME => "/etc/koha.conf";
96                                 # Default config file, if none is specified
97
98 $context = undef;               # Initially, no context is set
99 @context_stack = ();            # Initially, no saved contexts
100
101 # read_config_file
102 # Reads the specified Koha config file. Returns a reference-to-hash
103 # whose keys are the configuration variables, and whose values are the
104 # configuration values (duh).
105 # Returns undef in case of error.
106 #
107 # Revision History:
108 # 2004-08-10 A. Tarallo: Added code that checks if a variable was already
109 # assigned and prints a message, otherwise create a new entry in the hash to
110 # be returned. 
111 # Also added code that complaints if finds a line that isn't a variable 
112 # assignmet and skips the line.
113 # Added a quick hack that makes the trasnlation between the db_schema
114 # and the DBI driver for that eschema.
115 #
116 sub read_config_file
117 {
118         my $fname = shift;      # Config file to read
119         my $retval = {};        # Return value: ref-to-hash holding the
120                                 # configuration
121
122         open (CONF, $fname) or return undef;
123
124         while (<CONF>)
125         {
126                 my $var;                # Variable name
127                 my $value;              # Variable value
128
129                 chomp;
130                 s/#.*//;                # Strip comments
131                 next if /^\s*$/;        # Ignore blank lines
132
133                 # Look for a line of the form
134                 #       var = value
135                 if (!/^\s*(\w+)\s*=\s*(.*?)\s*$/)
136                 {
137                         print STDERR 
138                                 "$_ isn't a variable assignment, skipping it";
139                         next;
140                 }
141
142                 # Found a variable assignment
143                 if ( exists $retval->{$1} )
144                 {
145                         print STDERR "$var was already defined, ignoring\n";
146                 }else{
147                 # Quick hack for allowing databases name in full text
148                         if ( $1 eq "db_scheme" )
149                         {
150                                 $value = db_scheme2dbi($2);
151                         }else {
152                                 $value = $2;
153                         }
154                         $var = $1;
155                         $retval->{$var} = $value;
156                 }
157         }
158         close CONF;
159
160         return $retval;
161 }
162
163 # db_scheme2dbi
164 # Translates the full text name of a database into de appropiate dbi name
165
166 sub db_scheme2dbi
167 {
168         my $name = shift;
169
170         for ($name) {
171                 if (/MySQL|mysql/) { return("mysql"); }
172                 if (/Postgres|Pg|PostgresSQL/) { return("Pg"); }
173                 if (/Oracle|oracle|ORACLE/) { return("Oracle"); }
174         }
175         return undef;           # Just in case
176 }
177
178 sub import
179 {
180         my $package = shift;
181         my $conf_fname = shift;         # Config file name
182         my $context;
183
184         # Create a new context from the given config file name, if
185         # any, then set it as the current context.
186         $context = new C4::Context($conf_fname);
187         return undef if !defined($context);
188         $context->set_context;
189 }
190
191 =item new
192
193   $context = new C4::Context;
194   $context = new C4::Context("/path/to/koha.conf");
195
196 Allocates a new context. Initializes the context from the specified
197 file, which defaults to either the file given by the C<$KOHA_CONF>
198 environment variable, or F</etc/koha.conf>.
199
200 C<&new> does not set this context as the new default context; for
201 that, use C<&set_context>.
202
203 =cut
204 #'
205 # Revision History:
206 # 2004-08-10 A. Tarallo: Added check if the conf file is not empty
207 sub new
208 {
209         my $class = shift;
210         my $conf_fname = shift;         # Config file to load
211         my $self = {};
212
213         # check that the specified config file exists and is not empty
214         undef $conf_fname unless 
215                 (defined $conf_fname && -e $conf_fname && -s $conf_fname);
216         # Figure out a good config file to load if none was specified.
217         if (!defined($conf_fname))
218         {
219                 # If the $KOHA_CONF environment variable is set, use
220                 # that. Otherwise, use the built-in default.
221                 $conf_fname = $ENV{"KOHA_CONF"} ||
222                                 CONFIG_FNAME;
223         }
224         $self->{"config_file"} = $conf_fname;
225
226         # Load the desired config file.
227         $self->{"config"} = &read_config_file($conf_fname);
228         return undef if !defined($self->{"config"});
229
230         $self->{"dbh"} = undef;         # Database handle
231         $self->{"stopwords"} = undef; # stopwords list
232
233         bless $self, $class;
234         return $self;
235 }
236
237 =item set_context
238
239   $context = new C4::Context;
240   $context->set_context();
241 or
242   set_context C4::Context $context;
243
244   ...
245   restore_context C4::Context;
246
247 In some cases, it might be necessary for a script to use multiple
248 contexts. C<&set_context> saves the current context on a stack, then
249 sets the context to C<$context>, which will be used in future
250 operations. To restore the previous context, use C<&restore_context>.
251
252 =cut
253 #'
254 sub set_context
255 {
256         my $self = shift;
257         my $new_context;        # The context to set
258
259         # Figure out whether this is a class or instance method call.
260         #
261         # We're going to make the assumption that control got here
262         # through valid means, i.e., that the caller used an instance
263         # or class method call, and that control got here through the
264         # usual inheritance mechanisms. The caller can, of course,
265         # break this assumption by playing silly buggers, but that's
266         # harder to do than doing it properly, and harder to check
267         # for.
268         if (ref($self) eq "")
269         {
270                 # Class method. The new context is the next argument.
271                 $new_context = shift;
272         } else {
273                 # Instance method. The new context is $self.
274                 $new_context = $self;
275         }
276
277         # Save the old context, if any, on the stack
278         push @context_stack, $context if defined($context);
279
280         # Set the new context
281         $context = $new_context;
282 }
283
284 =item restore_context
285
286   &restore_context;
287
288 Restores the context set by C<&set_context>.
289
290 =cut
291 #'
292 sub restore_context
293 {
294         my $self = shift;
295
296         if ($#context_stack < 0)
297         {
298                 # Stack underflow.
299                 die "Context stack underflow";
300         }
301
302         # Pop the old context and set it.
303         $context = pop @context_stack;
304
305         # FIXME - Should this return something, like maybe the context
306         # that was current when this was called?
307 }
308
309 =item config
310
311   $value = C4::Context->config("config_variable");
312
313   $value = C4::Context->config_variable;
314
315 Returns the value of a variable specified in the configuration file
316 from which the current context was created.
317
318 The second form is more compact, but of course may conflict with
319 method names. If there is a configuration variable called "new", then
320 C<C4::Config-E<gt>new> will not return it.
321
322 =cut
323 #'
324 sub config
325 {
326         my $self = shift;
327         my $var = shift;                # The config variable to return
328
329         return undef if !defined($context->{"config"});
330                         # Presumably $self->{config} might be
331                         # undefined if the config file given to &new
332                         # didn't exist, and the caller didn't bother
333                         # to check the return value.
334
335         # Return the value of the requested config variable
336         return $context->{"config"}{$var};
337 }
338
339 =item preference
340
341   $sys_preference = C4::Context->preference("some_variable");
342
343 Looks up the value of the given system preference in the
344 systempreferences table of the Koha database, and returns it. If the
345 variable is not set, or in case of error, returns the undefined value.
346
347 =cut
348 #'
349 # FIXME - The preferences aren't likely to change over the lifetime of
350 # the script (and things might break if they did change), so perhaps
351 # this function should cache the results it finds.
352 sub preference
353 {
354         my $self = shift;
355         my $var = shift;                # The system preference to return
356         my $retval;                     # Return value
357         my $dbh = C4::Context->dbh;     # Database handle
358         my $sth;                        # Database query handle
359
360         # Look up systempreferences.variable==$var
361         $retval = $dbh->selectrow_array(<<EOT);
362                 SELECT  value
363                 FROM    systempreferences
364                 WHERE   variable='$var'
365                 LIMIT   1
366 EOT
367         return $retval;
368 }
369
370 sub boolean_preference ($) {
371         my $self = shift;
372         my $var = shift;                # The system preference to return
373         my $it = preference($self, $var);
374         return defined($it)? C4::Boolean::true_p($it): undef;
375 }
376
377 # AUTOLOAD
378 # This implements C4::Config->foo, and simply returns
379 # C4::Context->config("foo"), as described in the documentation for
380 # &config, above.
381
382 # FIXME - Perhaps this should be extended to check &config first, and
383 # then &preference if that fails. OTOH, AUTOLOAD could lead to crappy
384 # code, so it'd probably be best to delete it altogether so as not to
385 # encourage people to use it.
386 sub AUTOLOAD
387 {
388         my $self = shift;
389
390         $AUTOLOAD =~ s/.*:://;          # Chop off the package name,
391                                         # leaving only the function name.
392         return $self->config($AUTOLOAD);
393 }
394
395 # _new_dbh
396 # Internal helper function (not a method!). This creates a new
397 # database connection from the data given in the current context, and
398 # returns it.
399 sub _new_dbh
400 {
401         my $db_driver = $context->{"config"}{"db_scheme"} || "mysql";
402                 # FIXME - It should be possible to use "MySQL" instead
403                 # of "mysql", "PostgreSQL" instead of "Pg", and so
404                 # forth.
405         my $db_name   = $context->{"config"}{"database"};
406         my $db_host   = $context->{"config"}{"hostname"};
407         my $db_user   = $context->{"config"}{"user"};
408         my $db_passwd = $context->{"config"}{"pass"};
409         return DBI->connect("DBI:$db_driver:$db_name:$db_host",
410                             $db_user, $db_passwd);
411 }
412
413 =item dbh
414
415   $dbh = C4::Context->dbh;
416
417 Returns a database handle connected to the Koha database for the
418 current context. If no connection has yet been made, this method
419 creates one, and connects to the database.
420
421 This database handle is cached for future use: if you call
422 C<C4::Context-E<gt>dbh> twice, you will get the same handle both
423 times. If you need a second database handle, use C<&new_dbh> and
424 possibly C<&set_dbh>.
425
426 =cut
427 #'
428 sub dbh
429 {
430         my $self = shift;
431
432         # If there's already a database handle, return it.
433         return $context->{"dbh"} if defined($context->{"dbh"});
434
435         # No database handle yet. Create one.
436         $context->{"dbh"} = &_new_dbh();
437
438         return $context->{"dbh"};
439 }
440
441 =item new_dbh
442
443   $dbh = C4::Context->new_dbh;
444
445 Creates a new connection to the Koha database for the current context,
446 and returns the database handle (a C<DBI::db> object).
447
448 The handle is not saved anywhere: this method is strictly a
449 convenience function; the point is that it knows which database to
450 connect to so that the caller doesn't have to know.
451
452 =cut
453 #'
454 sub new_dbh
455 {
456         my $self = shift;
457
458         return &_new_dbh();
459 }
460
461 =item set_dbh
462
463   $my_dbh = C4::Connect->new_dbh;
464   C4::Connect->set_dbh($my_dbh);
465   ...
466   C4::Connect->restore_dbh;
467
468 C<&set_dbh> and C<&restore_dbh> work in a manner analogous to
469 C<&set_context> and C<&restore_context>.
470
471 C<&set_dbh> saves the current database handle on a stack, then sets
472 the current database handle to C<$my_dbh>.
473
474 C<$my_dbh> is assumed to be a good database handle.
475
476 =cut
477 #'
478 sub set_dbh
479 {
480         my $self = shift;
481         my $new_dbh = shift;
482
483         # Save the current database handle on the handle stack.
484         # We assume that $new_dbh is all good: if the caller wants to
485         # screw himself by passing an invalid handle, that's fine by
486         # us.
487         push @{$context->{"dbh_stack"}}, $context->{"dbh"};
488         $context->{"dbh"} = $new_dbh;
489 }
490
491 =item restore_dbh
492
493   C4::Context->restore_dbh;
494
495 Restores the database handle saved by an earlier call to
496 C<C4::Context-E<gt>set_dbh>.
497
498 =cut
499 #'
500 sub restore_dbh
501 {
502         my $self = shift;
503
504         if ($#{$context->{"dbh_stack"}} < 0)
505         {
506                 # Stack underflow
507                 die "DBH stack underflow";
508         }
509
510         # Pop the old database handle and set it.
511         $context->{"dbh"} = pop @{$context->{"dbh_stack"}};
512
513         # FIXME - If it is determined that restore_context should
514         # return something, then this function should, too.
515 }
516
517 =item stopwords
518
519   $dbh = C4::Context->stopwords;
520
521 Returns a hash with stopwords.
522
523 This hash is cached for future use: if you call
524 C<C4::Context-E<gt>stopwords> twice, you will get the same hash without real DB access
525
526 =cut
527 #'
528 sub stopwords
529 {
530         my $retval = {};
531
532         # If the hash already exists, return it.
533         return $context->{"stopwords"} if defined($context->{"stopwords"});
534
535         # No hash. Create one.
536         $context->{"stopwords"} = &_new_stopwords();
537
538         return $context->{"stopwords"};
539 }
540
541 # _new_stopwords
542 # Internal helper function (not a method!). This creates a new
543 # hash with stopwords
544 sub _new_stopwords
545 {
546         my $dbh = C4::Context->dbh;
547         my $stopwordlist;
548         my $sth = $dbh->prepare("select word from stopwords");
549         $sth->execute;
550         while (my $stopword = $sth->fetchrow_array) {
551                 my $retval = {};
552                 $stopwordlist->{$stopword} = uc($stopword);
553         }
554         return $stopwordlist;
555 }
556
557 1;
558 __END__
559
560 =back
561
562 =head1 ENVIRONMENT
563
564 =over 4
565
566 =item C<KOHA_CONF>
567
568 Specifies the configuration file to read.
569
570 =back
571
572 =head1 SEE ALSO
573
574 DBI(3)
575
576 =head1 AUTHOR
577
578 Andrew Arensburger <arensb at ooblick dot com>
579
580 =cut