Bug 29697: Replace GetMarcBiblio occurrences with $biblio->metadata->record
[srvgit] / Koha / UI / Form / Builder / Item.pm
1 package Koha::UI::Form::Builder::Item;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19 use MARC::Record;
20 use C4::Context;
21 use C4::Biblio qw( GetFrameworkCode GetMarcStructure IsMarcStructureInternal );
22 use C4::Koha qw( GetAuthorisedValues );
23 use C4::ClassSource qw( GetClassSources );
24
25 use Koha::Biblios;
26 use Koha::DateUtils qw( dt_from_string );
27 use Koha::Libraries;
28
29 =head1 NAME
30
31 Koha::UI::Form::Builder::Item
32
33 Helper to build a form to add or edit a new item.
34
35 =head1 API
36
37 =head2 Class methods
38
39 =cut
40
41 =head3 new
42
43     my $form = Koha::UI::Form::Builder::Item->new(
44         {
45             biblionumber => $biblionumber,
46             item => $current_item,
47         }
48     );
49
50 Constructor.
51 biblionumber should be passed if we are creating a new item.
52 For edition, an hashref representing the item to edit item must be passed.
53
54 =cut
55
56
57 sub new {
58     my ( $class, $params ) = @_;
59
60     my $self;
61     $self->{biblionumber} = $params->{biblionumber};
62     $self->{item} = $params->{item};
63
64     bless $self, $class;
65     return $self;
66 }
67
68 =head3 generate_subfield_form
69
70 Generate subfield's info for given tag, subfieldtag, etc.
71
72 =cut
73
74 sub generate_subfield_form {
75
76     my ($self, $params)    = @_;
77     my $tag         = $params->{tag};
78     my $subfieldtag = $params->{subfieldtag};
79     my $value       = $params->{value};
80     my $tagslib     = $params->{tagslib};
81     my $libraries   = $params->{libraries};
82     my $marc_record = $params->{marc_record};
83     my $restricted_edition = $params->{restricted_editition};
84     my $prefill_with_default_values = $params->{prefill_with_default_values};
85     my $branch_limit = $params->{branch_limit};
86     my $default_branches_empty = $params->{default_branches_empty};
87     my $readonly = $params->{readonly};
88
89     my $item         = $self->{item};
90     my $subfield     = $tagslib->{$tag}{$subfieldtag};
91     my $biblionumber = $self->{biblionumber};
92
93     my $frameworkcode = $biblionumber ? GetFrameworkCode($biblionumber) : q{};
94
95     my %subfield_data;
96     my $dbh = C4::Context->dbh;
97
98     my $index_subfield = int( rand(1000000) );
99     if ( $subfieldtag eq '@' ) {
100         $subfield_data{id} = "tag_" . $tag . "_subfield_00_" . $index_subfield;
101     }
102     else {
103         $subfield_data{id} =
104           "tag_" . $tag . "_subfield_" . $subfieldtag . "_" . $index_subfield;
105     }
106
107     $subfield_data{tag}      = $tag;
108     $subfield_data{subfield} = $subfieldtag;
109     $subfield_data{marc_lib} =
110         "<span title=\""
111       . $subfield->{lib} . "\">"
112       . $subfield->{lib}
113       . "</span>";
114     $subfield_data{mandatory}     = $subfield->{mandatory};
115     $subfield_data{important}     = $subfield->{important};
116     $subfield_data{repeatable}    = $subfield->{repeatable};
117     $subfield_data{maxlength}     = $subfield->{maxlength};
118     $subfield_data{display_order} = $subfield->{display_order};
119     $subfield_data{kohafield} =
120       $subfield->{kohafield} || 'items.more_subfields_xml';
121
122     if ( $prefill_with_default_values && ( !defined($value) || $value eq '' ) ) {
123         $value = $subfield->{defaultvalue} if !$item->{itemnumber}; # apply defaultvalue only to new items
124         if ($value) {
125
126 # get today date & replace <<YYYY>>, <<YY>>, <<MM>>, <<DD>> if provided in the default value
127             my $today_dt  = dt_from_string;
128             my $year      = $today_dt->strftime('%Y');
129             my $shortyear = $today_dt->strftime('%y');
130             my $month     = $today_dt->strftime('%m');
131             my $day       = $today_dt->strftime('%d');
132             $value =~ s/<<YYYY>>/$year/g;
133             $value =~ s/<<YY>>/$shortyear/g;
134             $value =~ s/<<MM>>/$month/g;
135             $value =~ s/<<DD>>/$day/g;
136
137             # And <<USER>> with surname (?)
138             my $username = (
139                   C4::Context->userenv
140                 ? C4::Context->userenv->{'surname'}
141                 : "superlibrarian"
142             );
143             $value =~ s/<<USER>>/$username/g;
144         }
145     }
146
147     $subfield_data{visibility} = "display:none;"
148       if ( ( $subfield->{hidden} > 4 ) || ( $subfield->{hidden} <= -4 ) );
149
150     my $pref_itemcallnumber = C4::Context->preference('itemcallnumber');
151     if (  $prefill_with_default_values
152         && !$value
153         && $subfield->{kohafield}
154         && $subfield->{kohafield} eq 'items.itemcallnumber'
155         && $pref_itemcallnumber )
156     {
157         foreach
158           my $pref_itemcallnumber_part ( split( /,/, $pref_itemcallnumber ) )
159         {
160             my $CNtag =
161               substr( $pref_itemcallnumber_part, 0, 3 );    # 3-digit tag number
162             my $CNsubfields =
163               substr( $pref_itemcallnumber_part, 3 );    # Any and all subfields
164             $CNsubfields = undef if $CNsubfields eq '';
165             my $temp2 = $marc_record->field($CNtag);
166
167             next unless $temp2;
168             $value = $temp2->as_string( $CNsubfields, ' ' );
169             last if $value;
170         }
171     }
172
173     if ( $subfield->{authorised_value} ) {
174         my @authorised_values;
175         my %authorised_lib;
176
177         # builds list, depending on authorised value...
178         if ( $subfield->{authorised_value} eq "LOST" ) {
179             my $ClaimReturnedLostValue =
180               C4::Context->preference('ClaimReturnedLostValue');
181             my $item_is_return_claim =
182                  $ClaimReturnedLostValue
183               && exists $item->{itemlost}
184               && $ClaimReturnedLostValue eq $item->{itemlost};
185             $subfield_data{IS_RETURN_CLAIM} = $item_is_return_claim;
186
187             $subfield_data{IS_LOST_AV} = 1;
188
189             push @authorised_values, qq{};
190             my $av = GetAuthorisedValues( $subfield->{authorised_value} );
191             for my $r (@$av) {
192                 push @authorised_values, $r->{authorised_value};
193                 $authorised_lib{ $r->{authorised_value} } = $r->{lib};
194             }
195         }
196         elsif ( $subfield->{authorised_value} eq "branches" ) {
197             push @authorised_values, "" if $default_branches_empty;
198             foreach my $thisbranch (@$libraries) {
199                 push @authorised_values, $thisbranch->{branchcode};
200                 $authorised_lib{ $thisbranch->{branchcode} } =
201                   $thisbranch->{branchname};
202                 $value = $thisbranch->{branchcode}
203                   if $thisbranch->{selected} && !$value;
204             }
205         }
206         elsif ( $subfield->{authorised_value} eq "itemtypes" ) {
207             push @authorised_values, "";
208             my $itemtypes;
209             if ($branch_limit) {
210                 $itemtypes = Koha::ItemTypes->search_with_localization(
211                     { branchcode => $branch_limit } );
212             }
213             else {
214                 $itemtypes = Koha::ItemTypes->search_with_localization;
215             }
216             while ( my $itemtype = $itemtypes->next ) {
217                 push @authorised_values, $itemtype->itemtype;
218                 $authorised_lib{ $itemtype->itemtype } =
219                   $itemtype->translated_description;
220             }
221
222             if (!$value && $biblionumber) {
223                 my $itype_sth = $dbh->prepare(
224                     "SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
225                 $itype_sth->execute($biblionumber);
226                 ($value) = $itype_sth->fetchrow_array;
227             }
228
229             #---- class_sources
230         }
231         elsif ( $subfield->{authorised_value} eq "cn_source" ) {
232             push @authorised_values, "";
233
234             my $class_sources = GetClassSources();
235             my $default_source =
236               C4::Context->preference("DefaultClassificationSource");
237
238             foreach my $class_source ( sort keys %$class_sources ) {
239                 next
240                   unless $class_sources->{$class_source}->{'used'}
241                   or ( $value and $class_source eq $value )
242                   or ( $class_source eq $default_source );
243                 push @authorised_values, $class_source;
244                 $authorised_lib{$class_source} =
245                   $class_sources->{$class_source}->{'description'};
246             }
247             $value = $default_source if !$value && $prefill_with_default_values;
248
249             #---- "true" authorised value
250         }
251         else {
252             push @authorised_values, qq{};
253             my $av = GetAuthorisedValues( $subfield->{authorised_value} );
254             for my $r (@$av) {
255                 push @authorised_values, $r->{authorised_value};
256                 $authorised_lib{ $r->{authorised_value} } = $r->{lib};
257             }
258         }
259
260         if ( $subfield->{hidden} > 4 or $subfield->{hidden} <= -4 ) {
261             $subfield_data{marc_value} = {
262                 type      => 'hidden',
263                 id        => $subfield_data{id},
264                 maxlength => $subfield_data{maxlength},
265                 value     => $value,
266                 (
267                     (
268                         grep { $_ eq $subfield->{authorised_value} }
269                           (qw(branches itemtypes cn_source))
270                     ) ? () : ( category => $subfield->{authorised_value} )
271                 ),
272             };
273         }
274         else {
275             $subfield_data{marc_value} = {
276                 type => 'select',
277                 id   => "tag_"
278                   . $tag
279                   . "_subfield_"
280                   . $subfieldtag . "_"
281                   . $index_subfield,
282                 values  => \@authorised_values,
283                 labels  => \%authorised_lib,
284                 default => $value,
285                 (
286                     (
287                         grep { $_ eq $subfield->{authorised_value} }
288                           (qw(branches itemtypes cn_source))
289                     ) ? () : ( category => $subfield->{authorised_value} )
290                 ),
291             };
292         }
293     }
294
295     # it's a thesaurus / authority field
296     elsif ( $subfield->{authtypecode} ) {
297         $subfield_data{marc_value} = {
298             type         => 'text_auth',
299             id           => $subfield_data{id},
300             maxlength    => $subfield_data{maxlength},
301             value        => $value,
302             authtypecode => $subfield->{authtypecode},
303         };
304     }
305
306     # it's a plugin field
307     elsif ( $subfield->{value_builder} ) {    # plugin
308         require Koha::FrameworkPlugin;
309         my $plugin = Koha::FrameworkPlugin->new(
310             {
311                 name       => $subfield->{'value_builder'},
312                 item_style => 1,
313             }
314         );
315         my $pars = {
316             dbh     => $dbh,
317             record  => $marc_record,
318             tagslib => $tagslib,
319             id      => $subfield_data{id},
320         };
321         $plugin->build($pars);
322         if ( !$plugin->errstr ) {
323             my $class = 'buttonDot' . ( $plugin->noclick ? ' disabled' : '' );
324             $subfield_data{marc_value} = {
325                 type       => 'text_plugin',
326                 id         => $subfield_data{id},
327                 maxlength  => $subfield_data{maxlength},
328                 value      => $value,
329                 class      => $class,
330                 nopopup    => $plugin->noclick,
331                 javascript => $plugin->javascript,
332             };
333         }
334         else {
335             warn $plugin->errstr;
336             $subfield_data{marc_value} = {
337                 type      => 'text',
338                 id        => $subfield_data{id},
339                 maxlength => $subfield_data{maxlength},
340                 value     => $value,
341             };    # supply default input form
342         }
343     }
344     elsif ( $tag eq '' ) {    # it's an hidden field
345         $subfield_data{marc_value} = {
346             type      => 'hidden',
347             id        => $subfield_data{id},
348             maxlength => $subfield_data{maxlength},
349             value     => $value,
350         };
351     }
352     elsif ( $subfield->{'hidden'} )
353     {                         # FIXME: shouldn't input type be "hidden" ?
354         $subfield_data{marc_value} = {
355             type      => 'text',
356             id        => $subfield_data{id},
357             maxlength => $subfield_data{maxlength},
358             value     => $value,
359         };
360     }
361     elsif (
362         ( $value and length($value) > 100 )
363         or ( C4::Context->preference("marcflavour") eq "UNIMARC"
364             and 300 <= $tag && $tag < 400 && $subfieldtag eq 'a' )
365         or ( C4::Context->preference("marcflavour") eq "MARC21"
366             and 500 <= $tag && $tag < 600 )
367       )
368     {
369         # oversize field (textarea)
370         $subfield_data{marc_value} = {
371             type  => 'textarea',
372             id    => $subfield_data{id},
373             value => $value,
374         };
375     }
376     else {
377         # it's a standard field
378         $subfield_data{marc_value} = {
379             type      => 'text',
380             id        => $subfield_data{id},
381             maxlength => $subfield_data{maxlength},
382             value     => $value,
383         };
384     }
385
386     # If we're on restricted editing, and our field is not in the list of subfields to allow,
387     # then it is read-only
388     $subfield_data{marc_value}->{readonly} = $readonly;
389
390     return \%subfield_data;
391 }
392
393 =head3 edit_form
394
395     my $subfields =
396       Koha::UI::Form::Builder::Item->new(
397         { biblionumber => $biblionumber, item => $current_item } )->edit_form(
398         {
399             branchcode           => $branchcode,
400             restricted_editition => $restrictededition,
401             (
402                 @subfields_to_prefill
403                 ? ( subfields_to_prefill => \@subfields_to_prefill )
404                 : ()
405             ),
406             prefill_with_default_values => 1,
407             branch_limit => C4::Context->userenv->{"branch"},
408         }
409     );
410
411 Returns the list of subfields to display on the add/edit item form.
412
413 Use it in the view with:
414   [% PROCESS subfields_for_item subfields => subfields %]
415
416 Parameters:
417
418 =over
419
420 =item branchcode
421
422 Pre-select a library (for logged in user)
423
424 =item restricted_editition
425
426 Flag to restrict the edition if the user does not have necessary permissions.
427
428 =item subfields_to_prefill
429
430 List of subfields to prefill (value of syspref SubfieldsToUseWhenPrefill)
431
432 =item subfields_to_allow
433
434 List of subfields to allow (value of syspref SubfieldsToAllowForRestrictedBatchmod or SubfieldsToAllowForRestrictedEditing)
435
436 =item ignore_not_allowed_subfields
437
438 If set, the subfields in subfields_to_allow will be ignored (ie. they will not be part of the subfield list.
439 If not set, the subfields in subfields_to_allow will be marked as readonly.
440
441 =item kohafields_to_ignore
442
443 List of subfields to ignore/skip
444
445 =item prefill_with_default_values
446
447 Flag to prefill with the default values defined in the framework.
448
449 =item branch_limit
450
451 Limit info depending on the library (so far only item types).
452
453 =item default_branches_empty
454
455 Flag to add an empty option to the library list.
456
457 =item ignore_invisible_subfields
458
459 Skip the subfields that are not visible on the editor.
460
461 When duplicating an item we do not want to retrieve the subfields that are hidden.
462
463 =back
464
465 =cut
466
467 sub edit_form {
468     my ( $self, $params ) = @_;
469
470     my $branchcode         = $params->{branchcode};
471     my $restricted_edition = $params->{restricted_editition};
472     my $subfields_to_prefill = $params->{subfields_to_prefill} || [];
473     my $subfields_to_allow = $params->{subfields_to_allow} || [];
474     my $ignore_not_allowed_subfields = $params->{ignore_not_allowed_subfields};
475     my $kohafields_to_ignore = $params->{kohafields_to_ignore} || [];
476     my $prefill_with_default_values = $params->{prefill_with_default_values};
477     my $branch_limit = $params->{branch_limit};
478     my $default_branches_empty = $params->{default_branches_empty};
479     my $ignore_invisible_subfields = $params->{ignore_invisible_subfields} || 0;
480
481     my $libraries =
482       Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
483     for my $library (@$libraries) {
484         $library->{selected} = 1 if $branchcode && $library->{branchcode} eq $branchcode;
485     }
486
487     my $item                = $self->{item};
488     my $marc_more_subfields = $item->{more_subfields_xml}
489       ?
490         # FIXME Use Maybe MARC::Record::new_from_xml if encoding issues on subfield (??)
491         MARC::Record->new_from_xml( $item->{more_subfields_xml}, 'UTF-8' )
492       : undef;
493
494     my $biblionumber   = $self->{biblionumber};
495     my $biblio         = Koha::Biblios->find($biblionumber);
496     my $frameworkcode  = $biblio ? GetFrameworkCode($biblionumber) : q{};
497     my $marc_record    = $biblio ? $biblio->metadata->record : undef;
498     my @subfields;
499     my $tagslib = GetMarcStructure( 1, $frameworkcode );
500     foreach my $tag ( keys %{$tagslib} ) {
501
502         foreach my $subfieldtag ( keys %{ $tagslib->{$tag} } ) {
503
504             my $subfield = $tagslib->{$tag}{$subfieldtag};
505
506             next if IsMarcStructureInternal($subfield);
507             next if $subfield->{tab} ne "10";
508             next
509               if grep { $subfield->{kohafield} && $subfield->{kohafield} eq $_ }
510               @$kohafields_to_ignore;
511
512             next
513               if $ignore_invisible_subfields
514               && ( $subfield->{hidden} > 4 || $subfield->{hidden} <= -4 );
515
516             my $readonly;
517             if (
518                 @$subfields_to_allow && !grep {
519                     sprintf( "%s\$%s", $subfield->{tagfield}, $subfield->{tagsubfield} ) eq $_
520                 } @$subfields_to_allow
521               )
522             {
523                 next if $ignore_not_allowed_subfields;
524                 $readonly = 1 if $restricted_edition;
525             }
526
527             my @values = ();
528
529             my $subfield_data;
530
531             if (
532                 !@$subfields_to_prefill
533                 || ( @$subfields_to_prefill && grep { $_ eq $subfieldtag }
534                     @$subfields_to_prefill )
535               )
536             {
537                 my $kohafield = $subfield->{kohafield};
538                 if ($kohafield) {
539
540                     # This is a mapped field
541                     ( my $attribute = $kohafield ) =~ s|^items\.||;
542                     push @values, $subfield->{repeatable}
543                       ? split '\s\|\s', $item->{$attribute}
544                       : $item->{$attribute}
545                       if defined $item->{$attribute};
546                 }
547                 else {
548                   # Not mapped, picked the values from more_subfields_xml's MARC
549                     if ($marc_more_subfields) {
550                         for my $f ( $marc_more_subfields->fields($tag) ) {
551                             push @values, $f->subfield($subfieldtag);
552                         }
553                     }
554                 }
555             }
556
557             @values = ('') unless @values;
558
559             for my $value (@values) {
560                 my $subfield_data = $self->generate_subfield_form(
561                     {
562                         tag                => $tag,
563                         subfieldtag        => $subfieldtag,
564                         value              => $value,
565                         tagslib            => $tagslib,
566                         libraries          => $libraries,
567                         marc_record        => $marc_record,
568                         restricted_edition => $restricted_edition,
569                         prefill_with_default_values => $prefill_with_default_values,
570                         branch_limit       => $branch_limit,
571                         default_branches_empty => $default_branches_empty,
572                         readonly           => $readonly
573                     }
574                 );
575                 push @subfields, $subfield_data;
576             }
577         }
578     }
579
580     @subfields = sort {
581              $a->{display_order} <=> $b->{display_order}
582           || $a->{subfield} cmp $b->{subfield}
583     } @subfields;
584
585     return \@subfields;
586
587 }
588
589 1;