Bug 11081: Port Koha::Contrib::Tamil indexer into Koha code base
[koha.git] / C4 / Creators / Layout.pm
1 package C4::Creators::Layout;
2
3 use strict;
4 use warnings;
5
6 use autouse 'Data::Dumper' => qw(Dumper);
7
8 use C4::Context;
9 use C4::Debug;
10 use C4::Creators::PDF;
11
12 BEGIN {
13     use version; our $VERSION = qv('3.07.00.049');
14 }
15
16 # FIXME: Consider this style parameter verification instead...
17 #  my %param = @_;
18 #   for (keys %param)
19 #    {   my $lc = lc($_);
20 #        if (exists $default{$lc})
21 #        {  $default{$lc} = $param{$_};
22 #        }
23 #        else
24 #        {  print STDERR "Unknown parameter $_ , not used \n";
25 #        }
26 #    }
27
28 sub _check_params {
29     my $exit_code = 0;
30     my @valtmpl_id_params = (
31         'layout_id',
32         'barcode_type',
33         'printing_type',
34         'layout_name',
35         'guidebox',
36         'oblique_title',
37         'font',
38         'font_size',
39         'callnum_split',
40         'text_justify',
41         'format_string',
42         'layout_xml',           # FIXME: all layouts should be stored in xml format to greatly simplify handling -chris_n
43         'creator',
44         'units',
45         'start_label',
46     );
47     if (scalar(@_) >1) {
48         my %given_params = @_;
49         foreach my $key (keys %given_params) {
50             if (!(grep m/$key/, @valtmpl_id_params)) {
51                 warn sprintf('(Multiple parameters) Unrecognized parameter type of "%s".', $key);
52                 $exit_code = 1;
53             }
54         }
55     }
56     else {
57         if (!(grep m/$_/, @valtmpl_id_params)) {
58             warn sprintf('(Single parameter) Unrecognized parameter type of "%s".', $_);
59             $exit_code = 1;
60         }
61     }
62     return $exit_code;
63 }
64
65 use constant PRESET_FIELDS => [qw(title author isbn issn itemtype barcode itemcallnumber)];
66 sub new {
67     my $invocant = shift;
68     my $self = '';
69     if (_check_params(@_) eq 1) {
70         return -1;
71     }
72     my $type = ref($invocant) || $invocant;
73     if (grep {$_ eq 'Labels'} @_) {
74        $self = {
75             layout_xml      =>      '',
76             units           =>      'POINT',
77             start_label     =>      1,
78             barcode_type    =>      'CODE39',
79             printing_type   =>      'BAR',
80             layout_name     =>      'DEFAULT',
81             guidebox        =>      0,
82             oblique_title   =>      1,
83             font            =>      'TR',
84             font_size       =>      3,
85             callnum_split   =>      0,
86             text_justify    =>      'L',
87             format_string   =>      join(', ', @{ PRESET_FIELDS() }),
88             @_,
89         };
90     }
91     elsif (grep {$_ eq 'Patroncards'} @_) {
92         $self = {
93             layout_xml => '<opt>Default Layout</opt>',
94             @_,
95         }
96     }
97     bless ($self, $type);
98     return $self;
99 }
100
101 sub retrieve {
102     my $invocant = shift;
103     my %opts = @_;
104     my $type = ref($invocant) || $invocant;
105     my $query = "SELECT * FROM creator_layouts WHERE layout_id = ? AND creator = ?";
106     my $sth = C4::Context->dbh->prepare($query);
107     $sth->execute($opts{'layout_id'}, $opts{'creator'});
108     if ($sth->err) {
109         warn sprintf('Database returned the following error: %s', $sth->errstr);
110         return -1;
111     }
112     my $self = $sth->fetchrow_hashref;
113     bless ($self, $type);
114     return $self;
115 }
116
117 sub delete {
118     my $self = {};
119     my %opts = ();
120     my $call_type = '';
121     my @params = ();
122     if (ref($_[0])) {
123         $self = shift;  # check to see if this is a method call
124         $call_type = 'C4::Labels::Layout->delete';
125         push @params, $self->{'layout_id'}, $self->{'creator'};
126     }
127     else {
128         my $class = shift;
129         %opts = @_;
130         $call_type = $class . '::delete';
131         push @params, $opts{'layout_id'}, $opts{'creator'};
132     }
133     if (scalar(@params) < 2) {   # If there is no layout id or creator type then we cannot delete it
134         warn sprintf('%s : Cannot delete layout as the profile id is invalid or non-existent.', $call_type) if !$params[0];
135         warn sprintf('%s : Cannot delete layout as the creator type is invalid or non-existent.', $call_type) if !$params[1];
136         return -1;
137     }
138     my $query = "DELETE FROM creator_layouts WHERE layout_id = ? AND creator = ?";
139     my $sth = C4::Context->dbh->prepare($query);
140     $sth->execute(@params);
141     if ($sth->err) {
142         warn sprintf('Database returned the following error on attempted DELETE: %s', $sth->errstr);
143         return -1;
144     }
145 }
146
147 sub save {
148     my $self = shift;
149     if ($self->{'layout_id'}) {        # if we have an id, the record exists and needs UPDATE
150         my @params;
151         my $query = "UPDATE creator_layouts SET ";
152         foreach my $key (keys %{$self}) {
153             next if ($key eq 'layout_id') || ($key eq 'creator');
154             push (@params, $self->{$key});
155             $query .= "$key=?, ";
156         }
157         $query = substr($query, 0, (length($query)-2));
158         $query .= " WHERE layout_id=? AND creator = ?;";
159         push (@params, $self->{'layout_id'}, $self->{'creator'});
160         my $sth = C4::Context->dbh->prepare($query);
161         #local $sth->{TraceLevel} = "3";        # enable DBI trace and set level; outputs to STDERR
162         $sth->execute(@params);
163         if ($sth->err) {
164             warn sprintf('Database returned the following error: %s', $sth->errstr);
165             return -1;
166         }
167         return $self->{'layout_id'};
168     }
169     else {                      # otherwise create a new record
170         my @params;
171         my $query = "INSERT INTO creator_layouts (";
172         foreach my $key (keys %{$self}) {
173             push (@params, $self->{$key});
174             $query .= "$key, ";
175         }
176         $query = substr($query, 0, (length($query)-2));
177         $query .= ") VALUES (";
178         for (my $i=1; $i<=(scalar keys %$self); $i++) {
179             $query .= "?,";
180         }
181         $query = substr($query, 0, (length($query)-1));
182         $query .= ");";
183         my $sth = C4::Context->dbh->prepare($query);
184         $sth->execute(@params);
185         if ($sth->err) {
186             warn sprintf('Database returned the following error: %s', $sth->errstr);
187             return -1;
188         }
189         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(layout_id) FROM creator_layouts;");
190         $sth1->execute();
191         my $id = $sth1->fetchrow_array;
192         $self->{'layout_id'} = $id;
193         return $id;
194     }
195 }
196
197 sub get_attr {
198     my $self = shift;
199     if (_check_params(@_) eq 1) {
200         return -1;
201     }
202     my ($attr) = @_;
203     if (exists($self->{$attr})) {
204         return $self->{$attr};
205     }
206     else {
207         return -1;
208     }
209     return;
210 }
211
212 sub set_attr {
213     my $self = shift;
214     if (_check_params(@_) eq 1) {
215         return -1;
216     }
217     my %attrs = @_;
218     foreach my $attrib (keys(%attrs)) {
219         $self->{$attrib} = $attrs{$attrib};
220     };
221     return 0;
222 }
223
224 sub get_text_wrap_cols {
225     my $self = shift;
226     my %params = @_;
227     my $string = '';
228     my $strwidth = 0;
229     my $col_count = 0;
230     my $textlimit = $params{'label_width'} - (( 3 * $params{'left_text_margin'} ) || 13.5 );
231
232     while ($strwidth < $textlimit) {
233         $string .= '8'; # using '8' as filling char instead of '0'
234         $col_count++;
235         $strwidth = C4::Creators::PDF->StrWidth( $string, $self->{'font'}, $self->{'font_size'} );
236     }
237     return $col_count;
238 }
239
240 1;
241 __END__
242
243 =head1 NAME
244
245 C4::Labels::Layout -A class for creating and manipulating layout objects in Koha
246
247 =head1 ABSTRACT
248
249 This module provides methods for creating, retrieving, and otherwise manipulating label layout objects used by Koha to create and export labels.
250
251 =head1 METHODS
252
253 =head2 new()
254
255     Invoking the I<new> method constructs a new layout object containing the default values for a layout.
256     The following parameters are optionally accepted as key => value pairs:
257
258         C<barcode_type>         Defines the barcode type to be used on labels. NOTE: At present only the following barcode types are supported in the label creator code:
259
260 =over 9
261
262 =item .
263             CODE39          = Code 3 of 9
264
265 =item .
266             CODE39MOD       = Code 3 of 9 with modulo 43 checksum
267
268 =item .
269             CODE39MOD10     = Code 3 of 9 with modulo 10 checksum
270
271 =item .
272             COOP2OF5        = A variant of 2 of 5 barcode based on NEC's "Process 8000" code
273
274 =item .
275             INDUSTRIAL2OF5  = The standard 2 of 5 barcode (a binary level bar code developed by Identicon Corp. and Computer Identics Corp. in 1970)
276
277 =back
278
279         C<printing_type>        Defines the general layout to be used on labels. NOTE: At present there are only five printing types supported in the label creator code:
280
281 =over 9
282
283 =item .
284 BIB     = Only the bibliographic data is printed
285
286 =item .
287 BARBIB  = Barcode proceeds bibliographic data
288
289 =item .
290 BIBBAR  = Bibliographic data proceeds barcode
291
292 =item .
293 ALT     = Barcode and bibliographic data are printed on alternating labels
294
295 =item .
296 BAR     = Only the barcode is printed
297
298 =back
299
300         C<layout_name>          The descriptive name for this layout.
301         C<guidebox>             Setting this to '1' will result in a guide box being drawn around the labels marking the edge of each label
302         C<font>                 Defines the type of font to be used on labels. NOTE: The following fonts are available by default on most systems:
303
304 =over 9
305
306 =item .
307 TR      = Times-Roman
308
309 =item .
310 TB      = Times Bold
311
312 =item .
313 TI      = Times Italic
314
315 =item .
316 TBI     = Times Bold Italic
317
318 =item .
319 C       = Courier
320
321 =item .
322 CB      = Courier Bold
323
324 =item .
325 CO      = Courier Oblique (Italic)
326
327 =item .
328 CBO     = Courier Bold Oblique
329
330 =item .
331 H       = Helvetica
332
333 =item .
334 HB      = Helvetica Bold
335
336 =item .
337 HBO     = Helvetical Bold Oblique
338
339 =back
340
341         C<font_size>            Defines the size of the font in postscript points to be used on labels
342         C<callnum_split>        Setting this to '1' will enable call number splitting on labels
343         C<text_justify>         Defines the text justification to be used on labels. NOTE: The following justification styles are currently supported by label creator code:
344
345 =over 9
346
347 =item .
348 L       = Left
349
350 =item .
351 C       = Center
352
353 =item .
354 R       = Right
355
356 =back
357
358         C<format_string>        Defines what fields will be printed and in what order they will be printed on labels. These include any of the data fields that may be mapped
359                                 to your MARC frameworks. Specify MARC subfields as a 4-character tag-subfield string: ie. 254a Enclose a whitespace-separated list of fields
360                                 to concatenate on one line in double quotes. ie. "099a 099b" or "itemcallnumber barcode" Static text strings may be entered in single-quotes:
361                                 ie. 'Some static text here.'
362
363     example:
364         C<my $layout = Layout->new(); # Creates and returns a new layout object>
365
366         C<my $layout = C4::Labels::Layout->new(barcode_type => 'CODE39', printing_type => 'BIBBAR', font => 'C', font_size => 6); # Creates and returns a new layout object using
367             the supplied values to override the defaults>
368
369     B<NOTE:> This layout is I<not> written to the database until save() is invoked. You have been warned!
370
371 =head2 retrieve(layout_id => layout_id)
372
373     Invoking the I<retrieve> method constructs a new layout object containing the current values for layout_id. The method returns a new object upon success and 1 upon failure.
374     Errors are logged to the Apache log.
375
376     example:
377         C<my $layout = Layout->retrieve(layout_id => 1); # Retrieves layout record 1 and returns an object containing the record>
378
379 =head2 delete()
380
381     Invoking the delete method attempts to delete the layout from the database. The method returns 0 upon success and -1 upon failure. Errors are logged to the Apache log.
382     NOTE: This method may also be called as a function and passed a key/value pair simply deleteing that template from the database. See the example below.
383
384     examples:
385         C<my $exitstat = $layout->delete(); # to delete the record behind the $layout object>
386         C<my $exitstat = Layout->delete(layout_id => 1); # to delete layout record 1>
387
388 =head2 save()
389
390     Invoking the I<save> method attempts to insert the layout into the database if the layout is new and update the existing layout record if the layout exists.
391     The method returns the new record id upon success and -1 upon failure (This avoids conflicting with a record id of 1). Errors are logged to the Apache log.
392
393     example:
394         C<my $exitstat = $layout->save(); # to save the record behind the $layout object>
395
396 =head2 get_attr($attribute)
397
398     Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
399
400     example:
401         C<my $value = $layout->get_attr($attribute);>
402
403 =head2 set_attr(attribute => value, attribute_2 => value)
404
405     Invoking the I<set_attr> method will set the value of the supplied attributes to the supplied values. The method accepts key/value pairs separated by
406     commas.
407
408     example:
409         C<$layout->set_attr(attribute => value);>
410
411 =head2 get_text_wrap_cols()
412
413     Invoking the I<get_text_wrap_cols> method will return the number of columns that can be printed on the label before wrapping to the next line.
414
415     examples:
416         C<my $text_wrap_cols = $layout->get_text_wrap_cols();>
417
418 =head1 AUTHOR
419
420 Chris Nighswonger <cnighswonger AT foundations DOT edu>
421
422 =head1 COPYRIGHT
423
424 Copyright 2009 Foundations Bible College.
425
426 =head1 LICENSE
427
428 This file is part of Koha.
429
430 Koha is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
431 Foundation; either version 2 of the License, or (at your option) any later version.
432
433 You should have received a copy of the GNU General Public License along with Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
434 Fifth Floor, Boston, MA 02110-1301 USA.
435
436 =head1 DISCLAIMER OF WARRANTY
437
438 Koha is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
439 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
440
441 =cut