Bug 17089: Koha::Ratings - Remove AddRating
[koha.git] / opac / opac-tags.pl
1 #!/usr/bin/perl
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
16 #
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>.
19
20
21 =head1 NAME
22
23 opac-tags.pl
24
25 =head1 DESCRIPTION
26
27 TODO :: Description here
28
29 C4::Scrubber is used to remove all markup content from the sumitted text.
30
31 =cut
32
33 use Modern::Perl;
34
35 use CGI qw ( -utf8 );
36 use CGI::Cookie; # need to check cookies before having CGI parse the POST request
37
38 use C4::Auth qw(:DEFAULT check_cookie_auth);
39 use C4::Context;
40 use C4::Debug;
41 use C4::Output qw(:html :ajax pagination_bar);
42 use C4::Scrubber;
43 use C4::Biblio;
44 use C4::Tags qw(add_tag get_approval_rows get_tag_rows remove_tag stratify_tags);
45 use C4::XSLT;
46
47 use Data::Dumper;
48
49 my %newtags = ();
50 my @deltags = ();
51 my %counts  = ();
52 my @errors  = ();
53 my $perBibResults = {};
54
55 # Indexes of @errors that do not apply to a particular biblionumber.
56 my @globalErrorIndexes = ();
57
58 sub ajax_auth_cgi {     # returns CGI object
59         my $needed_flags = shift;
60     my %cookies = CGI::Cookie->fetch;
61         my $input = CGI->new;
62     my $sessid = $cookies{'CGISESSID'}->value;
63         my ($auth_status, $auth_sessid) = check_cookie_auth($sessid, $needed_flags);
64         $debug and
65         print STDERR "($auth_status, $auth_sessid) = check_cookie_auth($sessid," . Dumper($needed_flags) . ")\n";
66         if ($auth_status ne "ok") {
67                 output_with_http_headers $input, undef,
68                 "window.alert('Your CGI session cookie ($sessid) is not current.  " .
69                 "Please refresh the page and try again.');\n", 'js';
70                 exit 0;
71         }
72         $debug and print STDERR "AJAX request: " . Dumper($input),
73                 "\n(\$auth_status,\$auth_sessid) = ($auth_status,$auth_sessid)\n";
74         return $input;
75 }
76
77 # The trick here is to support multiple tags added to multiple bilbios in one POST.
78 # The HTML might not use this, but it makes it more web-servicey from the start.
79 # So the name of param has to have biblionumber built in.
80 # For lack of anything more compelling, we just use "newtag[biblionumber]"
81 # We split the value into tags at comma and semicolon
82
83 my $is_ajax = is_ajax();
84 my $openadds = C4::Context->preference('TagsModeration') ? 0 : 1;
85 my $query = ($is_ajax) ? &ajax_auth_cgi({}) : CGI->new();
86 unless (C4::Context->preference('TagsEnabled')) {
87         push @errors, {+ tagsdisabled=>1 };
88     push @globalErrorIndexes, $#errors;
89 } else {
90         foreach ($query->param) {
91                 if (/^newtag(.*)/) {
92                         my $biblionumber = $1;
93                         unless ($biblionumber =~ /^\d+$/) {
94                                 $debug and warn "$_ references non numerical biblionumber '$biblionumber'";
95                                 push @errors, {+'badparam' => $_ };
96                 push @globalErrorIndexes, $#errors;
97                                 next;
98                         }
99                         $newtags{$biblionumber} = $query->param($_);
100                 } elsif (/^del(\d+)$/) {
101                         push @deltags, $1;
102                 }
103         }
104 }
105
106 my $add_op = (scalar(keys %newtags) + scalar(@deltags)) ? 1 : 0;
107 my ($template, $loggedinuser, $cookie);
108 if ($is_ajax) {
109         $loggedinuser = C4::Context->userenv->{'number'};  # must occur AFTER auth
110         $debug and print STDERR "op: $loggedinuser\n";
111 } else {
112         ($template, $loggedinuser, $cookie) = get_template_and_user({
113         template_name   => "opac-tags.tt",
114         query           => $query,
115         type            => "opac",
116         authnotrequired => ($add_op ? 0 : 1), # auth required to add tags
117         debug           => 1,
118         });
119 }
120
121 if ($add_op) {
122         unless ($loggedinuser) {
123                 push @errors, {+'login' => 1 };
124         push @globalErrorIndexes, $#errors;
125                 %newtags=();    # zero out any attempted additions
126                 @deltags=();    # zero out any attempted deletions
127         }
128 }
129
130 my $scrubber;
131 my @newtags_keys = (keys %newtags);
132 if (scalar @newtags_keys) {
133         $scrubber = C4::Scrubber->new();
134         foreach my $biblionumber (@newtags_keys) {
135         my $bibResults = {adds=>0, errors=>[]};
136                 my @values = split /[;,]/, $newtags{$biblionumber};
137                 foreach (@values) {
138                         s/^\s*(.+)\s*$/$1/;
139                         my $clean_tag = $scrubber->scrub($_);
140                         unless ($clean_tag eq $_) {
141                                 if ($clean_tag =~ /\S/) {
142                                         push @errors, {scrubbed=>$clean_tag};
143                                         push @{$bibResults->{errors}}, {scrubbed=>$clean_tag};
144                                 } else {
145                                         push @errors, {scrubbed_all_bad=>1};
146                                         push @{$bibResults->{errors}}, {scrubbed_all_bad=>1};
147                                         next;   # we don't add it if there's nothing left!
148                                 }
149                         }
150                         my $result = ($openadds) ?
151                                 add_tag($biblionumber,$clean_tag,$loggedinuser,$loggedinuser) : # pre-approved
152                                 add_tag($biblionumber,$clean_tag,$loggedinuser)   ;
153                         if ($result) {
154                                 $counts{$biblionumber}++;
155                 $bibResults->{adds}++;
156                         } else {
157                                 push @errors, {failed_add_tag=>$clean_tag};
158                                 push @{$bibResults->{errors}}, {failed_add_tag=>$clean_tag};
159                                 $debug and warn "add_tag($biblionumber,$clean_tag,$loggedinuser...) returned bad result (" . (defined $result ? $result : 'UNDEF') .")";
160                         }
161                 }
162         $perBibResults->{$biblionumber} = $bibResults;
163         }
164 }
165 my $dels = 0;
166 foreach (@deltags) {
167         if (remove_tag($_,$loggedinuser)) {
168                 $dels++;
169         } else {
170                 push @errors, {failed_delete=>$_};
171         }
172 }
173
174 if ($is_ajax) {
175         my $sum = 0;
176         foreach (values %counts) {$sum += $_;}
177         my $js_reply = sprintf("response = {\n\tadded: %d,\n\tdeleted: %d,\n\terrors: %d",$sum,$dels,scalar @errors);
178
179     # If no add attempts were made, flag global errors.
180     if (@globalErrorIndexes) {
181         $js_reply .= ",\n\tglobal_errors: [";
182         my $first = 1;
183         foreach (@globalErrorIndexes) {
184             $js_reply .= "," unless $first;
185             $first = 0;
186             $js_reply .= "\n\t\t$_";
187         }
188         $js_reply .= "\n\t]";
189     }
190     
191         my $err_string = '';
192         if (scalar @errors) {
193                 $err_string = ",\n\talerts: ["; # open response_function
194                 my $i = 1;
195                 foreach (@errors) {
196                         my $key = (keys %$_)[0];
197                         $err_string .= "\n\t\t KOHA.Tags.tag_message.$key(\"" . $_->{$key} . '")';
198                         if($i < scalar @errors){ $err_string .= ","; }
199                         $i++;
200                 }
201                 $err_string .= "\n\t]\n";       # close response_function
202         }
203
204     # Add per-biblionumber results for use on results page
205     my $js_perbib = "";
206     for my $bib (keys %$perBibResults) {
207         my $bibResult = $perBibResults->{$bib};
208         my $js_bibres = ",\n\t$bib: {\n\t\tadded: $bibResult->{adds}";
209         $js_bibres .= ",\n\t\terrors: [";
210         my $i = 0;
211         foreach (@{$bibResult->{errors}}) {
212             $js_bibres .= "," if ($i);
213                         my $key = (keys %$_)[0];
214                         $js_bibres .= "\n\t\t\t KOHA.Tags.tag_message.$key(\"" . $_->{$key} . '")';
215             $i++;
216         }
217         $js_bibres .= "\n\t\t]\n\t}";
218         $js_perbib .= $js_bibres;
219     }
220
221         output_with_http_headers($query, undef, "$js_reply\n$err_string\n$js_perbib\n};", 'js');
222         exit;
223 }
224
225 my $results = [];
226 my $my_tags = [];
227
228 if ($loggedinuser) {
229     $my_tags = get_tag_rows({borrowernumber=>$loggedinuser});
230     my $my_approved_tags = get_approval_rows({ approved => 1 });
231     foreach my $tag (@$my_tags) {
232         my $biblio = GetBiblioData($tag->{biblionumber});
233         my $record = &GetMarcBiblio( $tag->{biblionumber} );
234         $tag->{subtitle} = GetRecordValue( 'subtitle', $record, GetFrameworkCode( $tag->{biblionumber} ) );
235         $tag->{title} = $biblio->{title};
236         $tag->{author} = $biblio->{author};
237
238         my $xslfile = C4::Context->preference('OPACXSLTResultsDisplay');
239         my $lang   = $xslfile ? C4::Languages::getlanguage()  : undef;
240         my $sysxml = $xslfile ? C4::XSLT::get_xslt_sysprefs() : undef;
241
242         if ( $xslfile ) {
243             $tag->{XSLTBloc} = XSLTParse4Display(
244                     $tag->{ biblionumber }, $record, "OPACXSLTResultsDisplay",
245                     1, undef, $sysxml, $xslfile, $lang
246             );
247         }
248
249         my $date = $tag->{date_created} || '';
250         $date =~ /\s+(\d{2}\:\d{2}\:\d{2})/;
251         $tag->{time_created_display} = $1;
252         $tag->{approved} = ( grep { $_->{term} eq $tag->{term} and $_->{approved} } @$my_approved_tags );
253     }
254 }
255
256 $template->param(tagsview => 1);
257
258 if ($add_op) {
259         my $adds = 0;
260         for (values %counts) {$adds += $_;}
261         $template->param(
262                 add_op => 1,
263                 added_count => $adds,
264                 deleted_count => $dels,
265         );
266 } else {
267         my ($arg,$limit,$mine);
268         my $hardmax = 100;      # you might disagree what this value should be, but there definitely should be a max
269         $limit = $query->param('limit') || $hardmax;
270     $mine =  $query->param('mine') || 0; # set if the patron want to see only his own tags.
271         ($limit =~ /^\d+$/ and $limit <= $hardmax) or $limit = $hardmax;
272         $template->param(limit => $limit);
273         my $arghash = {approved=>1, limit=>$limit, 'sort'=>'-weight_total'};
274     $arghash->{'borrowernumber'} = $loggedinuser if $mine;
275         # ($openadds) or $arghash->{approved} = 1;
276         if ($arg = $query->param('tag')) {
277                 $arghash->{term} = $arg;
278         } elsif ($arg = $query->param('biblionumber')) {
279                 $arghash->{biblionumber} = $arg;
280         }
281         $results = get_approval_rows($arghash);
282     stratify_tags(10, $results); # work out the differents sizes for things
283         my $count = scalar @$results;
284         $template->param(TAGLOOP_COUNT => $count, mine => $mine);
285 }
286 (scalar @errors  ) and $template->param(ERRORS  => \@errors);
287 my @orderedresult = sort { uc($a->{'term'}) cmp uc($b->{'term'}) } @$results;
288 (scalar @$results) and $template->param(TAGLOOP => \@orderedresult );
289 (scalar @$my_tags) and $template->param(MY_TAGS => $my_tags);
290
291 output_html_with_http_headers $query, $cookie, $template->output;
292 __END__
293
294 =head1 EXAMPLE AJAX POST PARAMETERS
295
296 CGISESSID       7c6288263107beb320f70f78fd767f56
297 newtag396       fire,+<a+href="foobar.html">foobar</a>,+<img+src="foo.jpg"+/>
298
299 So this request is trying to add 3 tags to biblio #396.  The CGISESSID is the same as that the browser would
300 typically communicate using cookies.  If it is valid, the server will split the value of "newtag396" and 
301 process the components for addition.  In this case the intended tags are:
302         fire
303         <a+href="foobar.html">foobar</a>
304         <img src="foo.jpg" />
305
306 The first tag is acceptable.  The second will be scrubbed of markup, resulting in the tag "foobar".  
307 The third tag is all markup, and will be rejected.  
308
309 =head1 EXAMPLE AJAX JSON response
310
311 response = {
312         added: 2,
313         deleted: 0,
314         errors: 2,
315         alerts: [
316                  KOHA.Tags.tag_message.scrubbed("foobar"),
317                  KOHA.Tags.tag_message.scrubbed_all_bad("1"),
318         ],
319 };
320
321 =cut
322