Merge remote branch 'koha-fbc/k_bug_5247' into to-push
[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     my $sth = C4::Context->dbh->prepare($query);
99     $sth->execute($opts{'layout_id'}, $opts{'creator'});
100     if ($sth->err) {
101         warn sprintf('Database returned the following error: %s', $sth->errstr);
102         return -1;
103     }
104     my $self = $sth->fetchrow_hashref;
105     bless ($self, $type);
106     return $self;
107 }
108
109 sub delete {
110     my $self = {};
111     my %opts = ();
112     my $call_type = '';
113     my @params = ();
114     if (ref($_[0])) {
115         $self = shift;  # check to see if this is a method call
116         $call_type = 'C4::Labels::Layout->delete';
117         push @params, $self->{'layout_id'}, $self->{'creator'};
118     }
119     else {
120         my $class = shift;
121         %opts = @_;
122         $call_type = $class . '::delete';
123         push @params, $opts{'layout_id'}, $opts{'creator'};
124     }
125     if (scalar(@params) < 2) {   # If there is no layout id or creator type then we cannot delete it
126         warn sprintf('%s : Cannot delete layout as the profile id is invalid or non-existant.', $call_type) if !$params[0];
127         warn sprintf('%s : Cannot delete layout as the creator type is invalid or non-existant.', $call_type) if !$params[1];
128         return -1;
129     }
130     my $query = "DELETE FROM creator_layouts WHERE layout_id = ? AND creator = ?";
131     my $sth = C4::Context->dbh->prepare($query);
132     $sth->execute(@params);
133     if ($sth->err) {
134         warn sprintf('Database returned the following error on attempted DELETE: %s', $sth->errstr);
135         return -1;
136     }
137 }
138
139 sub save {
140     my $self = shift;
141     if ($self->{'layout_id'}) {        # if we have an id, the record exists and needs UPDATE
142         my @params;
143         my $query = "UPDATE creator_layouts SET ";
144         foreach my $key (keys %{$self}) {
145             next if ($key eq 'layout_id') || ($key eq 'creator');
146             push (@params, $self->{$key});
147             $query .= "$key=?, ";
148         }
149         $query = substr($query, 0, (length($query)-2));
150         $query .= " WHERE layout_id=? AND creator = ?;";
151         push (@params, $self->{'layout_id'}, $self->{'creator'});
152         my $sth = C4::Context->dbh->prepare($query);
153         #local $sth->{TraceLevel} = "3";        # enable DBI trace and set level; outputs to STDERR
154         $sth->execute(@params);
155         if ($sth->err) {
156             warn sprintf('Database returned the following error: %s', $sth->errstr);
157             return -1;
158         }
159         return $self->{'layout_id'};
160     }
161     else {                      # otherwise create a new record
162         my @params;
163         my $query = "INSERT INTO creator_layouts (";
164         foreach my $key (keys %{$self}) {
165             push (@params, $self->{$key});
166             $query .= "$key, ";
167         }
168         $query = substr($query, 0, (length($query)-2));
169         $query .= ") VALUES (";
170         for (my $i=1; $i<=(scalar keys %$self); $i++) {
171             $query .= "?,";
172         }
173         $query = substr($query, 0, (length($query)-1));
174         $query .= ");";
175         my $sth = C4::Context->dbh->prepare($query);
176         $sth->execute(@params);
177         if ($sth->err) {
178             warn sprintf('Database returned the following error: %s', $sth->errstr);
179             return -1;
180         }
181         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(layout_id) FROM creator_layouts;");
182         $sth1->execute();
183         my $id = $sth1->fetchrow_array;
184         $self->{'layout_id'} = $id;
185         return $id;
186     }
187 }
188
189 sub get_attr {
190     my $self = shift;
191     if (_check_params(@_) eq 1) {
192         return -1;
193     }
194     my ($attr) = @_;
195     if (exists($self->{$attr})) {
196         return $self->{$attr};
197     }
198     else {
199         return -1;
200     }
201     return;
202 }
203
204 sub set_attr {
205     my $self = shift;
206     if (_check_params(@_) eq 1) {
207         return -1;
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'} ) || 13.5 );
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 -1 on errors.
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