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