Bug 15468 - Search links on callnumbers with parentheses fails on OPAC results page
[koha.git] / C4 / Members / AttributeTypes.pm
blob234e1e08e609409a7fee3d50ae859708bef1e4db
1 package C4::Members::AttributeTypes;
3 # Copyright (C) 2008 LibLime
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use strict;
21 #use warnings; FIXME - Bug 2505
22 use C4::Context;
24 use vars qw($VERSION);
26 BEGIN {
27 # set the version for version checking
28 $VERSION = 3.07.00.049;
31 =head1 NAME
33 C4::Members::AttributeTypes - mananage extended patron attribute types
35 =head1 SYNOPSIS
37 my @attribute_types = C4::Members::AttributeTypes::GetAttributeTypes();
39 my $attr_type = C4::Members::AttributeTypes->new($code, $description);
40 $attr_type->code($code);
41 $attr_type->description($description);
42 $attr_type->repeatable($repeatable);
43 $attr_type->unique_id($unique_id);
44 $attr_type->opac_display($opac_display);
45 $attr_type->password_allowed($password_allowed);
46 $attr_type->staff_searchable($staff_searchable);
47 $attr_type->authorised_value_category($authorised_value_category);
48 $attr_type->store();
49 $attr_type->delete();
51 my $attr_type = C4::Members::AttributeTypes->fetch($code);
52 $attr_type = C4::Members::AttributeTypes->delete($code);
54 =head1 FUNCTIONS
56 =head2 GetAttributeTypes
58 my @attribute_types = C4::Members::AttributeTypes::GetAttributeTypes($all_fields);
60 Returns an array of hashrefs of each attribute type defined
61 in the database. The array is sorted by code. Each hashref contains
62 at least the following fields:
64 - code
65 - description
67 If $all_fields is true, then each hashref also contains the other fields from borrower_attribute_types.
69 =cut
71 sub GetAttributeTypes {
72 my $all = @_ ? shift : 0;
73 my $no_branch_limit = @_ ? shift : 0;
74 my $branch_limit = $no_branch_limit
75 ? 0
76 : C4::Context->userenv ? C4::Context->userenv->{"branch"} : 0;
77 my $select = $all ? '*' : 'DISTINCT(code), description, class';
79 my $dbh = C4::Context->dbh;
80 my $query = "SELECT $select FROM borrower_attribute_types";
81 $query .= qq{
82 LEFT JOIN borrower_attribute_types_branches ON bat_code = code
83 WHERE b_branchcode = ? OR b_branchcode IS NULL
84 } if $branch_limit;
85 $query .= " ORDER BY code";
86 my $sth = $dbh->prepare($query);
87 $sth->execute( $branch_limit ? $branch_limit : () );
88 my $results = $sth->fetchall_arrayref({});
89 $sth->finish;
90 return @$results;
93 sub GetAttributeTypes_hashref {
94 my %hash = map {$_->{code} => $_} GetAttributeTypes(@_);
95 return \%hash;
98 =head2 AttributeTypeExists
100 my $have_attr_xyz = C4::Members::AttributeTypes::AttributeTypeExists($code)
102 Returns true if we have attribute type C<$code>
103 in the database.
105 =cut
107 sub AttributeTypeExists {
108 my ($code) = @_;
109 my $dbh = C4::Context->dbh;
110 my $exists = $dbh->selectrow_array("SELECT code FROM borrower_attribute_types WHERE code = ?", undef, $code);
111 return $exists;
114 =head1 METHODS
116 my $attr_type = C4::Members::AttributeTypes->new($code, $description);
118 Create a new attribute type.
120 =cut
122 sub new {
123 my $class = shift;
124 my $self = {};
126 $self->{'code'} = shift;
127 $self->{'description'} = shift;
128 $self->{'repeatable'} = 0;
129 $self->{'unique_id'} = 0;
130 $self->{'opac_display'} = 0;
131 $self->{'password_allowed'} = 0;
132 $self->{'staff_searchable'} = 0;
133 $self->{'display_checkout'} = 0;
134 $self->{'authorised_value_category'} = '';
135 $self->{'category_code'} = '';
136 $self->{'category_description'} = '';
137 $self->{'class'} = '';
139 bless $self, $class;
140 return $self;
143 =head2 fetch
145 my $attr_type = C4::Members::AttributeTypes->fetch($code);
147 Fetches an attribute type from the database. If no
148 type with the given C<$code> exists, returns undef.
150 =cut
152 sub fetch {
153 my $class = shift;
154 my $code = shift;
155 my $self = {};
156 my $dbh = C4::Context->dbh();
158 my $sth = $dbh->prepare_cached("
159 SELECT borrower_attribute_types.*, categories.description AS category_description
160 FROM borrower_attribute_types
161 LEFT JOIN categories ON borrower_attribute_types.category_code=categories.categorycode
162 WHERE code =?");
163 $sth->execute($code);
164 my $row = $sth->fetchrow_hashref;
165 $sth->finish();
166 return unless defined $row;
168 $self->{'code'} = $row->{'code'};
169 $self->{'description'} = $row->{'description'};
170 $self->{'repeatable'} = $row->{'repeatable'};
171 $self->{'unique_id'} = $row->{'unique_id'};
172 $self->{'opac_display'} = $row->{'opac_display'};
173 $self->{'password_allowed'} = $row->{'password_allowed'};
174 $self->{'staff_searchable'} = $row->{'staff_searchable'};
175 $self->{'display_checkout'} = $row->{'display_checkout'};
176 $self->{'authorised_value_category'} = $row->{'authorised_value_category'};
177 $self->{'category_code'} = $row->{'category_code'};
178 $self->{'category_description'} = $row->{'category_description'};
179 $self->{'class'} = $row->{'class'};
181 $sth = $dbh->prepare("SELECT branchcode, branchname FROM borrower_attribute_types_branches, branches WHERE b_branchcode = branchcode AND bat_code = ?;");
182 $sth->execute( $code );
183 while ( my $data = $sth->fetchrow_hashref ) {
184 push @{ $self->{branches} }, $data;
186 $sth->finish();
188 bless $self, $class;
189 return $self;
192 =head2 store
194 $attr_type->store();
196 Stores attribute type in the database. If the type
197 previously retrieved from the database via the fetch()
198 method, the DB representation of the type is replaced.
200 =cut
202 sub store {
203 my $self = shift;
205 my $dbh = C4::Context->dbh;
206 my $sth;
207 my $existing = __PACKAGE__->fetch($self->{'code'});
208 if (defined $existing) {
209 $sth = $dbh->prepare_cached("UPDATE borrower_attribute_types
210 SET description = ?,
211 repeatable = ?,
212 unique_id = ?,
213 opac_display = ?,
214 password_allowed = ?,
215 staff_searchable = ?,
216 authorised_value_category = ?,
217 display_checkout = ?,
218 category_code = ?,
219 class = ?
220 WHERE code = ?");
221 } else {
222 $sth = $dbh->prepare_cached("INSERT INTO borrower_attribute_types
223 (description, repeatable, unique_id, opac_display, password_allowed,
224 staff_searchable, authorised_value_category, display_checkout, category_code, class, code)
225 VALUES (?, ?, ?, ?, ?,
226 ?, ?, ?, ?, ?, ?)");
228 $sth->bind_param(1, $self->{'description'});
229 $sth->bind_param(2, $self->{'repeatable'});
230 $sth->bind_param(3, $self->{'unique_id'});
231 $sth->bind_param(4, $self->{'opac_display'});
232 $sth->bind_param(5, $self->{'password_allowed'});
233 $sth->bind_param(6, $self->{'staff_searchable'});
234 $sth->bind_param(7, $self->{'authorised_value_category'});
235 $sth->bind_param(8, $self->{'display_checkout'});
236 $sth->bind_param(9, $self->{'category_code'} || undef);
237 $sth->bind_param(10, $self->{'class'});
238 $sth->bind_param(11, $self->{'code'});
239 $sth->execute;
241 if ( defined $$self{branches} ) {
242 $sth = $dbh->prepare("DELETE FROM borrower_attribute_types_branches WHERE bat_code = ?");
243 $sth->execute( $$self{code} );
244 $sth = $dbh->prepare(
245 "INSERT INTO borrower_attribute_types_branches
246 ( bat_code, b_branchcode )
247 VALUES ( ?, ? )"
249 for my $branchcode ( @{$$self{branches}} ) {
250 next if not $branchcode;
251 $sth->bind_param( 1, $$self{code} );
252 $sth->bind_param( 2, $branchcode );
253 $sth->execute;
256 $sth->finish;
259 =head2 code
261 my $code = $attr_type->code();
262 $attr_type->code($code);
264 Accessor. Note that the code is immutable once
265 a type is created or fetched from the database.
267 =cut
269 sub code {
270 my $self = shift;
271 return $self->{'code'};
274 =head2 description
276 my $description = $attr_type->description();
277 $attr_type->description($description);
279 Accessor.
281 =cut
283 sub description {
284 my $self = shift;
285 @_ ? $self->{'description'} = shift : $self->{'description'};
288 =head2 branches
290 my $branches = $attr_type->branches();
291 $attr_type->branches($branches);
293 Accessor.
295 =cut
297 sub branches {
298 my $self = shift;
299 @_ ? $self->{branches} = shift : $self->{branches};
302 =head2 repeatable
304 my $repeatable = $attr_type->repeatable();
305 $attr_type->repeatable($repeatable);
307 Accessor. The C<$repeatable> argument
308 is interpreted as a Perl boolean.
310 =cut
312 sub repeatable {
313 my $self = shift;
314 @_ ? $self->{'repeatable'} = ((shift) ? 1 : 0) : $self->{'repeatable'};
317 =head2 unique_id
319 my $unique_id = $attr_type->unique_id();
320 $attr_type->unique_id($unique_id);
322 Accessor. The C<$unique_id> argument
323 is interpreted as a Perl boolean.
325 =cut
327 sub unique_id {
328 my $self = shift;
329 @_ ? $self->{'unique_id'} = ((shift) ? 1 : 0) : $self->{'unique_id'};
331 =head2 opac_display
333 my $opac_display = $attr_type->opac_display();
334 $attr_type->opac_display($opac_display);
336 Accessor. The C<$opac_display> argument
337 is interpreted as a Perl boolean.
339 =cut
341 sub opac_display {
342 my $self = shift;
343 @_ ? $self->{'opac_display'} = ((shift) ? 1 : 0) : $self->{'opac_display'};
345 =head2 password_allowed
347 my $password_allowed = $attr_type->password_allowed();
348 $attr_type->password_allowed($password_allowed);
350 Accessor. The C<$password_allowed> argument
351 is interpreted as a Perl boolean.
353 =cut
355 sub password_allowed {
356 my $self = shift;
357 @_ ? $self->{'password_allowed'} = ((shift) ? 1 : 0) : $self->{'password_allowed'};
359 =head2 staff_searchable
361 my $staff_searchable = $attr_type->staff_searchable();
362 $attr_type->staff_searchable($staff_searchable);
364 Accessor. The C<$staff_searchable> argument
365 is interpreted as a Perl boolean.
367 =cut
369 sub staff_searchable {
370 my $self = shift;
371 @_ ? $self->{'staff_searchable'} = ((shift) ? 1 : 0) : $self->{'staff_searchable'};
374 =head2 display_checkout
376 my $display_checkout = $attr_type->display_checkout();
377 $attr_type->display_checkout($display_checkout);
379 Accessor. The C<$display_checkout> argument
380 is interpreted as a Perl boolean.
382 =cut
384 sub display_checkout {
385 my $self = shift;
386 @_ ? $self->{'display_checkout'} = ((shift) ? 1 : 0) : $self->{'display_checkout'};
389 =head2 authorised_value_category
391 my $authorised_value_category = $attr_type->authorised_value_category();
392 $attr_type->authorised_value_category($authorised_value_category);
394 Accessor.
396 =cut
398 sub authorised_value_category {
399 my $self = shift;
400 @_ ? $self->{'authorised_value_category'} = shift : $self->{'authorised_value_category'};
403 =head2 category_code
405 my $category_code = $attr_type->category_code();
406 $attr_type->category_code($category_code);
408 Accessor.
410 =cut
412 sub category_code {
413 my $self = shift;
414 @_ ? $self->{'category_code'} = shift : $self->{'category_code'};
417 =head2 category_description
419 my $category_description = $attr_type->category_description();
420 $attr_type->category_description($category_description);
422 Accessor.
424 =cut
426 sub category_description {
427 my $self = shift;
428 @_ ? $self->{'category_description'} = shift : $self->{'category_description'};
431 =head2 class
433 my $class = $attr_type->class();
434 $attr_type->class($class);
436 Accessor.
438 =cut
440 sub class {
441 my $self = shift;
442 @_ ? $self->{'class'} = shift : $self->{'class'};
446 =head2 delete
448 $attr_type->delete();
449 C4::Members::AttributeTypes->delete($code);
451 Delete an attribute type from the database. The attribute
452 type may be specified either by an object or by a code.
454 =cut
456 sub delete {
457 my $arg = shift;
458 my $code;
459 if (ref($arg) eq __PACKAGE__) {
460 $code = $arg->{'code'};
461 } else {
462 $code = shift;
465 my $dbh = C4::Context->dbh;
466 my $sth = $dbh->prepare_cached("DELETE FROM borrower_attribute_types WHERE code = ?");
467 $sth->execute($code);
468 $sth->finish;
471 =head2 num_patrons
473 my $count = $attr_type->num_patrons();
475 Returns the number of patron records that use
476 this attribute type.
478 =cut
480 sub num_patrons {
481 my $self = shift;
483 my $dbh = C4::Context->dbh;
484 my $sth = $dbh->prepare_cached("SELECT COUNT(DISTINCT borrowernumber)
485 FROM borrower_attributes
486 WHERE code = ?");
487 $sth->execute($self->{code});
488 my ($count) = $sth->fetchrow_array;
489 $sth->finish;
490 return $count;
493 =head2 get_patrons
495 my @borrowernumbers = $attr_type->get_patrons($attribute);
497 Returns the borrowernumber of the patron records that
498 have an attribute with the specifie value.
500 =cut
502 sub get_patrons {
503 my $self = shift;
504 my $value = shift;
506 my $dbh = C4::Context->dbh;
507 my $sth = $dbh->prepare_cached("SELECT DISTINCT borrowernumber
508 FROM borrower_attributes
509 WHERE code = ?
510 AND attribute = ?");
511 $sth->execute($self->{code}, $value);
512 my @results;
513 while (my ($borrowernumber) = $sth->fetchrow_array) {
514 push @results, $borrowernumber;
516 return @results;
519 =head1 AUTHOR
521 Koha Development Team <http://koha-community.org/>
523 Galen Charlton <galen.charlton@liblime.com>
525 =cut