Bug 26555: Make sure _messages is always initialized
[koha.git] / C4 / Creators / Template.pm
blob1614a0aecf466a115df3f8ba4ae20653ea8b402b
1 package C4::Creators::Template;
3 use strict;
4 use warnings;
5 use POSIX qw(ceil);
6 use autouse 'Data::Dumper' => qw(Dumper);
8 use C4::Context;
9 use C4::Debug;
10 use C4::Creators::Profile;
11 use C4::Creators::Lib qw(get_unit_values);
14 sub _check_params {
15 shift if $_[0] =~ m/::/; # this seems a bit hackish
16 my $given_params = {};
17 my $exit_code = 0;
18 my @valid_template_params = (
19 'profile_id',
20 'template_code',
21 'template_desc',
22 'page_width',
23 'page_height',
24 'label_width',
25 'label_height',
26 'card_width',
27 'card_height',
28 'top_text_margin',
29 'left_text_margin',
30 'top_margin',
31 'left_margin',
32 'cols',
33 'rows',
34 'col_gap',
35 'row_gap',
36 'units',
37 'creator',
38 'current_label',
40 if (scalar(@_) >1) {
41 $given_params = {@_};
42 foreach my $key (keys %{$given_params}) {
43 if (!(grep m/$key/, @valid_template_params)) {
44 warn sprintf('Unrecognized parameter type of "%s".', $key);
45 $exit_code = 1;
49 else {
50 if (!(grep m/$_/, @valid_template_params)) {
51 warn sprintf('Unrecognized parameter type of "%s".', $_);
52 $exit_code = 1;
55 return $exit_code;
58 sub _conv_points {
59 my $self = shift;
60 my @unit_value = grep {$_->{'type'} eq $self->{'units'}} @{get_unit_values()};
61 $self->{'page_width'} = $self->{'page_width'} * $unit_value[0]->{'value'};
62 $self->{'page_height'} = $self->{'page_height'} * $unit_value[0]->{'value'};
63 $self->{'label_width'} = $self->{'label_width'} * $unit_value[0]->{'value'};
64 $self->{'label_height'} = $self->{'label_height'} * $unit_value[0]->{'value'};
65 $self->{'top_text_margin'} = $self->{'top_text_margin'} * $unit_value[0]->{'value'};
66 $self->{'left_text_margin'} = $self->{'left_text_margin'} * $unit_value[0]->{'value'};
67 $self->{'top_margin'} = $self->{'top_margin'} * $unit_value[0]->{'value'};
68 $self->{'left_margin'} = $self->{'left_margin'} * $unit_value[0]->{'value'};
69 $self->{'col_gap'} = $self->{'col_gap'} * $unit_value[0]->{'value'};
70 $self->{'row_gap'} = $self->{'row_gap'} * $unit_value[0]->{'value'};
71 return $self;
74 sub _apply_profile {
75 my $self = shift;
76 my $creator = shift;
77 my $profile = C4::Creators::Profile->retrieve(profile_id => $self->{'profile_id'}, creator => $creator, convert => 1);
78 $self->{'top_margin'} = $self->{'top_margin'} + $profile->get_attr('offset_vert'); # controls vertical offset
79 $self->{'left_margin'} = $self->{'left_margin'} + $profile->get_attr('offset_horz'); # controls horizontal offset
80 $self->{'label_height'} = $self->{'label_height'} + $profile->get_attr('creep_vert'); # controls vertical creep
81 $self->{'label_width'} = $self->{'label_width'} + $profile->get_attr('creep_horz'); # controls horizontal creep
82 return $self;
85 sub new {
86 my $invocant = shift;
87 my $type = ref($invocant) || $invocant;
88 if (_check_params(@_) eq 1) {
89 return -1;
91 my $self = {
92 profile_id => 0,
93 template_code => 'DEFAULT TEMPLATE',
94 template_desc => 'Default description',
95 page_width => 0,
96 page_height => 0,
97 label_width => 0,
98 label_height => 0,
99 top_text_margin => 0,
100 left_text_margin => 0,
101 top_margin => 0,
102 left_margin => 0,
103 cols => 0,
104 rows => 0,
105 col_gap => 0,
106 row_gap => 0,
107 units => 'POINT',
108 template_stat => 0, # false if any data has changed and the db has not been updated
111 bless ($self, $type);
112 return $self;
115 sub retrieve {
116 my $invocant = shift;
117 my %opts = @_;
118 my $type = ref($invocant) || $invocant;
119 my $query = "SELECT * FROM " . $opts{'table_name'} . " WHERE template_id = ? AND creator = ?";
120 my $sth = C4::Context->dbh->prepare($query);
121 $sth->execute($opts{'template_id'}, $opts{'creator'});
122 if ($sth->err) {
123 warn sprintf('Database returned the following error: %s', $sth->errstr);
124 return -1;
126 my $self = $sth->fetchrow_hashref;
127 $self = _conv_points($self) if (($opts{convert} && $opts{convert} == 1) || $opts{profile_id});
128 $self = _apply_profile($self, $opts{'creator'}) if $opts{profile_id} && $self->{'profile_id'}; # don't bother if there is no profile_id
129 $self->{'template_stat'} = 1;
130 bless ($self, $type);
131 return $self;
134 sub delete {
135 my $self = {};
136 my %opts = ();
137 my $call_type = '';
138 my @query_params = ();
139 if (ref($_[0])) {
140 $self = shift; # check to see if this is a method call
141 $call_type = 'C4::Labels::Template->delete';
142 push @query_params, $self->{'template_id'}, $self->{'creator'};
144 else {
145 %opts = @_;
146 $call_type = 'C4::Labels::Template::delete';
147 push @query_params, $opts{'template_id'}, $opts{'creator'};
149 if (scalar(@query_params) < 2) { # If there is no template id or creator type then we cannot delete it
150 warn sprintf('%s : Cannot delete template as the template id is invalid or non-existent.', $call_type) if !$query_params[0];
151 warn sprintf('%s : Cannot delete template as the creator type is invalid or non-existent.', $call_type) if !$query_params[1];
152 return -1;
154 my $query = "DELETE FROM creator_templates WHERE template_id = ? AND creator = ?";
155 my $sth = C4::Context->dbh->prepare($query);
156 $sth->execute(@query_params);
157 $self->{'template_stat'} = 0;
160 sub save {
161 my $self = shift;
162 my %opts = @_;
163 if ($self->{'template_id'}) { # if we have an template_id, the record exists and needs UPDATE
164 my @params;
165 my $query = "UPDATE " . $opts{'table_name'} . " SET ";
166 foreach my $key (keys %{$self}) {
167 next if ($key eq 'template_id') || ($key eq 'template_stat') || ($key eq 'creator');
168 push (@params, $self->{$key});
169 $query .= "`$key`=?, ";
171 $query = substr($query, 0, (length($query)-2));
172 push (@params, $self->{'template_id'}, $self->{'creator'});
173 $query .= " WHERE template_id=? AND creator=?;";
174 my $sth = C4::Context->dbh->prepare($query);
175 $sth->execute(@params);
176 if ($sth->err) {
177 warn sprintf('Database returned the following error: %s', $sth->errstr);
178 return -1;
180 $self->{'template_stat'} = 1;
181 return $self->{'template_id'};
183 else { # otherwise create a new record
184 my @params;
185 my $query = "INSERT INTO " . $opts{'table_name'} ." (";
186 foreach my $key (keys %{$self}) {
187 next if $key eq 'template_stat';
188 push (@params, $self->{$key});
189 $query .= "`$key`, ";
191 $query = substr($query, 0, (length($query)-2));
192 $query .= ") VALUES (";
193 for (my $i=1; $i<=((scalar keys %$self) - 1); $i++) { # key count less keys not db related...
194 $query .= "?,";
196 $query = substr($query, 0, (length($query)-1));
197 $query .= ");";
198 my $sth = C4::Context->dbh->prepare($query);
199 $sth->execute(@params);
200 if ($sth->err) {
201 warn sprintf('Database returned the following error: %s', $sth->errstr);
202 return -1;
204 my $sth1 = C4::Context->dbh->prepare("SELECT MAX(template_id) FROM " . $opts{'table_name'} . ";");
205 $sth1->execute();
206 my $template_id = $sth1->fetchrow_array;
207 $self->{'template_id'} = $template_id;
208 $self->{'template_stat'} = 1;
209 return $template_id;
213 sub get_attr {
214 my $self = shift;
215 if (_check_params(@_) eq 1) {
216 return -1;
218 my ($attr) = @_;
219 if (exists($self->{$attr})) {
220 return $self->{$attr};
222 else {
223 return -1;
227 sub set_attr {
228 my $self = shift;
229 if (_check_params(@_) eq 1) {
230 return -1;
232 my %attrs = @_;
233 foreach my $attrib (keys(%attrs)) {
234 $self->{$attrib} = $attrs{$attrib};
238 sub get_label_position {
239 my ($self, $start_label) = @_;
240 my $current_label = $self->{'current_label'};
241 if ($start_label eq 1) {
242 $current_label->{'row_count'} = 1;
243 $current_label->{'col_count'} = 1;
244 $current_label->{'llx'} = $self->{'left_margin'};
245 $current_label->{'lly'} = ($self->{'page_height'} - $self->{'top_margin'} - $self->{'label_height'});
246 $self->{'current_label'} = $current_label;
247 return ($current_label->{'row_count'}, $current_label->{'col_count'}, $current_label->{'llx'}, $current_label->{'lly'});
249 else {
250 $current_label->{'row_count'} = ceil($start_label / $self->{'cols'});
251 $current_label->{'col_count'} = ($start_label - (($current_label->{'row_count'} - 1) * $self->{'cols'}));
252 $current_label->{'llx'} = $self->{'left_margin'} + ($self->{'label_width'} * ($current_label->{'col_count'} - 1)) + ($self->{'col_gap'} * ($current_label->{'col_count'} - 1));
253 $current_label->{'lly'} = $self->{'page_height'} - $self->{'top_margin'} - ($self->{'label_height'} * $current_label->{'row_count'}) - ($self->{'row_gap'} * ($current_label->{'row_count'} - 1));
254 $self->{'current_label'} = $current_label;
255 return ($current_label->{'row_count'}, $current_label->{'col_count'}, $current_label->{'llx'}, $current_label->{'lly'});
259 sub get_next_label_pos {
260 my $self = shift;
261 my $current_label = $self->{'current_label'};
262 my $new_page = 0;
263 if ($current_label->{'col_count'} lt $self->get_attr('cols')) {
264 $current_label->{'llx'} = ($current_label->{'llx'} + $self->get_attr('label_width') + $self->get_attr('col_gap'));
265 $current_label->{'col_count'}++;
267 else {
268 $current_label->{'llx'} = $self->get_attr('left_margin');
269 if ($current_label->{'row_count'} eq $self->get_attr('rows')) {
270 $new_page = 1;
271 $current_label->{'lly'} = ($self->get_attr('page_height') - $self->get_attr('top_margin') - $self->get_attr('label_height'));
272 $current_label->{'row_count'} = 1;
274 else {
275 $current_label->{'lly'} = ($current_label->{'lly'} - $self->get_attr('row_gap') - $self->get_attr('label_height'));
276 $current_label->{'row_count'}++;
278 $current_label->{'col_count'} = 1;
280 return ($current_label->{'llx'}, $current_label->{'lly'}, $new_page);
284 __END__
286 =head1 NAME
288 C4::Creators::Template - A class for creating and manipulating template objects in Koha
290 =head1 ABSTRACT
292 This module provides methods for creating, retrieving, and otherwise manipulating label template objects used by Koha.
294 =head1 METHODS
296 =head2 new()
298 Invoking the I<new> method constructs a new template object containing the default values for a template.
299 The following parameters are optionally accepted as key => value pairs:
301 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.
302 C<template_code> A template code. ie. 'Avery 5160 | 1 x 2-5/8'
303 C<template_desc> A readable description of the template. ie. '3 columns, 10 rows of labels'
304 C<page_width> The width of the page measured in the units supplied by the units parameter in this template.
305 C<page_height> The height of the page measured in the same units.
306 C<label_width> The width of a single label on the page this template applies to.
307 C<label_height> The height of a single label on the page.
308 C<top_text_margin> The measure of the top margin on a single label on the page.
309 C<left_text_margin> The measure of the left margin on a single label on the page.
310 C<top_margin> The measure of the top margin of the page.
311 C<left_margin> The measure of the left margin of the page.
312 C<cols> The number of columns of labels on the page.
313 C<rows> The number of rows of labels on the page.
314 C<col_gap> The measure of the gap between the columns of labels on the page.
315 C<row_gap> The measure of the gap between the rows of labels on the page.
316 C<units> The units of measure used for this template. These B<must> match the measures you supply above or
317 bad things will happen to your document. NOTE: The only supported units at present are:
319 =over 9
321 =item .
322 POINT = Postscript Points (This is the base unit in the Koha label creator.)
324 =item .
325 AGATE = Adobe Agates (5.1428571 points per)
327 =item .
328 INCH = US Inches (72 points per)
330 =item .
331 MM = SI Millimeters (2.83464567 points per)
333 =item .
334 CM = SI Centimeters (28.3464567 points per)
336 =back
338 example:
339 my $template = Template->new(); # Creates and returns a new template object with the defaults
341 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
342 the supplied values to override the defaults
344 B<NOTE:> This template is I<not> written to the database until save() is invoked. You have been warned!
346 =head2 retrieve(template_id => $template_id)
348 Invoking the I<retrieve> method constructs a new template object containing the current values for template_id. The method returns
349 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
350 below for further description.
352 examples:
354 C<my $template = C4::Labels::Template->retrieve(template_id => 1); # Retrieves template record 1 and returns an object containing the record>
356 C<my $template = C4::Labels::Template->retrieve(template_id => 1, convert => 1); # Retrieves template record 1, converts the units to points,
357 and returns an object containing the record>
359 C<my $template = C4::Labels::Template->retrieve(template_id => 1, profile_id => 1); # Retrieves template record 1, converts the units
360 to points, applies the currently associated profile id, and returns an object containing the record.>
362 =head2 delete()
364 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.
365 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.
367 examples:
368 C<my $exitstat = $template->delete(); # to delete the record behind the $template object>
369 C<my $exitstat = C4::Labels::Template::delete(template_id => 1); # to delete template record 1>
371 =head2 save()
373 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
374 the template exists. The method returns the new record template_id upon success and -1 upon failure (This avoids template_ids conflicting with a
375 record template_id of 1). Errors are logged to the Apache log.
377 example:
378 C<my $template_id = $template->save(); # to save the record behind the $template object>
380 =head2 get_attr($attribute)
382 Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
384 example:
385 C<my $value = $template->get_attr($attribute);>
387 =head2 set_attr(attribute => value, attribute_2 => value)
389 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
390 commas.
392 example:
393 C<$template->set_attr(attribute => value);>
395 =head2 get_label_position($start_label)
397 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
398 label for the template object.
400 examples:
401 C<my ($row_count, $col_count, $llx, $lly) = $template->get_label_position($start_label);>
403 =head1 AUTHOR
405 Chris Nighswonger <cnighswonger AT foundations DOT edu>
407 =head1 COPYRIGHT
409 Copyright 2009 Foundations Bible College.
411 =head1 LICENSE
413 This file is part of Koha.
415 Koha is free software; you can redistribute it and/or modify it
416 under the terms of the GNU General Public License as published by
417 the Free Software Foundation; either version 3 of the License, or
418 (at your option) any later version.
420 Koha is distributed in the hope that it will be useful, but
421 WITHOUT ANY WARRANTY; without even the implied warranty of
422 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
423 GNU General Public License for more details.
425 You should have received a copy of the GNU General Public License
426 along with Koha; if not, see <http://www.gnu.org/licenses>.
428 =head1 DISCLAIMER OF WARRANTY
430 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
431 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
433 =cut