Bug 17600: Standardize our EXPORT_OK
[srvgit] / C4 / Creators / Template.pm
1 package C4::Creators::Template;
2
3 use strict;
4 use warnings;
5 use POSIX qw( ceil );
6 use autouse 'Data::Dumper' => qw(Dumper);
7
8 use C4::Context;
9 use C4::Creators::Profile;
10 use C4::Creators::Lib qw( get_unit_values );
11
12
13 sub _check_params {
14     shift if $_[0] =~ m/::/; # this seems a bit hackish
15     my $given_params = {};
16     my $exit_code = 0;
17     my @valid_template_params = (
18         'profile_id',
19         'template_code',
20         'template_desc',
21         'page_width',
22         'page_height',
23         'label_width',
24         'label_height',
25         'card_width',
26         'card_height',
27         'top_text_margin',
28         'left_text_margin',
29         'top_margin',
30         'left_margin',
31         'cols',
32         'rows',
33         'col_gap',
34         'row_gap',
35         'units',
36         'creator',
37         'current_label',
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 $creator = shift;
76     my $profile = C4::Creators::Profile->retrieve(profile_id => $self->{'profile_id'}, creator => $creator, 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     my $type = ref($invocant) || $invocant;
87     if (_check_params(@_) eq 1) {
88         return -1;
89     }
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 " . $opts{'table_name'} . " WHERE template_id = ? AND creator = ?";
119     my $sth = C4::Context->dbh->prepare($query);
120     $sth->execute($opts{'template_id'}, $opts{'creator'});
121     if ($sth->err) {
122         warn sprintf('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, $opts{'creator'}) 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_params = ();
138     if (ref($_[0])) {
139         $self = shift;  # check to see if this is a method call
140         $call_type = 'C4::Labels::Template->delete';
141         push @query_params, $self->{'template_id'}, $self->{'creator'};
142     }
143     else {
144         %opts = @_;
145         $call_type = 'C4::Labels::Template::delete';
146         push @query_params, $opts{'template_id'}, $opts{'creator'};
147     }
148     if (scalar(@query_params) < 2) {   # If there is no template id or creator type then we cannot delete it
149         warn sprintf('%s : Cannot delete template as the template id is invalid or non-existent.', $call_type) if !$query_params[0];
150         warn sprintf('%s : Cannot delete template as the creator type is invalid or non-existent.', $call_type) if !$query_params[1];
151         return -1;
152     }
153     my $query = "DELETE FROM creator_templates WHERE template_id = ? AND creator = ?";
154     my $sth = C4::Context->dbh->prepare($query);
155     $sth->execute(@query_params);
156     $self->{'template_stat'} = 0;
157 }
158
159 sub save {
160     my $self = shift;
161     my %opts = @_;
162     if ($self->{'template_id'}) {        # if we have an template_id, the record exists and needs UPDATE
163         my @params;
164         my $query = "UPDATE " . $opts{'table_name'} . " SET ";
165         foreach my $key (keys %{$self}) {
166             next if ($key eq 'template_id') || ($key eq 'template_stat') || ($key eq 'creator');
167             push (@params, $self->{$key});
168             $query .= "`$key`=?, ";
169         }
170         $query = substr($query, 0, (length($query)-2));
171         push (@params, $self->{'template_id'}, $self->{'creator'});
172         $query .= " WHERE template_id=? AND creator=?;";
173         my $sth = C4::Context->dbh->prepare($query);
174         $sth->execute(@params);
175         if ($sth->err) {
176             warn sprintf('Database returned the following error: %s', $sth->errstr);
177             return -1;
178         }
179         $self->{'template_stat'} = 1;
180         return $self->{'template_id'};
181     }
182     else {                      # otherwise create a new record
183         my @params;
184         my $query = "INSERT INTO " . $opts{'table_name'} ." (";
185         foreach my $key (keys %{$self}) {
186             next if $key eq 'template_stat';
187             push (@params, $self->{$key});
188             $query .= "`$key`, ";
189         }
190         $query = substr($query, 0, (length($query)-2));
191         $query .= ") VALUES (";
192         for (my $i=1; $i<=((scalar keys %$self) - 1); $i++) {   # key count less keys not db related...
193             $query .= "?,";
194         }
195         $query = substr($query, 0, (length($query)-1));
196         $query .= ");";
197         my $sth = C4::Context->dbh->prepare($query);
198         $sth->execute(@params);
199         if ($sth->err) {
200             warn sprintf('Database returned the following error: %s', $sth->errstr);
201             return -1;
202         }
203         my $sth1 = C4::Context->dbh->prepare("SELECT MAX(template_id) FROM " . $opts{'table_name'} . ";");
204         $sth1->execute();
205         my $template_id = $sth1->fetchrow_array;
206         $self->{'template_id'} = $template_id;
207         $self->{'template_stat'} = 1;
208         return $template_id;
209     }
210 }
211
212 sub get_attr {
213     my $self = shift;
214     if (_check_params(@_) eq 1) {
215         return -1;
216     }
217     my ($attr) = @_;
218     if (exists($self->{$attr})) {
219         return $self->{$attr};
220     }
221     else {
222         return -1;
223     }
224 }
225
226 sub set_attr {
227     my $self = shift;
228     if (_check_params(@_) eq 1) {
229         return -1;
230     }
231     my %attrs = @_;
232     foreach my $attrib (keys(%attrs)) {
233         $self->{$attrib} = $attrs{$attrib};
234     };
235 }
236
237 sub get_label_position {
238     my ($self, $start_label) = @_;
239     my $current_label = $self->{'current_label'};
240     if ($start_label eq 1) {
241         $current_label->{'row_count'} = 1;
242         $current_label->{'col_count'} = 1;
243         $current_label->{'llx'} = $self->{'left_margin'};
244         $current_label->{'lly'} = ($self->{'page_height'} - $self->{'top_margin'} - $self->{'label_height'});
245         $self->{'current_label'} = $current_label;
246         return ($current_label->{'row_count'}, $current_label->{'col_count'}, $current_label->{'llx'}, $current_label->{'lly'});
247     }
248     else {
249         $current_label->{'row_count'} = ceil($start_label / $self->{'cols'});
250         $current_label->{'col_count'} = ($start_label - (($current_label->{'row_count'} - 1) * $self->{'cols'}));
251         $current_label->{'llx'} = $self->{'left_margin'} + ($self->{'label_width'} * ($current_label->{'col_count'} - 1)) + ($self->{'col_gap'} * ($current_label->{'col_count'} - 1));
252         $current_label->{'lly'} = $self->{'page_height'} - $self->{'top_margin'} - ($self->{'label_height'} * $current_label->{'row_count'}) - ($self->{'row_gap'} * ($current_label->{'row_count'} - 1));
253         $self->{'current_label'} = $current_label;
254         return ($current_label->{'row_count'}, $current_label->{'col_count'}, $current_label->{'llx'}, $current_label->{'lly'});
255     }
256 }
257
258 sub get_next_label_pos {
259     my $self = shift;
260     my $current_label = $self->{'current_label'};
261     my $new_page = 0;
262     if ($current_label->{'col_count'} lt $self->get_attr('cols')) {
263         $current_label->{'llx'} = ($current_label->{'llx'} + $self->get_attr('label_width') + $self->get_attr('col_gap'));
264         $current_label->{'col_count'}++;
265     }
266     else {
267         $current_label->{'llx'} = $self->get_attr('left_margin');
268         if ($current_label->{'row_count'} eq $self->get_attr('rows')) {
269             $new_page = 1;
270             $current_label->{'lly'} = ($self->get_attr('page_height') - $self->get_attr('top_margin') - $self->get_attr('label_height'));
271             $current_label->{'row_count'} = 1;
272         }
273         else {
274             $current_label->{'lly'} = ($current_label->{'lly'} - $self->get_attr('row_gap') - $self->get_attr('label_height'));
275             $current_label->{'row_count'}++;
276         }
277         $current_label->{'col_count'} = 1;
278     }
279     return ($current_label->{'llx'}, $current_label->{'lly'}, $new_page);
280 }
281
282 1;
283 __END__
284
285 =head1 NAME
286
287 C4::Creators::Template - A class for creating and manipulating template objects in Koha
288
289 =head1 ABSTRACT
290
291 This module provides methods for creating, retrieving, and otherwise manipulating label template objects used by Koha.
292
293 =head1 METHODS
294
295 =head2 new()
296
297     Invoking the I<new> method constructs a new template object containing the default values for a template.
298     The following parameters are optionally accepted as key => value pairs:
299
300         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.
301         C<template_code>        A template code. ie. 'Avery 5160 | 1 x 2-5/8'
302         C<template_desc>        A readable description of the template. ie. '3 columns, 10 rows of labels'
303         C<page_width>           The width of the page measured in the units supplied by the units parameter in this template.
304         C<page_height>          The height of the page measured in the same units.
305         C<label_width>          The width of a single label on the page this template applies to.
306         C<label_height>         The height of a single label on the page.
307         C<top_text_margin>      The measure of the top margin on a single label on the page.
308         C<left_text_margin>     The measure of the left margin on a single label on the page.
309         C<top_margin>           The measure of the top margin of the page.
310         C<left_margin>          The measure of the left margin of the page.
311         C<cols>                 The number of columns of labels on the page.
312         C<rows>                 The number of rows of labels on the page.
313         C<col_gap>              The measure of the gap between the columns of labels on the page.
314         C<row_gap>              The measure of the gap between the rows of labels on the page.
315         C<units>                The units of measure used for this template. These B<must> match the measures you supply above or
316                                 bad things will happen to your document. NOTE: The only supported units at present are:
317
318 =over 9
319
320 =item .
321 POINT   = Postscript Points (This is the base unit in the Koha label creator.)
322
323 =item .
324 AGATE   = Adobe Agates (5.1428571 points per)
325
326 =item .
327 INCH    = US Inches (72 points per)
328
329 =item .
330 MM      = SI Millimeters (2.83464567 points per)
331
332 =item .
333 CM      = SI Centimeters (28.3464567 points per)
334
335 =back
336
337     example:
338         my $template = Template->new(); # Creates and returns a new template object with the defaults
339
340         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
341             the supplied values to override the defaults
342
343     B<NOTE:> This template is I<not> written to the database until save() is invoked. You have been warned!
344
345 =head2 retrieve(template_id => $template_id)
346
347     Invoking the I<retrieve> method constructs a new template object containing the current values for template_id. The method returns
348     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
349     below for further description.
350
351     examples:
352
353         C<my $template = C4::Labels::Template->retrieve(template_id => 1); # Retrieves template record 1 and returns an object containing the record>
354
355         C<my $template = C4::Labels::Template->retrieve(template_id => 1, convert => 1); # Retrieves template record 1, converts the units to points,
356             and returns an object containing the record>
357
358         C<my $template = C4::Labels::Template->retrieve(template_id => 1, profile_id => 1); # Retrieves template record 1, converts the units
359             to points, applies the currently associated profile id, and returns an object containing the record.>
360
361 =head2 delete()
362
363     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.
364     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.
365
366     examples:
367         C<my $exitstat = $template->delete(); # to delete the record behind the $template object>
368         C<my $exitstat = C4::Labels::Template::delete(template_id => 1); # to delete template record 1>
369
370 =head2 save()
371
372     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
373     the template exists. The method returns the new record template_id upon success and -1 upon failure (This avoids template_ids conflicting with a
374     record template_id of 1). Errors are logged to the Apache log.
375
376     example:
377         C<my $template_id = $template->save(); # to save the record behind the $template object>
378
379 =head2 get_attr($attribute)
380
381     Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
382
383     example:
384         C<my $value = $template->get_attr($attribute);>
385
386 =head2 set_attr(attribute => value, attribute_2 => value)
387
388     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
389     commas.
390
391     example:
392         C<$template->set_attr(attribute => value);>
393
394 =head2 get_label_position($start_label)
395
396     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
397     label for the template object.
398
399     examples:
400         C<my ($row_count, $col_count, $llx, $lly) = $template->get_label_position($start_label);>
401
402 =head1 AUTHOR
403
404 Chris Nighswonger <cnighswonger AT foundations DOT edu>
405
406 =head1 COPYRIGHT
407
408 Copyright 2009 Foundations Bible College.
409
410 =head1 LICENSE
411
412 This file is part of Koha.
413
414 Koha is free software; you can redistribute it and/or modify it
415 under the terms of the GNU General Public License as published by
416 the Free Software Foundation; either version 3 of the License, or
417 (at your option) any later version.
418
419 Koha is distributed in the hope that it will be useful, but
420 WITHOUT ANY WARRANTY; without even the implied warranty of
421 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
422 GNU General Public License for more details.
423
424 You should have received a copy of the GNU General Public License
425 along with Koha; if not, see <http://www.gnu.org/licenses>.
426
427 =head1 DISCLAIMER OF WARRANTY
428
429 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
430 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
431
432 =cut