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