Fixing warns (tnx to Chris Cormack for identifying these)
[koha.git] / C4 / Labels / Template.pm
1 package C4::Labels::Template;
2
3 use strict;
4 use warnings;
5 use Sys::Syslog qw(syslog);
6 use PDF::Reuse;
7 use POSIX qw(ceil);
8
9 use C4::Context;
10 use C4::Debug;
11 use C4::Labels::Profile 1.000000;
12 use C4::Labels::PDF 1.000000;
13 use C4::Labels::Lib 1.000000 qw(get_unit_values);
14
15 BEGIN {
16     use version; our $VERSION = qv('1.0.0_1');
17 }
18
19 sub _check_params {
20     my $given_params = {};
21     my $exit_code = 0;
22     my @valid_template_params = (
23         'profile_id',
24         'template_code',
25         'template_desc',
26         'page_width',
27         'page_height',
28         'label_width',
29         'label_height',
30         'top_text_margin',
31         'left_text_margin',
32         'top_margin',
33         'left_margin',
34         'cols',
35         'rows',
36         'col_gap',
37         'row_gap',
38         'units',
39     );
40     if (scalar(@_) >1) {
41         $given_params = {@_};
42         foreach my $key (keys %{$given_params}) {
43             if (!(grep m/$key/, @valid_template_params)) {
44                 syslog("LOG_ERR", "C4::Labels::Template : Unrecognized parameter type of \"%s\".", $key);
45                 $exit_code = 1;
46             }
47         }
48     }
49     else {
50         if (!(grep m/$_/, @valid_template_params)) {
51             syslog("LOG_ERR", "C4::Labels::Template : Unrecognized parameter type of \"%s\".", $_);
52             $exit_code = 1;
53         }
54     }
55     return $exit_code;
56 }
57
58 sub _conv_points {
59     my $self = shift;
60     my @unit_value = grep {$_->{'type'} eq $self->{'units'}} @{get_unit_values()};
61     $self->{'page_width'}         = $self->{'page_width'} * $unit_value[0]->{'value'};
62     $self->{'page_height'}        = $self->{'page_height'} * $unit_value[0]->{'value'};
63     $self->{'label_width'}        = $self->{'label_width'} * $unit_value[0]->{'value'};
64     $self->{'label_height'}       = $self->{'label_height'} * $unit_value[0]->{'value'};
65     $self->{'top_text_margin'}    = $self->{'top_text_margin'} * $unit_value[0]->{'value'};
66     $self->{'left_text_margin'}   = $self->{'left_text_margin'} * $unit_value[0]->{'value'};
67     $self->{'top_margin'}         = $self->{'top_margin'} * $unit_value[0]->{'value'};
68     $self->{'left_margin'}        = $self->{'left_margin'} * $unit_value[0]->{'value'};
69     $self->{'col_gap'}            = $self->{'col_gap'} * $unit_value[0]->{'value'};
70     $self->{'row_gap'}            = $self->{'row_gap'} * $unit_value[0]->{'value'};
71     return $self;
72 }
73
74 sub _apply_profile {
75     my $self = shift;
76     my $profile = C4::Labels::Profile->retrieve(profile_id => $self->{'profile_id'}, convert => 1);
77     $self->{'top_margin'} = $self->{'top_margin'} + $profile->get_attr('offset_vert');      # controls vertical offset
78     $self->{'left_margin'} = $self->{'left_margin'} + $profile->get_attr('offset_horz');    # controls horizontal offset
79     $self->{'label_height'} = $self->{'label_height'} + $profile->get_attr('creep_vert');   # controls vertical creep
80     $self->{'label_width'} = $self->{'label_width'} + $profile->get_attr('creep_horz');     # controls horizontal creep
81     return $self;
82 }
83
84 sub new {
85     my $invocant = shift;
86     if (_check_params(@_) eq 1) {
87         return -1;
88     }
89     my $type = ref($invocant) || $invocant;
90     my $self = {
91         profile_id      =>      '0',
92         template_code   =>      'DEFAULT TEMPLATE',
93         template_desc   =>      'Default description',
94         page_width      =>      0,
95         page_height     =>      0,
96         label_width     =>      0,
97         label_height    =>      0,
98         top_text_margin =>      0,
99         left_text_margin =>      0,
100         top_margin      =>      0,
101         left_margin     =>      0,
102         cols            =>      0,
103         rows            =>      0,
104         col_gap         =>      0,
105         row_gap         =>      0,
106         units           =>      'POINT',
107         template_stat   =>      0,      # false if any data has changed and the db has not been updated
108         @_,
109     };
110     bless ($self, $type);
111     return $self;
112 }
113
114 sub retrieve {
115     my $invocant = shift;
116     my %opts = @_;
117     my $type = ref($invocant) || $invocant;
118     my $query = "SELECT * FROM labels_templates WHERE template_id = ?";  
119     my $sth = C4::Context->dbh->prepare($query);
120     $sth->execute($opts{template_id});
121     if ($sth->err) {
122         syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
123         return -1;
124     }
125     my $self = $sth->fetchrow_hashref;
126     $self = _conv_points($self) if (($opts{convert} && $opts{convert} == 1) || $opts{profile_id});
127     $self = _apply_profile($self) if $opts{profile_id} && $self->{'profile_id'};        # don't bother if there is no profile_id
128     $self->{'template_stat'} = 1;
129     bless ($self, $type);
130     return $self;
131 }
132
133 sub delete {
134     my $self = {};
135     my %opts = ();
136     my $call_type = '';
137     my $query_param = '';
138     if (ref($_[0])) {
139         $self = shift;  # check to see if this is a method call
140         $call_type = 'C4::Labels::Template->delete';
141         $query_param = $self->{'template_id'};
142     }
143     else {
144         %opts = @_;
145         $call_type = 'C4::Labels::Template::delete';
146         $query_param = $opts{'template_id'};
147     }
148     if ($query_param eq '') {   # If there is no template id then we cannot delete it
149         syslog("LOG_ERR", "%s : Cannot delete layout as the template id is invalid or non-existant.", $call_type);
150         return -1;
151     }
152     my $query = "DELETE FROM labels_templates WHERE template_id = ?";  
153     my $sth = C4::Context->dbh->prepare($query);
154     $sth->execute($query_param);
155     $self->{'template_stat'} = 0;
156 }
157
158 sub save {
159     my $self = shift;
160     if ($self->{'template_id'}) {        # if we have an template_id, the record exists and needs UPDATE
161         my @params;
162         my $query = "UPDATE labels_templates SET ";
163         foreach my $key (keys %{$self}) {
164             next if ($key eq 'template_id') || ($key eq 'template_stat');
165             push (@params, $self->{$key});
166             $query .= "$key=?, ";
167         }
168         $query = substr($query, 0, (length($query)-2));
169         push (@params, $self->{'template_id'});
170         $query .= " WHERE template_id=?;";
171         my $sth = C4::Context->dbh->prepare($query);
172         $sth->execute(@params);
173         if ($sth->err) {
174             syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
175             return -1;
176         }
177         $self->{'template_stat'} = 1;
178         return $self->{'template_id'};
179     }
180     else {                      # otherwise create a new record
181         my @params;
182         my $query = "INSERT INTO labels_templates (";
183         foreach my $key (keys %{$self}) {
184             next if $key eq 'template_stat';
185             push (@params, $self->{$key});
186             $query .= "$key, ";
187         }
188         $query = substr($query, 0, (length($query)-2));
189         $query .= ") VALUES (";
190         for (my $i=1; $i<=((scalar keys %$self) - 1); $i++) {   # key count less keys not db related...
191             $query .= "?,";
192         }
193         $query = substr($query, 0, (length($query)-1));
194         $query .= ");";
195         my $sth = C4::Context->dbh->prepare($query);
196         $sth->execute(@params);
197         if ($sth->err) {
198             syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
199             return -1;
200         }
201         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(template_id) FROM labels_templates;");
202         $sth1->execute();
203         my $template_id = $sth1->fetchrow_array;
204         $self->{'template_id'} = $template_id;
205         $self->{'template_stat'} = 1;
206         return $template_id;
207     }
208 }
209
210 sub get_attr {
211     my $self = shift;
212     if (_check_params(@_) eq 1) {
213         return -1;
214     }
215     my ($attr) = @_;
216     if (exists($self->{$attr})) {
217         return $self->{$attr};
218     }
219     else {
220         return -1;
221     }
222 }
223
224 sub set_attr {
225     my $self = shift;
226     if (_check_params(@_) eq 1) {
227         return -1;
228     }
229     my %attrs = @_;
230     foreach my $attrib (keys(%attrs)) {
231         $self->{$attrib} = $attrs{$attrib};
232     };
233 }
234
235 sub get_label_position {
236     my ($self, $start_label) = @_;
237     my ($row_count, $col_count, $llx, $lly) = 0,0,0,0;
238     if ($start_label eq 1) {
239         $row_count = 1;
240         $col_count = 1;
241         $llx = $self->{'left_margin'};
242         $lly = ($self->{'page_height'} - $self->{'top_margin'} - $self->{'label_height'});
243         return ($row_count, $col_count, $llx, $lly);
244     }
245     else {
246         $row_count = ceil($start_label / $self->{'cols'});
247         $col_count = ($start_label - (($row_count - 1) * $self->{'cols'}));
248         $llx = $self->{'left_margin'} + ($self->{'label_width'} * ($col_count - 1)) + ($self->{'col_gap'} * ($col_count - 1));
249         $lly = $self->{'page_height'} - $self->{'top_margin'} - ($self->{'label_height'} * $row_count) - ($self->{'row_gap'} * ($row_count - 1));
250         return ($row_count, $col_count, $llx, $lly);
251     }
252 }
253
254 1;
255 __END__
256
257 =head1 NAME
258
259 C4::Labels::Template - A class for creating and manipulating template objects in Koha
260
261 =head1 ABSTRACT
262
263 This module provides methods for creating, retrieving, and otherwise manipulating label template objects used by Koha to create and export labels.
264
265 =head1 METHODS
266
267 =head2 new()
268
269     Invoking the I<new> method constructs a new template object containing the default values for a template.
270     The following parameters are optionally accepted as key => value pairs:
271
272         C<profile_id>           A valid profile id to be assciated with this template. NOTE: The profile must exist in the database and B<not> be assigned to another template.
273         C<template_code>        A template code. ie. 'Avery 5160 | 1 x 2-5/8'
274         C<template_desc>        A readable description of the template. ie. '3 columns, 10 rows of labels'
275         C<page_width>           The width of the page measured in the units supplied by the units parameter in this template.
276         C<page_height>          The height of the page measured in the same units.
277         C<label_width>          The width of a single label on the page this template applies to.
278         C<label_height>         The height of a single label on the page.
279         C<top_text_margin>      The measure of the top margin on a single label on the page.
280         C<left_text_margin>     The measure of the left margin on a single label on the page.
281         C<top_margin>           The measure of the top margin of the page.
282         C<left_margin>          The measure of the left margin of the page.
283         C<cols>                 The number of columns of labels on the page.
284         C<rows>                 The number of rows of labels on the page.
285         C<col_gap>              The measure of the gap between the columns of labels on the page.
286         C<row_gap>              The measure of the gap between the rows of labels on the page.
287         C<units>                The units of measure used for this template. These B<must> match the measures you supply above or
288                                 bad things will happen to your document. NOTE: The only supported units at present are:
289
290 =over 9
291
292 =item .
293 POINT   = Postscript Points (This is the base unit in the Koha label creator.)
294
295 =item .
296 AGATE   = Adobe Agates (5.1428571 points per)
297
298 =item .
299 INCH    = US Inches (72 points per)
300
301 =item .
302 MM      = SI Millimeters (2.83464567 points per)
303
304 =item .
305 CM      = SI Centimeters (28.3464567 points per)
306
307 =back
308                                     
309     example:
310         my $template = Template->new(); # Creates and returns a new template object with the defaults
311
312         my $template = C4::Labels::Template->new(profile_id => 1, page_width => 8.5, page_height => 11.0, units => 'INCH'); # Creates and returns a new template object using
313             the supplied values to override the defaults
314
315     B<NOTE:> This template is I<not> written to the database untill save() is invoked. You have been warned!
316
317 =head2 retrieve(template_id => $template_id)
318
319     Invoking the I<retrieve> method constructs a new template object containing the current values for template_id. The method returns
320     a new object upon success and -1 upon failure. Errors are logged to the syslog. Two further options may be accessed. See the example
321     below for further description.
322
323     examples:
324
325         C<my $template = C4::Labels::Template->retrieve(template_id => 1); # Retrieves template record 1 and returns an object containing the record>
326
327         C<my $template = C4::Labels::Template->retrieve(template_id => 1, convert => 1); # Retrieves template record 1, converts the units to points,
328             and returns an object containing the record>
329
330         C<my $template = C4::Labels::Template->retrieve(template_id => 1, profile_id => 1); # Retrieves template record 1, converts the units
331             to points, applies the currently associated profile id, and returns an object containing the record.>
332
333 =head2 delete()
334
335     Invoking the delete method attempts to delete the template from the database. The method returns -1 upon failure. Errors are logged to the syslog.
336     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.
337
338     examples:
339         C<my $exitstat = $template->delete(); # to delete the record behind the $template object>
340         C<my $exitstat = C4::Labels::Template::delete(template_id => 1); # to delete template record 1>
341
342 =head2 save()
343
344     Invoking the I<save> method attempts to insert the template into the database if the template is new and update the existing template record if
345     the template exists. The method returns the new record template_id upon success and -1 upon failure (This avoids template_ids conflicting with a
346     record template_id of 1). Errors are logged to the syslog.
347
348     example:
349         C<my $template_id = $template->save(); # to save the record behind the $template object>
350
351 =head2 get_attr($attribute)
352
353     Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
354
355     example:
356         C<my $value = $template->get_attr($attribute);>
357
358 =head2 set_attr(attribute => value, attribute_2 => value)
359
360     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
361     commas.
362
363     example:
364         C<$template->set_attr(attribute => value);>
365
366 =head2 get_label_position($start_label)
367
368     Invoking the I<get_label_position> method will return the row, column coordinates on the starting page and the lower left x,y coordinates on the starting
369     label for the template object.
370
371     examples:
372         C<my ($row_count, $col_count, $llx, $lly) = $template->get_label_position($start_label);>
373
374 =head1 AUTHOR
375
376 Chris Nighswonger <cnighswonger AT foundations DOT edu>
377
378 =head1 COPYRIGHT
379
380 Copyright 2009 Foundations Bible College.
381
382 =head1 LICENSE
383
384 This file is part of Koha.
385        
386 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
387 Foundation; either version 2 of the License, or (at your option) any later version.
388
389 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,
390 Suite 330, Boston, MA  02111-1307 USA
391
392 =head1 DISCLAIMER OF WARRANTY
393
394 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
395 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
396
397 =cut