Extending Context.pm to add stopword management and using it in MARC-API.
[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 package C4::Context;
19 use strict;
20 use DBI;
21
22 use vars qw($VERSION $AUTOLOAD),
23         qw($context),
24         qw(@context_stack);
25
26 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
27                 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
28
29 =head1 NAME
30
31 C4::Context - Maintain and manipulate the context of a Koha script
32
33 =head1 SYNOPSIS
34
35   use C4::Context;
36
37   use C4::Context("/path/to/koha.conf");
38
39   $config_value = C4::Context->config("config_variable");
40   $db_handle = C4::Context->dbh;
41
42 =head1 DESCRIPTION
43
44 When a Koha script runs, it makes use of a certain number of things:
45 configuration settings in F</etc/koha.conf>, a connection to the Koha
46 database, and so forth. These things make up the I<context> in which
47 the script runs.
48
49 This module takes care of setting up the context for a script:
50 figuring out which configuration file to load, and loading it, opening
51 a connection to the right database, and so forth.
52
53 Most scripts will only use one context. They can simply have
54
55   use C4::Context;
56
57 at the top.
58
59 Other scripts may need to use several contexts. For instance, if a
60 library has two databases, one for a certain collection, and the other
61 for everything else, it might be necessary for a script to use two
62 different contexts to search both databases. Such scripts should use
63 the C<&set_context> and C<&restore_context> functions, below.
64
65 By default, C4::Context reads the configuration from
66 F</etc/koha.conf>. This may be overridden by setting the C<$KOHA_CONF>
67 environment variable to the pathname of a configuration file to use.
68
69 =head1 METHODS
70
71 =over 2
72
73 =cut
74 #'
75 # In addition to what is said in the POD above, a Context object is a
76 # reference-to-hash with the following fields:
77 #
78 # config
79 #       A reference-to-hash whose keys and values are the
80 #       configuration variables and values specified in the config
81 #       file (/etc/koha.conf).
82 # dbh
83 #       A handle to the appropriate database for this context.
84 # dbh_stack
85 #       Used by &set_dbh and &restore_dbh to hold other database
86 #       handles for this context.
87
88 use constant CONFIG_FNAME => "/etc/koha.conf";
89                                 # Default config file, if none is specified
90
91 $context = undef;               # Initially, no context is set
92 @context_stack = ();            # Initially, no saved contexts
93
94 # read_config_file
95 # Reads the specified Koha config file. Returns a reference-to-hash
96 # whose keys are the configuration variables, and whose values are the
97 # configuration values (duh).
98 # Returns undef in case of error.
99 sub read_config_file
100 {
101         my $fname = shift;      # Config file to read
102         my $retval = {};        # Return value: ref-to-hash holding the
103                                 # configuration
104
105         open (CONF, $fname) or return undef;
106
107         while (<CONF>)
108         {
109                 my $var;                # Variable name
110                 my $value;              # Variable value
111
112                 chomp;
113                 s/#.*//;                # Strip comments
114                 next if /^\s*$/;        # Ignore blank lines
115
116                 # Look for a line of the form
117                 #       var = value
118                 if (!/^\s*(\w+)\s*=\s*(.*?)\s*$/)
119                 {
120                         # FIXME - Complain about bogus line
121                         next;
122                 }
123
124                 # Found a variable assignment
125                 # FIXME - Ought to complain is this line sets a
126                 # variable that was already set.
127                 $var = $1;
128                 $value = $2;
129                 $retval->{$var} = $value;
130         }
131         close CONF;
132
133         return $retval;
134 }
135
136 sub import
137 {
138         my $package = shift;
139         my $conf_fname = shift;         # Config file name
140         my $context;
141
142         # Create a new context from the given config file name, if
143         # any, then set it as the current context.
144         $context = new C4::Context($conf_fname);
145         return undef if !defined($context);
146         $context->set_context;
147 }
148
149 =item new
150
151   $context = new C4::Context;
152   $context = new C4::Context("/path/to/koha.conf");
153
154 Allocates a new context. Initializes the context from the specified
155 file, which defaults to either the file given by the C<$KOHA_CONF>
156 environment variable, or F</etc/koha.conf>.
157
158 C<&new> does not set this context as the new default context; for
159 that, use C<&set_context>.
160
161 =cut
162 #'
163 sub new
164 {
165         my $class = shift;
166         my $conf_fname = shift;         # Config file to load
167         my $self = {};
168
169         # Figure out a good config file to load if none was specified.
170         if (!defined($conf_fname))
171         {
172                 # If the $KOHA_CONF environment variable is set, use
173                 # that. Otherwise, use the built-in default.
174                 $conf_fname = $ENV{"KOHA_CONF"} ||
175                                 CONFIG_FNAME;
176         }
177
178         $self->{"config_file"} = $conf_fname;
179
180         # Load the desired config file.
181         $self->{"config"} = &read_config_file($conf_fname);
182         return undef if !defined($self->{"config"});
183
184         $self->{"dbh"} = undef;         # Database handle
185         $self->{"stopwords"} = undef; # stopwords list
186
187         bless $self, $class;
188         return $self;
189 }
190
191 =item set_context
192
193   $context = new C4::Context;
194   $context->set_context();
195 or
196   set_context C4::Context $context;
197
198   ...
199   restore_context C4::Context;
200
201 In some cases, it might be necessary for a script to use multiple
202 contexts. C<&set_context> saves the current context on a stack, then
203 sets the context to C<$context>, which will be used in future
204 operations. To restore the previous context, use C<&restore_context>.
205
206 =cut
207 #'
208 sub set_context
209 {
210         my $self = shift;
211         my $new_context;        # The context to set
212
213         # Figure out whether this is a class or instance method call.
214         #
215         # We're going to make the assumption that control got here
216         # through valid means, i.e., that the caller used an instance
217         # or class method call, and that control got here through the
218         # usual inheritance mechanisms. The caller can, of course,
219         # break this assumption by playing silly buggers, but that's
220         # harder to do than doing it properly, and harder to check
221         # for.
222         if (ref($self) eq "")
223         {
224                 # Class method. The new context is the next argument.
225                 $new_context = shift;
226         } else {
227                 # Instance method. The new context is $self.
228                 $new_context = $self;
229         }
230
231         # Save the old context, if any, on the stack
232         push @context_stack, $context if defined($context);
233
234         # Set the new context
235         $context = $new_context;
236 }
237
238 =item restore_context
239
240   &restore_context;
241
242 Restores the context set by C<&set_context>.
243
244 =cut
245 #'
246 sub restore_context
247 {
248         my $self = shift;
249
250         if ($#context_stack < 0)
251         {
252                 # Stack underflow.
253                 die "Context stack underflow";
254         }
255
256         # Pop the old context and set it.
257         $context = pop @context_stack;
258
259         # FIXME - Should this return something, like maybe the context
260         # that was current when this was called?
261 }
262
263 =item config
264
265   $value = C4::Context->config("config_variable");
266
267   $value = C4::Context->config_variable;
268
269 Returns the value of a variable specified in the configuration file
270 from which the current context was created.
271
272 The second form is more compact, but of course may conflict with
273 method names. If there is a configuration variable called "new", then
274 C<C4::Config-E<gt>new> will not return it.
275
276 =cut
277 #'
278 sub config
279 {
280         my $self = shift;
281         my $var = shift;                # The config variable to return
282
283         return undef if !defined($context->{"config"});
284                         # Presumably $self->{config} might be
285                         # undefined if the config file given to &new
286                         # didn't exist, and the caller didn't bother
287                         # to check the return value.
288
289         # Return the value of the requested config variable
290         return $context->{"config"}{$var};
291 }
292
293 # AUTOLOAD
294 # This implements C4::Config->foo, and simply returns
295 # C4::Context->config("foo"), as described in the documentation for
296 # &config, above.
297 sub AUTOLOAD
298 {
299         my $self = shift;
300
301         $AUTOLOAD =~ s/.*:://;          # Chop off the package name,
302                                         # leaving only the function name.
303         return $self->config($AUTOLOAD);
304 }
305
306 # _new_dbh
307 # Internal helper function (not a method!). This creates a new
308 # database connection from the data given in the current context, and
309 # returns it.
310 sub _new_dbh
311 {
312         my $db_driver = $context->{"config"}{"db_scheme"} || "mysql";
313         my $db_name   = $context->{"config"}{"database"};
314         my $db_host   = $context->{"config"}{"hostname"};
315         my $db_user   = $context->{"config"}{"user"};
316         my $db_passwd = $context->{"config"}{"pass"};
317
318         return DBI->connect("DBI:$db_driver:$db_name:$db_host",
319                             $db_user, $db_passwd);
320 }
321
322 =item dbh
323
324   $dbh = C4::Context->dbh;
325
326 Returns a database handle connected to the Koha database for the
327 current context. If no connection has yet been made, this method
328 creates one, and connects to the database.
329
330 This database handle is cached for future use: if you call
331 C<C4::Context-E<gt>dbh> twice, you will get the same handle both
332 times. If you need a second database handle, use C<&new_dbh> and
333 possibly C<&set_dbh>.
334
335 =cut
336 #'
337 sub dbh
338 {
339         my $self = shift;
340
341         # If there's already a database handle, return it.
342         return $context->{"dbh"} if defined($context->{"dbh"});
343
344         # No database handle yet. Create one.
345         $context->{"dbh"} = &_new_dbh();
346
347         return $context->{"dbh"};
348 }
349
350 =item new_dbh
351
352   $dbh = C4::Context->new_dbh;
353
354 Creates a new connection to the Koha database for the current context,
355 and returns the database handle (a C<DBI::db> object).
356
357 The handle is not saved anywhere: this method is strictly a
358 convenience function; the point is that it knows which database to
359 connect to so that the caller doesn't have to know.
360
361 =cut
362 #'
363 sub new_dbh
364 {
365         my $self = shift;
366
367         return &_new_dbh();
368 }
369
370 =item set_dbh
371
372   $my_dbh = C4::Connect->new_dbh;
373   C4::Connect->set_dbh($my_dbh);
374   ...
375   C4::Connect->restore_dbh;
376
377 C<&set_dbh> and C<&restore_dbh> work in a manner analogous to
378 C<&set_context> and C<&restore_context>.
379
380 C<&set_dbh> saves the current database handle on a stack, then sets
381 the current database handle to C<$my_dbh>.
382
383 C<$my_dbh> is assumed to be a good database handle.
384
385 =cut
386 #'
387 sub set_dbh
388 {
389         my $self = shift;
390         my $new_dbh = shift;
391
392         # Save the current database handle on the handle stack.
393         # We assume that $new_dbh is all good: if the caller wants to
394         # screw himself by passing an invalid handle, that's fine by
395         # us.
396         push @{$context->{"dbh_stack"}}, $context->{"dbh"};
397         $context->{"dbh"} = $new_dbh;
398 }
399
400 =item restore_dbh
401
402   C4::Context->restore_dbh;
403
404 Restores the database handle saved by an earlier call to
405 C<C4::Context-E<gt>set_dbh>.
406
407 =cut
408 #'
409 sub restore_dbh
410 {
411         my $self = shift;
412
413         if ($#{$context->{"dbh_stack"}} < 0)
414         {
415                 # Stack underflow
416                 die "DBH stack underflow";
417         }
418
419         # Pop the old database handle and set it.
420         $context->{"dbh"} = pop @{$context->{"dbh_stack"}};
421
422         # FIXME - If it is determined that restore_context should
423         # return something, then this function should, too.
424 }
425
426 =item stopwords
427
428   $dbh = C4::Context->stopwords;
429
430 Returns a hash with stopwords.
431
432 This hash is cached for future use: if you call
433 C<C4::Context-E<gt>stopwords> twice, you will get the same hash without real DB access
434
435 =cut
436 #'
437 sub stopwords
438 {
439         my $retval = {};
440
441         # If the hash already exists, return it.
442         return $context->{"stopwords"} if defined($context->{"stopwords"});
443
444         # No hash. Create one.
445         $context->{"stopwords"} = &_new_stopwords();
446
447         return $context->{"stopwords"};
448 }
449
450 # _new_stopwords
451 # Internal helper function (not a method!). This creates a new
452 # hash with stopwords
453 sub _new_stopwords
454 {
455         my $dbh = &dbh;
456         my $stopwordlist;
457         my $sth = $dbh->prepare("select word from stopwords");
458         $sth->execute;
459         while (my $stopword = $sth->fetchrow_array) {
460         my $retval = {};
461         $stopwordlist->{$stopword} = uc($stopword);
462         }
463         return $stopwordlist;
464 #       my $db_driver = $context->{"config"}{"db_scheme"} || "mysql";
465 #       my $db_name   = $context->{"config"}{"database"};
466 #       my $db_host   = $context->{"config"}{"hostname"};
467 #       my $db_user   = $context->{"config"}{"user"};
468 #       my $db_passwd = $context->{"config"}{"pass"};
469
470 #       return DBI->connect("DBI:$db_driver:$db_name:$db_host",
471 #                           $db_user, $db_passwd);
472 }
473
474 1;
475 __END__
476 =back
477
478 =head1 ENVIRONMENT
479
480 =over 4
481
482 =item C<KOHA_CONF>
483
484 Specifies the configuration file to read.
485
486 =back
487
488 =head1 SEE ALSO
489
490 L<DBI(3)|DBI>
491
492 =head1 AUTHOR
493
494 Andrew Arensburger
495
496 =cut