From 8e3910b16f34e4520c06fe602bcdce3f7825014b Mon Sep 17 00:00:00 2001 From: finanalyst Date: Sun, 24 Jun 2018 15:12:38 +0800 Subject: [PATCH] new logging functionality and DataManager compatibility --- CHANGELOG.md | 10 ++ README.md | 26 +++++- blueprints.yaml | 71 +++++++++++++- languages.yaml | 14 ++- shortcodes/SqlTableShortcode.php | 154 ++++++++++++++++--------------- sqlite.php | 73 ++++++++------- sqlite.yaml | 6 +- 7 files changed, 238 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f315889..dc33ae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v1.4.0 +## 23 June 2018 +1. [](#enhancement) + * More logging options, allowing for the SELECT, INSERT and UPDATE stanzas (in addition to Errors) + to be trapped. + * Append the logged data to the directory `user/data/sqlite` as a 'log.txt' file + * This allows an Administrator to view the data from within the Admin panel using the DataManager plugin. +2. [](#change of configuration) + * The default configuration for the placement of the sqlite3 database is now `user/data/sqlite` + # v1.3.0 ## 11 June 2018 1. [](#enhancement) diff --git a/README.md b/README.md index cb64ed2..9286507 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ You should now have all the plugin files under and [Problems](https://github.com/getgrav/grav-plugin-problems) to operate. The plugin also requires that the **SQLite3** extension is available with the version of php operating on your site. +### Database Installation +An adminstrator must create a directory for the database and place within it the *sqlite3* database file. It is recommended that the directory is `user/data/sqlite` (see configuration). ## Configuration @@ -36,18 +38,32 @@ Here is the default configuration and an explanation of available options: ```yaml enabled: true -database_route: data +database_route: data/sqlite database_name: db.sqlite3 -error_logging: false +logging: false +all_logging: false # this option only becomes active when logging is True +error_logging: false # this option only becomes active is False +select_logging: false +insert_logging: false +update_logging: false ``` - `enabled` turns on the plugin for the whole site. If `false`, then making it active on a page will have no effect. - `database_route` is the Grav route (relative to the 'user' subdirectory) to the location of the `SQLite3` database. - `database_name` is the full name (typically with the extension .sqlite3) of the database file. It is the responsibility of the site developer/maintainer to create the database. -- `error_logging` when false, nothing extra happens. When `true`, the SQL errors are logged to the file `sqlite_errors.txt` in the directory given by `database_route`. If however there is an error in setting `database_route`, -then the directory is `user/data` +- `logging` when false, nothing extra happens. When `true`, SQL related data is logged to a file called `sqlite.txt` in the directory given by `database_route`. If however there is an error in setting `database_route`, +then the directory is `user/data/sqlite`. + +>SUGGESTION: If the DataManager plugin is installed and the default route is retained, then the SQL logs can be viewed +from the Admin panel. + +- `all_logging` only become active when `logging` is enabled. If true, then all stanzas and errors are recorded. +- `error_logging` only becomes active when `logging` is enabled and `all_logging` is not enabled. +- `select_logging` only becomes active when `logging` is enabled and `all_logging` is not enabled. +- `insert_logging` only becomes active when `logging` is enabled and `all_logging` is not enabled. +- `update_logging` only becomes active when `logging` is enabled and `all_logging` is not enabled. >NOTE: The database must exist. If it does not, then an error is generated. -`error_logging` should not be used in production settings as it writes to the hard drive, slowing performance. +`logging` should not be used in production settings as it writes to the hard drive, slowing performance. ### Per page configuration Shortcodes can be enabled separately using the `shortcode-core` configuration. To disable shortcodes being used on all pages, but only used on selected pages, configure the shortcode-core plugin inside the Admin panel with `enabled=true` and `active=false`. Then on each page where shortcodes are used, include in the front section of the page: diff --git a/blueprints.yaml b/blueprints.yaml index 42aa78f..0aadb8a 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Sqlite -version: '1.3.0' +version: '1.4.0' description: Plugin to select, update and insert into an sqlite3 database icon: database author: @@ -26,7 +26,7 @@ form: default: data label: PLUGIN_SQLITE.DATABASE_ROUTE help: PLUGIN_SQLITE.DATABASE_ROUTE_HELP - error_logging: + logging: type: toggle highlight: 0 default: 0 @@ -35,5 +35,68 @@ form: 0: Disabled validate: type: bool - label: PLUGIN_SQLITE.ERROR_LOGGING - help: PLUGIN_SQLITE.ERROR_LOGGING_HELP + label: PLUGIN_SQLITE.LOGGING + help: PLUGIN_SQLITE.LOGGING_HELP + log_check: + type: conditional + condition: config.plugins.sqlite.logging + fields: + all_logging: + type: toggle + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + label: PLUGIN_SQLITE.ALL_LOGGING + help: PLUGIN_SQLITE.ALL_LOGGING_HELP + discrete_logging: + type: conditional + condition: "config.plugins.sqlite.logging and not config.plugins.sqlite.all_logging " + fields: + error_logging: + type: toggle + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + label: PLUGIN_SQLITE.ERROR_LOGGING + help: PLUGIN_SQLITE.ERROR_LOGGING_HELP + select_logging: + type: toggle + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + label: PLUGIN_SQLITE.SELECT_LOGGING + help: PLUGIN_SQLITE.SELECT_LOGGING_HELP + insert_logging: + type: toggle + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + label: PLUGIN_SQLITE.INSERT_LOGGING + help: PLUGIN_SQLITE.INSERT_LOGGING_HELP + update_logging: + type: toggle + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + label: PLUGIN_SQLITE.UPDATE_LOGGING + help: PLUGIN_SQLITE.UPDATE_LOGGING_HELP diff --git a/languages.yaml b/languages.yaml index a6f2bdd..e7908e5 100644 --- a/languages.yaml +++ b/languages.yaml @@ -16,5 +16,15 @@ en: SQL_ERROR_3: generates the error. UPDATE_WHERE: A where expression is mandatory (either as a parameter or as a form field) when using an "sql-update" Form action. UPDATE_ERROR: The following UPDATE error was generated:
%s - ERROR_LOGGING: Turn on Logging. (Not for production!!) - ERROR_LOGGING_HELP: Log file is saved when an error is detected in DATABASE_ROUTE, or /user/data if former fails. + LOGGING: Turn on Logging. (Not for production!!) + LOGGING_HELP: Log file is appended when an error is detected in DATABASE_ROUTE, or /user/data if former fails. + ALL_LOGGING: Turn on All Logging. + ALL_LOGGING_HELP: Log file is appended when an error is detected in DATABASE_ROUTE, or /user/data if former fails. + ERROR_LOGGING: Turn on Error Logging. + ERROR_LOGGING_HELP: Log file is appended when an error is detected in DATABASE_ROUTE, or /user/data if former fails. + SELECT_LOGGING: Turn on Select Logging. + SELECT_LOGGING_HELP: Log file is appended when a SELECT stanza is sent to database. + INSERT_LOGGING: Turn on Insert Logging. + INSERT_LOGGING_HELP: Log file is appended when an INSERT stanza is sent to database. + UPDATE_LOGGING: Turn on Update Logging. + UPDATE_LOGGING_HELP: Log file is appended when an UPDATE stanza is sent to database. diff --git a/shortcodes/SqlTableShortcode.php b/shortcodes/SqlTableShortcode.php index a28c1bd..986364d 100644 --- a/shortcodes/SqlTableShortcode.php +++ b/shortcodes/SqlTableShortcode.php @@ -6,84 +6,92 @@ class SqlTableShortcode extends Shortcode { - public function init() - { - $this->shortcode->getHandlers()->add('sql-table', function(ShortcodeInterface $sc) { - if ( isset($this->grav['sqlite']['error']) && $this->grav['sqlite']['error'] ) { - if ($this->grav['sqlite']['logging']) { - $this->log_error($this->grav['sqlite']['error']); - } - return - $this->twig->processTemplate( - 'partials/sql-db-error.html.twig', - [ 'message' => $this->grav['sqlite']['error'] - ]); - } - // database exists - $s = $sc->getContent(); - // process any twig variables in the SQL stanza - $s = $this->grav['twig']->processString($s); - $stanza = html_entity_decode(preg_replace('/\<\/?p.*?\>|\n/i',' ',$s)); // remove

embedded by markdown - $params = $sc->getParameters(); - $db = $this->grav['sqlite']['db']; - try { - $query = $db->query($stanza); - if ( ! $query ) throw new \Exception('No sql output from ' . $stanza); - $fields = array(); - $cols = $query->numColumns(); - if ( $cols < 1 ) throw new \Exception('No columns from ' . $stanza); - for ( $i = 0; $i < $cols; $i ++) { - array_push($fields, $query->columnName($i)); - } - $rows = array(); - while ( $row = $query->fetchArray(SQLITE3_ASSOC) ) { - array_push($rows,$row); - } - // first check whether json option is present, if so, ignore other options - if ( array_key_exists( 'json', $params) ) { - $output = $this->twig->processTemplate('partials/sql-json.html.twig', - [ - 'rows' => $rows - ]); - } else { - // find if there are hidden columns - $hidden = array(); - if ( isset( $params['hidden'])) { - $hidden = array_fill_keys(preg_split('/\s+/', $params['hidden'] ), 1); + const ERROR = 2; // binary flags for each log type + const SELECT = 4; + public function init() + { + $this->shortcode->getHandlers()->add('sql-table', function(ShortcodeInterface $sc) { + if ( isset($this->grav['sqlite']['error']) && $this->grav['sqlite']['error'] ) { + $this->log($this->grav['sqlite']['error']); + return + $this->twig->processTemplate( + 'partials/sql-db-error.html.twig', + [ 'message' => $this->grav['sqlite']['error'] + ]); } - $output = $this->twig->processTemplate('partials/sql-table.html.twig', - [ - 'fields' => $fields, - 'rows' => $rows, - 'hidden' => $hidden, - 'class' => isset( $params['class']) ? $params['class'] : '', - 'id' => isset($params['id']) ? $param['id'] : '' - ]); - } - return $output; - } catch( \Exception $e) { - if ($this->grav['sqlite']['logging']) { - $this->log_error('message: ' . $e->getMessage() . "\ncontent: $stanza"); - } - return - $this->twig->processTemplate( - 'partials/sql-sql-error.html.twig', - [ - 'message' => $e->getMessage(), - 'content' => $stanza - ]); - } + // database exists + $s = $sc->getContent(); + // process any twig variables in the SQL stanza + $s = $this->grav['twig']->processString($s); + $stanza = html_entity_decode(preg_replace('/\<\/?p.*?\>\s*|\n\s*/i',' ',$s)); // remove

embedded by markdown + $this->log(self::SELECT, $stanza); + $params = $sc->getParameters(); + $db = $this->grav['sqlite']['db']; + try { + $query = $db->query($stanza); + if ( ! $query ) throw new \Exception('No sql output from ' . $stanza); + $fields = array(); + $cols = $query->numColumns(); + if ( $cols < 1 ) throw new \Exception('No columns from ' . $stanza); + for ( $i = 0; $i < $cols; $i ++) { + array_push($fields, $query->columnName($i)); + } + $rows = array(); + while ( $row = $query->fetchArray(SQLITE3_ASSOC) ) { + array_push($rows,$row); + } + // first check whether json option is present, if so, ignore other options + if ( array_key_exists( 'json', $params) ) { + $output = $this->twig->processTemplate('partials/sql-json.html.twig', + [ + 'rows' => $rows + ]); + } else { + // find if there are hidden columns + $hidden = array(); + if ( isset( $params['hidden'])) { + $hidden = array_fill_keys(preg_split('/\s+/', $params['hidden'] ), 1); + } + $output = $this->twig->processTemplate('partials/sql-table.html.twig', + [ + 'fields' => $fields, + 'rows' => $rows, + 'hidden' => $hidden, + 'class' => isset( $params['class']) ? $params['class'] : '', + 'id' => isset($params['id']) ? $param['id'] : '' + ] + ); + } + return $output; + } catch( \Exception $e) { + $this->log(self::ERROR, 'message: ' . $e->getMessage() . "\ncontent: $stanza"); + return + $this->twig->processTemplate( + 'partials/sql-sql-error.html.twig', + [ + 'message' => $e->getMessage(), + 'content' => $stanza + ] + ); + } }); } - public function log_error(String $msg) { - $path = $this->grav['sqlite']['path'] . 'sqlite_errors.txt'; + public function log($type, $msg) { + $log_val =$this->grav['sqlite']['logging']; + if ( $log_val == 0 ) return; + + $path = $this->grav['sqlite']['path'] . DS . 'sqlite.html'; $datafh = File::instance($path); - if ( file_exists($path) ) { - $datafh->save($datafh->content() . "\n" . $msg); - } else { - $datafh->save($msg); - chmod($path, 0666); + if ( ($log_val & self::ERROR) && ($type & self::ERROR) + || ($log_val & self::SELECT) && ($type & self::SELECT) + ) { + if ( file_exists($path) ) { + $datafh->save($datafh->content() . '
' . date('Y-m-d:H:i') . ': ' . $msg); + } else { + $datafh->save('' . date('Y-m-d:H:i') . ': ' . $msg); + chmod($path, 0666); + } } } } diff --git a/sqlite.php b/sqlite.php index 41431e7..2e9c8e0 100644 --- a/sqlite.php +++ b/sqlite.php @@ -6,12 +6,15 @@ use RocketTheme\Toolbox\File\File; use Thunder\Shortcode\Shortcode\ShortcodeInterface; use SQLite3; +use ReflectionProperty; class SqlitePlugin extends Plugin { - protected $handlers; - protected $assets; protected $sqlite; + const ERROR = 2; // binary flags for each log type + const SELECT = 4; + const INSERT = 8; + const UPDATE = 16; public static function getSubscribedEvents() { @@ -29,8 +32,15 @@ public function onPluginsInitialized() $dbname = $this->config->get('plugins.sqlite.database_name'); $path = $this->grav['locator']->findResource("user://$route", true); // path is also used for error logging, so there must be a valid route in case user supplied route fails. - $this->sqlite['path'] = $path?: $this->grav['locator']->findResource("user://data", true); - $this->sqlite['logging'] = $this->config->get('plugins.sqlite.error_logging'); + $this->sqlite['path'] = $path?: $this->grav['locator']->findResource("user://data/sqlite", true); + $this->sqlite['logging'] = $this->config->get('plugins.sqlite.logging') * // is either 0 or 1 + ( $this->config->get('plugins.sqlite.all_logging') ? (self::ERROR+self::SELECT+self::INSERT+self::UPDATE) + : ( $this->config->get('plugins.sqlite.error_logging') * self::ERROR + + $this->config->get('plugins.sqlite.select_logging') * self::SELECT + + $this->config->get('plugins.sqlite.insert_logging') * self::INSERT + + $this->config->get('plugins.sqlite.update_logging') * self::UPDATE + ) + ); $dbloc = $path . DS . $dbname; if ( file_exists($dbloc) ) { $this->sqlite['db'] = new SQLite3($dbloc); @@ -62,15 +72,13 @@ public function onShortcodeHandlers(Event $e) public function onFormProcessed(Event $event) { if ( isset($this->grav['sqlite']['error']) && $this->grav['sqlite']['error'] ) { - if ($this->grav['sqlite']['logging']) { - $this->log_error($this->grav['sqlite']['error']); - } - $this->grav->fireEvent('onFormValidationError', new Event([ + $this->log(self::ERROR,$this->grav['sqlite']['error']); + $this->grav->fireEvent('onFormValidationError', new Event([ 'form' => $event['form'], 'message' => sprintf($this->grav['language']->translate(['PLUGIN_SQLITE.DATABASE_ERROR']), $this->grav['sqlite']['error']) - ])); - $event->stopPropagation(); - return; + ])); + $event->stopPropagation(); + return; } $action = $event['action']; $params = $event['params']; @@ -97,11 +105,12 @@ public function onFormProcessed(Event $event) $set = 'SET '; $nxt = false; foreach ( $data as $field => $value ) { - $set .= ( $nxt ? ', ' : '') ; - $set .= $field . '="' . $value . '"' ; - $nxt = true; + $set .= ( $nxt ? ', ' : '') ; + $set .= $field . '="' . $value . '"' ; + $nxt = true; } $sql ="INSERT INTO {$params['table']} ( $fields ) VALUES ( $values )"; + $this->log(self::INSERT,$sql); $db = $this->grav['sqlite']['db']; try { $db->exec($sql) ; @@ -112,9 +121,7 @@ public function onFormProcessed(Event $event) } else { $msg .= $this->grav['language']->translate(['PLUGIN_SQLITE.OTHER_SQL_ERROR']) . "
$sql"; } - if ($this->grav['sqlite']['logging']) { - $this->log_error($msg); - } + $this->log(self::ERROR,$msg); $this->grav->fireEvent('onFormValidationError', new Event([ 'form' => $event['form'], 'message' => $msg @@ -129,9 +136,7 @@ public function onFormProcessed(Event $event) } if ( ! isset( $params['where'] ) and ! isset($data['where'])) { // where expression is mandatory, so fail if not set - if ($this->grav['sqlite']['logging']) { - $this->log_error($this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_WHERE'])); - } + $this->log(self::ERROR,$this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_WHERE'])); $this->grav->fireEvent('onFormValidationError', new Event([ 'form' => $event['form'], 'message' => $this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_WHERE']) @@ -155,15 +160,13 @@ public function onFormProcessed(Event $event) $set .= $field . '="' . $value . '"' ; $nxt = true; } - $sql ="UPDATE {$params['table']} $set WHERE $where"; + $this->log(self::UPDATE,$sql); $db = $this->grav['sqlite']['db']; try { $db->exec($sql) ; } catch ( \Exception $e ) { - if ($this->grav['sqlite']['logging']) { - $this->log_error(sprintf($this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_ERROR']),$e->getMessage())); - } + $this->log(self::ERROR,sprintf($this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_ERROR']),$e->getMessage())); $this->grav->fireEvent('onFormValidationError', new Event([ 'form' => $event['form'], 'message' => sprintf($this->grav['language']->translate(['PLUGIN_SQLITE.UPDATE_ERROR']),$e->getMessage()) @@ -174,14 +177,22 @@ public function onFormProcessed(Event $event) } } - public function log_error(String $msg) { - $path = $this->grav['sqlite']['path'] . 'sqlite_errors.txt'; + public function log($type, $msg) { + $log_val =$this->grav['sqlite']['logging']; + if ( $log_val == 0 ) return; + + $path = $this->grav['sqlite']['path'] . DS . 'sqlite.html'; $datafh = File::instance($path); - if ( file_exists($path) ) { - $datafh->save($datafh->content() . "\n" . $msg); - } else { - $datafh->save($msg); - chmod($path, 0666); + if ( ($log_val & self::ERROR) && ($type & self::ERROR) + || ($log_val & self::INSERT) && ($type & self::INSERT) + || ($log_val & self::UPDATE) && ($type & self::UPDATE) + ) { + if ( file_exists($path) ) { + $datafh->save($datafh->content() . '
' . date('Y-m-d:H:i') . ': ' . $msg); + } else { + $datafh->save('' . date('Y-m-d:H:i') . ': ' . $msg); + chmod($path, 0666); + } } } } diff --git a/sqlite.yaml b/sqlite.yaml index 385fd0a..e7a1020 100644 --- a/sqlite.yaml +++ b/sqlite.yaml @@ -1,4 +1,8 @@ enabled: true -database_route: data +database_route: data/sqlite database_name: db.sqlite3 +logging: false error_logging: false +select_logging: false +insert_logging: false +update_logging: false