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