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