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