################## ##History Search## ################## use EPrints; use Data::Dumper; use Time::Piece; use Time::Seconds; use strict; my $session = new EPrints::Session; my $content = "text/html"; $session->send_http_header( content_type=>$content ); if( !( $session->current_user && $session->current_user->value("usertype") =~ /^(admin|local_admin)$/ ) ) { print "You must be logged in as an admin to view this page\n"; $session->terminate; exit( 0 ); } #get field you want to watch my $fname = $session->param( "fname" ); #get start date my $start_date = $session->param( "start_date" ); #get end date my $end_date = $session->param( "end_date" ); # Validation against XSS if ( EPrints::Utils::is_set( $fname ) && $fname !~ m/^[a-zA-Z0-9_]+$/ ) { my $message = "Specified field name is not valid"; EPrints::Apache::AnApache::send_status_line( $session->{"request"}, 422, $message ); exit( 0 ); } if ( EPrints::Utils::is_set( $start_date ) && $start_date !~ m/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ ) { my $message = "Specified start date is not valid"; EPrints::Apache::AnApache::send_status_line( $session->{"request"}, 422, $message ); exit( 0 ); } if ( EPrints::Utils::is_set( $end_date ) && $end_date !~ m/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ ) { my $message = "Specified end date is not valid"; EPrints::Apache::AnApache::send_status_line( $session->{"request"}, 422, $message ); exit( 0 ); } #title my $title = $session->make_element( "h2" ); $title->appendChild( $session->make_text( "History Search" ) ); #page my $page = $session->make_doc_fragment(); #render warning area my $message_div = $session->make_element( "div", id=>"ep_messages", class=>"history_warning"); $page->appendChild( $message_div ); my $warning_div = $session->make_element( "div", class=>"ep_msg_warning" ); $message_div->appendChild( $warning_div ); my $content_div = $session->make_element( "div", id=>"warning_content", class=>"ep_msg_warning_content" ); $warning_div->appendChild( $content_div ); #render controls my $controls = $session->make_element( "div", class=>"history_search controls"); my $form = $session->make_element( "form" ); $form->setAttribute( "onsubmit", "return validateHistorySearch()" ); $controls->appendChild( $form ); #print list of fields my $fields = $session->make_element( "div", class=>"fields" ); my $dataset = "eprint"; my $eprint_ds = $session->dataset( $dataset ); my $field_label = $session->make_element( "label", for=>"fname" ); $field_label->appendChild( $session->make_text( "Field" ) ); $fields->appendChild( $field_label ); my $field_select = $session->make_element( "select", name=>"fname" ); my @fields = $eprint_ds->fields; foreach my $field (@fields) { my $n = $field->name; my $name = $field->render_name; my $option = $session->make_element( "option", value=>$n ); if( $fname eq $n ) { $option->setAttribute( "selected", "selected" ); } $option->appendChild( $name ); $field_select->appendChild( $option ); } $fields->appendChild( $field_select ); $form->appendChild( $fields ); #print start date my $start_div = $session->make_element( "div", class=>"startdate" ); my $start_label = $session->make_element( "label", for=>"start_date" ); $start_label->appendChild( $session->make_text( "Start Date" ) ); $start_div->appendChild( $start_label ); my $start_input = $session->make_element( "input", type=>"text", id=>"start_date", name=>"start_date", value=>$start_date, placeholder=>"yyyy-mm-dd" ); $start_div->appendChild( $start_input ); $form->appendChild( $start_div ); #print end date my $end_div = $session->make_element( "div", class=>"enddate" ); my $end_label = $session->make_element( "label", for=>"end_date" ); $end_label->appendChild( $session->make_text( "End Date" ) ); $end_div->appendChild( $end_label ); my $end_input = $session->make_element( "input", type=>"text", id=>"end_date", name=>"end_date", value=>$end_date, placeholder=>"yyyy-mm-dd" ); $end_div->appendChild( $end_input ); $form->appendChild( $end_div ); #print submit my $submit_div = $session->make_element( "div", class=>"submit" ); my $submit = $session->make_element( "input", type=>"submit", value=>"Search" ); $submit_div->appendChild( $submit ); $form->appendChild( $submit_div ); #end controls $page->appendChild( $controls ); #do the history search my $hist_ds = $session->dataset( "history" ); my $search_string; if(defined $fname) { my $field = $eprint_ds->field( $fname ); my $field_name = $field->render_name; my $sql = "SELECT historyid, objectid, revision, timestamp_year, timestamp_month, timestamp_day FROM history WHERE datasetid = 'eprint' AND action = 'modify' AND (details LIKE '%|$fname|%' OR details LIKE '$fname|%' OR details LIKE '%|$fname' OR details LIKE '$fname')"; $search_string = $search_string . 'Changes to "'.$field_name.'" '; my $using_dates = 0; if( defined $start_date && $start_date =~ /(\d{4})-?(\d{1,2})?-?(\d{1,2})?/ ) { $using_dates = 1; my $y = $1; my $m = sprintf( "%02d", $2 ); my $d = sprintf( "%02d", $3 ); my $s_date = "$y$m$d"; $sql = $sql . " AND CONCAT(timestamp_year,LPAD(timestamp_month,2,0),LPAD(timestamp_day,2,0)) > $s_date"; my $date_string = make_date_string( $d, $m, $y ); if( defined $end_date && $end_date =~ /(\d{4})-?(\d{1,2})?-?(\d{1,2})?/ ) { $search_string = $search_string . "between $date_string and "; } else { $search_string = $search_string . "since $date_string"; } } if( defined $end_date && $end_date =~ /(\d{4})-?(\d{1,2})?-?(\d{1,2})?/ ) { $using_dates = 1; my $y = $1; my $m = sprintf( "%02d", $2 ); my $d = sprintf( "%02d", $3 ); my $e_date = "$y$m$d"; $sql = $sql . " AND CONCAT(timestamp_year,LPAD(timestamp_month,2,0),LPAD(timestamp_day,2,0)) < $e_date"; my $date_string = make_date_string( $d, $m, $y ); if( defined $start_date && $start_date =~ /(\d{4})-?(\d{1,2})?-?(\d{1,2})?/ ) { $search_string = $search_string . $date_string; } else { $search_string = $search_string . "before $date_string"; } } if( !$using_dates ) { $sql = $sql . " ORDER BY historyid DESC"; $sql = $sql . " LIMIT 100"; $search_string = 'The last 100 changes to "'.$field_name.'"'; } else { $sql = $sql . " ORDER BY historyid ASC"; } my $sth = $session->get_database->prepare( $sql ); $session->get_database->execute( $sth , $sql ); my $search_title = $session->make_element( "h2" ); $search_title->appendChild( $session->make_text( $search_string ) ); $page->appendChild( $search_title ); my $table = $session->make_element( "table" ); my $tr = $session->make_element( "tr" ); my @headings = ("EPrint ID", "Timestamp", "Old Value", "New Value" ); foreach my $h (@headings) { my $th = $session->make_element( "th" ); $th->appendChild( $session->make_text( $h ) ); $tr->appendChild( $th ); } $table->appendChild( $tr ); $page->appendChild( $table ); while( my( $historyid, $eprintid, $revision, $y, $m, $d ) = $sth->fetchrow_array ) { #does the eprint exist??? my $eprint = $eprint_ds->dataobj( $eprintid ); if( defined $eprint ) { #get the history object my $hist = $hist_ds->dataobj( $historyid ); my $old_value = "Unknown"; #get previous state my $previous = $hist->get_previous(); if( defined( $previous ) ) { my $r_file = $previous->get_stored_file( "dataobj.xml" ); my $r_file_new = defined($r_file) ? $r_file->get_local_copy() : undef; open(my $fh, "xmllint --recover $r_file_new |"); my $parser = XML::LibXML->new( expand_entities=>1, load_external_dtd=>1 ); my $doc = XML::LibXML->load_xml( IO => $fh ); $old_value = get_content_value( $doc, $fname ) || "Unknown"; } #get current state my $r_file = $hist->get_stored_file( "dataobj.xml" ); my $r_file_new = defined($r_file) ? $r_file->get_local_copy() : undef; open(my $fh, "xmllint --recover $r_file_new |"); my $parser = XML::LibXML->new( expand_entities=>1, load_external_dtd=>1 ); my $doc = XML::LibXML->load_xml( IO => $fh ); my $new_value = get_content_value( $doc, $fname ); if( $d =~ /^\d{1}$/ ) { $d = "0$d"; } if( $m =~ /^\d{1}$/ ) { $m = "0$m"; } #render in a more human-readable way if appropriate if( $field->isa( "EPrints::MetaField::Set" ) ) { $old_value = $field->render_value( $session, $old_value ) . "\n" unless $old_value eq "Unknown"; $new_value = $field->render_value( $session, $new_value ) . "\n" unless $new_value eq "Unknown"; } my $result_row = $session->make_element( "tr" ); $result_row->appendChild( make_td( $session, $eprintid ) ); $result_row->appendChild( make_td( $session, "$d/$m/$y" ) ); $result_row->appendChild( make_td( $session, $old_value ) ); $result_row->appendChild( make_td( $session, $new_value ) ); $table->appendChild( $result_row ); } } } my $template = 'default'; $session->build_page( $title, $page, "history_search", undef, $template ); $session->send_page(); $session->terminate; exit; sub make_date_string{ my ( $d, $m, $y ) = @_; my $string = $y; if( $m != "00" ) { $string = "$m/$y"; } if( $d != "00" ) { $string = "$d/$m/$y"; } return $string; } sub make_td{ my( $session, $value ) = @_; my $td = $session->make_element( "td" ); $td->appendChild( $session->make_text( $value ) ); return $td; } sub get_content_value{ my( $doc, $fname ) = @_; my @possible_contents = (); my @contents = $doc->getElementsByTagName( $fname ); foreach my $c (@contents) { push @possible_contents, $c->textContent; } return $possible_contents[0]; }