type) { case 'key': return 'Keyword'; case 'txt': return 'Text'; case 'sty': return 'Style'; case 'fld': return 'Field'; case 'var': return 'Variable'; case 'str': return 'String'; case 'bra': return 'Brackets'; case 'eof': return 'End'; default: return $token->type; } } public function __construct($content = null) { $this->content = $content; } } class WikipluginDBReportField { public $name; public $variable; public $break; public $index; public function __construct($text) { global $wikiplugin_dbreport_fields, $wikiplugin_dbreport_fields_allowed; $this->name = stripcslashes($text); if ($text[0] == '$') { $this->variable = substr($this->name, 1); // print("new variable $this->variable "); } else { // add to the list of parsed fields // if ($wikiplugin_dbreport_fields_allowed) $wikiplugin_dbreport_fields[] =& $this; } } public function text() { global $wikiplugin_dbreport_record; if (isset($this->index)) { // indexed field return (string) ($wikiplugin_dbreport_record[$this->index]); } elseif (isset($this->variable)) { // PHP variable if (isset($GLOBALS[$this->variable])) { return (string) $GLOBALS[$this->variable]; } elseif (isset($_SESSION[$this->variable])) { return (string) $_SESSION[$this->variable]; } elseif (isset($_REQUEST[$this->variable])) { return (string) $_REQUEST[$this->variable]; } } else { return "[$this->name]"; } } public function code() { if (isset($this->variable)) { return '[$' . addcslashes($this->variable, "\0..\37[]$\\") . ']'; } else { return '[' . addcslashes($this->name, "\0..\37[]$\\") . ']'; } } public function html() { return htmlentities($this->text()); } public function uri() { return urlencode($this->text()); } } class WikipluginDBReportString { public $literal; public function __construct($text) { $this->literal = stripcslashes($text); } public function text() { return $this->literal; } public function code() { return addcslashes($this->literal, "\0..\37[]\\"); } public function html() { return htmlentities($this->text()); } public function uri() { return $this->text(); } } class WikipluginDBReportContent { public $elements; public function parse_text(&$text) { $parse_state = 0; $parse_text = ''; $pos = 0; $len = strlen($text); while ($pos < $len) { $char = $text[$pos++]; switch ($parse_state) { case 0: // start of next token switch ($char) { case '[': $parse_state = 3; break; case '\\': $parse_state = 2; $parse_text .= $char; break; default: $parse_state = 1; $parse_text .= $char; } break; case 1: // text string switch ($char) { case '[': unset($this->elements); $this->elements[] = new WikipluginDBReportString($parse_text); $parse_text = ''; $parse_state = 3; break; case '\\': $parse_state = 2; $parse_text .= $char; break; default: $parse_text .= $char; } break; case 2: // literal escape $parse_text .= $char; $parse_state = 1; break; case 3: // field text switch ($char) { case '[': break; case ']': unset($this->elements); $this->elements[] = new WikipluginDBReportField($parse_text); $parse_text = ''; $parse_state = 0; break; case '\\': $parse_state = 4; $parse_text .= $char; break; default: $parse_text .= $char; } break; case 4: // field escape $parse_text .= $char; $parse_state = 3; break; } } // hanging text is parsed as a string if ($parse_state != 0) { unset($this->elements); $this->elements[] = new WikipluginDBReportString($parse_text); } } public function append_field($name) { unset($this->elements); $this->elements[] = new WikipluginDBReportField($name); } public function append_variable($name) { $this->elements[] = new WikipluginDBReportField('$' . $name); } public function append_string($text) { $this->elements[] = new WikipluginDBReportString($text); } public function append($text) { $this->parse_text($text); } public function __construct(&$token) { switch ($token->type) { case 'txt': $this->parse_text($token->content); break; case 'fld': $this->append_field($token->content); break; case 'var': $this->append_variable($token->content); break; } } public function text() { $result = ''; if (isset($this->elements)) { foreach ($this->elements as $element) { $result .= $element->text(); } } return $result; } public function code() { $result = ''; if (isset($this->elements)) { foreach ($this->elements as $element) { $result .= $element->code(); } } return $result; } public function html() { $result = ''; if (isset($this->elements)) { foreach ($this->elements as $element) { $result .= $element->html(); } } return $result; } public function uri() { $result = ''; if (isset($this->elements)) { foreach ($this->elements as $element) { $result .= $element->uri(); } } return $result; } } class WikipluginDBReportText extends WikipluginDBReportContent { public $link; public $style; public function code() { $result = '"' . addcslashes(parent::code(), '"') . '"'; if (isset($this->style)) { $result .= $this->style->code(); } if (isset($this->link)) { $result .= ' ' . $this->link->code(); } return $result; } public function html() { $html = parent::html(); if (isset($this->style)) { $html = $this->style->html_start() . $html . $this->style->html_end(); } if (isset($this->link)) { $html = $this->link->html_start() . $html . $this->link->html_end(); } return $html; } } class WikipluginDBReportStyle { public $tag; public $class; public $style; public function __construct(&$token) { if (is_object($token)) { if ($token->content['class']) { $subtoken =& $token->content['class']; unset($this->class); $this->class = new WikipluginDBReportContent($subtoken); } if ($token->content['style']) { $subtoken =& $token->content['style']; unset($this->style); $this->style = new WikipluginDBReportContent($subtoken); } } elseif (is_string($token)) { unset($subtoken); $subtoken = new WikipluginDBReportToken($token); $subtoken->type = 'txt'; unset($this->class); $this->class = new WikipluginDBReportContent($subtoken); } } public function code() { $code = ':'; if (isset($this->class)) { $code .= addcslashes($this->class->code(), ' '); } elseif ($this->tag != 'span') { $code .= $this->tag; } // if (isset($this->class)) $code .= $this->class->code(); if (isset($this->style)) { $code .= "{" . $this->style->code() . "}"; } return $code; } public function attributes() { if (isset($this->class)) { $html .= ' class="' . $this->class->html() . '"'; } if (isset($this->style)) { $html .= ' style="' . $this->style->html() . '"'; } return $html; } public function html_start() { if (isset($this->class)) { $class = $this->class->html(); switch (strtolower($class)) { case 'u': case 'b': case 'i': case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': $this->tag = $class; $html = '<' . $class; break; default: $this->tag = 'span'; $html = 'tag = 'span'; $html = 'style)) { $html .= ' style="' . $this->style->html() . '"'; } $html .= '>'; return $html; } public function html_end() { return '' . $this->tag . '>'; } } class WikipluginDBReportLink { public $style; public $contents; public function code() { $result = '<'; if (isset($this->contents)) { foreach ($this->contents as $content) { if (is_a($content, 'WikipluginDBReportContent')) { $result .= '"' . $content->code() . '"'; } elseif (is_a($content, 'WikipluginDBReportField')) { $result .= $content->code(); } } } if (isset($this->style)) { $result .= $this->style->code(); } $result .= '>'; return $result; } public function uri() { if (isset($this->contents)) { foreach ($this->contents as $content) { if (is_a($content, 'WikipluginDBReportContent')) { $uri .= $content->html(); } elseif (is_a($content, 'WikipluginDBReportField')) { $uri .= $content->uri(); } } } return $uri; } public function html_start() { $html = 'style) { $html .= $this->style->attributes(); } $html .= '>'; return $html; } public function html_end() { return ''; } public function html_onclick() { return 'onclick="document.location.href="' . $this->uri() . '""'; } } class WikipluginDBReportCell { public $link; public $style; public $colspan; public $rowspan; public $contents; public function code($mode) { $result = 'CELL'; if (isset($this->colspan) && isset($this->rowspan)) { if ($this->rowspan != 1) { $result .= ' ROWSPAN ' . $this->rowspan; } if ($this->colspan != 1) { $result .= ' COLSPAN ' . $this->colspan; } } elseif (isset($this->colspan)) { if ($this->colspan != 1) { if ($mode == 'ROW') { $result .= ' SPAN ' . $this->colspan; } else { $result .= ' COLSPAN ' . $this->colspan; } } } elseif (isset($this->rowspan)) { if ($this->rowspan != 1) { if ($mode == 'ROW') { $result .= ' ROWSPAN ' . $this->rowspan; } else { $result .= ' SPAN ' . $this->rowspan; } } } if (isset($this->style)) { $result .= ' ' . $this->style->code(); } if (isset($this->link)) { $result .= ' ' . $this->link->code(); } if (isset($this->contents)) { foreach ($this->contents as $content) { $result .= ' ' . $content->code(); } } return $result; } public function html($heading = false) { if ($heading) { $html = '
| ';
switch (gettype($error)) {
case 'array':
foreach ($error as $entry) {
$return .= $entry . ' '; } break; case 'string': case 'object': $return .= (string) $error; break; default: $return .= gettype($error) . ' ERROR!'; } $return .= ' |
| ';
switch (gettype($error)) {
case 'array':
foreach ($msg as $entry) {
$return .= $entry . ' '; } break; default: $return .= (string) $msg; } $return .= ' |
mysql://user:pass@server/database',
'since' => '3.0',
'default' => '',
'filter' => 'url',
],
'db' => [
'required' => false,
'name' => tra('Wiki DSN Name'),
'description' => tra('The name of a DSN connection defined by the Wiki administrator.'),
'since' => '3.0',
'default' => '',
'filter' => 'text',
],
'wiki' => [
'required' => false,
'name' => tra('Wiki Syntax'),
'description' => tra('Parse wiki syntax within the report (not parsed by default)'),
'since' => '3.0',
'default' => '',
'filter' => 'digits',
'options' => [
['text' => '', 'value' => ''],
['text' => tra('Yes'), 'value' => 1],
['text' => tra('No'), 'value' => 0]
],
],
'debug' => [
'required' => false,
'name' => tra('Debug'),
'description' => tra('Display the parsed report definition (not displayed by default)'),
'since' => '3.0',
'default' => '',
'filter' => 'digits',
'options' => [
['text' => '', 'value' => ''],
['text' => tra('Yes'), 'value' => 1],
['text' => tra('No'), 'value' => 0]
],
],
'audit' => [
'required' => false,
'name' => tra('Audit'),
'description' => tr('Create a log entry containing information about the SQL call.'),
'since' => '21.2',
'default' => '0',
],
'audit_csv' => [
'required' => false,
'name' => tra('Audit CSV path'),
'description' => tr('If set, a CSV file will be created or appended with information about the SQL call performed.'),
'since' => '21.2',
'filter' => 'text',
'default' => '',
],
],
];
}
function wikiplugin_dbreport($data, $params)
{
// TikiWiki globals
global $tikilib, $user, $group, $page, $prefs;
global $wikiplugin_dbreport_errors, $wikiplugin_dbreport_fields;
// wikiplugin_dbreport globals
global $wikiplugin_dbreport_errors;
global $wikiplugin_dbreport_fields;
global $wikiplugin_dbreport_fields_allowed;
global $wikiplugin_dbreport_record;
// initialize globals
$wikiplugin_dbreport_errors = [];
$wikiplugin_dbreport_fields = [];
$wikiplugin_dbreport_fields_allowed = false;
$wikiplugin_dbreport_record = null;
// extract parameters
extract($params, EXTR_SKIP);
// we need a dsn or db parameter
if (! isset($dsn) && ! isset($db)) {
return tra('Missing db or dsn parameter');
}
// parse the report definition
$parse_fix = ($_REQUEST['preview']) && ($prefs['tiki_release'] == '2.2');
if ($parse_fix) {
$report =& wikiplugin_dbreport_parse($data);
} else {
$report =& wikiplugin_dbreport_parse(html_entity_decode($data));
}
// were there errors?
if ($wikiplugin_dbreport_errors) {
$ret .= wikiplugin_dbreport_error_box($wikiplugin_dbreport_errors);
return $ret;
}
// create the bind variables array
$bindvars = [];
if (isset($report->params)) {
foreach ($report->params as $param) {
if (isset($param->name)) {
$bindvars[$param->name] = $param->text();
} else {
$bindvars[] = $param->text();
}
}
}
// translate db name into dsn
if (isset($db)) {
$perms = Perms::get([ 'type' => 'dsn', 'object' => $db ]);
if (! $perms->dsn_query) {
return tra('You do not have the permission that is needed to use this feature');
}
// retrieve the dsn string
$dsn = $tikilib->get_dsn_by_name($db);
}
// open the database
if (isset($dsn)) {
// Force autoloading
if (! class_exists('ADOConnection')) {
return tr('AdoDb not found');
}
$ado = ADONewConnection($dsn);
if (! $ado) {
$ret .= wikiplugin_dbreport_error_box($ado->ErrorMsg());
return $ret;
} else {
// execute sql query
$ado->SetFetchMode(ADODB_FETCH_BOTH);
$query =& $ado->Execute($report->sql, $bindvars);
$field_count = $query->FieldCount();
$fetchfield = 'FetchField';
if (! $query) {
$ret .= wikiplugin_dbreport_error_box($ado->ErrorMsg());
return $ret;
}
}
} else {
return (tra('No DSN connection string found!'));
}
// create an array of field names and their index
$field_index = [];
// $field_count = $query->FieldCount();
for ($index = 0; $index < $field_count; $index++) {
$column =& $query->$fetchfield($index);
// some PDO connections (eg. oci) won't be able to return meta info on the column
if ($column->name == 'bad getColumnMeta()' && $ado->fetchMode === ADODB_FETCH_BOTH) {
$internal_field_keys = array_keys($query->fields);
$column->name = $internal_field_keys[$index * 2];
}
$field_index[$column->name] = $index;
}
// go through the parsed fields and assign indexes
foreach ($wikiplugin_dbreport_fields as $key => $value) {
$parse_field =& $wikiplugin_dbreport_fields[$key];
$index = $field_index[$parse_field->name];
if (isset($index)) {
$parse_field->index = $index;
} else {
// not a valid field. log the message.
$ret .= wikiplugin_dbreport_error_box("The Field '$parse_field->name' was not returned by the SQL query.");
return $ret;
}
}
// does the report have a table definition?
if (! isset($report->table)) {
// create a default definition from the data
$report->table = new WikipluginDBReportTable();
$style = 'sortable';
$report->table->style = new WikipluginDBReportStyle($style);
$header = new WikipluginDBReportLine();
$style = 'heading';
$header->styles[] = new WikipluginDBReportStyle($style);
$report->table->headers[] =& $header;
$row = new WikipluginDBReportLine();
$style = 'even';
$row->styles[] = new WikipluginDBReportStyle($style);
$style = 'odd';
$row->styles[] = new WikipluginDBReportStyle($style);
$report->table->rows[] =& $row;
// fill in the cells
$field_count = $query->FieldCount();
for ($index = 0; $index < $field_count; $index++) {
// get the query field
$column =& $query->FetchField($index);
// create the header cell
unset($text);
$text = new WikipluginDBReportText(new WikipluginDBReportToken());
$text->append_string($column->name);
unset($cell);
$cell = new WikipluginDBReportCell();
// $style = 'heading';
// $cell->style = new WikipluginDBReportStyle($style);
$cell->contents[] =& $text;
$header->cells[] =& $cell;
// create the rows cell
unset($cell);
$cell = new WikipluginDBReportCell();
unset($field);
$field = new WikipluginDBReportField($column->name);
$field->index = $index;
$cell->contents[] =& $field;
$row->cells[] =& $cell;
}
}
// are we debugging?
if ($debug) {
$ret .= wikiplugin_dbreport_message_box("~np~" . htmlspecialchars($report->code()) . "~/np~"); } // generate the report if (! $wiki) { $ret .= '~np~'; } if (! $query->EOF) { // get the first row $current_row = $query->FetchRow(); // start the group breaks if (isset($report->groups)) { foreach ($report->groups as $group) { $group->check_break($current_row); $ret .= $group->start_html($current_row); } } // first row is always considered 'after a break' $breaking = true; // go through the rows while ($current_row) { // do we generate a table header? if ($breaking) { $ret .= $report->table->header_row_html($current_row); } // write the table row $ret .= $report->table->record_row_html($current_row); // get the next row if ($query->EOF) { unset($next_row); $breaking = true; } else { $next_row = $query->FetchRow(); $breaking = false; } // check group breaks if (isset($report->groups)) { $break_end = ''; $break_start = ''; foreach ($report->groups as $group) { if (isset($next_row)) { $breaking = ($group->check_break($next_row) || $breaking); } if ($breaking) { $break_end = $group->end_html($current_row) . $break_end; if (isset($next_row)) { $break_start .= $group->start_html($next_row); } } } } if ($breaking) { $ret .= $report->table->footer_row_html($current_row); $ret .= $break_end; $ret .= $break_start; } // move to the next row $current_row = $next_row; } } else { // no records returned. output the fail message if ($report->fail) { $ret .= $report->fail->html(); } } if (! $wiki) { $ret .= '~/np~'; } // close the database connection $query->Close(); $ado->Close(); if (! empty($params['audit'])) { TikiLib::lib('logs')->add_log('wikiplugin_dbreport', "Page - " . $_GET['page'] . "\nParameters - " . print_r($bindvars, true)); } if (! empty($params['audit_csv'])) { $headers = ['date', 'user', 'page', 'vars']; $contentRow[] = [ $tikilib->date_format($prefs['short_date_format'] . ' ' . $prefs['long_time_format'], $tikilib->now), $user, isset($_GET['page']) ? $_GET['page'] : '', $bindvars ]; if (! FileHelper::appendCSV($params['audit_csv'], $headers, $contentRow)) { Feedback::error(tr('Unable to create or open the file "%0" to log the SQL operation,', $params['audit_csv'])); } } // return the result return $ret; }