package WebGUI::Asset::Wobject::SQLReport; #------------------------------------------------------------------- # WebGUI is Copyright 2001-2007 Plain Black Corporation. #------------------------------------------------------------------- # Please read the legal notices (docs/legal.txt) and the license # (docs/license.txt) that came with this distribution before using # this software. #------------------------------------------------------------------- # http://www.plainblack.com info@plainblack.com #------------------------------------------------------------------- use strict; use WebGUI::DatabaseLink; use WebGUI::International; use WebGUI::Macro; use WebGUI::Paginator; use WebGUI::SQL; use WebGUI::Utility; use WebGUI::Asset::Wobject; use WebGUI::Cache; use WebGUI::Text qw(:csv); our @ISA = qw(WebGUI::Asset::Wobject); #------------------------------------------------------------------- sub definition { my $class = shift; my $session = shift; my $definition = shift; my $i18n = WebGUI::International->new($session,"Asset_SQLReport"); push(@{$definition}, { assetName=>$i18n->get('assetName'), uiLevel => 5, icon=>'sqlReport.gif', tableName=>'SQLReport', className=>'WebGUI::Asset::Wobject::SQLReport', properties=>{ templateId =>{ fieldType=>"template", defaultValue=>'PBtmpl0000000000000059' }, cacheTimeout=>{ fieldType=>"interval", defaultValue=>0 }, paginateAfter=>{ fieldType=>"integer", defaultValue=>50 }, dbQuery1=>{ fieldType=>"codearea", defaultValue=>undef }, prequeryStatements1=>{ fieldType=>"codearea", defaultValue=>undef }, preprocessMacros1=>{ fieldType=>"yesNo", defaultValue=>0 }, placeholderParams1=>{ fieldType=>"textarea", defaultValue=>undef }, databaseLinkId1=>{ fieldType=>"databaseLink", defaultValue=>0 }, dbQuery2=>{ fieldType=>"codearea", defaultValue=>undef }, prequeryStatements2=>{ fieldType=>"codearea", defaultValue=>undef }, preprocessMacros2=>{ fieldType=>"yesNo", defaultValue=>0 }, placeholderParams2=>{ fieldType=>"textarea", defaultValue=>undef }, databaseLinkId2=>{ fieldType=>"databaseLink", defaultValue=>0 }, dbQuery3=>{ fieldType=>"codearea", defaultValue=>undef }, prequeryStatements3=>{ fieldType=>"codearea", defaultValue=>undef }, preprocessMacros3=>{ fieldType=>"yesNo", defaultValue=>0 }, placeholderParams3=>{ fieldType=>"textarea", defaultValue=>undef }, databaseLinkId3=>{ fieldType=>"databaseLink", defaultValue=>0 }, dbQuery4=>{ fieldType=>"codearea", defaultValue=>undef }, prequeryStatements4=>{ fieldType=>"codearea", defaultValue=>undef }, preprocessMacros4=>{ fieldType=>"yesNo", defaultValue=>0 }, placeholderParams4=>{ fieldType=>"textarea", defaultValue=>undef }, databaseLinkId4=>{ fieldType=>"databaseLink", defaultValue=>0 }, dbQuery5=>{ fieldType=>"codearea", defaultValue=>undef }, prequeryStatements5=>{ fieldType=>"codearea", defaultValue=>undef }, preprocessMacros5=>{ fieldType=>"yesNo", defaultValue=>0 }, placeholderParams5=>{ fieldType=>"textarea", defaultValue=>undef }, databaseLinkId5=>{ fieldType=>"databaseLink", defaultValue=>0 }, debugMode=>{ fieldType=>"yesNo", defaultValue=>0 }, # download downloadType=>{ fieldType=>"text", defaultValue=>"none", }, downloadFilename=>{ fieldType=>"text", defaultValue=>"", }, downloadTemplateId=>{ fieldType=>"template", defaultValue=>'SQLReportDownload0001', }, downloadMimeType=>{ fieldType=>"text", defaultValue=>"text/html", }, downloadUserGroup=>{ fieldType=>"group", defaultValue=>"text/html", }, } }); return $class->SUPER::definition($session, $definition); } #------------------------------------------------------------------- sub getEditForm { my $self = shift; my $tabform = $self->SUPER::getEditForm(); my $i18n = WebGUI::International->new($self->session,"Asset_SQLReport"); $tabform->getTab("display")->template( -value=>$self->getValue('templateId'), -label=>$i18n->get(72), -hoverHelp=>$i18n->get('72 description'), -namespace=>"SQLReport" ); $tabform->getTab("properties")->yesNo( -name=>"debugMode", -label=>$i18n->get(16), -hoverHelp=>$i18n->get('16 description'), -value=>$self->getValue("debugMode") ); $tabform->getTab("display")->interval( -name=>"cacheTimeout", -label=>$i18n->get('cache timeout'), -hoverHelp=>$i18n->get('cache timeout description'), -uiLevel => 8, -value=>$self->getValue("cacheTimeout") ); ### Download # Download Type my %downloadTypes; tie %downloadTypes, 'Tie::IxHash', "none" => "No Download", "csv" => "CSV", "template" => "Template", ; $tabform->getTab("properties")->radioList( -name=>"downloadType", # ID is downloadType_formId -label=>$i18n->get("download type"), -hoverHelp=>$i18n->get("download type description"), -vertical=>1, -options=> \%downloadTypes, -defaultValue=>"none", -value=>$self->getValue("downloadType"), -extras=> "onclick='changeDownloadType(this)'" ); # Download Filename $tabform->getTab("properties")->text( -name=>"downloadFilename", # ID is downloadFilename_formId -label=>$i18n->get("download filename"), -hoverHelp=>$i18n->get("download filename description"), -value=>$self->getValue("downloadFilename"), ); # Download template (if necessary) $tabform->getTab("properties")->template( -name=>"downloadTemplateId", # ID is downloadTemplateId_formId -label=>$i18n->get("download template"), -hoverHelp=>$i18n->get("download template description"), -value=>$self->getValue("downloadTemplateId"), -namespace=>"SQLReport/Download", ); # Download mimeType (if necessary) my %downloadMimeType; tie %downloadMimeType, 'Tie::IxHash', "application/octet-stream" => "application/octet-stream", "application/xml" => "application/xml", "application/csv" => "application/csv", "text/html" => "text/html", "text/plain" => "text/plain", ; $tabform->getTab("properties")->selectBox( -name=>"downloadMimeType", -label=>$i18n->get("download mimetype"), -hoverHelp=>$i18n->get("download mimetype description"), -options=> \%downloadMimeType, -value=>$self->getValue("downloadMimeType"), -defaultValue=>"application/octet-stream", ); # Download UserGroup $tabform->getTab("security")->group( -name=>"downloadUserGroup", -label=>$i18n->get("download usergroup"), -hoverHelp=>$i18n->get("download usergroup description"), -value=>$self->getValue("downloadUserGroup"), -defaultValue=>$self->getValue("groupIdView"), ); # javascript $self->session->style->setScript("/extras/wobject/SQLReport/editFormDownload.js"); ### /DOWNLOAD # Add toggleQuery javascript $tabform->getTab("properties")->raw(qq| |); for my $nr (1..5) { # Set TR class for this query properties $tabform->getTab("properties")->trClass("query".$nr); $tabform->getTab("properties")->readOnly( -value=>"
'.Dumper($var).''; # Add the "Download data" link if the user is allowed to download if ($self->getValue("downloadType") ne "none" && $self->session->user->isInGroup($self->getValue("downloadUserGroup")) ) { $var->{'canDownload'} = 1; $var->{'downloadLink'} = $self->_getDownloadLink($self); } my $out = $self->processTemplate($var,undef,$self->{_viewTemplate}); if (!$self->session->var->isAdminOn && $self->get("cacheTimeout") > 10) { WebGUI::Cache->new($self->session,"view_".$self->getId)->set($out,$self->get("cacheTimeout")); } return $out; } #------------------------------------------------------------------- # Create download link be adding all the passed in parameters and values to the url sub _getDownloadLink { my $self = shift; my %params; for my $nr (1 .. 5) { foreach my $row (split(/\n/,$self->{_query}{$nr}{placeholderParams})) { $row =~ s/^\s+//; $row =~ s/\s+$//; if ($row =~ /^form:(.*)/) { $params{$1} ||= $self->session->form->param($1); } } } my $url = "func=download"; for my $param (sort keys %params) { $url .= ";$param=" . $self->session->url->escape($params{$param}); } return $self->getUrl($url); } #------------------------------------------------------------------- sub _storeQueries { my $self = shift; for my $nr (1..5) { if($self->get("dbQuery".$nr)) { $self->{_query}{$nr} = { dbQuery => $self->get("dbQuery".$nr), databaseLinkId => $self->get("databaseLinkId".$nr), preprocessMacros => $self->get("preprocessMacros".$nr), placeholderParams => $self->get("placeholderParams".$nr), rowData => {}, }; } } } #------------------------------------------------------------------- sub _parsePlaceholderParams { my $self = shift; my $params = shift; my @placeholderParams; foreach my $row (split(/\n/,$params)) { $row =~ s/^\s+//; $row =~ s/\s+$//; next if ($row eq ''); my ($type,$field) = split(/:/,$row); my $param; if($type =~ /^form/) { $param = $self->session->form->process($field); } elsif ($type =~ /^query(\d)/) { $param = $self->{_query}{$1}{rowData}{$field}; } else { $param = $row; } WebGUI::Macro::process($self->session,\$param); push(@placeholderParams, $param); } return \@placeholderParams; } #------------------------------------------------------------------- # _processQuery($nest, $page, $nr) # Recursive sub to process this SQLReport's queries. # Arguments: $nest - If true, will run the nested queries. Defaults to true # $page - If true, will paginate. Defaults to true # $nr - This query number. Defaults to one. This is used # internally to tell which query we're performing. You should not # send this yourself. # Returns: A reference to a datastructure containing template variables to # be passed to processTemplate() sub _processQuery { my $self = shift; my $nest = shift; $nest = 1 unless defined $nest; my $page = shift; $page = 1 unless defined $page; my $nr = shift || 1; my ($query, %var, $prefix); if($nr > 1) { $prefix = 'query'.$nr.'.'; } # Parse placeholder parameters my $placeholderParams = $self->_parsePlaceholderParams($self->{_query}{$nr}{placeholderParams}); # Preprocess macros if ($self->{_query}{$nr}{preprocessMacros}) { $query = $self->{_query}{$nr}{dbQuery}; WebGUI::Macro::process($self->session,\$query); } else { $query = $self->{_query}{$nr}{dbQuery}; } my $i18n = WebGUI::International->new($self->session,"Asset_SQLReport"); push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get(17).$query}); push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get('debug placeholder parameters').join(",",@$placeholderParams)}); my $dbLink = WebGUI::DatabaseLink->new($self->session,$self->{_query}{$nr}{databaseLinkId}) || return $self->session->errorHandler->error("Could not find database link '".$self->{_query}{$nr}{databaseLinkId}."'. Has it been created?"); my $dbh = $dbLink->db; if (defined $dbh) { if ($dbLink->queryIsAllowed($query)) { # Check and execute prequery statements first foreach (split(/\n/, $self->getValue("prequeryStatements".$nr))) { my $prequeryStatement = $_; WebGUI::Macro::process($self->session, \$prequeryStatement) if ($self->{_query}{$nr}{preprocessMacros}); if ($dbLink->queryIsAllowed($prequeryStatement)) { my $sth = $dbh->unconditionalRead($prequeryStatement); if ($sth->errorCode > 0) { push(@{$self->{_debug_loop}},{ 'debug.output' => $i18n->get('Prequery error').' "'.$prequeryStatement.'": '.$sth->errorMessage }); } else { push(@{$self->{_debug_loop}},{ 'debug.output' => "Prequery: $prequeryStatement" }); } $sth->finish; } else { push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get("Prequery not allowed").$prequeryStatement}); } } my $url = $self->getUrl('func=view'); foreach ($self->session->form->param) { unless ($_ eq "pn" || $_ eq "op" || $_ eq "func" || $_ =~ /identifier/i || $_ =~ /password/i) { $url = $self->session->url->append($url, $self->session->url->escape($_) .'='.$self->session->url->escape($self->session->form->process($_))); } } my $paginateAfter = ($page == 1) ? $self->get("paginateAfter") : 99999999; my $paginatePage = ($nr > 1) ? 1 : $self->session->form->param('pn'); my $p = WebGUI::Paginator->new($self->session,$url,$paginateAfter, undef, $paginatePage); my $error = $p->setDataByQuery($query,$dbh,1,$placeholderParams); if ($error ne "") { $self->session->errorHandler->warn("There was a problem with the query: ".$error); push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get(11)." ".$error}); } else { my $first = 1; my @columns; my @rows; my $rownum = 1; foreach my $data (@{$p->getPageData($paginatePage)}) { $self->{_query}{$nr}{rowData} = $data; my %row; my $colnum = 1; my @fields; foreach my $name ($p->getColumnNames) { if ($first) { push(@columns,{ 'column.number'=>$colnum, 'column.name'=>$name }); } push(@fields,{ 'field.number'=>$colnum, 'field.name'=>$name, 'field.value'=>$data->{$name} }); $colnum++; $row{$prefix.'row.field.'.$name.'.value'} = $data->{$name}; } # Process nested query if($nest && $self->{_query}{$nr + 1}{dbQuery}) { my $nest = $self->_processQuery($nest,$page,$nr+1); %row = (%row , %$nest); $row{$prefix.'hasNest'} = $nest->{'query'.($nr+1).'.rows.count'}; } $row{$prefix.'row.number'} = $rownum; $row{$prefix.'row.field_loop'} = \@fields; push(@rows,\%row); $first = 0; $rownum++; } $var{$prefix.'columns_loop'} = \@columns; $var{$prefix.'rows_loop'} = \@rows; $var{$prefix.'columns.count'} = scalar(@columns); $var{$prefix.'rows.count'} = $p->getRowCount; $var{$prefix.'rows.count.isZero'} = ($p->getRowCount < 1); $var{$prefix.'rows.count.isZero.label'} = $i18n->get(18); $p->appendTemplateVars(\%var) if ($nr == 1); } } else { push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get(10)}); $self->session->errorHandler->warn("SQLReport [".$self->getId."] The SQL query is improperly formatted."); } $dbLink->disconnect; } else { push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get(12)}); $self->session->errorHandler->warn("SQLReport [".$self->getId."] Could not connect to database."); } return \%var; } #------------------------------------------------------------------- =head2 www_download ( ) Calls download() to let user download the SQLReport in the configured manner. =cut sub www_download { my $self = shift; # Only allow if download type is not "none" return undef if $self->getValue("downloadType") eq "none"; # Only allow users in appropriate group return $self->session->privilege->noAccess() unless $self->session->user->isInGroup($self->getValue("downloadUserGroup")); # Set filename and mimetype if ($self->getValue("downloadType") eq "csv") { $self->session->http->setFilename($self->getValue("downloadFilename"),"application/octet-stream"); } else { $self->session->http->setFilename($self->getValue("downloadFilename"),$self->getValue("downloadMimeType")); } $self->session->http->sendHeader; return $self->download; } #------------------------------------------------------------------- =head2 www_view ( ) See WebGUI::Asset::Wobject::www_view() for details. =cut sub www_view { my $self = shift; $self->session->http->setCacheControl($self->get("cacheTimeout")); $self->SUPER::www_view(@_); } 1;