Bug 24706: Fix ergonomic issue on the list view if empty
[koha.git] / C4 / Creators / Layout.pm
blob2a689e03528aca2cbde858b84c74b0d845c799e8
1 package C4::Creators::Layout;
3 use strict;
4 use warnings;
6 use autouse 'Data::Dumper' => qw(Dumper);
8 use C4::Context;
9 use C4::Debug;
10 use C4::Creators::PDF;
13 # FIXME: Consider this style parameter verification instead...
14 # my %param = @_;
15 # for (keys %param)
16 # { my $lc = lc($_);
17 # if (exists $default{$lc})
18 # { $default{$lc} = $param{$_};
19 # }
20 # else
21 # { print STDERR "Unknown parameter $_ , not used \n";
22 # }
23 # }
25 sub _check_params {
26 my $exit_code = 0;
27 my @valtmpl_id_params = (
28 'layout_id',
29 'barcode_type',
30 'printing_type',
31 'layout_name',
32 'guidebox',
33 'oblique_title',
34 'font',
35 'font_size',
36 'callnum_split',
37 'text_justify',
38 'format_string',
39 'layout_xml', # FIXME: all layouts should be stored in xml format to greatly simplify handling -chris_n
40 'creator',
41 'units',
42 'start_label',
44 if (scalar(@_) >1) {
45 my %given_params = @_;
46 foreach my $key (keys %given_params) {
47 if (!(grep m/$key/, @valtmpl_id_params)) {
48 warn sprintf('(Multiple parameters) Unrecognized parameter type of "%s".', $key);
49 $exit_code = 1;
53 else {
54 if (!(grep m/$_/, @valtmpl_id_params)) {
55 warn sprintf('(Single parameter) Unrecognized parameter type of "%s".', $_);
56 $exit_code = 1;
59 return $exit_code;
62 use constant PRESET_FIELDS => [qw(title author isbn issn itemtype barcode itemcallnumber)];
63 sub new {
64 my $invocant = shift;
65 my $self = '';
66 if (_check_params(@_) eq 1) {
67 return -1;
69 my $type = ref($invocant) || $invocant;
70 if (grep {$_ eq 'Labels'} @_) {
71 $self = {
72 layout_xml => '',
73 units => 'POINT',
74 start_label => 1,
75 barcode_type => 'CODE39',
76 printing_type => 'BAR',
77 layout_name => 'DEFAULT',
78 guidebox => 0,
79 oblique_title => 1,
80 font => 'TR',
81 font_size => 3,
82 callnum_split => 0,
83 text_justify => 'L',
84 format_string => join(', ', @{ PRESET_FIELDS() }),
85 @_,
88 elsif (grep {$_ eq 'Patroncards'} @_) {
89 $self = {
90 layout_xml => '<opt>Default Layout</opt>',
91 @_,
94 bless ($self, $type);
95 return $self;
98 sub retrieve {
99 my $invocant = shift;
100 my %opts = @_;
101 my $type = ref($invocant) || $invocant;
102 my $query = "SELECT * FROM creator_layouts WHERE layout_id = ? AND creator = ?";
103 my $sth = C4::Context->dbh->prepare($query);
104 $sth->execute($opts{'layout_id'}, $opts{'creator'});
105 if ($sth->err) {
106 warn sprintf('Database returned the following error: %s', $sth->errstr);
107 return -1;
109 my $self = $sth->fetchrow_hashref;
110 bless ($self, $type);
111 return $self;
114 sub delete {
115 my $self = {};
116 my %opts = ();
117 my $call_type = '';
118 my @params = ();
119 if (ref($_[0])) {
120 $self = shift; # check to see if this is a method call
121 $call_type = 'C4::Labels::Layout->delete';
122 push @params, $self->{'layout_id'}, $self->{'creator'};
124 else {
125 my $class = shift;
126 %opts = @_;
127 $call_type = $class . '::delete';
128 push @params, $opts{'layout_id'}, $opts{'creator'};
130 if (scalar(@params) < 2) { # If there is no layout id or creator type then we cannot delete it
131 warn sprintf('%s : Cannot delete layout as the profile id is invalid or non-existent.', $call_type) if !$params[0];
132 warn sprintf('%s : Cannot delete layout as the creator type is invalid or non-existent.', $call_type) if !$params[1];
133 return -1;
135 my $query = "DELETE FROM creator_layouts WHERE layout_id = ? AND creator = ?";
136 my $sth = C4::Context->dbh->prepare($query);
137 $sth->execute(@params);
138 if ($sth->err) {
139 warn sprintf('Database returned the following error on attempted DELETE: %s', $sth->errstr);
140 return -1;
144 sub save {
145 my $self = shift;
146 if ($self->{'layout_id'}) { # if we have an id, the record exists and needs UPDATE
147 my @params;
148 my $query = "UPDATE creator_layouts SET ";
149 foreach my $key (keys %{$self}) {
150 next if ($key eq 'layout_id') || ($key eq 'creator');
151 push (@params, $self->{$key});
152 $query .= "$key=?, ";
154 $query = substr($query, 0, (length($query)-2));
155 $query .= " WHERE layout_id=? AND creator = ?;";
156 push (@params, $self->{'layout_id'}, $self->{'creator'});
157 my $sth = C4::Context->dbh->prepare($query);
158 #local $sth->{TraceLevel} = "3"; # enable DBI trace and set level; outputs to STDERR
159 $sth->execute(@params);
160 if ($sth->err) {
161 warn sprintf('Database returned the following error: %s', $sth->errstr);
162 return -1;
164 return $self->{'layout_id'};
166 else { # otherwise create a new record
167 my @params;
168 delete $self->{layout_id}; # Could be an empty string
169 my $query = "INSERT INTO creator_layouts (";
170 foreach my $key (keys %{$self}) {
171 push (@params, $self->{$key});
172 $query .= "$key, ";
174 $query = substr($query, 0, (length($query)-2));
175 $query .= ") VALUES (";
176 for (my $i=1; $i<=(scalar keys %$self); $i++) {
177 $query .= "?,";
179 $query = substr($query, 0, (length($query)-1));
180 $query .= ");";
181 my $sth = C4::Context->dbh->prepare($query);
182 $sth->execute(@params);
183 if ($sth->err) {
184 warn sprintf('Database returned the following error: %s', $sth->errstr);
185 return -1;
187 my $sth1 = C4::Context->dbh->prepare("SELECT MAX(layout_id) FROM creator_layouts;");
188 $sth1->execute();
189 my $id = $sth1->fetchrow_array;
190 $self->{'layout_id'} = $id;
191 return $id;
195 sub get_attr {
196 my $self = shift;
197 if (_check_params(@_) eq 1) {
198 return -1;
200 my ($attr) = @_;
201 if (exists($self->{$attr})) {
202 return $self->{$attr};
204 else {
205 return -1;
207 return;
210 sub set_attr {
211 my $self = shift;
212 if (_check_params(@_) eq 1) {
213 return -1;
215 my %attrs = @_;
216 foreach my $attrib (keys(%attrs)) {
217 $self->{$attrib} = $attrs{$attrib};
219 return 0;
222 sub get_text_wrap_cols {
223 my $self = shift;
224 my %params = @_;
225 my $string = '';
226 my $strwidth = 0;
227 my $col_count = 0;
228 my $textlimit = $params{'label_width'} - (( 3 * $params{'left_text_margin'} ) || 13.5 );
230 while ($strwidth < $textlimit) {
231 $string .= '8'; # using '8' as filling char instead of '0'
232 $col_count++;
233 $strwidth = C4::Creators::PDF->StrWidth( $string, $self->{'font'}, $self->{'font_size'} );
235 return $col_count;
239 __END__
241 =head1 NAME
243 C4::Labels::Layout -A class for creating and manipulating layout objects in Koha
245 =head1 ABSTRACT
247 This module provides methods for creating, retrieving, and otherwise manipulating label layout objects used by Koha to create and export labels.
249 =head1 METHODS
251 =head2 new()
253 Invoking the I<new> method constructs a new layout object containing the default values for a layout.
254 The following parameters are optionally accepted as key => value pairs:
256 C<barcode_type> Defines the barcode type to be used on labels. NOTE: At present only the following barcode types are supported in the label creator code:
258 =over 9
260 =item .
261 CODE39 = Code 3 of 9
263 =item .
264 CODE39MOD = Code 3 of 9 with modulo 43 checksum
266 =item .
267 CODE39MOD10 = Code 3 of 9 with modulo 10 checksum
269 =item .
270 COOP2OF5 = A variant of 2 of 5 barcode based on NEC's "Process 8000" code
272 =item .
273 INDUSTRIAL2OF5 = The standard 2 of 5 barcode (a binary level bar code developed by Identicon Corp. and Computer Identics Corp. in 1970)
275 =back
277 C<printing_type> Defines the general layout to be used on labels. NOTE: At present there are only five printing types supported in the label creator code:
279 =over 9
281 =item .
282 BIB = Only the bibliographic data is printed
284 =item .
285 BARBIB = Barcode proceeds bibliographic data
287 =item .
288 BIBBAR = Bibliographic data proceeds barcode
290 =item .
291 ALT = Barcode and bibliographic data are printed on alternating labels
293 =item .
294 BAR = Only the barcode is printed
296 =back
298 C<layout_name> The descriptive name for this layout.
299 C<guidebox> Setting this to '1' will result in a guide box being drawn around the labels marking the edge of each label
300 C<font> Defines the type of font to be used on labels. NOTE: The following fonts are available by default on most systems:
302 =over 9
304 =item .
305 TR = Times-Roman
307 =item .
308 TB = Times Bold
310 =item .
311 TI = Times Italic
313 =item .
314 TBI = Times Bold Italic
316 =item .
317 C = Courier
319 =item .
320 CB = Courier Bold
322 =item .
323 CO = Courier Oblique (Italic)
325 =item .
326 CBO = Courier Bold Oblique
328 =item .
329 H = Helvetica
331 =item .
332 HB = Helvetica Bold
334 =item .
335 HBO = Helvetical Bold Oblique
337 =back
339 C<font_size> Defines the size of the font in postscript points to be used on labels
340 C<callnum_split> Setting this to '1' will enable call number splitting on labels
341 C<text_justify> Defines the text justification to be used on labels. NOTE: The following justification styles are currently supported by label creator code:
343 =over 9
345 =item .
346 L = Left
348 =item .
349 C = Center
351 =item .
352 R = Right
354 =back
356 C<format_string> Defines what fields will be printed and in what order they will be printed on labels. These include any of the data fields that may be mapped
357 to your MARC frameworks. Specify MARC subfields as a 4-character tag-subfield string: ie. 254a Enclose a whitespace-separated list of fields
358 to concatenate on one line in double quotes. ie. "099a 099b" or "itemcallnumber barcode" Static text strings may be entered in single-quotes:
359 ie. 'Some static text here.'
361 example:
362 C<my $layout = Layout->new(); # Creates and returns a new layout object>
364 C<my $layout = C4::Labels::Layout->new(barcode_type => 'CODE39', printing_type => 'BIBBAR', font => 'C', font_size => 6); # Creates and returns a new layout object using
365 the supplied values to override the defaults>
367 B<NOTE:> This layout is I<not> written to the database until save() is invoked. You have been warned!
369 =head2 retrieve(layout_id => layout_id)
371 Invoking the I<retrieve> method constructs a new layout object containing the current values for layout_id. The method returns a new object upon success and 1 upon failure.
372 Errors are logged to the Apache log.
374 example:
375 C<my $layout = Layout->retrieve(layout_id => 1); # Retrieves layout record 1 and returns an object containing the record>
377 =head2 delete()
379 Invoking the delete method attempts to delete the layout from the database. The method returns 0 upon success and -1 upon failure. Errors are logged to the Apache log.
380 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.
382 examples:
383 C<my $exitstat = $layout->delete(); # to delete the record behind the $layout object>
384 C<my $exitstat = Layout->delete(layout_id => 1); # to delete layout record 1>
386 =head2 save()
388 Invoking the I<save> method attempts to insert the layout into the database if the layout is new and update the existing layout record if the layout exists.
389 The method returns the new record id upon success and -1 upon failure (This avoids conflicting with a record id of 1). Errors are logged to the Apache log.
391 example:
392 C<my $exitstat = $layout->save(); # to save the record behind the $layout object>
394 =head2 get_attr($attribute)
396 Invoking the I<get_attr> method will return the value of the requested attribute or -1 on errors.
398 example:
399 C<my $value = $layout->get_attr($attribute);>
401 =head2 set_attr(attribute => value, attribute_2 => value)
403 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
404 commas.
406 example:
407 C<$layout->set_attr(attribute => value);>
409 =head2 get_text_wrap_cols()
411 Invoking the I<get_text_wrap_cols> method will return the number of columns that can be printed on the label before wrapping to the next line.
413 examples:
414 C<my $text_wrap_cols = $layout->get_text_wrap_cols();>
416 =head1 AUTHOR
418 Chris Nighswonger <cnighswonger AT foundations DOT edu>
420 =head1 COPYRIGHT
422 Copyright 2009 Foundations Bible College.
424 =head1 LICENSE
426 This file is part of Koha.
428 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
429 Foundation; either version 2 of the License, or (at your option) any later version.
431 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,
432 Fifth Floor, Boston, MA 02110-1301 USA.
434 =head1 DISCLAIMER OF WARRANTY
436 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
437 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
439 =cut