Bug 29857: (QA follow-up) Fix unit test Object.t
[koha.git] / authorities / authorities.pl
1 #!/usr/bin/perl
2
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21 use Modern::Perl;
22
23 use CGI qw ( -utf8 );
24 use C4::Auth qw( get_template_and_user );
25 use C4::Output qw( output_html_with_http_headers );
26 use C4::AuthoritiesMarc qw( AddAuthority ModAuthority GetAuthority GetTagsLabels GetAuthMARCFromKohaField FindDuplicateAuthority );
27 use C4::ImportBatch qw( GetImportRecordMarc );
28 use C4::Context;
29 use Date::Calc qw( Today );
30 use MARC::File::USMARC;
31 use MARC::File::XML;
32 use C4::Biblio qw( TransformHtmlToMarc );
33 use Koha::Authority::Types;
34 use Koha::ItemTypes;
35 use vars qw( $tagslib);
36 use vars qw( $authorised_values_sth);
37 use vars qw( $is_a_modif );
38
39 our($authorised_values_sth,$is_a_modif,$usedTagsLib,$mandatory_z3950);
40
41 =head1 FUNCTIONS
42
43 =over
44
45 =item build_authorized_values_list
46
47 builds list, depending on authorised value...
48
49 =cut
50
51 sub MARCfindbreeding_auth {
52     my ( $id ) = @_;
53     my ($marc, $encoding) = GetImportRecordMarc($id);
54     if ($marc) {
55         my $record = MARC::Record->new_from_usmarc($marc);
56         if ( !defined(ref($record)) ) {
57                 return -1;
58         } else {
59             return $record, $encoding;
60         }
61     } else {
62         return -1;
63     }
64 }
65
66 sub build_authorized_values_list {
67     my ( $tag, $subfield, $value, $dbh, $authorised_values_sth,$index_tag,$index_subfield ) = @_;
68
69     my @authorised_values;
70     my %authorised_lib;
71
72     my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
73     push @authorised_values, q{} unless $tagslib->{$tag}->{$subfield}->{mandatory} && $value;
74
75     if ( $category eq "branches" ) {
76         my $sth = $dbh->prepare( "select branchcode,branchname from branches order by branchname" );
77         $sth->execute;
78         while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
79             push @authorised_values, $branchcode;
80             $authorised_lib{$branchcode} = $branchname;
81         }
82     }
83     elsif ( $category eq "itemtypes" ) {
84         my $itemtypes = Koha::ItemTypes->search_with_localization;
85         while ( my $itemtype = $itemtypes->next ) {
86             push @authorised_values, $itemtype->itemtype;
87             $authorised_lib{$itemtype->itemtype} = $itemtype->translated_description;
88         }
89     }
90     else { # "true" authorised value
91         $authorised_values_sth->execute(
92             $tagslib->{$tag}->{$subfield}->{authorised_value}
93         );
94         while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
95             push @authorised_values, $value;
96             $authorised_lib{$value} = $lib;
97         }
98     }
99
100     return {
101         type     => 'select',
102         id       => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
103         name     => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
104         values   => \@authorised_values,
105         labels   => \%authorised_lib,
106         default  => $value,
107         ( ( grep { $_ eq $category } ( qw(branches itemtypes cn_source) ) ) ? () : ( category => $category ) ),
108     };
109 }
110
111 =item create_input
112
113 builds the <input ...> entry for a subfield.
114
115 =cut
116
117 sub create_input {
118     my ( $tag, $subfield, $value, $index_tag, $rec, $authorised_values_sth, $cgi ) = @_;
119
120     my $index_subfield = CreateKey(); # create a specifique key for each subfield
121
122     # determine maximum length; 9999 bytes per ISO 2709 except for leader and MARC21 008
123     my $max_length = 9999;
124     if ($tag eq '000') {
125         $max_length = 24;
126     } elsif ($tag eq '008' and C4::Context->preference('marcflavour') eq 'MARC21')  {
127         $max_length = 40;
128     }
129
130     # if there is no value provided but a default value in parameters, get it
131     if ($value eq '') {
132         $value = $tagslib->{$tag}->{$subfield}->{defaultvalue} if !$cgi->param('authid'); # only for new records
133         if (!defined $value) {
134             $value = q{};
135         }
136
137         # get today date & replace YYYY, MM, DD if provided in the default value
138         my ( $year, $month, $day ) = Today();
139         $month = sprintf( "%02d", $month );
140         $day   = sprintf( "%02d", $day );
141         $value =~ s/YYYY/$year/g;
142         $value =~ s/MM/$month/g;
143         $value =~ s/DD/$day/g;
144     }
145     my $dbh = C4::Context->dbh;
146
147     # map '@' as "subfield" label for fixed fields
148     # to something that's allowed in a div id.
149     my $id_subfield = $subfield;
150     $id_subfield = "00" if $id_subfield eq "@";
151
152     my %subfield_data = (
153         tag        => $tag,
154         subfield   => $id_subfield,
155         marc_lib       => $tagslib->{$tag}->{$subfield}->{lib},
156         tag_mandatory  => $tagslib->{$tag}->{mandatory},
157         mandatory      => $tagslib->{$tag}->{$subfield}->{mandatory},
158         repeatable     => $tagslib->{$tag}->{$subfield}->{repeatable},
159         kohafield      => $tagslib->{$tag}->{$subfield}->{kohafield},
160         index          => $index_tag,
161         id             => "tag_".$tag."_subfield_".$id_subfield."_".$index_tag."_".$index_subfield,
162         value          => $value,
163         random         => CreateKey(),
164     );
165
166     if(exists $mandatory_z3950->{$tag.$subfield}){
167         $subfield_data{z3950_mandatory} = $mandatory_z3950->{$tag.$subfield};
168     }
169     
170     $subfield_data{visibility} = "display:none;"
171         if( $tagslib->{$tag}->{$subfield}->{hidden} and $value ne ''
172             or ($value eq '' and !$tagslib->{$tag}->{$subfield}->{mandatory})
173         );
174     
175     # it's an authorised field
176     if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
177         $subfield_data{marc_value} =
178         build_authorized_values_list( $tag, $subfield, $value, $dbh,
179             $authorised_values_sth,$index_tag,$index_subfield );
180
181     # it's a thesaurus / authority field
182     }
183     elsif ( $tagslib->{$tag}->{$subfield}->{authtypecode} ) {
184         $subfield_data{marc_value} = {
185             type         => 'text1',
186             id           => $subfield_data{id},
187             name         => $subfield_data{id},
188             value        => $value,
189             authtypecode => $tagslib->{$tag}->{$subfield}->{authtypecode},
190         };
191     }
192     elsif ( $tagslib->{$tag}->{$subfield}->{'value_builder'} ) { # plugin
193         require Koha::FrameworkPlugin;
194         my $plugin = Koha::FrameworkPlugin->new({
195             name => $tagslib->{$tag}->{$subfield}->{'value_builder'},
196         });
197         my $pars=  { dbh => $dbh, record => $rec, tagslib =>$tagslib,
198             id => $subfield_data{id} };
199         $plugin->build( $pars );
200         if( !$plugin->errstr ) {
201             $subfield_data{marc_value} = {
202                 type       => 'text2',
203                 id        => $subfield_data{id},
204                 name      => $subfield_data{id},
205                 value     => $value,
206                 maxlength => $max_length,
207                 javascript => $plugin->javascript,
208                 noclick    => $plugin->noclick,
209             };
210         } else { # warn and supply default field
211             warn $plugin->errstr;
212             $subfield_data{marc_value} = {
213                 type      => 'text',
214                 id        => $subfield_data{id},
215                 name      => $subfield_data{id},
216                 value     => $value,
217                 maxlength => $max_length,
218             };
219         }
220     }
221     # it's an hidden field
222     elsif ( $tag eq '' ) {
223         $subfield_data{marc_value} = {
224             type      => 'hidden',
225             id        => $subfield_data{id},
226             name      => $subfield_data{id},
227             value     => $value,
228             maxlength => $max_length,
229         }
230     }
231     elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) {
232         $subfield_data{marc_value} = {
233             type => 'text',
234             id        => $subfield_data{id},
235             name      => $subfield_data{id},
236             value     => $value,
237             maxlength => $max_length,
238         };
239
240         # it's a standard field
241     }
242     else {
243         if (
244             length($value) > 100
245             or
246             ( C4::Context->preference("marcflavour") eq "UNIMARC" && $tag >= 300
247                 and $tag < 400 && $subfield eq 'a' )
248             or (    $tag >= 600
249                 and $tag < 700
250                 && C4::Context->preference("marcflavour") eq "MARC21" )
251         )
252         {
253             $subfield_data{marc_value} = {
254                 type => 'textarea',
255                 id        => $subfield_data{id},
256                 name      => $subfield_data{id},
257                 value     => $value,
258                 maxlength => $max_length,
259             };
260
261         }
262         else {
263             $subfield_data{marc_value} = {
264                 type => 'text',
265                 id        => $subfield_data{id},
266                 name      => $subfield_data{id},
267                 value     => $value,
268                 maxlength => $max_length,
269             };
270
271         }
272     }
273     if ($cgi->param('tagreport') && $subfield_data{tag} == $cgi->param('tagreport')) {
274         $subfield_data{marc_value}{value} = $cgi->param('tag'. $cgi->param('tagbiblio') . 'subfield' . $subfield_data{subfield});
275     }
276     $subfield_data{'index_subfield'} = $index_subfield;
277     return \%subfield_data;
278 }
279
280 =item format_indicator
281
282 Translate indicator value for output form - specifically, map
283 indicator = ' ' to ''.  This is for the convenience of a cataloger
284 using a mouse to select an indicator input.
285
286 =cut
287
288 sub format_indicator {
289     my $ind_value = shift;
290     return '' if not defined $ind_value;
291     return '' if $ind_value eq ' ';
292     return $ind_value;
293 }
294
295 =item CreateKey
296
297 Create a random value to set it into the input name
298
299 =cut
300
301 sub CreateKey {
302     return int(rand(1000000));
303 }
304
305 =item GetMandatoryFieldZ3950
306
307     This function returns a hashref which contains all mandatory field
308     to search with z3950 server.
309
310 =cut
311
312 sub GetMandatoryFieldZ3950 {
313     my $authtypecode = shift;
314     if ( C4::Context->preference('marcflavour') eq 'MARC21' ){
315         return {
316             '100a' => 'authorpersonal',
317             '110a' => 'authorcorp',
318             '111a' => 'authormeetingcon',
319             '130a' => 'uniformtitle',
320             '150a' => 'subject',
321         };
322     }else{
323         return {
324             '200a' => 'authorpersonal',
325             '210a' => 'authormeetingcon', #210 in UNIMARC is used for both corporation and meeting
326             '230a' => 'uniformtitle',
327         };
328     }
329 }
330
331 sub build_tabs {
332     my ( $template, $record, $dbh, $encoding,$input ) = @_;
333
334     # fill arrays
335     my @loop_data = ();
336     my $tag;
337
338     my $authorised_values_sth = $dbh->prepare(
339         "SELECT authorised_value,lib
340         FROM authorised_values
341         WHERE category=? ORDER BY lib"
342     );
343     
344     # in this array, we will push all the 10 tabs
345     # to avoid having 10 tabs in the template : they will all be in the same BIG_LOOP
346     my @BIG_LOOP;
347     my %seen;
348     my @tab_data; # all tags to display
349     
350     foreach my $used ( keys %$tagslib ){
351         push @tab_data,$used if not $seen{$used};
352         $seen{$used}++;
353     }
354         
355     my $max_num_tab=9;
356     # loop through each tab 0 through 9
357     for ( my $tabloop = 0 ; $tabloop <= $max_num_tab ; $tabloop++ ) {
358         my @loop_data = (); #innerloop in the template.
359         my $i = 0;
360         foreach my $tag (sort @tab_data) {
361             $i++;
362             next if ! $tag;
363             my ($indicator1, $indicator2);
364             my $index_tag = CreateKey;
365
366             # if MARC::Record is not empty =>use it as master loop, then add missing subfields that should be in the tab.
367             # if MARC::Record is empty => use tab as master loop.
368             if ( $record != -1 && ( $record->field($tag) || $tag eq '000' ) ) {
369                 my @fields;
370                 if ( $tag ne '000' ) {
371                                 @fields = $record->field($tag);
372                 }
373                 else {
374                 push @fields, $record->leader(); # if tag == 000
375                 }
376                 # loop through each field
377                 foreach my $field (@fields) {
378                     
379                     my @subfields_data;
380                     if ( $tag < 10 ) {
381                         my ( $value, $subfield );
382                         if ( $tag ne '000' ) {
383                             $value    = $field->data();
384                             $subfield = "@";
385                         }
386                         else {
387                             $value    = $field;
388                             $subfield = '@';
389                         }
390                         next if ( $tagslib->{$tag}->{$subfield}->{tab} ne $tabloop );
391                         next if $tagslib->{$tag}->{$subfield}->{hidden} && $subfield ne '9';
392                         push(
393                             @subfields_data,
394                             &create_input(
395                                 $tag, $subfield, $value, $index_tag, $record,
396                                 $authorised_values_sth,$input
397                             )
398                         );
399                     }
400                     else {
401                         my @subfields = $field->subfields();
402                         foreach my $subfieldcount ( 0 .. $#subfields ) {
403                             my $subfield = $subfields[$subfieldcount][0];
404                             my $value    = $subfields[$subfieldcount][1];
405                             next if ( length $subfield != 1 );
406                             next if ( $tagslib->{$tag}->{$subfield}->{tab} ne $tabloop );
407                             next if $tagslib->{$tag}->{$subfield}->{hidden} && $subfield ne '9';
408                             push(
409                                 @subfields_data,
410                                 &create_input(
411                                     $tag, $subfield, $value, $index_tag,
412                                     $record, $authorised_values_sth,$input
413                                 )
414                             );
415                         }
416                     }
417
418                     # now, loop again to add parameter subfield that are not in the MARC::Record
419                     foreach my $subfield ( sort( keys %{ $tagslib->{$tag} } ) )
420                     {
421                         next if ( length $subfield != 1 );
422                         next if ( $tagslib->{$tag}->{$subfield}->{tab} ne $tabloop );
423                         next if ( $tag < 10 );
424                         next if $tagslib->{$tag}->{$subfield}->{hidden} && $subfield ne '9';
425                         next if ( defined( $field->subfield($subfield) ) );
426                         push(
427                             @subfields_data,
428                             &create_input(
429                                 $tag, $subfield, '', $index_tag, $record,
430                                 $authorised_values_sth,$input
431                             )
432                         );
433                     }
434                     if ( $#subfields_data >= 0 ) {
435                         # build the tag entry.
436                         # note that the random() field is mandatory. Otherwise, on repeated fields, you'll 
437                         # have twice the same "name" value, and cgi->param() will return only one, making
438                         # all subfields to be merged in a single field.
439                         my %tag_data = (
440                             tag           => $tag,
441                             index         => $index_tag,
442                             tag_lib       => $tagslib->{$tag}->{lib},
443                             repeatable       => $tagslib->{$tag}->{repeatable},
444                             mandatory       => $tagslib->{$tag}->{mandatory},
445                             subfield_loop => \@subfields_data,
446                             fixedfield    => ($tag < 10)?(1):(0),
447                             random        => CreateKey,
448                         );
449                         if ($tag >= 10){ # no indicator for theses tag
450                             $tag_data{indicator1} = format_indicator($field->indicator(1)),
451                             $tag_data{indicator2} = format_indicator($field->indicator(2)),
452                         }
453                         push( @loop_data, \%tag_data );
454                     }
455                 } # foreach $field end
456
457             # if breeding is empty
458             }
459             else {
460                 my @subfields_data;
461                 foreach my $subfield (
462                     sort { $a->{display_order} <=> $b->{display_order} || $a->{subfield} cmp $b->{subfield} }
463                     grep { ref($_) && %$_ } # Not a subfield (values for "important", "lib", "mandatory", etc.) or empty
464                     values %{ $tagslib->{$tag} } )
465                 {
466                     next if $subfield->{hidden} && $subfield->{subfield} ne '9';
467                     next if ( $subfield->{tab} ne $tabloop );
468                     push(
469                         @subfields_data,
470                         &create_input(
471                             $tag, $subfield->{subfield}, '', $index_tag, $record,
472                             $authorised_values_sth,$input
473                         )
474                     );
475                 }
476                 if ( $#subfields_data >= 0 ) {
477                     my %tag_data = (
478                         tag              => $tag,
479                         index            => $index_tag,
480                         tag_lib          => $tagslib->{$tag}->{lib},
481                         repeatable       => $tagslib->{$tag}->{repeatable},
482                         mandatory       => $tagslib->{$tag}->{mandatory},
483                         indicator1       => $indicator1,
484                         indicator2       => $indicator2,
485                         subfield_loop    => \@subfields_data,
486                         tagfirstsubfield => $subfields_data[0],
487                         fixedfield       => ($tag < 10)?(1):(0)
488                     );
489                     
490                     push @loop_data, \%tag_data ;
491                 }
492             }
493         }
494         if ( $#loop_data >= 0 ) {
495             push @BIG_LOOP, {
496                 number    => $tabloop,
497                 innerloop => \@loop_data,
498             };
499         }
500     }
501     $template->param( BIG_LOOP => \@BIG_LOOP );
502 }
503
504
505 sub build_hidden_data {
506     # build hidden data =>
507     # we store everything, even if we show only requested subfields.
508
509     my @loop_data =();
510     my $i=0;
511     foreach my $tag (keys %{$tagslib}) {
512         my $previous_tag = '';
513
514         # loop through each subfield
515         foreach my $subfield (keys %{$tagslib->{$tag}}) {
516             next if ($subfield eq 'lib');
517             next if ($subfield eq 'tab');
518             next if ($subfield eq 'mandatory');
519                 next if ($subfield eq 'repeatable');
520             next if ($tagslib->{$tag}->{$subfield}->{'tab'}  ne "-1");
521             my %subfield_data;
522             $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
523             $subfield_data{marc_mandatory}=$tagslib->{$tag}->{$subfield}->{mandatory};
524             $subfield_data{marc_repeatable}=$tagslib->{$tag}->{$subfield}->{repeatable};
525             $subfield_data{marc_value} = {
526                 type => 'hidden_simple',
527                 name => 'field_value[]',
528             };
529             push(@loop_data, \%subfield_data);
530             $i++
531         }
532     }
533 }
534
535 =back
536
537 =cut
538
539
540 # ======================== 
541 #          MAIN 
542 #=========================
543 my $input = CGI->new;
544 my $z3950 = $input->param('z3950');
545 my $error = $input->param('error');
546 my $authid=$input->param('authid'); # if authid exists, it's a modif, not a new authority.
547 my $op = $input->param('op');
548 my $nonav = $input->param('nonav');
549 my $myindex = $input->param('index');
550 my $linkid=$input->param('linkid');
551 my $authtypecode = $input->param('authtypecode');
552 my $breedingid    = $input->param('breedingid');
553
554
555 my $dbh = C4::Context->dbh;
556 if(!$authtypecode) {
557     $authtypecode = $authid ? Koha::Authorities->find($authid)->authtypecode : '';
558 }
559
560 my ($template, $loggedinuser, $cookie)
561     = get_template_and_user({template_name => "authorities/authorities.tt",
562                             query => $input,
563                             type => "intranet",
564                             flagsrequired => {editauthorities => 1},
565                             });
566 $template->param(nonav   => $nonav,index=>$myindex,authtypecode=>$authtypecode,breedingid=>$breedingid);
567
568 $tagslib = GetTagsLabels(1,$authtypecode);
569 $mandatory_z3950 = GetMandatoryFieldZ3950($authtypecode);
570
571 my $record=-1;
572 my $encoding="";
573 if (($authid) && !($breedingid)){
574     $record = GetAuthority($authid);
575 }
576 if ($breedingid) {
577     ( $record, $encoding ) = MARCfindbreeding_auth( $breedingid );
578 }
579
580 my ($oldauthnumtagfield,$oldauthnumtagsubfield);
581 my ($oldauthtypetagfield,$oldauthtypetagsubfield);
582 $is_a_modif=0;
583 if ($authid) {
584     $is_a_modif=1;
585     ($oldauthnumtagfield,$oldauthnumtagsubfield) = GetAuthMARCFromKohaField("auth_header.authid",$authtypecode);
586     ($oldauthtypetagfield,$oldauthtypetagsubfield) = GetAuthMARCFromKohaField("auth_header.authtypecode",$authtypecode);
587 }
588 $op ||= q{};
589 #------------------------------------------------------------------------------------------------------------------------------
590 if ($op eq "add") {
591 #------------------------------------------------------------------------------------------------------------------------------
592     # rebuild
593     my @tags = $input->multi_param('tag');
594     my @subfields = $input->multi_param('subfield');
595     my @values = $input->multi_param('field_value');
596     # build indicator hash.
597     my @ind_tag = $input->multi_param('ind_tag');
598     my @indicator = $input->multi_param('indicator');
599     my $record = TransformHtmlToMarc($input, 0);
600
601     my ($duplicateauthid,$duplicateauthvalue);
602      ($duplicateauthid,$duplicateauthvalue) = FindDuplicateAuthority($record,$authtypecode) if ($op eq "add") && (!$is_a_modif);
603     my $confirm_not_duplicate = $input->param('confirm_not_duplicate');
604     # it is not a duplicate (determined either by Koha itself or by user checking it's not a duplicate)
605     if (!$duplicateauthid or $confirm_not_duplicate) {
606         if ($is_a_modif ) {     
607             ModAuthority($authid,$record,$authtypecode);
608         } else {
609             ($authid) = AddAuthority($record,$authid,$authtypecode);
610         }
611         if ($myindex) {
612             print $input->redirect("blinddetail-biblio-search.pl?authid=$authid&index=$myindex");
613         } else {
614             print $input->redirect("detail.pl?authid=$authid");
615         }
616         exit;
617     } else {
618     # it may be a duplicate, warn the user and do nothing
619         build_tabs($template, $record, $dbh, $encoding,$input);
620         build_hidden_data;
621         $template->param(authid =>$authid,
622                         duplicateauthid     => $duplicateauthid,
623                         duplicateauthvalue  => $duplicateauthvalue->{'authorized'}->[0]->{'heading'},
624                         );
625     }
626 } elsif ($op eq "delete") {
627 #------------------------------------------------------------------------------------------------------------------------------
628         DelAuthority({ authid => $authid });
629         if ($nonav){
630             print $input->redirect("auth_finder.pl");
631         }else{
632             print $input->redirect("authorities-home.pl?authid=0");
633         }
634                 exit;
635 } else {
636 if ($op eq "duplicate")
637         {
638                 $authid = "";
639         }
640         build_tabs ($template, $record, $dbh,$encoding,$input);
641         build_hidden_data;
642         $template->param(oldauthtypetagfield=>$oldauthtypetagfield, oldauthtypetagsubfield=>$oldauthtypetagsubfield,
643                         oldauthnumtagfield=>$oldauthnumtagfield, oldauthnumtagsubfield=>$oldauthnumtagsubfield,
644                         authid                      => $authid , authtypecode=>$authtypecode,   );
645 }
646
647 my $authority_types = Koha::Authority::Types->search( {}, { order_by => ['authtypetext'] } );
648
649 my $type = $authority_types->find($authtypecode);
650 $template->param(
651     authority_types => $authority_types,
652     authtypecode    => $authtypecode,
653     authid          => $authid,
654     linkid          => $linkid,
655     authtypetext    => $type ? $type->authtypetext : "",
656     hide_marc       => C4::Context->preference('hide_marc'),
657 );
658 output_html_with_http_headers $input, $cookie, $template->output;