package EPrints::Plugin::Screen::Report;
# Abstract class that handles the Report tools
use JSON qw(encode_json);
use EPrints::Plugin::Screen;
@ISA = ( 'EPrints::Plugin::Screen' );
use strict;
sub new
{
my( $class, %params ) = @_;
my $self = $class->SUPER::new(%params);
push @{$self->{actions}}, qw( export search newsearch update );
$self->{sconf} = "report";
$self->{appears} = [
{
place => "key_tools",
position => 1000,
},
];
return $self;
}
sub get_report { shift->{report} }
sub can_be_viewed
{
my( $self ) = @_;
return 1 if( $self->{public} ); #allow a report to be publicly available
return 0 if( !defined $self->{repository}->current_user );
return $self->allow( 'report' );
}
sub allow_export { shift->can_be_viewed }
sub action_export {}
sub wishes_to_export {
$_[0]->repository->param( 'export' ) ||
$_[0]->repository->param( 'ajax' );
}
sub export_mimetype
{
my( $self ) = @_;
my $plugin = $self->{processor}->{plugin};
if( !defined $plugin )
{
if( $self->repository->param( "ajax" ) )
{
return "application/json; charset=utf-8";
}
return "text/html; charset=utf-8";
}
return $plugin->param( "mimetype" );
}
sub export
{
my( $self ) = @_;
my $part = $self->repository->param( "ajax" );
my $f = "ajax_$part";
if( $self->can( $f ) )
{
binmode(STDOUT, ":utf8");
return $self->$f;
}
my $plugin = $self->{processor}->{plugin};
return $self->SUPER::export if !defined $plugin;
my $items = $self->items;
if( $plugin->{grouped} && defined $self->{processor}->{group} ) #some export plugins can accept groupings
{
$items = $self->get_grouped_items( $items, 0 );
}
$plugin->initialise_fh( \*STDOUT );
$plugin->output_list(
list => $items,
fh => \*STDOUT,
exportfields => $self->{processor}->{exportfields},
dataset => $self->{processor}->{dataset},
plugin => $self,
);
}
sub allow_search { return 1; }
#generates search config
sub _create_search
{
my( $self ) = @_;
my $session = $self->{session};
my $report_plugin = $self->{processor}->screen;
# Do not create a search config if this is not configured in the report
return if ! defined $report_plugin->{searchdatasetid};
$self->{processor}->{report_plugin} = $report_plugin;
my $report_ds = $session->dataset( $report_plugin->{searchdatasetid} );
if( defined $report_ds )
{
$self->{processor}->{datasetid} = $report_ds->base_id;
my $sconf = $report_ds->search_config( $report_plugin->{sconf} );
my $format = "report/" . $report_ds->base_id;
$self->{processor}->{search} = $session->plugin( "Search" )->plugins(
{
keep_cache => 1,
session => $self->{session},
dataset => $report_ds,
%{$sconf}
},
type => "Search",
can_search => $format,
);
}
}
sub allow_newsearch { return 1; }
sub allow_update { return 1; }
sub action_newsearch
{
my( $self ) = @_;
my $session = $self->{session};
#$self->{processor}->{report} = $session->param( 'report' );
#$self->{processor}->{screenid} = $self->{processor}->{report};
#$self->{processor}->{action} = "newsearch";
#$self->_create_search;
}
sub action_update
{
my( $self ) = @_;
my $session = $self->{session};
#$self->{processor}->{report} = $session->param( 'report' );
#$self->{processor}->{screenid} = $self->{processor}->{report};
#$self->{processor}->{action} = "newsearch";
#$self->_create_search;
}
sub action_search
{
my( $self ) = @_;
$self->{processor}->{action} = "search";
#read parameters
my $session = $self->{session};
$self->{processor}->{report} = $session->param( 'report' );
$self->{processor}->{screenid} = $self->{processor}->{report};
$self->_create_search;
my $loaded = 0;
my $id = $session->param( "cache" );
if( defined $id )
{
$loaded = $self->{processor}->{search}->from_cache( $id );
}
if( !$loaded )
{
my $exp = $session->param( "exp" );
if( defined $exp )
{
$self->{processor}->{search}->from_string( $exp );
# cache expired...
$loaded = 1;
}
}
my @problems;
if( !$loaded )
{
for( $self->{processor}->{search}->from_form )
{
$self->{processor}->add_message( "warning", $_ );
}
}
#display the results
$self->render;
}
sub properties_from
{
my( $self ) = @_;
my $repo = $self->repository;
$self->SUPER::properties_from;
if( defined ( my $dsid = $self->param( "datasetid" ) ) )
{
$self->{processor}->{dataset} = $self->repository->dataset( $dsid );
}
# sf2 - TODO - bark if dataset is not set? perhaps there are other ways to get the objects from...
my $report = $self->get_report();
#get a search object if we have one from a previous search action, so that we might later use it to do an export or search action
$self->_create_search;
if( defined $self->repository->param( "search" ) )
{
$self->{processor}->{search}->from_string( $self->repository->param( "search" ) ) if defined $self->{processor}->{search};
$self->{processor}->{export_search} = 1;
}
my $format = $self->repository->param( "export" );
if( $format && $report )
{
my $plugin = $self->repository->plugin( "Export::$format", report => $report );
if( defined $plugin && ( $plugin->can_accept( "report/$report" ) || ($plugin->can_accept( "report/generic" ) ) ) )
{
$self->{processor}->{plugin} = $plugin;
}
}
#list of export fields retrieved from non-abstract instances of reports
my @exportfields;
if( defined $repo->config( $self->{export_conf}, "exportfields" ) )
{
my @keys;
if( defined $repo->config( $self->{export_conf}, "exportfield_order" ) )
{
@keys = @{$repo->config( $self->{export_conf}, "exportfield_order" )};
}
else
{
@keys = keys %{$repo->config( $self->{export_conf}, "exportfields" )};
}
foreach my $key ( @keys )
{
foreach my $fieldname ( @{$repo->config( $self->{export_conf}, "exportfields" )->{$key}} )
{
push @exportfields, $fieldname if defined $self->repository->param( $fieldname );
}
}
}
my $sort = $self->repository->param( "sort" );
if( defined $sort )
{
$self->{processor}->{sort} = $sort;
}
my $group = $self->repository->param( "group" );
$self->{processor}->{group_exp} = $group; #store the group expression for later use
#decode the group expression
my %group_opts;
if( defined $group )
{
#get field and associated configuration about the group, e.g. res, truncate, reverse_order etc.
my @group = split(/;/, $group );
my $first = 1;
foreach my $g (@group)
{
if( $first )
{
$group = $g;
}
else
{
my @opts = split(/=/, $g );
$group_opts{$opts[0]} = $opts[1];
}
$first = 0;
}
$self->{processor}->{group} = $group;
$self->{processor}->{group_opts} = \%group_opts;
}
$self->{processor}->{exportfields} = \@exportfields;
}
# \@({meta_fields=>[ "field1", "field2" "document.field3" ], merge=>"ANY", match=>"EX", value=>"bees"}, {meta_fields=>[ "field4" ], value=>"honey"});
# e.g.
# return [ { meta_fields => [ 'type' ], value => 'article' } ]
sub filters
{
return [];
}
# how to select items i.e. the slice of data we want to validate/export?
#
sub items
{
my( $self ) = @_;
if( $self->{processor}->{action} eq "search" || $self->{processor}->{export_search} )
{
my $report = $self->{processor}->{report_plugin};
$report->apply_filters if $report->can( 'apply_filters' );
my $items = $self->{processor}->{search}->perform_search;
if( defined $self->{processor}->{sort} )
{
$items = $items->reorder( $self->{processor}->{sort} );
}
return $items;
}
elsif( defined $self->{processor}->{dataset} )
{
my %search_opts = ( filters => $self->filters, satisfy_all => 1 );
if( defined $self->param( 'custom_order' ) )
{
$search_opts{custom_order} = $self->param( 'custom_order' );
}
if( defined $self->{processor}->{sort} ) #an ordering specified by the user should trump the reports custom order
{
$search_opts{custom_order} = $self->{processor}->{sort};
}
return $self->{processor}->{dataset}->search( %search_opts );
}
# we can't return an EPrints::List if {dataset} is not defined
return undef;
}
# from Reports/ROS/Journals.pm
# TODO Note quite a lot of replication between this and Export::Report::CSV::output_dataobj
sub validate_dataobj
{
my( $plugin, $dataobj ) = @_;
my $repo = $plugin->repository;
my $report_fields = $plugin->report_fields( $dataobj );
my $val_fields = $plugin->validate_fields( $dataobj );
# related objects and their datasets
my $objects = $plugin->get_related_objects( $dataobj );
my $valid_ds = {};
foreach my $dsid ( keys %$objects )
{
$valid_ds->{$dsid} = $repo->dataset( $dsid );
}
my @problems;
foreach my $field ( @{ $plugin->report_fields_order( $dataobj ) || [] } )
{
# validation action
my $v_field = $val_fields->{$field};
next unless defined $v_field; # no validation required
# simple case - code handles validation
if( ref( $v_field ) eq 'CODE' )
{
# a sub{} we need to run
eval {
&$v_field( $plugin, $objects, \@problems );
};
if( $@ )
{
$repo->log( "Validation Runtime error: $@" );
}
next;
}
elsif( lc $v_field ne "required" )
{
$repo->log( "Validation Runtime error: $v_field must be code ref or 'required'" );
next;
}
# check required values
my $value; # the value to validate
my $ep_field = $report_fields->{$field};
if( ref( $ep_field ) eq 'CODE' )
{
# a sub{} we need to run
eval {
$value = &$ep_field( $plugin, $objects );
};
if( $@ )
{
$repo->log( "Validation Runtime error: $@" );
}
}
elsif( $ep_field =~ /^([a-z_]+)\.([0-9a-z_]+)$/ )
{
# a straight mapping with an EPrints field
my( $ds_id, $ep_fieldname ) = ( $1, $2 );
my $ds = $valid_ds->{$ds_id};
if( defined $ds && $ds->has_field( $ep_fieldname ) )
{
$value = $objects->{$ds_id}->value( $ep_fieldname );
}
else
{
# dataset or field doesn't exist
$repo->log( "Validation Runtime error: dataset $ds_id or field $ep_fieldname doesn't exist" );
}
}
# is field set?
if( !EPrints::Utils::is_set( $value ) )
{
push @problems, "Missing required field $field";
}
}
return @problems;
}
# TODO Note copy of Export::Report::get_related_objects
sub get_related_objects
{
my( $plugin, $dataobj ) = @_;
my $cmd = [ 'reports', $plugin->get_report, 'get_related_objects' ];
if( $plugin->repository->can_call( @$cmd ) )
{
return $plugin->repository->call( $cmd, $plugin->repository, $dataobj ) || {};
}
# just pass the dataobj itself
return {
$dataobj->dataset->confid => $dataobj,
};
}
# TODO Note copy of Export::Report::report_fields_order
sub report_fields_order
{
my( $plugin ) = @_;
return $plugin->{report_fields_order} if( defined $plugin->{report_fields_order} );
my $report = $plugin->get_report();
return [] unless( defined $report );
$plugin->{report_fields_order} = $plugin->repository->config( 'reports', $report, 'fields' );
return $plugin->{report_fields_order};
}
# TODO Note copy of Export::Report::report_fields
sub report_fields
{
my( $plugin ) = @_;
return $plugin->{report_fields} if( defined $plugin->{report_fields} );
my $report = $plugin->get_report();
return [] unless( defined $report );
$plugin->{report_fields} = $plugin->repository->config( 'reports', $report, 'mappings' );
return $plugin->{report_fields};
}
sub validate_fields
{
my( $plugin ) = @_;
return $plugin->{validate_fields} if( defined $plugin->{validate_fields} );
my $report = $plugin->get_report();
return [] unless( defined $report );
$plugin->{validate_fields} = $plugin->repository->config( 'reports', $report, 'validate' );
return $plugin->{validate_fields};
}
## rendering
# The "splash page"
sub render_splash_page
{
my( $self ) = @_;
my $repo = $self->repository;
my @plugins = $self->report_plugins;
if( !scalar( @plugins ) )
{
return $self->html_phrase( "no_reports" );
}
my @labels;
my @panels;
#preset reports
push @labels, $repo->html_phrase( "reports_preset" );
my $preset = $repo->make_element( "div" );
my $presets_added = 0;
# top category: by classname > Report::ROS::SomeReport1, Report::ROS::SomeReport2
my $ul = $self->repository->make_element( 'ul', class => 'ep_report_category' );
# cat ~ category - !meeow
my $cat = "";
my $cat_li = undef;
my $cat_ul = undef;
#prepare hash of reporting plugins
my %report_hash;
foreach my $report_plugin ( sort { $a->get_subtype cmp $b->get_subtype } @plugins )
{
my $plugin_cat = $report_plugin->get_subtype;
$plugin_cat =~ s/^Report::([^:]+):?:?(.*)$/$1/g;
if( $cat ne $plugin_cat ) #we have a top-category
{
$cat = $plugin_cat;
$report_hash{$cat} = [];
}
if( EPrints::Utils::is_set( $2 ) )
{
push @{$report_hash{$cat}}, $report_plugin;
}
}
for my $top_cat( sort keys %report_hash )
{
my @report_plugins = @{$report_hash{$top_cat}};
if( scalar( @report_plugins ) > 0 )
{
#render top-category
$cat_li = $ul->appendChild( $self->repository->make_element( 'li' ) );
$cat_li->appendChild( $self->repository->html_phrase( "Plugin/Screen/Report/$top_cat:title" ) );
$cat_ul = $cat_li->appendChild( $self->repository->make_element( 'ul', class => 'ep_report_items' ) );
foreach my $r ( @report_plugins )
{
my $sub_li = $cat_ul->appendChild( $self->repository->make_element( 'li' ) );
$sub_li->appendChild( $r->render_action_link );
$presets_added++;
}
}
}
$preset->appendChild( $ul );
push @panels, $preset;
#custom reports
push @labels, $repo->html_phrase( "reports_custom" );
my $custom = $repo->make_element( "div", id=>"custom_report" );
my $form = $repo->render_form( "get" );
$form->appendChild( $self->render_controls( 1 ) );
#add each report to the select component and generate search form if required
my $report_select = $repo->make_element( "select", name=>"report", id=>"select_report" );
my %search_forms;
my $custom_reports = 0;
#foreach my $report_plugin ( @plugins )
foreach my $report_plugin ( sort { $a->get_subtype cmp $b->get_subtype } @plugins )
{
if( $report_plugin->param( "custom" ) )
{
$custom_reports++;
my $formid = $report_plugin->{sconf};
#add to select component
my $id = $report_plugin->{report};
my $option = $repo->make_element( "option", value => $report_plugin->get_subtype, form => $formid );
#set default option for select component if required (i.e. we have come from a refine search or new search link)
my $refine_sconf = $self->{session}->param( "sconf" );
if( ( $self->{processor}->{action} eq "update" || $self->{processor}->{action} eq "newsearch" ) && defined $refine_sconf && $refine_sconf eq $formid )
{
$option->setAttribute( selected => "selected" );
}
#set option text and add to select
$option->appendChild( $report_plugin->render_title );
$report_select->appendChild( $option );
#create search form
#get report dataset and appropriate search config
my $report_ds = $repo->dataset( $report_plugin->{searchdatasetid} );
my $sconf = $report_ds->search_config( $report_plugin->{sconf} ) ;
#my $search = EPrints::Search->new(
# keep_cache => 1,
# session => $repo,
# dataset => $report_ds,
# %{$sconf}
#);
my $format = "report/" . $report_ds->base_id;
my $searchexp = $repo->plugin( "Search" )->plugins(
{
keep_cache => 1,
session => $self->{session},
dataset => $report_ds,
%{$sconf}
},
type => "Search",
can_search => $format,
);
$searchexp->from_form;
#generate the form
my $frag = $self->render_search_fields( $searchexp, $formid );
$search_forms{$formid} = $frag unless exists $search_forms{$formid};
}
}
$form->appendChild( $report_select );
$form->appendChild( $repo->render_hidden_field( "screen", $self->{screenid} ) );
#render possible search forms
foreach my $formid (keys %search_forms)
{
my $table = $repo->make_element( "table", class=>"ep_search_fields", id=>$formid, style=>"display: none" );
$form->appendChild( $table );
$table->appendChild( $search_forms{$formid} );
}
$form->appendChild( $self->render_controls );
$custom->appendChild( $form );
#javascript for changing forms based on report selection
$custom->appendChild( $repo->make_javascript( 'initReportForm();' ) );
if( $custom_reports && $presets_added > 0 ) #set up tab interface
{
my @labels;
my @panels;
push @labels, $repo->html_phrase( "reports_preset" );
push @labels, $repo->html_phrase( "reports_custom" );
push @panels, $preset;
push @panels, $custom;
my %opts;
if( $self->{processor}->{action} eq "newsearch" || $self->{processor}->{action} eq "update" )
{
$opts{current} = 1;
}
return $repo->xhtml->tabs(\@labels, \@panels, %opts );
}
elsif( $presets_added > 0 )
{
return $preset;
}
else
{
return $custom;
}
}
sub render_search_fields
{
my( $self, $search, $formid ) = @_;
my $exp = $self->{session}->param( "exp" );
my $sconf = $self->{session}->param( "sconf" );
if( defined $exp && defined $sconf && $sconf eq $formid )
{
$search->from_string( $exp );
}
my $frag = $self->{session}->make_doc_fragment;
foreach my $sf ( $search->get_non_filter_searchfields )
{
$frag->appendChild(
$self->{session}->render_row_with_help(
help_prefix => $sf->get_form_prefix."_help",
help => $sf->render_help,
label => $sf->render_name,
field => $sf->render,
no_toggle => ( $sf->{show_help} eq "always" ),
no_help => ( $sf->{show_help} eq "never" ),
) );
}
return $frag;
}
sub render_controls
{
my( $self, $with_js ) = @_;
my $div = $self->{session}->make_element(
"div" ,
class => "ep_search_buttons" );
$div->appendChild( $self->{session}->render_action_buttons(
_order => [ "search" ],
#newsearch => $self->{session}->phrase( "lib/searchexpression:action_reset" ),
search => $self->{session}->phrase( "lib/searchexpression:action_search" ) )
);
my $xml = $self->{session}->xml;
if( $with_js )
{
my $clear_form = $div->appendChild( $self->render_clearform( $xml ) );
}
my $clear_btn = $div->appendChild( $xml->create_element( "button",
type => "button",
onclick => "clearForm();",
class => "ep_form_action_button clear_button",
) );
$clear_btn->appendChild( $xml->create_text_node( $self->{session}->html_phrase( "lib/searchexpression:action_reset" ) ) );
return $div;
}
sub render
{
my( $self ) = @_;
# if users access Screen::Report directly we want to display some sort of menu
# where users can select viewable reports
if( ( "EPrints::Plugin::".$self->get_id eq __PACKAGE__ && $self->{processor}->{action} ne "search" ) || $self->{processor}->{action} eq "newsearch" )
{
return $self->render_splash_page;
}
my $repo = $self->repository;
my $chunk = $repo->make_doc_fragment;
$chunk->appendChild( $self->render_export_bar );
$chunk->appendChild( $self->render_group_options );
$chunk->appendChild( $self->render_sort_options );
if( $self->{processor}->{action} eq "search" )
{
$chunk->appendChild( $self->render_refine_search );
}
my $items = $self->items;
if( !defined $items || $items->count == 0 )
{
# No items message
}
my $json;
if( defined $self->{processor}->{group} && $self->{processor}->{group} ne "" )
{
my $grouped = $self->get_grouped_items( $items, 1 );
$json = encode_json $grouped;
}
else
{
my $item_ids = defined $items ? $items->ids : [];
$json = "[".join(',',@$item_ids)."]";
}
my $url = $repo->current_url( host => 1 );
my $parameters = URI->new;
$parameters->query_form(
$self->hidden_bits,
);
$parameters = $parameters->query;
my $ds = $repo->dataset( $self->param( 'datasetid' ) ) if defined $self->param( 'datasetid' );
my $prefix = $ds->base_id if defined $ds;
# the main
my $container_id = sprintf( "ep_report_%s\_container", $self->get_report );
#update javascript parameters if coming from a search request
if( $self->{processor}->{action} eq "search" )
{
my $plugin = $self->{processor}->{report};
$plugin =~ s/:/%3A/g;
$parameters = "screen=$plugin";
$prefix = $self->{processor}->{datasetid};
$container_id = sprintf( "ep_report_%s\_container", $self->{processor}->{report_plugin}->{report} );
}
#show/hide compliance
my $show_compliance = 1;
$show_compliance = $self->{show_compliance} if defined $self->{show_compliance};
#custom labels
my $labels = 0;
$labels = encode_json $self->{labels} if defined $self->{labels};
$chunk->appendChild( $repo->make_javascript( <<"EOJ" ) );
document.observe("dom:loaded", function() {
new EPrints_Screen_Report_Loader( {
ids: $json,
step: 20,
prefix: '$prefix',
url: '$url',
parameters: '$parameters',
container_id: '$container_id',
show_compliance: $show_compliance,
labels: $labels
} ).execute();
});
EOJ
$chunk->appendChild( $repo->make_element( 'div', class => 'ep_report_page', id => $container_id ) );
#show search controls after the results too
if( $self->{processor}->{action} eq "search" )
{
$chunk->appendChild( $self->render_refine_search );
}
return $chunk;
}
sub render_export_bar
{
my( $self ) = @_;
my $repo = $self->repository;
my $chunk = $repo->make_doc_fragment;
my @plugins = $self->export_plugins;
return $chunk unless( scalar( @plugins ) || defined( $repo->config( $self->{export_conf}, "exportfields" ) ) );
my $report_ds = $repo->dataset( $self->{datasetid} );
my $form = $self->render_form;
$form->setAttribute( method => "get" );
if( defined $self->repository->param( "search" ) || $self->{processor}->{action} eq "search" )
{
$form->appendChild( $repo->render_hidden_field( "search", $self->{processor}->{search}->serialise) );
}
if( defined $self->{processor}->{sort} )
{
$form->appendChild( $repo->render_hidden_field( "sort", $self->{processor}->{sort} ) );
}
if( defined $self->{processor}->{group_exp} )
{
$form->appendChild( $repo->render_hidden_field( "group", $self->{processor}->{group_exp} ) );
}
if( !defined( $repo->config( $self->{export_conf}, "exportfields" ) ) )
{
#no custom export fields defined, use export plugins designed for this report
my $select = $form->appendChild( $repo->render_option_list(
name => 'export',
values => [map { $_->get_subtype } @plugins],
labels => {map { $_->get_subtype => $_->get_name } @plugins},
) );
}
else
{
#provide list of default export plugins for reports
@plugins = $self->export_plugins( "generic" );
my $select = $form->appendChild( $repo->render_option_list(
name => 'export',
values => [map { $_->get_subtype } @plugins],
labels => {map { $_->get_subtype => $_->get_name } @plugins},
) );
#create labels and panels for tabbed interfaced
my $xml = $repo->xml;
my $xhtml = $repo->xhtml;
my $select_all = $form->appendChild( $self->render_selectall( $xml ) );
my $select_btn = $form->appendChild( $xml->create_element( "button",
type => "button",
onclick => "toggleCheckboxes();",
class => "ep_form_action_button select_button",
) );
$select_btn->appendChild( $xml->create_text_node( $repo->html_phrase( "report_select" ) ) );
#allow user to choose which fields they want to export
my $export_options = $repo->make_element( "div" );
my @keys;
if( defined $repo->config( $self->{export_conf}, "exportfield_order" ) )
{
@keys = @{$repo->config( $self->{export_conf}, "exportfield_order" )};
}
else
{
@keys = keys %{$repo->config( $self->{export_conf}, "exportfields" )};
}
foreach my $key ( @keys )
{
#create a new list
my $ul = $repo->make_element( "ul",
style => "list-style-type: none"
);
my $count = 0; #count how many fields we add
foreach my $fieldname( @{$repo->config( $self->{export_conf}, "exportfields" )->{$key}} )
{
if( defined $repo->config( $self->{export_conf}, "custom_export" ) && exists ${$repo->config( $self->{export_conf}, "custom_export" )}{$fieldname} ) #we have a custom export function instead
{
$count++;
$self->_export_field_checkbox( $repo, $fieldname, $ul, $repo->html_phrase( "exportfieldoptions:$fieldname" ) );
}
elsif( defined EPrints::Utils::field_from_config_string( $report_ds, $fieldname ) )
{
my $field = EPrints::Utils::field_from_config_string( $report_ds, $fieldname );
$count++;
$self->_export_field_checkbox( $repo, $fieldname, $ul, $field->render_name );
}
}
if( $count ) #only add options if we have any fields to show
{
my $div = $repo->make_element( "div", class=>"report_export_options" );
$div->appendChild( my $h = $repo->make_element( "h4" ) );
$h->appendChild( $repo->html_phrase( "exportfields:$key" ) );
$div->appendChild( $ul );
$export_options->appendChild( $div );
}
}
$form->appendChild( $export_options );
}
$form->appendChild(
$repo->render_button(
name => "_action_export",
class => "ep_form_action_button",
value => $repo->phrase( 'cgi/users/edit_eprint:export' )
) );
#create a collapsible box
my $imagesurl = $repo->current_url( path => "static", "style/images" );
my %options;
$options{session} = $repo;
$options{id} = "ep_report_export";
$options{title} = $repo->html_phrase( "export_title" );
$options{collapsed} = 1;
$options{content} = $form;
$options{show_icon_url} = "$imagesurl/multi_down.png";
$options{hide_icon_url} = "$imagesurl/multi_up.png";
my $box = $repo->make_element( "div", style=>"text-align: left" );
$box->appendChild( EPrints::Box::render( %options ) );
$chunk->appendChild( $box );
return $chunk;
}
sub render_sort_options
{
my( $self ) = @_;
my $repo = $self->repository;
my $chunk = $repo->make_doc_fragment;
return $chunk unless( defined( $repo->config( $self->{sort_conf}, "sortfields" ) ) );
my $sort_conf = $repo->config( $self->{sort_conf}, "sortfields" );
#build the form
my $form = $self->render_form;
$form->setAttribute( name => "sort_report" );
$form->setAttribute( method => "get" );
$chunk->appendChild( $form );
if( defined $repo->param( "search" ) || $self->{processor}->{action} eq "search" )
{
$form->appendChild( $repo->render_hidden_field( "search", $self->{processor}->{search}->serialise) );
}
if( defined $self->{processor}->{group_exp} )
{
$form->appendChild( $repo->render_hidden_field( "group", $self->{processor}->{group_exp} ) );
}
#display the links that will trigger the form
my $first = 1;
my $sort_links = $repo->make_doc_fragment;
foreach my $sort_name ( keys %{$sort_conf} )
{
my $sort_value = $sort_conf->{$sort_name};
if( $first )
{
$form->appendChild( $repo->render_hidden_field( "sort", $sort_value) );
}
if( !$first )
{
$sort_links->appendChild( $repo->html_phrase( "Update/Views:group_seperator" ) );
}
if( defined $self->{processor}->{sort} && $self->{processor}->{sort} eq $sort_value )
{
my $strong = $repo->make_element( "strong" );
$strong->appendChild( $repo->html_phrase( $self->{sort_conf} . ":sort:" . $sort_name ) );
$sort_links->appendChild( $strong );
}
else
{
my $link = $repo->render_link( 'javascript:sort_report("'.$sort_value.'")' );
$link->appendChild( $repo->html_phrase( $self->{sort_conf} . ":sort:" . $sort_name ) );
$sort_links->appendChild( $link );
}
$first = 0;
}
$chunk->appendChild( $repo->html_phrase( "Report:sort_links", links=>$sort_links ) );
return $chunk;
}
sub render_group_options
{
my( $self ) = @_;
my $repo = $self->repository;
my $chunk = $repo->make_doc_fragment;
return $chunk unless( defined( $repo->config( $self->{group_conf}, "groupfields" ) ) );
my $group_conf = $repo->config( $self->{group_conf}, "groupfields" );
#build the form
my $form = $self->render_form;
$form->setAttribute( name => "group_report" );
$form->setAttribute( method => "get" );
$chunk->appendChild( $form );
if( defined $repo->param( "search" ) || $self->{processor}->{action} eq "search" )
{
$form->appendChild( $repo->render_hidden_field( "search", $self->{processor}->{search}->serialise) );
}
if( defined $self->{processor}->{sort} )
{
$form->appendChild( $repo->render_hidden_field( "sort", $self->{processor}->{sort} ) );
}
#display the links that will trigger the form
my $first = 1;
my $group_links = $repo->make_doc_fragment;
foreach my $group_value ( @{$group_conf} )
{
my ($group_field) = split(/;/, $group_value );
if( $first )
{
$form->appendChild( $repo->render_hidden_field( "group", $group_value) );
}
if( !$first )
{
$group_links->appendChild( $repo->html_phrase( "Update/Views:group_seperator" ) );
}
if( defined $self->{processor}->{group} && $self->{processor}->{group} eq $group_field )
{
my $strong = $repo->make_element( "strong" );
$strong->appendChild( $repo->html_phrase( $self->{group_conf} . ":group:" . $group_field ) );
$group_links->appendChild( $strong );
}
else
{
my $link = $repo->render_link( 'javascript:group_report("'.$group_value.'")' );
$link->appendChild( $repo->html_phrase( $self->{group_conf} . ":group:" . $group_field ) );
$group_links->appendChild( $link );
}
$first = 0;
}
#no grouping link at the end
$group_links->appendChild( $repo->html_phrase( "Update/Views:group_seperator" ) );
if( defined $self->{processor}->{group} && $self->{processor}->{group} ne "" )
{
my $link = $repo->render_link( 'javascript:group_report("")' );
$link->appendChild( $repo->html_phrase( "report:no_grouping" ) );
$group_links->appendChild( $link );
}
else
{
my $strong = $repo->make_element( "strong" );
$strong->appendChild( $repo->html_phrase( "report:no_grouping" ) );
$group_links->appendChild( $strong );
}
$chunk->appendChild( $repo->html_phrase( "Report:group_links", links=>$group_links ) );
return $chunk;
}
sub render_refine_search
{
my( $self ) = @_;
my $repo = $self->repository;
my $chunk = $repo->make_doc_fragment;
if( defined $repo->param( "search" ) || $self->{processor}->{action} eq "search" )
{
my $escexp = $self->{processor}->{search}->serialise;
my $cacheid = $repo->param( "cache" );
my $sconf = $self->{sconf};
#set up new search link
my $new_baseurl = URI->new( $self->{session}->get_uri );
$new_baseurl->query_form(
screen => "Report",
sconf => $sconf,
);
my $search_links = $repo->make_doc_fragment;
my $new_link = $repo->render_link( "$new_baseurl&_action_newsearch=1" );
$new_link->appendChild( $repo->html_phrase( "lib/searchexpression:new" ) );
$search_links->appendChild( $new_link );
#add a separator...
$search_links->appendChild( $repo->html_phrase( "Update/Views:group_seperator" ) );
#set up refine search link
my $refine_baseurl = URI->new( $self->{session}->get_uri );
$refine_baseurl->query_form(
cache => $cacheid,
exp => $escexp,
screen => "Report",
dataset => $self->{datasetid},
order => $self->{processor}->{search}->{custom_order},
sconf => $sconf,
);
my $refine_link = $repo->render_link( "$refine_baseurl&_action_update=1" );
$refine_link->appendChild( $repo->html_phrase( "lib/searchexpression:refine" ) );
$search_links->appendChild( $refine_link );
$chunk->appendChild( $repo->html_phrase( "Report:search_links", links=>$search_links ) );
}
return $chunk;
}
#adds a new checkbox to allow the user to choose which fields to export
sub _export_field_checkbox
{
my( $self, $repo, $fieldname, $ul, $fieldlabel ) = @_;
my $li = $repo->make_element( "li" );
$ul->appendChild( $li );
my $checkbox = $repo->make_element( "input", type => "checkbox", id => $fieldname, name => $fieldname, value => $fieldname );
if( defined $repo->config( $self->{export_conf}, "exportfield_defaults" ) )
{
if( ( grep { $fieldname eq $_ } @{$repo->config( $self->{export_conf}, "exportfield_defaults" )} ) || ( scalar( @{$repo->config( $self->{export_conf}, "exportfield_defaults" )} ) == 0 ) )
{
#only check defaults or check everything if defaults not defined
$checkbox->setAttribute( "checked", "yes" );
}
}
my $label = $repo->make_element( "label", for => $fieldname );
$label->appendChild( $fieldlabel );
$li->appendChild( $checkbox );
$li->appendChild( $label );
}
### utility methods
# TODO should use "JSON" package
sub to_json
{
my( $self, $object ) = @_;
return "" if( !defined $object );
# UTF-8 issues:
# return JSON->new->utf8(1)->encode( $object );
if( ref( $object ) eq 'HASH' )
{
my @stuff;
while( my( $k, $v ) = each( %$object ) )
{
next if( !EPrints::Utils::is_set( $v ) ); # or 'null' ?
push @stuff, EPrints::Utils::js_string( $k ).':'.$self->to_json( $v )
}
return '{' . join( ",", @stuff ) . '}';
}
elsif( ref( $object ) eq 'ARRAY' )
{
my @stuff;
foreach( @$object )
{
next if( !EPrints::Utils::is_set( $_ ) );
push @stuff, $self->to_json( $_ );
}
return '[' . join( ",", @stuff ) . ']';
}
return EPrints::Utils::js_string( $object );
}
sub export_plugins
{
my( $self, $generic ) = @_;
my @plugin_ids;
my $repo = $self->repository;
if( defined $repo->config( $self->{export_conf}, "export_plugins" ) )
{
@plugin_ids = @{$repo->config( $self->{export_conf}, "export_plugins" )};
}
elsif( $generic )
{
@plugin_ids = $repo->plugin_list(
type => "Export",
can_accept => "report/generic",
is_visible => "staff",
is_advertised => 1,
);
}
else
{
@plugin_ids = $repo->plugin_list(
type => "Export",
can_accept => "report/".$self->get_report,
is_visible => "staff",
is_advertised => 1,
);
}
my @plugins;
foreach my $id ( @plugin_ids )
{
my $p = $repo->plugin( "$id" ) or next;
push @plugins, $p;
}
return @plugins;
}
sub report_plugins
{
my( $self ) = @_;
# sf2 - can't list via type => "Search::Report" ?
my @plugin_ids = $self->repository->plugin_list(
type => "Screen",
);
my @plugins;
foreach my $id ( @plugin_ids )
{
next if( $id !~ /^Screen::Report::/ ); # note this also filters out $self (aka Screen::Report)
my $p = $self->repository->plugin( "$id" );
next if( !defined $p || !$p->can_be_viewed );
push @plugins, $p;
}
return @plugins;
}
#returns a hash of values mapped to a label and a list
#if ids_only is set the list of items are just represented by their id (used by report JS)
sub get_grouped_items
{
my( $self, $items, $ids_only ) = @_;
my $session = $self->{session};
my %grouped;
my $grouping = $self->{processor}->{group};
my $metafield = $items->get_dataset->field( $grouping );
#set group_opts if appropriate
if( defined $self->{processor}->{group_opts} )
{
if( $metafield->type eq "date" )
{
$metafield->{render_res} = $self->{processor}->{group_opts}->{res} if exists $self->{processor}->{group_opts}->{res};
}
}
#create a hash of field values to items (or item ids)
$items->map( sub {
my( $session, $dataset, $item ) = @_;
my $multiple = $metafield->get_property( "multiple" );
my @group_values;
if( $multiple )
{
@group_values = @{$item->value( $grouping )};
}
else
{
@group_values = ($item->value( $grouping ));
}
if( scalar @group_values > 0 )
{
foreach my $group_value ( @group_values )
{
my $group_id = $metafield->get_id_from_value( $session, $group_value );
#truncate group if appropriate
if( defined $self->{processor}->{group_opts} )
{
$group_id = substr( "\u$group_id", 0, $self->{processor}->{group_opts}->{truncate} ) if exists $self->{processor}->{group_opts}->{truncate};
}
if( exists $grouped{$group_id} ) #we've already set this list up, push a new item on to the list
{
if( $ids_only ) #sometimes we only want ids rather than the whole item
{
push @{$grouped{$group_id}}, $item->id;
}
else
{
push @{$grouped{$group_id}}, $item;
}
}
else #set up a list for this group
{
my @grouped_items;
if( $ids_only ) #sometimes we only want ids rather than the whole item
{
@grouped_items = ($item->id);
}
else
{
@grouped_items = ($item);
}
$grouped{$group_id} = [@grouped_items];
}
}
}
else
{
my $group_id = "Unspecified " . $metafield->name;
if( exists $grouped{$group_id} ) #we've already set this list up, push a new item on to the list
{
if( $ids_only ) #sometimes we only want ids rather than the whole item
{
push @{$grouped{$group_id}}, $item->id;
}
else
{
push @{$grouped{$group_id}}, $item;
}
}
else #set up a list for this group
{
my @grouped_items;
if( $ids_only ) #sometimes we only want ids rather than the whole item
{
@grouped_items = ($item->id);
}
else
{
@grouped_items = ($item);
}
$grouped{$group_id} = [@grouped_items];
}
}
} );
#now sort the groups and add human readable labels
my @sorted_groups;
my $reverse = 0;
$reverse = $self->{processor}->{group_opts}->{reverse_order} if exists $self->{processor}->{group_opts}->{reverse_order};
if( $metafield->type eq "namedset" )
{
my( $tags, $labels ) = $metafield->tags_and_labels( $session );
#convert tags to a hash of values and a priority (for ordering)
my %priority;
my $index = 1;
foreach my $tag (@{$tags} )
{
$priority{$tag} = $index;
$index++;
}
foreach my $key (sort {$priority{$a} <=> $priority{$b}} keys %grouped)
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, $labels->{$key} );
}
}
elsif( $metafield->type eq "subject" )
{
my $ds = $session->dataset( "subject" );
my @values = keys %grouped;
my $sorted = $metafield->sort_values( $session, \@values );
my %priority;
my $index = 1;
foreach my $value (@{$sorted} )
{
$priority{$value} = $index;
$index++;
}
foreach my $key (sort {$priority{$a} <=> $priority{$b}} keys %grouped)
{
my $subj = $ds->dataobj( $key );
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, EPrints::Utils::tree_to_utf8( $subj->render_description ) ) if defined $subj;
}
}
elsif( $metafield->type eq "date" )
{
if( $reverse )
{
foreach my $key ( sort {$b <=> $a} keys %grouped )
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, $key );
}
}
else
{
foreach my $key ( sort {$a <=> $b} keys %grouped )
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, $key );
}
}
}
else
{
if( $reverse )
{
foreach my $key ( sort {$b cmp $a} keys %grouped )
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, $key );
}
}
else
{
foreach my $key ( sort keys %grouped )
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$key}, $key );
}
}
}
#check for any unspecified groups
my $group_id = "Unspecified " . $metafield->name;
if( exists $grouped{$group_id} )
{
push @sorted_groups, $self->_make_grouped_item( $grouped{$group_id}, $group_id );
}
return \@sorted_groups;
}
sub _make_grouped_item
{
my( $self, $list, $label ) = @_;
my %group;
$group{list} = $list;
$group{label} = $label;
return \%group;
}
sub render_selectall
{
my( $self, $xml ) = @_;
my $toggle_function = '
var isChecked = true;
function toggleCheckboxes() {
var export_options = document.getElementsByClassName("report_export_options");
for( export_option of export_options )
{
var checkboxes = export_option.getElementsByTagName( "input" )
for( checkbox of checkboxes )
{
if(isChecked)
{
checkbox.checked = "";
}
else
{
checkbox.checked = "checked";
}
}
}
isChecked = !isChecked;
}';
my $js_tag = $xml->create_element( "script" );
$js_tag->appendChild( $xml->create_text_node( $toggle_function ) );
return $js_tag;
}
sub render_clearform
{
my( $self, $xml ) = @_;
my $clear_function = '
function clearForm(){
var report = document.getElementById( "custom_report" );
var form = report.getElementsByClassName("selected_form")[0];
var inputs = form.getElementsByTagName( "input" );
for( input of inputs )
{
field_type = input.type.toLowerCase();
console.log(field_type);
switch (field_type)
{
case "text":
case "textarea":
input.value = "";
break;
case "radio":
case "checkbox":
if (input.checked)
{
input.checked = false;
}
break;
case "select-one":
case "select-multi":
input.selectedIndex = -1;
break;
default:
break;
}
}
var selects = form.getElementsByTagName( "select" );
for( select of selects )
{
if( select.hasAttribute("multiple") )
{
select.selectedIndex = -1;
}
}
}';
my $js_tag = $xml->create_element( "script" );
$js_tag->appendChild( $xml->create_text_node( $clear_function ) );
return $js_tag;
}
1;