class FrxReportGenerator in Forena Reports 7.2
Same name and namespace in other branches
- 6.2 FrxReportGenerator.inc \FrxReportGenerator
- 7.3 FrxReportGenerator.inc \FrxReportGenerator
- 7.4 FrxReportGenerator.inc \FrxReportGenerator
Hierarchy
- class \FrxReportGenerator
Expanded class hierarchy of FrxReportGenerator
File
- ./
FrxReportGenerator.inc, line 12 - Common functions used throughout the project but loaded in this file to keep the module file lean.
View source
class FrxReportGenerator {
public $app;
private $repositories = array();
static $instance = '';
public $title;
public $content;
public static function instance() {
global $forena_application_class;
if (!self::$instance) {
if (!$forena_application_class) {
$forena_application_class = 'FrxDrupalApplication';
}
self::$instance = new FrxReportGenerator($forena_application_class);
}
return self::$instance;
}
public function __construct($application_class) {
$app = $this->app = new $application_class();
}
/**
* Get name from argument 1 or alterntaively from a file name
*
* @param unknown_type $name
*/
public function report_desc($name = '') {
$app = $this->app;
// Get defined document types
$doctypes = $this
->supported_doctypes();
$parts = explode('.', $name);
$name = '';
if ($parts) {
foreach ($parts as $part) {
if (!isset($doctypes[$part])) {
$name .= '/' . $part;
}
else {
$desc['format'] = $part;
}
}
}
$desc['name'] = trim($name, '/');
$report_path = $app
->configuration('report_repos');
@(list($dir, $name_part) = explode('/', $name_part, -2));
if (!$name_part) {
$dir = '';
}
$desc['directory'] = $report_path . '/' . $dir;
$desc['filename'] = $report_path . '/' . trim($name, ' /') . '.frx';
$desc['link'] = 'reports/' . trim(str_replace('/', '.', $name), '.');
$desc['exists'] = file_exists($desc['filename']);
return $desc;
}
/**
* Determines which css files need to be loaded.
*
* @param array $desc Report descriptor from forena_rport_desc
* @param string $form The report "form" to be used. From the report
* @param string $format Document format that will be used for the report.
* @return array A list of css files that should be applied to the report.
*/
function report_css($desc, $form, $format = '') {
$css_files = array();
// First check for the form file
$path = $this->app
->configuration('report_repos');
if (file_exists($path . '/' . $form . '.css')) {
$css_files[] = $path . '/' . $form . '.css';
}
if ($format && file_exists($path . '/' . $form . '-' . $format . '.css')) {
$css_files[] = $path . '/' . $form . '-' . $format . '.css';
}
// Now check for a report specific file
$base_file = $path . '/' . $desc['name'];
if ($format && file_exists($base_file . '-' . $format . '.css')) {
$css_files[] = $base_file . '-' . $format . '.css';
}
elseif (file_exists($base_file . '.css')) {
$css_files[] = $base_file . '.css';
}
return $css_files;
}
/**
* Determines which js files need to be loaded.
*
* @param array $desc Report descriptor from forena_rport_desc
* @param string $form The report "form" to be used. From the report
* @param string $format Document format that will be used for the report.
* @return array A list of css files that should be applied to the report.
*/
function report_js($desc, $form, $format = '') {
$js_files = array();
// First check for the form file
$path = $this->app
->configuration('report_repos');
if (file_exists($path . '/' . $form . '.js')) {
$js_files[] = $path . '/' . $form . '.js';
}
if ($format && file_exists($path . '/' . $form . '-' . $format . '.js')) {
$js_files[] = $path . '/' . $form . '-' . $format . '.js';
}
// Now check for a report specific file
$base_file = $path . '/' . $desc['name'];
if ($format && file_exists($base_file . '-' . $format . '.js')) {
$js_files[] = $base_file . '-' . $format . '.js';
}
elseif (file_exists($base_file . '.js')) {
$js_files[] = $base_file . '.js';
}
return $js_files;
}
/**
* Clear session data because we want to reload.
*/
public function clear_session() {
if (isset($_SESSION['forena_access'])) {
unset($_SESSION['forena_access']);
}
}
/**
* Check access control using the block in a data block. In this case
* public assess returns true.
* @param $block Repository block used to test access
* @param $path xpath to user right within xml data.
* @param $access Access to test
* @return unknown_type
*/
function block_access($block, $path, $access, $cache = TRUE) {
// PUBLIC always returns true.
if ($access == 'PUBLIC') {
return TRUE;
}
if (!isset($_SESSION['forena_access'])) {
$_SESSION['forena_access'] = array();
}
if ($cache && isset($_SESSION['forena_access'][$block])) {
$rights = $_SESSION['forena_access'][$block];
}
else {
$rights = array();
// Get the block from the repository
$frxData = FrxData::instance();
$frxData
->push(array(), 'frx-block-access');
$data = $this
->invoke_data_provider($block, array(), NULL);
$frxData
->pop();
if ($data) {
if (!$path) {
$path = '*/*';
}
$nodes = $data
->xpath($path);
if ($nodes) {
foreach ($nodes as $node) {
$rights[] = (string) $node;
}
}
$_SESSION['forena_access'][$block] = $rights;
}
}
foreach ($rights as $right) {
if ($access == $right) {
return TRUE;
}
}
return FALSE;
}
// Putting this in a function to sandbox the repository settings
function load_repository(&$repo) {
// First determine if the class file exisits
$path = @$repo['path'];
$conf = array();
if (file_exists($path . '/settings.php')) {
// Override with repository specific data
include $path . '/settings.php';
}
$repo = array_merge($conf, $repo);
if (!isset($repos['data']) || !is_object($repo['data'])) {
$repo['data'] = $this
->load_provider($repo, $path);
}
}
/**
* Load the data provider class based on the class name.
*
* @param string $name
* @return object The data provider object
*/
function load_provider($conf, $repo_path) {
@($name = isset($conf['data provider']) ? $conf['data provider'] : $conf['data_engine']);
$this
->define_plugins();
// Instantiate the path
if (class_exists($name)) {
$o = new $name($conf, $repo_path);
return $o;
}
else {
$this->app
->error('Data provider not found for ' . $conf['title']);
}
}
/*
* Get the repository data for an a repository name.
* If no repository is specified the descriptors for all repositories are returned.
*/
function repository($name = '') {
static $repos = '';
global $_forena_repositories;
// Empty repository so we need to initialize
if (!$repos) {
// Build the default sample one
$path = $this->app
->forena_path();
$repos = array();
// Load the repository list from the global settings.php file.
$repos = $this->app
->repositories();
if ($_forena_repositories) {
array_merge($repos, $_forena_repositories);
}
}
// Now determine if the object exists
if ($name) {
if (isset($repos[$name])) {
if (@(!is_object($repos[$name]['data']))) {
$this
->load_repository($repos[$name]);
}
return $repos[$name];
}
else {
$this->app
->error('Undefined repository' . $name, "Undefined Repository: {$name} ");
}
}
else {
return $repos;
}
}
/**
* Accessor function named more conveniently
* Enter description here ...
* @param unknown_type $block_name
* @param unknown_type $parms
*/
public function block_xml($block_name, $parms = array()) {
return $this
->invoke_data_provider($block_name, $parms);
}
/**
* Extract the data by running a block
*
* @param unknown_type $data_block
* @param unknown_type $parameters
* @param string $clause order / where cause
* @return unknown
*/
function invoke_data_provider($data_block, $parameters = array(), $clause = '') {
list($provider, $block) = explode('/', $data_block, 2);
// Get the data
$repos = $this
->repository($provider);
if (isset($repos['enabled']) && !$repos['enabled']) {
return '';
}
if (isset($repos['user callback'])) {
$user_fn = $repos['user callback'];
if (is_callable($user_fn)) {
$current_user = $user_fn();
FrxData::instance()
->setValue('current_user', $current_user);
}
}
if ($repos['data']) {
$provider = $repos['data'];
$access = TRUE;
if (method_exists($provider, 'data')) {
$xml = $provider
->data($block, $parameters, $clause);
}
return $xml;
}
}
/**
* Returns an array of information about the data block
* @param $data_block
* @return unknown_type
*/
function forena_load_block($data_block, $clause = '') {
@(list($provider, $block) = explode('/', $data_block, 2));
// Get the data
$repos = forena_repository($provider);
if (isset($repos['data'])) {
$provider = $repos['data'];
if (method_exists($provider, 'load_block')) {
$block_info = $provider
->load_block($block, $clause);
}
return $block_info;
}
}
/**
* Load the formatters for all initialized repositories.
*
*/
function get_formatter($fkey) {
// Get all repositories
$repos = $this
->repository();
$formats = array();
foreach ($repos as $k => $r) {
$provider = isset($r['data']) ? $r['data'] : NULL;
if ($provider && method_exists($provider, 'formats')) {
$f = $provider
->formats();
if (isset($f[$fkey]) && method_exists($provider, $fkey)) {
// We found an object with the advertised method return it
return $provider;
}
}
}
//Did not find the formater in the data provider
//Look to see if it's in a control class
$controls = $this
->define_controls();
foreach ($controls as $k => $r) {
$provider = $r;
if ($provider && method_exists($provider, 'formats')) {
$f = $provider
->formats();
if (isset($f[$fkey]) && method_exists($provider, $fkey)) {
// We found an object with the advertised method return it
return $provider;
}
}
}
return $formats;
}
/**
* Accepts the name of a file
*
* Returns a xml object of the file.
*
*/
function get_report($report_name, $data = array()) {
$r = null;
$this
->alter_parameters($report_name, $data);
if ($report_name) {
$r_text = $this->app
->load_report($report_name);
if ($r_text) {
$r = new FrxReport($r_text, $data);
}
}
return $r;
}
function alter_parameters($report_name, &$data) {
if (method_exists($this->app, 'alter_parameters')) {
$this->app
->alter_parameters($report_name, $data);
}
}
/**
* Enter description here...
*
* @param simplexml $xml
* @param string $tag
* @return string
*/
function inner_xml($xml, $tag) {
if (is_object($xml) && is_object($xml->{$tag})) {
$xml_data = $xml->{$tag}
->asXML();
$xml_data = preg_replace("/<\\/?" . $tag . "(.|\\s)*?>/", "", $xml_data);
}
return $xml_data;
}
/**
* Accepts the name of the html tag, and the string the tag is in.
*
* Returns the string within the html tag name
*
*/
function get_html($tag, $r_text) {
$open = strpos($r_text, $tag);
$close = strpos($r_text, '>', $open);
$next = strpos($r_text, '<', $close + 1);
$str = substr($r_text, $close + 1, $next - ($close + 1));
return $str;
}
function format_data($value, $format, $format_str, $teng = '') {
$fo = $this
->get_formatter($format);
if ($fo) {
$ret = $fo
->{$format}($value, $format_str, $teng);
}
else {
$ret = $value;
}
return $ret;
}
/**
* Returns an array of information about the data block
* @param $data_block
* @return unknown_type
*/
function load_block($data_block, $clause = '') {
list($provider, $block) = explode('/', $data_block, 2);
// Get the data
$repos = $this
->repository($provider);
if ($repos['data']) {
$provider = $repos['data'];
if (method_exists($provider, 'load_block')) {
$block_info = $provider
->load_block($block, $clause);
}
return $block_info;
}
}
/**
* Load a block file form disk and introspect the comments to determine security
* Return a structured array based on this.
*
* @param unknown_type $filepath
* @param unknown_type $comment
*/
function load_block_file($filepath) {
if (!file_exists($filepath)) {
return '';
}
$block_data = file_get_contents($filepath);
return $block_data;
}
/**
* Loads all of the include files that
*/
function define_plugins($class = '') {
$plugins = $this->app
->plugins();
foreach ($plugins as $p) {
if ($class == '' || $class == $p['class']) {
if ($p['file']) {
include_once trim($p['file'], '/');
}
}
}
}
/**
* returns a list of instances of the control classes
* if there wasn't a class, returns an empty string.
*/
function define_controls($class = '') {
static $instances = '';
if (!$instances) {
$classes = $this->app
->controls();
foreach ($classes as $c) {
if ($class == '' || $class == $c['class']) {
if ($c['file']) {
include_once $c['file'];
if (class_exists($c['class'])) {
$instances[$c['class']] = new $c['class']();
}
}
}
}
}
if ($class) {
return @$instances[$class];
}
return $instances;
}
/**
* Gets the correct format function
* to render the document and returns an
* object of that class.
*
* If it fails it returns a 0;
*/
function get_doctypes($fkey) {
$controls = $this
->define_controls();
foreach ($controls as $k => $r) {
$provider = $r;
if ($provider && method_exists($provider, 'doc_types')) {
$f = $provider
->doc_types();
if (isset($f[$fkey]) && method_exists($provider, $f[$fkey])) {
// We found an object with the advertised method return it
return $provider;
}
}
}
return 0;
}
/**
*
* @return returns an array of supported document format extensions
*
*/
function supported_doctypes() {
$controls = $this
->define_controls();
$supported_doctypes = array();
if ($controls) {
foreach ($controls as $k => $r) {
$provider = $r;
if ($provider && method_exists($provider, 'doc_types')) {
$f = $provider
->doc_types();
$supported_doctypes = array_merge($supported_doctypes, $f);
}
}
}
$temp = array_keys($supported_doctypes);
$temp[] = 'web';
if ($supported_doctypes) {
$supported_doctypes = array_combine($temp, $temp);
}
return $supported_doctypes;
}
/**
* Returns an object of the template class
* that has a method named templates.
*
* If it fails it returns a 0;
*/
function get_templates($fkey) {
$templates = $this
->supported_templates();
if (class_exists($fkey)) {
return new $fkey();
}
}
/**
*
* @return returns an array of supported templates
*
*/
function supported_templates() {
require_once 'templates/FrxTemplate.inc';
require_once 'templates/FrxTable.inc';
require_once 'templates/FrxFieldTable.inc';
require_once 'templates/FrxEmailMerge.inc';
require_once 'templates/FrxGraphTemplate.inc';
require_once 'templates/FrxRptInclude.inc';
$templates = array(
'FrxTable' => 'Table',
'FrxTemplate' => 'Document',
'FrxEmailMerge' => 'Email Merge',
'FrxFieldTable' => 'Fields',
'FrxGraphTemplate' => 'Graph or Chart',
);
return $templates;
}
function supported_formats() {
$controls = $this
->define_controls();
$supported_formats = array();
$f = array();
foreach ($controls as $k => $r) {
$provider = $r;
if ($provider && method_exists($provider, 'formats')) {
$f = $provider
->formats();
$supported_formats = array_merge($supported_formats, $f);
}
}
return $supported_formats;
}
/**
*
* @param unknown_type $format: The extension of the document to be rendered
* @param unknown_type $output: The string of the page to be displayed
* @return: The document in the requested format. Returns a string if not
* able to format.
*/
function generate_doc($format, $output, $options = array(), $print = TRUE) {
watchdog('debug', 'format' . $format);
$doc = $this
->get_doctypes($format);
if ($doc) {
$all_methods = $doc
->doc_types();
$method = $all_methods[$format];
$ret = $doc
->{$method}($output, $options);
}
if ($ret && $print) {
// If an object was found, set the appropriate mime type (header doctype).
// $flen = strlen($ret);
switch ($format) {
case 'doc':
header('Content-Type: application/msword');
header('Cache-Control:');
header('Pragma:');
header("Cache-Control: must-revalidate");
break;
case 'pdf':
header('Content-Type: application/pdf');
header('Cache-Control:');
header('Pragma:');
header("Cache-Control: must-revalidate");
header("Content-Disposition: attachment; filename=report.pdf");
break;
case 'xls':
header('Content-Type: application/msexcel');
header('Cache-Control:');
header('Pragma:');
header("Cache-Control: must-revalidate");
header("Content-Disposition: attachment; filename=report.xls");
break;
case 'csv':
header('Content-Type: application/csv');
header('Cache-Control:');
header('Pragma:');
header("Cache-Control: must-revalidate");
header("Content-Disposition: attachment; filename=report.csv");
break;
case 'svg':
header('Content-Type: image/svg+xml');
header('Cache-Control:');
header('Pragma:');
header('Cache-Control: must-revalidate');
break;
}
}
return $ret;
}
/**
* Load and render a report based on a drupal path.
* In this function the arglist is used to get the full path to the report.
*
* @return unknown
*/
function report($name_in, $parms = array(), $print = TRUE) {
$output = '';
$desc = $this
->report_desc($name_in);
$name = $desc['name'];
$format = isset($desc['format']) ? $desc['format'] : '';
$filename = $desc['filename'];
$css_files = array();
$js_files = array();
// Determine the data to get.
if (!$parms) {
$parms = array_merge($_GET, $_POST);
unset($parms['q']);
}
else {
$parms = (array) $parms;
}
if ($name) {
$r = $this
->get_report($name, $parms);
if (!$r || !$r->rpt_xml) {
$this->app
->error('Could not load report. Check for invalid XML.');
return '';
}
//check for default parameters
$r_params = $r->parameters;
$reload_params = FALSE;
$missing_parms = FALSE;
//Set default Parameters
if ($r_params) {
//put default parameters in parms array
foreach ($r_params as $key => $parm) {
if ((@$parms[$key] == '' || @$parms[$key] == array()) && @$parm['value']) {
$value = (string) $parm['value'];
if (strpos($value, '|') !== FALSE) {
$value = explode('|', $value);
}
$parms[$key] = $value;
$reload_params = TRUE;
}
//do not show report if a required parameter does not have a value
//force the user to input a parameter
if (@(!$parms[$key]) && @strcmp($parm['require'], "1") == 0 && !$parm['value']) {
$missing_parms = TRUE;
}
}
}
//Reload report if parameters were missing
if ($reload_params) {
$r = $this
->get_report($name, $parms);
}
$form = isset($r->options['form']) ? $r->options['form'] : '';
$rpt_xml = $r->rpt_xml;
// Default the form
if (!$form) {
$form = $this->app
->configuration('default_form');
}
$this->form = $form;
if (!$missing_parms) {
$output .= $r
->render($format);
$css_files = $this
->report_css($desc, $form, $format);
$js_files = $this
->report_js($desc, $form, $format);
}
//put title on top of report
$title = (string) $r->title;
$this->title = $title;
if ($format && $format != 'web') {
//a format was requested
$header = '<h1>' . $title . '</h1>';
$output = $header . $output;
$css_text = '';
$r_text = '';
if ($css_files) {
foreach ($css_files as $css_file) {
$css_text .= file_get_contents($css_file);
}
}
$options = array(
'css' => $css_text,
'docname' => str_replace(' ', '_', $title),
'xml' => $r_text,
'title' => $title,
);
$output = $this
->generate_doc($format, $output, $options, $print);
if ($format != 'email') {
print $output;
}
else {
return $output;
}
exit;
}
else {
//Creating links for downloadable documents.
//build querystring for document href
$q = '';
foreach ($parms as $key => $value) {
$q .= "&" . $key . '=' . $value;
}
$q = trim($q, '&');
//Building the document links
//@TODO: Move this to better location
$rpt_xml = $r->rpt_xml;
$nodes = $rpt_xml
->xpath('//frx:docgen/frx:doc');
$div = '<div class="doclinks">';
$default_doctypes = $this->app
->configuration('doc_defaults', array());
if (!$missing_parms) {
if (!$nodes) {
//show the default.
if ($default_doctypes) {
foreach ($default_doctypes as $value) {
if (is_object($this
->get_doctypes($value))) {
$div .= $this
->link(strtoupper($value) . ' ', 'report_doc/' . $name_in . '.' . $value, array(
'query' => $parms,
'class' => 'doclinks',
));
}
}
}
}
else {
//There were nodes. show the prefered doc types
$doctypes = $this
->supported_doctypes();
foreach ($nodes as $value) {
$arr = $value
->attributes();
$type = (string) $arr['type'];
if (@$doctypes[$type]) {
if (is_object($this
->get_doctypes($type))) {
$div .= $this
->link(strtoupper($type) . ' ', 'report_doc/' . $name_in . '.' . $type, array(
'query' => $parms,
'class' => 'doclinks',
));
}
}
}
}
}
$div .= '</div>';
$output = $div . $this->app
->theme($r, $title, $format);
$this->app
->add_css($this->app
->forena_path() . '/forena.css');
if ($css_files) {
foreach ($css_files as $css_file) {
$this->app
->add_css($css_file);
}
}
if ($js_files) {
foreach ($js_files as $js_file) {
$this->app
->add_js($js_file);
}
}
return $output;
}
}
else {
$this->app
->not_found();
}
}
/*
* Recieves a datablock and returns an array of values from the data block.
* @data_block: name of the data block to be invoked for values
* @field: Specific field name within the data block. The values returned will only
* come from this field.
* @params: filtering for the data block
* @where_clause: Where clause for data block to be filtered against.
*
*/
function data_block_params($data_block, $field, $label, $params = array(), $clause = '') {
FrxData::instance()
->push($params);
$xml = $this
->invoke_data_provider($data_block, $params, $clause);
FrxData::instance()
->pop();
$list = array();
if ($xml) {
$path = $field ? $field : '*[1]';
$label_path = $label ? $label : '*[2]';
//walk through the $xml.
$rows = $xml
->xpath('*');
if ($rows) {
foreach ($rows as $row) {
$value = $row
->xpath($path);
$label_field = $row
->xpath($label_path);
$label_value = $label_field ? (string) $label_field[0] : (string) $value[0];
$list[(string) $value[0]] = (string) $label_value;
}
}
}
return $list;
}
public function debug($short_message = '', $log = '') {
$this->app
->debug($short_message, $log);
}
public function error($short_message = '', $log = '') {
$this->app
->error($short_message, $log);
}
public function configuration($name) {
return $this->app
->configuration($name);
}
public function report_path() {
return $this->app
->configuration('report_repos');
}
public function link($title, $path, $options = array()) {
return $this->app
->link($title, $path, $options);
}
public function url($path, $options = array()) {
return $this->app
->url($path, $options);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
FrxReportGenerator:: |
public | property | ||
FrxReportGenerator:: |
public | property | ||
FrxReportGenerator:: |
static | property | ||
FrxReportGenerator:: |
private | property | ||
FrxReportGenerator:: |
public | property | ||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | Check access control using the block in a data block. In this case public assess returns true. | ||
FrxReportGenerator:: |
public | function | Accessor function named more conveniently Enter description here ... | |
FrxReportGenerator:: |
public | function | Clear session data because we want to reload. | |
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
function | returns a list of instances of the control classes if there wasn't a class, returns an empty string. | ||
FrxReportGenerator:: |
function | Loads all of the include files that | ||
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
function | Returns an array of information about the data block | ||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | @return: The document in the requested format. Returns a string if not able to format. | ||
FrxReportGenerator:: |
function | Gets the correct format function to render the document and returns an object of that class. | ||
FrxReportGenerator:: |
function | Load the formatters for all initialized repositories. | ||
FrxReportGenerator:: |
function | Accepts the name of the html tag, and the string the tag is in. | ||
FrxReportGenerator:: |
function | Accepts the name of a file | ||
FrxReportGenerator:: |
function | Returns an object of the template class that has a method named templates. | ||
FrxReportGenerator:: |
function | Enter description here... | ||
FrxReportGenerator:: |
public static | function | ||
FrxReportGenerator:: |
function | Extract the data by running a block | ||
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
function | Returns an array of information about the data block | ||
FrxReportGenerator:: |
function | Load a block file form disk and introspect the comments to determine security Return a structured array based on this. | ||
FrxReportGenerator:: |
function | Load the data provider class based on the class name. | ||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | Load and render a report based on a drupal path. In this function the arglist is used to get the full path to the report. | ||
FrxReportGenerator:: |
function | Determines which css files need to be loaded. | ||
FrxReportGenerator:: |
public | function | Get name from argument 1 or alterntaively from a file name | |
FrxReportGenerator:: |
function | Determines which js files need to be loaded. | ||
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
function | |||
FrxReportGenerator:: |
public | function | ||
FrxReportGenerator:: |
public | function |