bug 3651 followup: updated for new GetMember() parameter style
[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 -1;
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 -1;
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 }
140
141 sub save {
142     my $self = shift;
143     if ($self->{'layout_id'}) {        # if we have an id, the record exists and needs UPDATE
144         my @params;
145         my $query = "UPDATE creator_layouts SET ";
146         foreach my $key (keys %{$self}) {
147             next if ($key eq 'layout_id') || ($key eq 'creator');
148             push (@params, $self->{$key});
149             $query .= "$key=?, ";
150         }
151         $query = substr($query, 0, (length($query)-2));
152         $query .= " WHERE layout_id=? AND creator = ?;";
153         push (@params, $self->{'layout_id'}, $self->{'creator'});
154         my $sth = C4::Context->dbh->prepare($query);
155         #local $sth->{TraceLevel} = "3";        # enable DBI trace and set level; outputs to STDERR
156         $sth->execute(@params);
157         if ($sth->err) {
158             warn sprintf('Database returned the following error: %s', $sth->errstr);
159             return -1;
160         }
161         return $self->{'layout_id'};
162     }
163     else {                      # otherwise create a new record
164         my @params;
165         my $query = "INSERT INTO creator_layouts (";
166         foreach my $key (keys %{$self}) {
167             push (@params, $self->{$key});
168             $query .= "$key, ";
169         }
170         $query = substr($query, 0, (length($query)-2));
171         $query .= ") VALUES (";
172         for (my $i=1; $i<=(scalar keys %$self); $i++) {
173             $query .= "?,";
174         }
175         $query = substr($query, 0, (length($query)-1));
176         $query .= ");";
177         my $sth = C4::Context->dbh->prepare($query);
178         $sth->execute(@params);
179         if ($sth->err) {
180             warn sprintf('Database returned the following error: %s', $sth->errstr);
181             return -1;
182         }
183         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(layout_id) FROM creator_layouts;");
184         $sth1->execute();
185         my $id = $sth1->fetchrow_array;
186         $self->{'layout_id'} = $id;
187         return $id;
188     }
189 }
190
191 sub get_attr {
192     my $self = shift;
193     if (_check_params(@_) eq 1) {
194         return -1;
195     }
196     my ($attr) = @_;
197     if (exists($self->{$attr})) {
198         return $self->{$attr};
199     }
200     else {
201         return -1;
202     }
203     return;
204 }
205
206 sub set_attr {
207     my $self = shift;
208     if (_check_params(@_) eq 1) {
209         return -1;
210     }
211     my %attrs = @_;
212     foreach my $attrib (keys(%attrs)) {
213         $self->{$attrib} = $attrs{$attrib};
214     };
215     return 0;
216 }
217
218 sub get_text_wrap_cols {
219     my $self = shift;
220     my %params = @_;
221     my $string = '';
222     my $strwidth = 0;
223     my $col_count = 0;
224     my $textlimit = $params{'label_width'} - ( 3 * $params{'left_text_margin'});
225
226     while ($strwidth < $textlimit) {
227         $string .= '0';
228         $col_count++;
229         $strwidth = C4::Creators::PDF->StrWidth( $string, $self->{'font'}, $self->{'font_size'} );
230     }
231     return $col_count;
232 }
233
234 1;
235 __END__
236
237 =head1 NAME
238
239 C4::Labels::Layout -A class for creating and manipulating layout objects in Koha
240
241 =head1 ABSTRACT
242
243 This module provides methods for creating, retrieving, and otherwise manipulating label layout objects used by Koha to create and export labels.
244
245 =head1 METHODS
246
247 =head2 new()
248
249     Invoking the I<new> method constructs a new layout object containing the default values for a layout.
250     The following parameters are optionally accepted as key => value pairs:
251
252         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:
253
254 =over 9
255
256 =item .
257             CODE39          = Code 3 of 9
258
259 =item .
260             CODE39MOD       = Code 3 of 9 with modulo 43 checksum
261
262 =item .
263             CODE39MOD10     = Code 3 of 9 with modulo 10 checksum
264
265 =item .
266             COOP2OF5        = A varient of 2 of 5 barcode based on NEC's "Process 8000" code
267
268 =item .
269             INDUSTRIAL2OF5  = The standard 2 of 5 barcode (a binary level bar code developed by Identicon Corp. and Computer Identics Corp. in 1970)
270
271 =back
272
273         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:
274
275 =over 9
276
277 =item .
278 BIB     = Only the bibliographic data is printed
279
280 =item .
281 BARBIB  = Barcode proceeds bibliographic data
282
283 =item .
284 BIBBAR  = Bibliographic data proceeds barcode
285
286 =item .
287 ALT     = Barcode and bibliographic data are printed on alternating labels
288
289 =item .
290 BAR     = Only the barcode is printed
291
292 =back
293
294         C<layout_name>          The descriptive name for this layout.
295         C<guidebox>             Setting this to '1' will result in a guide box being drawn around the labels marking the edge of each label
296         C<font>                 Defines the type of font to be used on labels. NOTE: The following fonts are available by default on most systems:
297
298 =over 9
299
300 =item .
301 TR      = Times-Roman
302
303 =item .
304 TB      = Times Bold
305
306 =item .
307 TI      = Times Italic
308
309 =item .
310 TBI     = Times Bold Italic
311
312 =item .
313 C       = Courier
314
315 =item .
316 CB      = Courier Bold
317
318 =item .
319 CO      = Courier Oblique (Italic)
320
321 =item .
322 CBO     = Courier Bold Oblique
323
324 =item .
325 H       = Helvetica
326
327 =item .
328 HB      = Helvetica Bold
329
330 =item .
331 HBO     = Helvetical Bold Oblique
332
333 =back
334
335         C<font_size>            Defines the size of the font in postscript points to be used on labels
336         C<callnum_split>        Setting this to '1' will enable call number splitting on labels
337         C<text_justify>         Defines the text justification to be used on labels. NOTE: The following justification styles are currently supported by label creator code:
338
339 =over 9
340
341 =item .
342 L       = Left
343
344 =item .
345 C       = Center
346
347 =item .
348 R       = Right
349
350 =back
351
352         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
353                                 to your MARC frameworks. Specify MARC subfields as a 4-character tag-subfield string: ie. 254a Enclose a whitespace-separated list of fields
354                                 to concatenate on one line in double quotes. ie. "099a 099b" or "itemcallnumber barcode" Static text strings may be entered in single-quotes:
355                                 ie. 'Some static text here.'
356
357     example:
358         C<my $layout = Layout->new(); # Creates and returns a new layout object>
359
360         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
361             the supplied values to override the defaults>
362
363     B<NOTE:> This layout is I<not> written to the database until save() is invoked. You have been warned!
364
365 =head2 retrieve(layout_id => layout_id)
366
367     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.
368     Errors are logged to the Apache log.
369
370     example:
371         C<my $layout = Layout->retrieve(layout_id => 1); # Retrieves layout record 1 and returns an object containing the record>
372
373 =head2 delete()
374
375     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.
376     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.
377
378     examples:
379         C<my $exitstat = $layout->delete(); # to delete the record behind the $layout object>
380         C<my $exitstat = Layout->delete(layout_id => 1); # to delete layout record 1>
381
382 =head2 save()
383
384     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.
385     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.
386
387     example:
388         C<my $exitstat = $layout->save(); # to save the record behind the $layout object>
389
390 =head2 get_attr($attribute)
391
392     Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
393
394     example:
395         C<my $value = $layout->get_attr($attribute);>
396
397 =head2 set_attr(attribute => value, attribute_2 => value)
398
399     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
400     commas.
401
402     example:
403         C<$layout->set_attr(attribute => value);>
404
405 =head2 get_text_wrap_cols()
406
407     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.
408
409     examples:
410         C<my $text_wrap_cols = $layout->get_text_wrap_cols();>
411
412 =head1 AUTHOR
413
414 Chris Nighswonger <cnighswonger AT foundations DOT edu>
415
416 =head1 COPYRIGHT
417
418 Copyright 2009 Foundations Bible College.
419
420 =head1 LICENSE
421
422 This file is part of Koha.
423
424 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
425 Foundation; either version 2 of the License, or (at your option) any later version.
426
427 You should have received a copy of the GNU General Public License along with Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
428 Suite 330, Boston, MA  02111-1307 USA
429
430 =head1 DISCLAIMER OF WARRANTY
431
432 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
433 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
434
435 =cut