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