From bbb3f00955d11a35d5bbf1ce9ed0489f32bb2288 Mon Sep 17 00:00:00 2001 From: Joe Atzberger Date: Mon, 19 May 2008 16:23:37 -0500 Subject: [PATCH] Initial commit for Tags back-end moderation. Requires AJAX functions from Output. Signed-off-by: Joshua Ferraro --- C4/Tags.pm | 52 +-- .../intranet-tmpl/prog/en/modules/tags/review.tmpl | 361 +++++++++++++++++++++ tags/review.pl | 238 ++++++++++++++ 3 files changed, 629 insertions(+), 22 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tags/review.tmpl create mode 100755 tags/review.pl diff --git a/C4/Tags.pm b/C4/Tags.pm index 4d3c1c1a83..68591b4653 100644 --- a/C4/Tags.pm +++ b/C4/Tags.pm @@ -60,10 +60,14 @@ INIT { $select_all = "SELECT " . join(',',@fields) . "\n FROM tags_all\n"; } -sub remove_tag ($) { - my $tag_id = shift; - my $rows = get_tag_rows({tag_id=>$tag_id}) or return 0; - (scalar(@$rows) == 1) or return undef; +sub remove_tag ($;$) { + my $tag_id = shift or return undef; + my $user_id = (@_) ? shift : undef; + my $rows = (defined $user_id) ? + get_tag_rows({tag_id=>$tag_id, borrowernumber=>$user_id}) : + get_tag_rows({tag_id=>$tag_id}) ; + $rows or return 0; + (scalar(@$rows) == 1) or return undef; # should never happen (duplicate ids) my $row = shift(@$rows); ($tag_id == $row->{tag_id}) or return 0; my $tags = get_tags({term=>$row->{term}, biblionumber=>$row->{biblionumber}}); @@ -126,11 +130,11 @@ sub get_tag_rows ($) { carp "Empty argument key to get_tag_rows: ignoring!"; next; } - unless (1 == scalar grep {/^ $key $/xi} @ok_fields) { + unless (1 == scalar grep {/^ $key $/x} @ok_fields) { carp "get_tag_rows received unreconized argument key '$key'."; next; } - if ($key =~ /^limit$/i) { + if ($key eq 'limit') { my $val = $hash->{$key}; unless ($val =~ /^(\d+,)?\d+$/) { carp "Non-nuerical limit value '$val' ignored!"; @@ -167,18 +171,18 @@ sub get_tags (;$) { # i.e., from tags_index carp "Empty argument key to get_tags: ignoring!"; next; } - unless (1 == scalar grep {/^ $key $/xi} @ok_fields) { + unless (1 == scalar grep {/^ $key $/x} @ok_fields) { carp "get_tags received unreconized argument key '$key'."; next; } - if ($key =~ /^limit$/i) { + if ($key eq 'limit') { my $val = $hash->{$key}; unless ($val =~ /^(\d+,)?\d+$/) { carp "Non-nuerical limit value '$val' ignored!"; next; } $limit = " LIMIT $val\n"; - } elsif ($key =~ /^sort$/i) { + } elsif ($key eq 'sort') { foreach my $by (split /\,/, $hash->{$key}) { unless ( $by =~ /^([-+])?(term)/ or @@ -197,10 +201,12 @@ sub get_tags (;$) { # i.e., from tags_index } } else { - my $whereval = $key; - ($key =~ /^term$/i) and $whereval = 'tags_index.term'; - $wheres .= ($wheres) ? " AND $whereval = ?\n" : " WHERE $whereval = ?\n"; - push @exe_args, $hash->{$key}; + my $whereval = $hash->{$key}; + my $longkey = ($key eq 'term') ? 'tags_index.term' : $key; + my $op = ($whereval =~ s/^(>=|<=)// or + $whereval =~ s/^(>|=|<)// ) ? $1 : '='; + $wheres .= ($wheres) ? " AND $longkey $op ?\n" : " WHERE $longkey $op ?\n"; + push @exe_args, $whereval; } } my $query = " @@ -233,18 +239,18 @@ sub get_approval_rows (;$) { # i.e., from tags_approval carp "Empty argument key to get_approval_rows: ignoring!"; next; } - unless (1 == scalar grep {/^ $key $/xi} @ok_fields) { + unless (1 == scalar grep {/^ $key $/x} @ok_fields) { carp "get_approval_rows received unreconized argument key '$key'."; next; } - if ($key =~ /^limit$/i) { + if ($key eq 'limit') { my $val = $hash->{$key}; unless ($val =~ /^(\d+,)?\d+$/) { carp "Non-nuerical limit value '$val' ignored!"; next; } $limit = " LIMIT $val\n"; - } elsif ($key =~ /^sort$/i) { + } elsif ($key eq 'sort') { foreach my $by (split /\,/, $hash->{$key}) { unless ( $by =~ /^([-+])?(term)/ or @@ -265,10 +271,11 @@ sub get_approval_rows (;$) { # i.e., from tags_approval } } else { - my $whereval = $key; - # ($key =~ /^term$/i) and $whereval = 'tags_index.term'; - $wheres .= ($wheres) ? " AND $whereval = ?\n" : " WHERE $whereval = ?\n"; - push @exe_args, $hash->{$key}; + my $whereval = $hash->{$key}; + my $op = ($whereval =~ s/^(>=|<=)// or + $whereval =~ s/^(>|=|<)// ) ? $1 : '='; + $wheres .= ($wheres) ? " AND $key $op ?\n" : " WHERE $key $op ?\n"; + push @exe_args, $whereval; } } my $query = " @@ -298,7 +305,7 @@ sub is_approved ($) { my $sth = C4::Context->dbh->prepare("SELECT approved FROM tags_approval WHERE term = ?"); $sth->execute($term); unless ($sth->rows) { - $ext_dict and return (spellcheck($term) ? 0 : 1); + $ext_dict and return (spellcheck($term) ? 0 : 1); # spellcheck returns empty on OK word return undef; } return $sth->fetch; @@ -391,7 +398,8 @@ sub add_tag_approval ($;$$) { # or disapproval } sub mod_tag_approval ($$$) { - my $operator = shift or return undef; + my $operator = shift; + defined $operator or return undef; # have to test defined to allow =0 (kohaadmin) my $term = shift or return undef; my $approval = (@_ ? shift : 1); # default is to approve my $query = "UPDATE tags_approval SET approved_by=?, approved=?, date_approved=NOW() WHERE term = ?"; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tags/review.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/tags/review.tmpl new file mode 100644 index 0000000000..402451e673 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tags/review.tmpl @@ -0,0 +1,361 @@ + +Koha › Tags › <!-- TMPL_IF NAME="do_it" -->Review › <!-- TMPL_ELSE -->Review Tags<!-- /TMPL_IF --> + + + + + + + + + + + +
+
+
+
+ +
+
+ Filters + +
+
+ + + + + + + + + + + + + +
TermStatusReviewerDate
" /> + + " /> + + + " /> + /lib/calendar/cal.gif" border="0" alt="Show Calendar" style="cursor: pointer;" id="openCalendarFrom" /> +
+ + " /> + /lib/calendar/cal.gif" border="0" alt="Show Calendar" style="cursor: pointer;" id="openCalendarTo" /> +
+
+
+ +
+

Displaying + ALL + Approved + Pending + Rejected + Terms +

+

+ + + + +

+ +
+ Approved + Rejected + Tested + Unkown Operation () on + + Term(s). +
+ + +
+ + ERROR: Date from is not a legal value (). + ERROR: Date to is not a legal value (). + ERROR: Failed to approve term (). + ERROR: Failed to reject term (). + ERROR: No match for user (). FILTER REQUIRES BORROWERNUMBER (not name). + ERROR: No match for borrowernumber (). + ERROR: The root koha user in your KOHA_CONF file + (default: kohaadmin) is not a valid tag moderator. These actions are logged + by borrowernumber, so the moderator must exist in your borrowers table. + Please login as a different authorized staff user to moderate tags. + Unrecognized error! + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  StatusTermWeightActionsReviewerDate
+ " name="tags" /> + rejected + OK + + + + + + &approved=all"> + + +
+ +
+
+
+
+

Tags

+ +
+ Terms Summary + + + + + + + + + + + + + +
Approved:
Rejected:
Pending:
Total:
+   +
+
+ Test Blacklist +
Enter a word or phrase here to test against your whitelist/blacklist.
+
+ + +
+ + + "" is permitted. + + "" is prohibited. + + +
+
+
+
+
+ + diff --git a/tags/review.pl b/tags/review.pl new file mode 100755 index 0000000000..291e84a906 --- /dev/null +++ b/tags/review.pl @@ -0,0 +1,238 @@ +#!/usr/bin/perl + +# This software is placed under the gnu General Public License, v2 (http://www.gnu.org/licenses/gpl.html) + +# Copyright 2008 LibLime +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +use warnings; +use strict; +use Data::Dumper; +use POSIX; +use CGI; +use CGI::Cookie; # need to check cookies before having CGI parse the POST request + +use C4::Auth qw(:DEFAULT check_cookie_auth);; +use C4::Context; +use C4::Dates qw(format_date format_date_in_iso); +# use C4::Koha; +use C4::Output 3.02 qw(:html :ajax pagination_bar); +use C4::Debug; +use C4::Tags 0.02 qw(get_tags get_approval_rows whitelist blacklist is_approved); + +sub counts () { + my $query = "SELECT " . + # (SELECT count(*) FROM tags_all ) as tags_all, + # (SELECT count(*) FROM tags_index ) as tags_index, + " (SELECT count(*) FROM tags_approval WHERE approved= 1) as approved_count, + (SELECT count(*) FROM tags_approval WHERE approved=-1) as rejected_count, + (SELECT count(*) FROM tags_approval WHERE approved= 0) as unapproved_count + "; + my $sth = C4::Context->dbh->prepare($query); + $sth->execute; + my $result = $sth->fetchrow_hashref(); + $result->{approved_total} = $result->{approved_count} + $result->{rejected_count} + $result->{unapproved_count}; + $debug and warn "counts returned: " . Dumper $result; + return $result; +} + +my $script_name = "/cgi-bin/koha/tags/review.pl"; +my $needed_flags = { tools => 'moderate_comments' }; # FIXME: replace when more specific permission is created. + +sub ajax_auth_cgi ($) { # returns CGI object + my $needed_flags = shift; + my %cookies = fetch CGI::Cookie; + my $input = CGI->new; + my $sessid = $cookies{'CGISESSID'}->value || $input->param('CGISESSID'); + my ($auth_status, $auth_sessid) = check_cookie_auth($sessid, $needed_flags); + $debug and + print STDERR "($auth_status, $auth_sessid) = check_cookie_auth($sessid," . Dumper($needed_flags) . ")\n"; + if ($auth_status ne "ok") { + output_ajax_with_http_headers $input, + "window.alert('Your CGI session cookie ($sessid) is not current. " . + "Please refresh the page and try again.');\n"; + exit 0; + } + $debug and print STDERR "AJAX request: " . Dumper($input), + "\n(\$auth_status,\$auth_sessid) = ($auth_status,$auth_sessid)\n"; + return $input; +} + +if (is_ajax()) { + my $input = &ajax_auth_cgi($needed_flags); + my $operator = C4::Context->userenv->{'number'}; # must occur AFTER auth + $debug and print STDERR "op: " . Dumper($operator) . "\n"; + my ($tag, $js_reply); + if ($tag = $input->param('test')) { + $js_reply = ( is_approved( $tag) ? 'success' : 'failure') . "_test('$tag');\n"; + } + if ($tag = $input->param('ok')) { + $js_reply = ( whitelist($operator,$tag) ? 'success' : 'failure') . "_approve('$tag');\n"; + } + if ($tag = $input->param('rej')) { + $js_reply = ( blacklist($operator,$tag) ? 'success' : 'failure') . "_reject('$tag');\n"; + } + output_ajax_with_http_headers $input, $js_reply; + exit; +} + +### Below is the sad, boring, necessary non-AJAX HTML code. + +my $input = CGI->new; +my ($template, $borrowernumber, $cookie) = get_template_and_user({ + template_name => "tags/review.tmpl", + query => $input, + type => "intranet", + debug => 1, + authnotrequired => 0, + flagsrequired => $needed_flags, +}); + +my ($op, @errors, @tags); +$op = $input->param('op') || 'none'; +@tags = $input->param('tags'); + +$borrowernumber == 0 and push @errors, {op_zero=>1}; + if ($op eq 'approve') { + foreach (@tags) { + whitelist($borrowernumber,$_) or push @errors, {failed_ok=>$_}; + } +} elsif ($op eq 'reject' ) { + foreach (@tags) { + blacklist($borrowernumber,$_) or push @errors, {failed_rej=>$_}; + } +} elsif ($op eq 'test' ) { + my $tag = $input->param('test'); + push @tags, $tag; + $template->param( + test_term => $tag, + (is_approved($tag) ? 'verdict_ok' : 'verdict_rej') => 1, + ); +} + +my $counts = &counts; +foreach (keys %$counts) { + $template->param($_ => $counts->{$_}); +} + +sub pagination_calc ($;$) { + my $query = shift or return undef; + my $hardlimit = (@_) ? shift : 100; # hardcoded, could be another syspref + my $pagesize = $query->param('limit' ) || $hardlimit; + my $page = $query->param('page' ) || 1; + my $offset = $query->param('offset') || 0; + ($pagesize <= $hardlimit) or $pagesize = $hardlimit; + if ($page > 1) { + $offset = ($page-1)*$pagesize; + } else { + $page = 1; + } + return ($pagesize,$page,$offset); +} + +my ($pagesize,$page,$offset) = pagination_calc($input,100); + +my %filters = ( + limit => $offset ? "$offset,$pagesize" : $pagesize, + sort => 'approved,-weight_total,+term', +); +my ($filter,$date_from,$date_to); +if (defined $input->param('approved')) { # 0 is valid value, must check defined + $filter = $input->param('approved'); +} else { + $filter = 0; +} +if ($filter eq 'all') { + $template->param(filter_approved_all => 1); +} elsif ($filter =~ /-?[01]/) { + $filters{approved} = $filter; + $template->param( + ($filter == 1 ? 'filter_approved_ok' : + $filter == 0 ? 'filter_approved_pending' : + $filter == -1 ? 'filter_approved_rej' : + 'filter_approved') => 1 + ); +} + +# my $q_count = get_approval_rows({limit=>$pagesize, sort=>'approved,-weight_total,+term', count=>1}); +if ($filter = $input->param('tag')) { + $template->param(filter_tag=>$filter); + $filters{term} = $filter; +} +if ($filter = $input->param('from')) { + if ($date_from = format_date_in_iso($filter)) { + $template->param(filter_date_approved_from=>$filter); + $filters{date_approved} = ">=$date_from"; + } else { + push @errors, {date_from=>$filter}; + } +} +if ($filter = $input->param('to')) { + if ($date_to = format_date_in_iso($filter)) { + $template->param(filter_date_approved_to=>$filter); + $filters{date_approved} = "<=$date_to"; + } else { + push @errors, {date_to=>$filter}; + } +} +if ($filter = $input->param('approver')) { # name (or borrowernumber) from input box + if (($filter =~ /^\d+$/ and $filter > 0) or + (1) ){ # $filter=get borrowernumber from name + $template->param(filter_approver=>$filter); + $filters{approved_by} = $filter; + # } else { + push @errors, {approver=>$filter}; + } +} +if ($filter = $input->param('approved_by')) { # borrowernumber from link + if ($filter =~ /^\d+$/ and $filter > 0) { + $template->param(filter_approver=>$filter); + $filters{approved_by} = $filter; + } else { + push @errors, {approved_by=>$filter}; + } +} +$debug and print STDERR "filters: " . Dumper(\%filters); +my $tagloop = get_approval_rows(\%filters); +my $qstring = $input->query_string; +$qstring =~ s/([&;])*\blimit=\d+//; # remove pagination vars +# $qstring =~ s/([&;])*\bpage=\d+//; # remove pagination vars +# $qstring =~ s/\&[\&]+/&/g; # compress duplicates +# $qstring =~ s/;;+/;/g; # compress duplicates +# $qstring =~ s/\&;//g; # remove empties +# $qstring =~ s/;+$//; # remove trailing delim +$qstring =~ s/^;+//; # remove leading delim +$qstring = "limit=$pagesize" . ($qstring ? '&' . $qstring : ''); +$debug and print STDERR "number of approval_rows: " . scalar(@$tagloop) . "rows\n"; +(scalar @errors) and $template->param(message_loop=>\@errors); +$template->param( + DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(), + offset => $offset, # req'd for EXPR + op => $op, + op_count => scalar(@tags), + script_name => $script_name, + approved => 0, # dummy value (also EXPR) + tagloop => $tagloop, + pagination_bar => pagination_bar( + "$script_name?$qstring\&", + ceil($counts->{approved_total}/$pagesize), # $page, 'page' + ) +); + +output_html_with_http_headers $input, $cookie, $template->output; +__END__ + -- 2.11.0