[3/40] Work on C4::Labels::Profile module and tests
[koha.git] / C4 / Labels / Template.pm
1 package C4::Labels::Template;
2
3 # Copyright 2009 Foundations Bible College.
4 #
5 # This file is part of Koha.
6 #       
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 use warnings;
22 use Sys::Syslog qw(syslog);
23 use Data::Dumper;
24 use PDF::Reuse;
25
26 use C4::Context;
27 use C4::Debug;
28 use C4::Labels::Profile 1.000000;
29 use C4::Labels::PDF 1.000000;
30
31 BEGIN {
32     use version; our $VERSION = qv('1.0.0_1');
33 }
34
35 my $unit_values = { 
36     POINT       => 1,
37     INCH        => 72,
38     MM          => 2.83464567,
39     CM          => 28.3464567,
40 };
41
42 sub _check_params {
43     my $given_params = {};
44     my $exit_code = 0;
45     my @valid_template_params = (
46         'tmpl_code',
47         'tmpl_desc',
48         'page_width',
49         'page_height',
50         'label_width',
51         'label_height',
52         'top_text_margin',
53         'left_text_margin',
54         'top_margin',
55         'left_margin',
56         'cols',
57         'rows',
58         'col_gap',
59         'row_gap',
60         'units',
61         'font_size',
62         'font',
63     );
64     if (scalar(@_) >1) {
65         $given_params = {@_};
66         foreach my $key (keys %{$given_params}) {
67             if (!(grep m/$key/, @valid_template_params)) {
68                 syslog("LOG_ERR", "C4::Labels::Template : Unrecognized parameter type of \"%s\".", $key);
69                 $exit_code = 1;
70             }
71         }
72     }
73     else {
74         if (!(grep m/$_/, @valid_template_params)) {
75             syslog("LOG_ERR", "C4::Labels::Template : Unrecognized parameter type of \"%s\".", $_);
76             $exit_code = 1;
77         }
78     }
79     return $exit_code;
80 }
81
82 sub _conv_points {
83     my $self = shift;
84     $self->{page_width}         = $self->{page_width} * $unit_values->{$self->{units}};
85     $self->{page_height}        = $self->{page_height} * $unit_values->{$self->{units}};
86     $self->{label_width}        = $self->{label_width} * $unit_values->{$self->{units}};
87     $self->{label_height}       = $self->{label_height} * $unit_values->{$self->{units}};
88     $self->{top_text_margin}    = $self->{top_text_margin} * $unit_values->{$self->{units}};
89     $self->{left_text_margin}   = $self->{left_text_margin} * $unit_values->{$self->{units}};
90     $self->{top_margin}         = $self->{top_margin} * $unit_values->{$self->{units}};
91     $self->{left_margin}        = $self->{left_margin} * $unit_values->{$self->{units}};
92     $self->{col_gap}            = $self->{col_gap} * $unit_values->{$self->{units}};
93     $self->{row_gap}            = $self->{row_gap} * $unit_values->{$self->{units}};
94     return $self;
95 }
96
97 sub _apply_profile {
98     my $self = shift;
99     my $profile_id = shift;
100     my $profile = C4::Labels::Profile->retrieve(profile_id => $profile_id, convert => 1);
101     $self->{top_margin} = $self->{top_margin} + $profile->get_attr('offset_vert');      # controls vertical offset
102     $self->{left_margin} = $self->{left_margin} + $profile->get_attr('offset_horz');    # controls horizontal offset
103     $self->{label_height} = $self->{label_height} + $profile->get_attr('creep_vert');   # controls vertical creep
104     $self->{label_width} = $self->{label_width} + $profile->get_attr('creep_horz');     # controls horizontal creep
105     return $self;
106 }
107
108 =head1 NAME
109
110 C4::Labels::Template - A class for creating and manipulating template objects in Koha
111
112 =cut
113
114 =head1 METHODS
115
116 =head2 C4::Labels::Template->new()
117
118     Invoking the I<new> method constructs a new template object containing the default values for a template.
119
120     example:
121         my $template = Template->new(); # Creates and returns a new template object
122
123     B<NOTE:> This template is I<not> written to the database untill $template->save() is invoked. You have been warned!
124
125 =cut
126
127 sub new {
128     my $invocant = shift;
129     if (_check_params(@_) eq 1) {
130         return 1;
131     }
132     my $type = ref($invocant) || $invocant;
133     my $self = {
134         tmpl_code       =>      '',
135         tmpl_desc       =>      '',
136         page_width      =>      0,
137         page_height     =>      0,
138         label_width     =>      0,
139         label_height    =>      0,
140         top_text_margin =>      0,
141         left_text_margin =>      0,
142         top_margin      =>      0,
143         left_margin     =>      0,
144         cols            =>      0,
145         rows            =>      0,
146         col_gap         =>      0,
147         row_gap         =>      0,
148         units           =>      'POINT',
149         font_size       =>      3,
150         font            =>      'TR',
151         tmpl_stat       =>      0,      # false if any data has changed and the db has not been updated
152         @_,
153     };
154     bless ($self, $type);
155     return $self;
156 }
157
158 =head2 C4::Labels::Template->retrieve(template_id)
159
160     Invoking the I<retrieve> method constructs a new template object containing the current values for template_id. The method returns
161     a new object upon success and 1 upon failure. Errors are logged to the syslog. Two further options may be accessed. See the example
162     below for further description.
163
164     examples:
165
166         my $template = C4::Labels::Template->retrieve(template_id => 1); # Retrieves template record 1 and returns an object containing the record
167
168         my $template = C4::Labels::Template->retrieve(template_id => 1, convert => 1); # Retrieves template record 1, converts the units to points,
169             and returns an object containing the record
170
171         my $template = C4::Labels::Template->retrieve(template_id => 1, profile_id => profile_id); # Retrieves template record 1, converts the units
172             to points, applies the given profile id, and returns an object containing the record
173
174 =cut
175
176 sub retrieve {
177     my $invocant = shift;
178     my %opts = @_;
179     my $type = ref($invocant) || $invocant;
180     my $query = "SELECT * FROM labels_templates WHERE tmpl_id = ?";  
181     my $sth = C4::Context->dbh->prepare($query);
182     $sth->execute($opts{template_id});
183     if ($sth->err) {
184         syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
185         return 1;
186     }
187     my $self = $sth->fetchrow_hashref;
188     $self = _conv_points($self) if (($opts{convert} && $opts{convert} == 1) || $opts{profile_id});
189     $self = _apply_profile($self, $opts{profile_id}) if $opts{profile_id};
190     $self->{tmpl_stat} = 1;
191     bless ($self, $type);
192     return $self;
193 }
194
195 =head2 C4::Labels::Template->delete(tmpl_id => template_id) |  $template->delete()
196
197     Invoking the delete method attempts to delete the template from the database. The method returns 0 upon success
198     and 1 upon failure. Errors are logged to the syslog.
199
200     examples:
201         my $exitstat = $template->delete(); # to delete the record behind the $template object
202         my $exitstat = C4::Labels::Template->delete(tmpl_id => 1); # to delete template record 1
203
204 =cut
205
206 sub delete {
207     my $self = shift;
208     if (!$self->{tmpl_id}) {   # If there is no template tmpl_id then we cannot delete it
209         syslog("LOG_ERR", "Cannot delete template as it has not been saved.");
210         return 1;
211     }
212     my $query = "DELETE FROM labels_templates WHERE tmpl_id = ?";  
213     my $sth = C4::Context->dbh->prepare($query);
214     $sth->execute($self->{tmpl_id});
215     $self->{tmpl_stat} = 0;
216     return 0;
217 }
218
219 =head2 $template->save()
220
221     Invoking the I<save> method attempts to insert the template into the database if the template is new and
222     update the existing template record if the template exists. The method returns the new record tmpl_id upon
223     success and -1 upon failure (This avotmpl_ids conflicting with a record tmpl_id of 1). Errors are logged to the syslog.
224
225     example:
226         my $exitstat = $template->save(); # to save the record behind the $template object
227
228 =cut
229
230 sub save {
231     my $self = shift;
232     if ($self->{'tmpl_id'}) {        # if we have an tmpl_id, the record exists and needs UPDATE
233         my @params;
234         my $query = "UPDATE labels_templates SET ";
235         foreach my $key (keys %{$self}) {
236             next if ($key eq 'tmpl_id') || ($key eq 'tmpl_stat');
237             push (@params, $self->{$key});
238             $query .= "$key=?, ";
239         }
240         $query = substr($query, 0, (length($query)-2));
241         push (@params, $self->{'tmpl_id'});
242         $query .= " WHERE tmpl_id=?;";
243         warn "DEBUG: Updating: $query\n" if $debug;
244         my $sth = C4::Context->dbh->prepare($query);
245         $sth->execute(@params);
246         if ($sth->err) {
247             syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
248             return -1;
249         }
250         $self->{tmpl_stat} = 1;
251         return $self->{'tmpl_id'};
252     }
253     else {                      # otherwise create a new record
254         my @params;
255         my $query = "INSERT INTO labels_templates (";
256         foreach my $key (keys %{$self}) {
257             next if $key eq 'tmpl_stat';
258             push (@params, $self->{$key});
259             $query .= "$key, ";
260         }
261         $query = substr($query, 0, (length($query)-2));
262         $query .= ") VALUES (";
263         for (my $i=1; $i<=((scalar keys %$self) - 1); $i++) {   # key count less keys not db related...
264             $query .= "?,";
265         }
266         $query = substr($query, 0, (length($query)-1));
267         $query .= ");";
268         warn "DEBUG: Saving: $query\n" if $debug;
269         my $sth = C4::Context->dbh->prepare($query);
270         $sth->execute(@params);
271         if ($sth->err) {
272             syslog("LOG_ERR", "Database returned the following error: %s", $sth->errstr);
273             return -1;
274         }
275         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(tmpl_id) FROM labels_templates;");
276         $sth1->execute();
277         my $tmpl_id = $sth1->fetchrow_array;
278         $self->{tmpl_id} = $tmpl_id;
279         $self->{tmpl_stat} = 1;
280         return $tmpl_id;
281     }
282 }
283
284 =head2 $template->get_attr("attr")
285
286     Invoking the I<get_attr> method will return the value of the requested attribute or 1 on errors.
287
288     example:
289         my $value = $template->get_attr("attr");
290
291 =cut
292
293 sub get_attr {
294     my $self = shift;
295     if (_check_params(@_) eq 1) {
296         return 1;
297     }
298     my ($attr) = @_;
299     if (exists($self->{$attr})) {
300         return $self->{$attr};
301     }
302     else {
303         return 1;
304     }
305 }
306
307 =head2 $template->set_attr(attr, value)
308
309     Invoking the I<set_attr> method will set the value of the supplied attribute to the supplied value.
310
311     example:
312         $template->set_attr(attr => value);
313
314 =cut
315
316 sub set_attr {
317     my $self = shift;
318     if (_check_params(@_) eq 1) {
319         return 1;
320     }
321     my ($attr, $value) = @_;
322     $self->{$attr} = $value;
323 }
324
325 =head2 $template->get_text_wrap_cols()
326
327     Invoking the I<get_text_wrap_cols> method will return the number of columns that can be printed on the
328     label before wrapping to the next line.
329
330     examples:
331         my $text_wrap_cols = $template->get_text_wrap_cols();
332
333 =cut
334
335 sub get_text_wrap_cols {
336     my $self = shift;
337     my $string = '';
338     my $strwidth = 0;
339     my $col_count = 0;
340     my $textlimit = $self->{label_width} - ( 3 * $self->{left_text_margin});
341
342     while ($strwidth < $textlimit) {
343         $string .= '0';
344         $col_count++;
345         $strwidth = C4::Labels::PDF->StrWidth( $string, $self->{font}, $self->{font_size} );
346     }
347     return $col_count;
348 }
349
350 =head2 $template->get_label_position($start_label)
351
352     Invoking the I<get_label_position> method will return the row, column coordinates on the starting page
353     and the lower left x,y coordinates on the starting label for the template object.
354
355     examples:
356         my ($row_count, $col_count, $llx, $lly) = $template->get_label_position($start_label);
357
358 =cut
359
360 sub get_label_position {
361     my ($self, $start_label) = @_;
362     my ($row_count, $col_count, $llx, $lly) = 0,0,0,0;
363     if ($start_label eq 1) {
364         $row_count = 1;
365         $col_count = 1;
366         $llx = $self->{left_margin};
367         $lly = ($self->{page_height} - $self->{top_margin} - $self->{label_height});
368         return ($row_count, $col_count, $llx, $lly);
369     }
370     else {
371         $row_count = ceil($start_label / $self->{cols});
372         $col_count = ($start_label - (($row_count - 1) * $self->{cols}));
373         $llx = $self->{left_margin} + ($self->{label_width} * ($col_count - 1)) + ($self->{col_gap} * ($col_count - 1));
374         $lly = $self->{page_height} - $self->{top_margin} - ($self->{label_height} * $row_count) - ($self->{row_gap} * ($row_count - 1));
375         return ($row_count, $col_count, $llx, $lly);
376     }
377 }
378
379 1;
380 __END__
381
382 =head1 AUTHOR
383
384 Chris Nighswonger <cnighswonger AT foundations DOT edu>
385
386 =cut