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