Bug 36794: Illegitimate modification of biblionumber subfield content (999 $c)
[koha.git] / Koha / UI / Form / Builder / Biblio.pm
1 package Koha::UI::Form::Builder::Biblio;
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 C4::Context;
20 use C4::ClassSource qw( GetClassSources );
21 use Koha::DateUtils qw( dt_from_string );
22 use Koha::ItemTypes;
23 use Koha::Libraries;
24
25 =head1 NAME
26
27 Koha::UI::Form::Builder::Biblio
28
29 Helper to build a form to add or edit a new biblio
30
31 =head1 API
32
33 =head2 Class methods
34
35 =cut
36
37 =head3 new
38
39     my $form = Koha::UI::Form::Builder::Biblio->new(
40         {
41             biblionumber => $biblionumber,
42         }
43     );
44
45 =cut
46
47
48 sub new {
49     my ( $class, $params ) = @_;
50
51     my $self = {};
52
53     $self->{biblionumber} = $params->{biblionumber} =~ s/\D//gr;
54     # just in case biblionumber obtained from CGI and passed directly here contains weird characters like spaces
55
56     bless $self, $class;
57     return $self;
58 }
59
60 =head3 generate_subfield_form
61
62     Generate subfield's info for given tag, subfieldtag, etc.
63
64 =cut
65
66 sub generate_subfield_form {
67     my ($self, $params) = @_;
68
69     my $tag = $params->{tag};
70     my $subfield = $params->{subfield};
71     my $value = $params->{value} // '';
72     my $index_tag = $params->{index_tag};
73     my $rec = $params->{record};
74     my $hostitemnumber = $params->{hostitemnumber};
75     my $op = $params->{op} // '';
76     my $changed_framework = $params->{changed_framework};
77     my $breedingid = $params->{breedingid};
78     my $tagslib = $params->{tagslib};
79     my $mandatory_z3950 = $params->{mandatory_z3950} // {};
80
81     my $index_subfield = $self->create_key(); # create a specific key for each subfield
82
83     # Apply optional framework default value when it is a new record,
84     # or when editing as new (duplicating a record),
85     # or when changing a record's framework,
86     # or when importing a record,
87     # based on the ApplyFrameworkDefaults setting.
88     # Substitute date parts, user name
89     my $applydefaults = C4::Context->preference('ApplyFrameworkDefaults');
90     if ( $value eq '' && (
91         ( $applydefaults =~ /new/ && !$self->{biblionumber} ) ||
92         ( $applydefaults =~ /duplicate/ && $op eq 'duplicate' ) ||
93         ( $applydefaults =~ /changed/ && $changed_framework ) ||
94         ( $applydefaults =~ /imported/ && $breedingid )
95     ) ) {
96         $value = $tagslib->{$tag}->{$subfield}->{defaultvalue} // q{};
97
98         # get today date & replace <<YYYY>>, <<YY>>, <<MM>>, <<DD>> if provided in the default value
99         my $today_dt = dt_from_string;
100         my $year = $today_dt->strftime('%Y');
101         my $shortyear = $today_dt->strftime('%y');
102         my $month = $today_dt->strftime('%m');
103         my $day = $today_dt->strftime('%d');
104         $value =~ s/<<YYYY>>/$year/g;
105         $value =~ s/<<YY>>/$shortyear/g;
106         $value =~ s/<<MM>>/$month/g;
107         $value =~ s/<<DD>>/$day/g;
108         # And <<USER>> with surname (?)
109         my $username=(C4::Context->userenv?C4::Context->userenv->{'surname'}:"superlibrarian");
110         $value=~s/<<USER>>/$username/g;
111     }
112
113     my $dbh = C4::Context->dbh;
114
115     # map '@' as "subfield" label for fixed fields
116     # to something that's allowed in a div id.
117     my $id_subfield = $subfield;
118     $id_subfield = "00" if $id_subfield eq "@";
119
120     my %subfield_data = (
121         tag        => $tag,
122         subfield   => $id_subfield,
123         marc_lib       => $tagslib->{$tag}->{$subfield}->{lib},
124         tag_mandatory  => $tagslib->{$tag}->{mandatory},
125         mandatory      => $tagslib->{$tag}->{$subfield}->{mandatory},
126         important      => $tagslib->{$tag}->{$subfield}->{important},
127         repeatable     => $tagslib->{$tag}->{$subfield}->{repeatable},
128         kohafield      => $tagslib->{$tag}->{$subfield}->{kohafield},
129         index          => $index_tag,
130         id             => "tag_".$tag."_subfield_".$id_subfield."_".$index_tag."_".$index_subfield,
131         value          => $value,
132         maxlength      => $tagslib->{$tag}->{$subfield}->{maxlength},
133         random         => $self->create_key(),
134     );
135
136     if (exists $mandatory_z3950->{$tag.$subfield}){
137         $subfield_data{z3950_mandatory} = $mandatory_z3950->{$tag.$subfield};
138     }
139     # Subfield is hidden depending of hidden and mandatory flag, and is always
140     # shown if it contains anything or if its field is mandatory or important.
141     my $tdef = $tagslib->{$tag};
142     $subfield_data{visibility} = "display:none;"
143         if $tdef->{$subfield}->{hidden} % 2 == 1 &&
144            $value eq '' &&
145            !$tdef->{$subfield}->{mandatory} &&
146            !$tdef->{mandatory} &&
147            !$tdef->{$subfield}->{important} &&
148            !$tdef->{important};
149     # expand all subfields of 773 if there is a host item provided in the input
150     $subfield_data{visibility} = '' if ($tag eq '773' and $hostitemnumber);
151
152
153     # it's an authorised field
154     if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
155         $subfield_data{marc_value} = $self->build_authorized_values_list(
156             {
157                 tag => $tag,
158                 subfield => $subfield,
159                 value => $value,
160                 index_tag => $index_tag,
161                 index_subfield => $index_subfield,
162                 tagslib => $tagslib,
163             }
164         );
165     }
166     # it's a subfield $9 linking to an authority record - see bug 2206 and 28022
167     elsif ($subfield eq "9" and
168            exists($tagslib->{$tag}->{'a'}->{authtypecode}) and
169            defined($tagslib->{$tag}->{'a'}->{authtypecode}) and
170            $tagslib->{$tag}->{'a'}->{authtypecode} ne '' and
171            $tagslib->{$tag}->{'a'}->{hidden} > -4 and
172            $tagslib->{$tag}->{'a'}->{hidden} < 5) {
173         $subfield_data{marc_value} = {
174             type      => 'text',
175             id        => $subfield_data{id},
176             name      => $subfield_data{id},
177             value     => $value,
178             size      => 5,
179             maxlength => $subfield_data{maxlength},
180             readonly  => 1,
181         };
182
183     # it's a thesaurus / authority field
184     }
185     elsif ( $tagslib->{$tag}->{$subfield}->{authtypecode} ) {
186         # when authorities auto-creation is allowed, do not set readonly
187         my $is_readonly = C4::Context->preference('RequireChoosingExistingAuthority');
188
189         $subfield_data{marc_value} = {
190             type      => 'text',
191             id        => $subfield_data{id},
192             name      => $subfield_data{id},
193             value     => $value,
194             size      => 67,
195             maxlength => $subfield_data{maxlength},
196             readonly  => ($is_readonly) ? 1 : 0,
197             authtype  => $tagslib->{$tag}->{$subfield}->{authtypecode},
198         };
199
200     # it's a plugin field
201     } elsif ( $tagslib->{$tag}->{$subfield}->{'value_builder'} ) {
202         require Koha::FrameworkPlugin;
203         my $plugin = Koha::FrameworkPlugin->new( {
204             name => $tagslib->{$tag}->{$subfield}->{'value_builder'},
205         });
206         my $pars= { dbh => $dbh, record => $rec, tagslib => $tagslib,
207             id => $subfield_data{id} };
208         $plugin->build( $pars );
209         if( !$plugin->errstr ) {
210             $subfield_data{marc_value} = {
211                 type           => 'text_complex',
212                 id             => $subfield_data{id},
213                 name           => $subfield_data{id},
214                 value          => $value,
215                 size           => 67,
216                 maxlength      => $subfield_data{maxlength},
217                 javascript     => $plugin->javascript,
218                 plugin         => $plugin->name,
219                 noclick        => $plugin->noclick,
220             };
221         } else {
222             warn $plugin->errstr;
223             # supply default input form
224             $subfield_data{marc_value} = {
225                 type      => 'text',
226                 id        => $subfield_data{id},
227                 name      => $subfield_data{id},
228                 value     => $value,
229                 size      => 67,
230                 maxlength => $subfield_data{maxlength},
231                 readonly  => 0,
232             };
233         }
234
235     # it's an hidden field
236     } elsif ( $tag eq '' ) {
237         $subfield_data{marc_value} = {
238             type      => 'hidden',
239             id        => $subfield_data{id},
240             name      => $subfield_data{id},
241             value     => $value,
242             size      => 67,
243             maxlength => $subfield_data{maxlength},
244         };
245
246     }
247     else {
248         # it's a standard field
249         if (
250             length($value) > 100
251             or
252             ( C4::Context->preference("marcflavour") eq "UNIMARC" && $tag >= 300
253                 and $tag < 400 && $subfield eq 'a' )
254             or (    $tag >= 500
255                 and $tag < 600
256                 && C4::Context->preference("marcflavour") eq "MARC21" )
257           )
258         {
259             $subfield_data{marc_value} = {
260                 type      => 'textarea',
261                 id        => $subfield_data{id},
262                 name      => $subfield_data{id},
263                 value     => $value,
264             };
265
266         }
267         else {
268             $subfield_data{marc_value} = {
269                 type      => 'text',
270                 id        => $subfield_data{id},
271                 name      => $subfield_data{id},
272                 value     => $value,
273                 size      => 67,
274                 maxlength => $subfield_data{maxlength},
275                 readonly  => 0,
276             };
277
278         }
279     }
280     $subfield_data{'index_subfield'} = $index_subfield;
281
282     return \%subfield_data;
283 }
284
285 =head3 build_authorized_values_list
286
287     Return list of authorized values for given tag, subfield
288
289 =cut
290
291 sub build_authorized_values_list {
292     my ($self, $params) = @_;
293
294     my $tag = $params->{tag};
295     my $subfield = $params->{subfield};
296     my $value = $params->{value};
297     my $index_tag = $params->{index_tag};
298     my $index_subfield = $params->{index_subfield};
299     my $tagslib = $params->{tagslib};
300
301     my @authorised_values;
302     my %authorised_lib;
303
304     # builds list, depending on authorised value...
305
306     #---- branch
307     my $category = $tagslib->{$tag}->{$subfield}->{authorised_value};
308     if ( $category eq "branches" ) {
309         my $libraries = Koha::Libraries->search_filtered({}, {order_by => ['branchname']});
310         while ( my $l = $libraries->next ) {
311             push @authorised_values, $l->branchcode;
312             $authorised_lib{$l->branchcode} = $l->branchname;
313         }
314     }
315     elsif ( $category eq "itemtypes" ) {
316         push @authorised_values, "";
317
318         my $itemtype;
319         my $itemtypes = Koha::ItemTypes->search_with_localization;
320         while ( $itemtype = $itemtypes->next ) {
321             push @authorised_values, $itemtype->itemtype;
322             $authorised_lib{$itemtype->itemtype} = $itemtype->translated_description;
323         }
324         $value = $itemtype unless ($value);
325     }
326     elsif ( $category eq "cn_source" ) {
327         push @authorised_values, "";
328
329         my $class_sources = GetClassSources();
330
331         my $default_source = C4::Context->preference("DefaultClassificationSource");
332
333         foreach my $class_source (sort keys %$class_sources) {
334             next unless $class_sources->{$class_source}->{'used'} or
335                         ($value and $class_source eq $value) or
336                         ($class_source eq $default_source);
337             push @authorised_values, $class_source;
338             $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
339         }
340         $value = $default_source unless $value;
341     }
342     else {
343         my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{branch} : '';
344         my $query = 'SELECT authorised_value, lib FROM authorised_values';
345         $query .= ' LEFT JOIN authorised_values_branches ON ( id = av_id )' if $branch_limit;
346         $query .= ' WHERE category = ?';
347         $query .= ' AND ( branchcode = ? OR branchcode IS NULL )' if $branch_limit;
348         $query .= ' GROUP BY authorised_value,lib ORDER BY lib, lib_opac';
349         my $authorised_values_sth = C4::Context->dbh->prepare($query);
350
351         $authorised_values_sth->execute(
352             $tagslib->{$tag}->{$subfield}->{authorised_value},
353             $branch_limit ? $branch_limit : (),
354         );
355
356         push @authorised_values, "";
357
358         while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
359             push @authorised_values, $value;
360             $authorised_lib{$value} = $lib;
361         }
362     }
363
364     return {
365         type     => 'select',
366         id       => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
367         name     => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
368         default  => $value,
369         values   => \@authorised_values,
370         labels   => \%authorised_lib,
371         ( ( grep { $_ eq $category } ( qw(branches itemtypes cn_source) ) ) ? () : ( category => $category ) ),
372     };
373
374 }
375
376 =head3 create_key
377
378     Create unique key for subfields
379
380 =cut
381
382 sub create_key {
383     return int(rand(1000000));
384 }
385
386 1;