diff --git a/libraries/tbl_tracking.lib.php b/libraries/tbl_tracking.lib.php
new file mode 100644
index 0000000000..fa3e550cb2
--- /dev/null
+++ b/libraries/tbl_tracking.lib.php
@@ -0,0 +1,1206 @@
+= $filter_ts_from
+ && $timestamp <= $filter_ts_to
+ && (in_array('*', $filter_users) || $filtered_user)
+ ) {
+ $tmp_entries[] = array(
+ 'id' => $id,
+ 'timestamp' => $timestamp,
+ 'username' => $entry['username'],
+ 'statement' => $entry['statement']
+ );
+ }
+ $id++;
+ }
+ return($tmp_entries);
+}
+
+/**
+ * Function to get html for data definition and data manipulation statements
+ *
+ * @param string $url_query url query
+ * @param int $last_version last version
+ *
+ * @return string
+ */
+function PMA_getHtmlForDataDefinitionAndManipulationStatements($url_query,
+ $last_version
+) {
+ $html = '
';
+ $html .= '
';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get html for activate tracking
+ *
+ * @param string $url_query url query
+ * @param int $last_version last version
+ *
+ * @return string
+ */
+function PMA_getHtmlForActivateTracking($url_query, $last_version)
+{
+ $html = '';
+ $html .= '';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get html for deactivating tracking
+ *
+ * @param string $url_query url query
+ * @param int $last_version last version
+ *
+ * @return string
+ */
+function PMA_getHtmlForDeactivateTracking($url_query, $last_version)
+{
+ $html = '';
+ $html .= '';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get the list versions of the table
+ *
+ * @return array
+ */
+function PMA_getListOfVersionsOfTable()
+{
+ $sql_query = " SELECT * FROM " .
+ PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) . "." .
+ PMA_Util::backquote($GLOBALS['cfg']['Server']['tracking']) .
+ " WHERE db_name = '" . PMA_Util::sqlAddSlashes($_REQUEST['db']) . "' ".
+ " AND table_name = '" . PMA_Util::sqlAddSlashes($_REQUEST['table']) ."' ".
+ " ORDER BY version DESC ";
+
+ return PMA_queryAsControlUser($sql_query);
+}
+
+/**
+ * Function to get html for displaying last version number
+ *
+ * @param array $sql_result sql result
+ * @param int $last_version last version
+ * @param array $url_params url parameters
+ * @param string $url_query url query
+ *
+ * @return string
+ */
+function PMA_getHtmlForTableVersionDetails($sql_result, $last_version, $url_params,
+ $url_query
+) {
+ $html = '';
+ $html .= '';
+ $html .= '';
+ $html .= '| ' . __('Database') . ' | ';
+ $html .= '' . __('Table') . ' | ';
+ $html .= '' . __('Version') . ' | ';
+ $html .= '' . __('Created') . ' | ';
+ $html .= '' . __('Updated') . ' | ';
+ $html .= '' . __('Status') . ' | ';
+ $html .= '' . __('Show') . ' | ';
+ $html .= '
';
+ $html .= '';
+ $html .= '';
+
+ $style = 'odd';
+ $GLOBALS['dbi']->dataSeek($sql_result, 0);
+ while ($version = $GLOBALS['dbi']->fetchArray($sql_result)) {
+ if ($version['tracking_active'] == 1) {
+ $version_status = __('active');
+ } else {
+ $version_status = __('not active');
+ }
+ if ($version['version'] == $last_version) {
+ if ($version['tracking_active'] == 1) {
+ $tracking_active = true;
+ } else {
+ $tracking_active = false;
+ }
+ }
+ $html .= '';
+ $html .= '| ' . htmlspecialchars($version['db_name']) . ' | ';
+ $html .= '' . htmlspecialchars($version['table_name']) . ' | ';
+ $html .= '' . htmlspecialchars($version['version']) . ' | ';
+ $html .= '' . htmlspecialchars($version['date_created']) . ' | ';
+ $html .= '' . htmlspecialchars($version['date_updated']) . ' | ';
+ $html .= '' . $version_status . ' | ';
+ $html .= '' . __('Tracking report') . '';
+ $html .= '| ' . __('Structure snapshot') . '';
+ $html .= ' | ';
+ $html .= '
';
+
+ if ($style == 'even') {
+ $style = 'odd';
+ } else {
+ $style = 'even';
+ }
+ }
+
+ $html .= '';
+ $html .= '
';
+
+ if ($tracking_active) {
+ $html .= PMA_getHtmlForDeactivateTracking($url_query, $last_version);
+ } else {
+ $html .= PMA_getHtmlForActivateTracking($url_query, $last_version);
+ }
+
+ return $html;
+}
+
+/**
+ * Function to get the last version number of a table
+ *
+ * @param array $sql_result sql result
+ *
+ * @return int
+ */
+function PMA_getTableLastVersionNumber($sql_result)
+{
+ $maxversion = $GLOBALS['dbi']->fetchArray($sql_result);
+ $last_version = $maxversion['version'];
+
+ return $last_version;
+}
+
+/**
+ * Function to get sql results for selectable tables
+ *
+ * @return array
+ */
+function PMA_getSQLResultForSelectableTables()
+{
+ include_once 'libraries/relation.lib.php';
+
+ $sql_query = " SELECT DISTINCT db_name, table_name FROM " .
+ PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) . "." .
+ PMA_Util::backquote($GLOBALS['cfg']['Server']['tracking']) .
+ " WHERE db_name = '" . PMA_Util::sqlAddSlashes($GLOBALS['db']) . "' " .
+ " ORDER BY db_name, table_name";
+
+ return PMA_queryAsControlUser($sql_query);
+}
+
+/**
+ * Function to get html for selectable table rows
+ *
+ * @param array $selectable_tables_sql_result sql results for selectable rows
+ * @param string $url_query url query
+ *
+ * @return string
+ */
+function PMA_getHtmlForSelectableTables($selectable_tables_sql_result, $url_query)
+{
+ $html = '';
+
+ return $html;
+}
+
+/**
+ * Function to get html for tracking report and tracking report export
+ *
+ * @param string $url_query url query
+ * @param array $data data
+ * @param array $url_params url params
+ * @param array $selection_schema selection schema
+ * @param array $selection_data selection data
+ * @param bool $selection_both selection both
+ * @param int $filter_ts_to filter time stamp from
+ * @param int $filter_ts_from filter time stamp tp
+ * @param array $filter_users filter users
+ *
+ * @return string
+ */
+function PMA_getHtmlForTrackingReport($url_query, $data, $url_params,
+ $selection_schema, $selection_data, $selection_both, $filter_ts_to,
+ $filter_ts_from, $filter_users
+) {
+ $html = '';
+
+ $html .= '' . __('Tracking statements') . ' '
+ . htmlspecialchars($data['tracking']) . '
';
+ $html .= '
';
+
+ $html .= '';
+ $html .= '';
+ $html .= '';
+ $html .= "
\n";
+
+ return $html;
+}
+/**
+ * Function to get html for data manipulation statements
+ *
+ * @param array $data data
+ * @param array $filter_users filter users
+ * @param int $filter_ts_from filter time staml from
+ * @param int $filter_ts_to filter time stamp to
+ * @param array $url_params url parameters
+ * @param int $ddlog_count data definition log count
+ * @param string $drop_image_or_text drop image or text
+ *
+ * @return string
+ */
+function PMA_getHtmlForDataManipulationStatements($data, $filter_users,
+ $filter_ts_from, $filter_ts_to, $url_params, $ddlog_count,
+ $drop_image_or_text
+) {
+ $i = $ddlog_count;
+ $html = '';
+ $html .= '';
+ $html .= '';
+ $html .= '| # | ';
+ $html .= '' . __('Date') . ' | ';
+ $html .= '' . __('Username') . ' | ';
+ $html .= '' . __('Data manipulation statement') . ' | ';
+ $html .= '' . __('Delete') . ' | ';
+ $html .= '
';
+ $html .= '';
+ $html .= '';
+
+ $style = 'odd';
+ foreach ($data['dmlog'] as $entry) {
+ $html .= PMA_getHtmlForDataManipulationStatement(
+ $entry, $filter_users, $filter_ts_from, $filter_ts_to, $style, $i,
+ $url_params, $ddlog_count, $drop_image_or_text
+ );
+ if ($style == 'even') {
+ $style = 'odd';
+ } else {
+ $style = 'even';
+ }
+ $i++;
+ }
+ $html .= '';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get html for data manipulation statements
+ *
+ * @param array $entry entry
+ * @param array $filter_users filter users
+ * @param int $filter_ts_from filter time stamp from
+ * @param int $filter_ts_to filter time stamp to
+ * @param string $style style
+ * @param int $i field number
+ * @param array $url_params url parameters
+ * @param int $ddlog_count data definition log count
+ * @param string $drop_image_or_text drop image or text
+ *
+ * @return string
+ */
+function PMA_getHtmlForDataManipulationStatement($entry, $filter_users,
+ $filter_ts_from, $filter_ts_to, $style, $i, $url_params, $ddlog_count,
+ $drop_image_or_text
+) {
+ $statement = PMA_Util::formatSql($entry['statement'], true);
+ $timestamp = strtotime($entry['date']);
+ $filtered_user = in_array($entry['username'], $filter_users);
+ if ($timestamp >= $filter_ts_from
+ && $timestamp <= $filter_ts_to
+ && (in_array('*', $filter_users) || $filtered_user)
+ ) {
+ $html = '';
+ $html .= '| ' . $i . ' | ';
+ $html .= ''
+ . htmlspecialchars($entry['date']) . ' | ';
+ $html .= ''
+ . htmlspecialchars($entry['username']) . ' | ';
+ $html .= '' . $statement . ' | ';
+ $html .= ''
+ . $drop_image_or_text
+ . ' | ';
+ $html .= '
';
+ }
+
+ return $html;
+}
+/**
+ * Function to get html for data definition statements in schema snapshot
+ *
+ * @param array $data data
+ * @param array $filter_users filter users
+ * @param int $filter_ts_from filter time stamp from
+ * @param int $filter_ts_to filter time stamp to
+ * @param array $url_params url parameters
+ * @param string $drop_image_or_text drop image or text
+ *
+ * @return string
+ */
+function PMA_getHtmlForDataDefinitionStatements($data, $filter_users,
+ $filter_ts_from, $filter_ts_to, $url_params, $drop_image_or_text
+) {
+ $i = 1;
+ $html = '';
+ $html .= '';
+ $html .= '';
+ $html .= '| # | ';
+ $html .= '' . __('Date') . ' | ';
+ $html .= '' . __('Username') . ' | ';
+ $html .= '' . __('Data definition statement') . ' | ';
+ $html .= '' . __('Delete') . ' | ';
+ $html .= '
';
+ $html .= '';
+ $html .= '';
+
+ $style = 'odd';
+ foreach ($data['ddlog'] as $entry) {
+ $html .= PMA_getHtmlForDataDefinitionStatement(
+ $entry, $filter_users, $filter_ts_from, $filter_ts_to, $style, $i,
+ $url_params, $drop_image_or_text
+ );
+ if ($style == 'even') {
+ $style = 'odd';
+ } else {
+ $style = 'even';
+ }
+ $i++;
+ }
+ $html .= '';
+ $html .= '
';
+
+ return array($html, $i);
+}
+/**
+ * Function to get html for a data definition statement in schema snapshot
+ *
+ * @param array $entry entry
+ * @param array $filter_users filter users
+ * @param int $filter_ts_from filter time stamp from
+ * @param int $filter_ts_to filter time stamp to
+ * @param string $style style
+ * @param int $i column number
+ * @param array $url_params url parameters
+ * @param string $drop_image_or_text drop image or text
+ *
+ * @return string
+ */
+function PMA_getHtmlForDataDefinitionStatement($entry, $filter_users,
+ $filter_ts_from, $filter_ts_to, $style, $i, $url_params, $drop_image_or_text
+) {
+ $statement = PMA_Util::formatSql($entry['statement'], true);
+ $timestamp = strtotime($entry['date']);
+ $filtered_user = in_array($entry['username'], $filter_users);
+ if ($timestamp >= $filter_ts_from
+ && $timestamp <= $filter_ts_to
+ && (in_array('*', $filter_users) || $filtered_user)
+ ) {
+ $html = '';
+ $html .= '| ' . $i . ' | ';
+ $html .= ''
+ . htmlspecialchars($entry['date']) . ' | ';
+ $html .= ''
+ . htmlspecialchars($entry['username']) . ' | ';
+ $html .= '' . $statement . ' | ';
+ $html .= '' . $drop_image_or_text
+ . ' | ';
+ $html .= '
';
+ }
+
+ return $html;
+}
+/**
+ * Function to get html for schema snapshot
+ *
+ * @param string $url_query url query
+ *
+ * @return string
+ */
+function PMA_getHtmlForSchemaSnapshot($url_query)
+{
+ $html = '';
+ $data = PMA_Tracker::getTrackedData(
+ $_REQUEST['db'], $_REQUEST['table'], $_REQUEST['version']
+ );
+
+ // Get first DROP TABLE/VIEW and CREATE TABLE/VIEW statements
+ $drop_create_statements = $data['ddlog'][0]['statement'];
+
+ if (strstr($data['ddlog'][0]['statement'], 'DROP TABLE')
+ || strstr($data['ddlog'][0]['statement'], 'DROP VIEW')
+ ) {
+ $drop_create_statements .= $data['ddlog'][1]['statement'];
+ }
+ // Print SQL code
+ $html .= PMA_Util::getMessage(
+ sprintf(
+ __('Version %s snapshot (SQL code)'),
+ htmlspecialchars($_REQUEST['version'])
+ ),
+ $drop_create_statements
+ );
+
+ // Unserialize snapshot
+ $temp = unserialize($data['schema_snapshot']);
+ $columns = $temp['COLUMNS'];
+ $indexes = $temp['INDEXES'];
+ $html .= PMA_getHtmlForColumns($columns);
+
+ if (count($indexes) > 0) {
+ $html .= PMA_getHtmlForIndexes($indexes);
+ } // endif
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get html for displaying columns in the schema snapshot
+ *
+ * @param array $columns columns
+ *
+ * @return string
+ */
+function PMA_getHtmlForColumns($columns)
+{
+ $html = '' . __('Structure') . '
';
+ $html .= '';
+ $html .= '';
+ $html .= '';
+ $html .= '| ' . __('Column') . ' | ';
+ $html .= '' . __('Type') . ' | ';
+ $html .= '' . __('Collation') . ' | ';
+ $html .= '' . __('Null') . ' | ';
+ $html .= '' . __('Default') . ' | ';
+ $html .= '' . __('Extra') . ' | ';
+ $html .= '' . __('Comment') . ' | ';
+ $html .= '
';
+ $html .= '';
+ $html .= '';
+ $style = 'odd';
+ foreach ($columns as $field) {
+ $html .= PMA_getHtmlForColumn($field, $style);
+ if ($style == 'even') {
+ $style = 'odd';
+ } else {
+ $style = 'even';
+ }
+ }
+
+ $html .= '';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to get html for column
+ *
+ * @param array $field field
+ * @param string $style style
+ *
+ * @return string
+ */
+function PMA_getHtmlForColumn($field, $style)
+{
+ $html = '';
+ if ($field['Key'] == 'PRI') {
+ $html .= '| ' . htmlspecialchars($field['Field']) . ' | ';
+ } else {
+ $html .= '' . htmlspecialchars($field['Field']) . ' | ';
+ }
+ $html .= "\n";
+ $html .= '' . htmlspecialchars($field['Type']) . ' | ';
+ $html .= '' . htmlspecialchars($field['Collation']) . ' | ';
+ $html .= '' . (($field['Null'] == 'YES') ? __('Yes') : __('No')) . ' | ';
+ $html .= '';
+ if (isset($field['Default'])) {
+ $extracted_columnspec = PMA_Util::extractColumnSpec($field['Type']);
+ if ($extracted_columnspec['type'] == 'bit') {
+ // here, $field['Default'] contains something like b'010'
+ $html .= PMA_Util::convertBitDefaultValue($field['Default']);
+ } else {
+ $html .= htmlspecialchars($field['Default']);
+ }
+ } else {
+ if ($field['Null'] == 'YES') {
+ $html .= 'NULL';
+ } else {
+ $html .= '' . _pgettext('None for default', 'None') . '';
+ }
+ }
+ $html .= ' | ';
+ $html .= '' . htmlspecialchars($field['Extra']) . ' | ';
+ $html .= '' . htmlspecialchars($field['Comment']) . ' | ';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Fuunction to get html for the indexes in schema snapshot
+ *
+ * @param array $indexes indexes
+ *
+ * @return string
+ */
+function PMA_getHtmlForIndexes($indexes)
+{
+ $html = '' . __('Indexes') . '
';
+ $html .= '';
+ $html .= '';
+ $html .= '';
+ $html .= '| ' . __('Keyname') . ' | ';
+ $html .= '' . __('Type') . ' | ';
+ $html .= '' . __('Unique') . ' | ';
+ $html .= '' . __('Packed') . ' | ';
+ $html .= '' . __('Column') . ' | ';
+ $html .= '' . __('Cardinality') . ' | ';
+ $html .= '' . __('Collation') . ' | ';
+ $html .= '' . __('Null') . ' | ';
+ $html .= '' . __('Comment') . ' | ';
+ $html .= '
';
+ $html .= '';
+
+ $style = 'odd';
+ foreach ($indexes as $index) {
+ $html .= PMA_getHtmlForIndex($index, $style);
+ if ($style == 'even') {
+ $style = 'odd';
+ } else {
+ $style = 'even';
+ }
+ }
+ $html .= '';
+ $html .= '
';
+ return $html;
+}
+
+/**
+ * Funtion to get html for an index in schema snapshot
+ *
+ * @param array $index index
+ * @param string $style style
+ *
+ * @return string
+ */
+function PMA_getHtmlForIndex($index, $style)
+{
+ if ($index['Non_unique'] == 0) {
+ $str_unique = __('Yes');
+ } else {
+ $str_unique = __('No');
+ }
+ if ($index['Packed'] != '') {
+ $str_packed = __('Yes');
+ } else {
+ $str_packed = __('No');
+ }
+
+ $html .= '';
+ $html .= '| ' . htmlspecialchars($index['Key_name']) . ' | ';
+ $html .= '' . htmlspecialchars($index['Index_type']) . ' | ';
+ $html .= '' . $str_unique . ' | ';
+ $html .= '' . $str_packed . ' | ';
+ $html .= '' . htmlspecialchars($index['Column_name']) . ' | ';
+ $html .= '' . htmlspecialchars($index['Cardinality']) . ' | ';
+ $html .= '' . htmlspecialchars($index['Collation']) . ' | ';
+ $html .= '' . htmlspecialchars($index['Null']) . ' | ';
+ $html .= '' . htmlspecialchars($index['Comment']) . ' | ';
+ $html .= '
';
+
+ return $html;
+}
+
+/**
+ * Function to handle the tracking report
+ *
+ * @param array &$data tracked data
+ *
+ * @return void
+ */
+function PMA_deleteTrackingReportRows(&$data)
+{
+ if (isset($_REQUEST['delete_ddlog'])) {
+ // Delete ddlog row data
+ PMA_handleDeleteDataDefinitionsLog($data);
+ }
+
+ if (isset($_REQUEST['delete_dmlog'])) {
+ // Delete dmlog row data
+ PMA_handleDeleteDataManipulationLog($data);
+ }
+}
+
+/**
+ * Function to handle the delete ddlog row data
+ *
+ * @param array &$data tracked data
+ *
+ * @return void
+ */
+function PMA_handleDeleteDataDefinitionsLog(&$data)
+{
+ $delete_id = $_REQUEST['delete_ddlog'];
+
+ // Only in case of valable id
+ if ($delete_id == (int)$delete_id) {
+ unset($data['ddlog'][$delete_id]);
+
+ $successfullyDeleted = PMA_Tracker::changeTrackingData(
+ $_REQUEST['db'], $_REQUEST['table'],
+ $_REQUEST['version'], 'DDL', $data['ddlog']
+ );
+ if ($successfullyDeleted) {
+ $msg = PMA_Message::success(
+ __('Tracking data definition successfully deleted')
+ );
+ } else {
+ $msg = PMA_Message::rawError(__('Query error'));
+ }
+ $msg->display();
+ }
+}
+
+/**
+ * Function to handle the delete of fmlog rows
+ *
+ * @param array &$data tracked data
+ *
+ * @return void
+ */
+function PMA_handleDeleteDataManipulationLog(&$data)
+{
+ $delete_id = $_REQUEST['delete_dmlog'];
+
+ // Only in case of valable id
+ if ($delete_id == (int)$delete_id) {
+ unset($data['dmlog'][$delete_id]);
+
+ $successfullyDeleted = PMA_Tracker::changeTrackingData(
+ $_REQUEST['db'], $_REQUEST['table'],
+ $_REQUEST['version'], 'DML', $data['dmlog']
+ );
+ if ($successfullyDeleted) {
+ $msg = PMA_Message::success(
+ __('Tracking data manipulation successfully deleted')
+ );
+ } else {
+ $msg = PMA_Message::rawError(__('Query error'));
+ }
+ $msg->display();
+ }
+}
+
+/**
+ * Function to export as sql dump
+ *
+ * @param array $entries entries
+ *
+ * @return void
+ */
+function PMA_exportAsSQLDump($entries)
+{
+ $new_query = "# "
+ . __(
+ 'You can execute the dump by creating and using a temporary database. '
+ . 'Please ensure that you have the privileges to do so.'
+ )
+ . "\n"
+ . "# " . __('Comment out these two lines if you do not need them.') . "\n"
+ . "\n"
+ . "CREATE database IF NOT EXISTS pma_temp_db; \n"
+ . "USE pma_temp_db; \n"
+ . "\n";
+
+ foreach ($entries as $entry) {
+ $new_query .= $entry['statement'];
+ }
+ $msg = PMA_Message::success(
+ __('SQL statements exported. Please copy the dump or execute it.')
+ );
+ $msg->display();
+
+ $db_temp = $GLOBALS['db'];
+ $table_temp = $GLOBALS['table'];
+
+ $GLOBALS['db'] = $GLOBALS['table'] = '';
+ include_once './libraries/sql_query_form.lib.php';
+
+ PMA_getHtmlForSqlQueryForm($new_query, 'sql');
+
+ $GLOBALS['db'] = $db_temp;
+ $GLOBALS['table'] = $table_temp;
+}
+
+/**
+ * Function to export as sql execution
+ *
+ * @param array $entries entries
+ *
+ * @return array
+ */
+function PMA_exportAsSQLExecution($entries)
+{
+ foreach ($entries as $entry) {
+ $sql_result = $GLOBALS['dbi']->query("/*NOTRACK*/\n" . $entry['statement']);
+ }
+ $msg = PMA_Message::success(__('SQL statements executed.'));
+ $msg->display();
+
+ return $sql_result;
+}
+
+/**
+ * Function to export as entries
+ *
+ * @param array $entries entries
+ *
+ * @return void
+ */
+function PMA_exportAsFileDownload($entries)
+{
+ @ini_set('url_rewriter.tags', '');
+
+ $dump = "# " . sprintf(
+ __('Tracking report for table `%s`'), htmlspecialchars($_REQUEST['table'])
+ )
+ . "\n" . "# " . date('Y-m-d H:i:s') . "\n";
+ foreach ($entries as $entry) {
+ $dump .= $entry['statement'];
+ }
+ $filename = 'log_' . htmlspecialchars($_REQUEST['table']) . '.sql';
+ PMA_downloadHeader($filename, 'text/x-sql', strlen($dump));
+
+ $response = PMA_Response::getInstance();
+ $response->addHTML($dump);
+
+ exit();
+}
+
+/**
+ * Function to activate tracking
+ *
+ * @return void
+ */
+function PMA_activateTracking()
+{
+ $activated = PMA_Tracker::activateTracking(
+ $GLOBALS['db'], $GLOBALS['table'], $_REQUEST['version']
+ );
+ if ($activated) {
+ $msg = PMA_Message::success(
+ sprintf(
+ __('Tracking for %1$s was activated at version %2$s.'),
+ htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table']),
+ htmlspecialchars($_REQUEST['version'])
+ )
+ );
+ $msg->display();
+ }
+}
+
+/**
+ * Function to deactivate tracking
+ *
+ * @return void
+ */
+function PMA_deactivateTracking()
+{
+ $deactivated = PMA_Tracker::deactivateTracking(
+ $GLOBALS['db'], $GLOBALS['table'], $_REQUEST['version']
+ );
+ if ($deactivated) {
+ $msg = PMA_Message::success(
+ sprintf(
+ __('Tracking for %1$s was deactivated at version %2$s.'),
+ htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table']),
+ htmlspecialchars($_REQUEST['version'])
+ )
+ );
+ $msg->display();
+ }
+}
+
+/**
+ * Function to get tracking set
+ *
+ * @return string
+ */
+function PMA_getTrackingSet()
+{
+ $tracking_set = '';
+
+ if ($_REQUEST['alter_table'] == true) {
+ $tracking_set .= 'ALTER TABLE,';
+ }
+ if ($_REQUEST['rename_table'] == true) {
+ $tracking_set .= 'RENAME TABLE,';
+ }
+ if ($_REQUEST['create_table'] == true) {
+ $tracking_set .= 'CREATE TABLE,';
+ }
+ if ($_REQUEST['drop_table'] == true) {
+ $tracking_set .= 'DROP TABLE,';
+ }
+ if ($_REQUEST['create_index'] == true) {
+ $tracking_set .= 'CREATE INDEX,';
+ }
+ if ($_REQUEST['drop_index'] == true) {
+ $tracking_set .= 'DROP INDEX,';
+ }
+ if ($_REQUEST['insert'] == true) {
+ $tracking_set .= 'INSERT,';
+ }
+ if ($_REQUEST['update'] == true) {
+ $tracking_set .= 'UPDATE,';
+ }
+ if ($_REQUEST['delete'] == true) {
+ $tracking_set .= 'DELETE,';
+ }
+ if ($_REQUEST['truncate'] == true) {
+ $tracking_set .= 'TRUNCATE,';
+ }
+ $tracking_set = rtrim($tracking_set, ',');
+
+ return $tracking_set;
+}
+
+/**
+ * Function to create the tracking version
+ *
+ * @return void
+ */
+function PMA_createTrackingVersion()
+{
+ $tracking_set = PMA_getTrackingSet();
+
+ $versionCreated = PMA_Tracker::createVersion(
+ $GLOBALS['db'],
+ $GLOBALS['table'],
+ $_REQUEST['version'],
+ $tracking_set,
+ PMA_Table::isView($GLOBALS['db'], $GLOBALS['table'])
+ );
+ if ($versionCreated) {
+ $msg = PMA_Message::success(
+ sprintf(
+ __('Version %1$s was created, tracking for %2$s is active.'),
+ htmlspecialchars($_REQUEST['version']),
+ htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table'])
+ )
+ );
+ $msg->display();
+ }
+}
+
+/**
+ * Function to get the entries
+ *
+ * @param array $data data
+ * @param int $filter_ts_from filter time stamp from
+ * @param int $filter_ts_to filter time stamp to
+ * @param array $filter_users filter users
+ *
+ * @return array
+ */
+function PMA_getEntries($data, $filter_ts_from, $filter_ts_to, $filter_users)
+{
+ $entries = array();
+ // Filtering data definition statements
+ if ($_REQUEST['logtype'] == 'schema'
+ || $_REQUEST['logtype'] == 'schema_and_data'
+ ) {
+ $entries = array_merge(
+ $entries,
+ PMA_filterTracking(
+ $data['ddlog'], $filter_ts_from, $filter_ts_to, $filter_users
+ )
+ );
+ }
+
+ // Filtering data manipulation statements
+ if ($_REQUEST['logtype'] == 'data'
+ || $_REQUEST['logtype'] == 'schema_and_data'
+ ) {
+ $entries = array_merge(
+ $entries,
+ PMA_filterTracking(
+ $data['dmlog'], $filter_ts_from, $filter_ts_to, $filter_users
+ )
+ );
+ }
+
+ // Sort it
+ foreach ($entries as $key => $row) {
+ $ids[$key] = $row['id'];
+ $timestamps[$key] = $row['timestamp'];
+ $usernames[$key] = $row['username'];
+ $statements[$key] = $row['statement'];
+ }
+
+ array_multisort(
+ $timestamps, SORT_ASC, $ids, SORT_ASC, $usernames,
+ SORT_ASC, $statements, SORT_ASC, $entries
+ );
+
+ return $entries;
+}
+?>
diff --git a/tbl_tracking.php b/tbl_tracking.php
index 039fcf9694..c4cbd7a65b 100644
--- a/tbl_tracking.php
+++ b/tbl_tracking.php
@@ -9,6 +9,8 @@
// Run common work
require_once './libraries/common.inc.php';
+require_once './libraries/tbl_tracking.lib.php';
+
define('TABLE_MAY_BE_ABSENT', true);
require './libraries/tbl_common.inc.php';
$url_query .= '&goto=tbl_tracking.php&back=tbl_tracking.php';
@@ -51,100 +53,14 @@ if (isset($_REQUEST['report']) || isset($_REQUEST['report_export'])) {
// Prepare export
if (isset($_REQUEST['report_export'])) {
-
- /**
- * Filters tracking entries
- *
- * @param array $data the entries to filter
- * @param string $filter_ts_from "from" date
- * @param string $filter_ts_to "to" date
- * @param string $filter_users users
- *
- * @return array filtered entries
- */
- function PMA_filterTracking(
- $data, $filter_ts_from, $filter_ts_to, $filter_users
- ) {
- $tmp_entries = array();
- $id = 0;
- foreach ($data as $entry) {
- $timestamp = strtotime($entry['date']);
-
- if ($timestamp >= $filter_ts_from
- && $timestamp <= $filter_ts_to
- && (in_array('*', $filter_users)
- || in_array($entry['username'], $filter_users))
- ) {
- $tmp_entries[] = array(
- 'id' => $id,
- 'timestamp' => $timestamp,
- 'username' => $entry['username'],
- 'statement' => $entry['statement']
- );
- }
- $id++;
- }
- return($tmp_entries);
- }
-
- $entries = array();
- // Filtering data definition statements
- if ($_REQUEST['logtype'] == 'schema'
- || $_REQUEST['logtype'] == 'schema_and_data'
- ) {
- $entries = array_merge(
- $entries,
- PMA_filterTracking(
- $data['ddlog'], $filter_ts_from, $filter_ts_to, $filter_users
- )
- );
- }
-
- // Filtering data manipulation statements
- if ($_REQUEST['logtype'] == 'data'
- || $_REQUEST['logtype'] == 'schema_and_data'
- ) {
- $entries = array_merge(
- $entries,
- PMA_filterTracking(
- $data['dmlog'], $filter_ts_from, $filter_ts_to, $filter_users
- )
- );
- }
-
- // Sort it
- foreach ($entries as $key => $row) {
- $ids[$key] = $row['id'];
- $timestamps[$key] = $row['timestamp'];
- $usernames[$key] = $row['username'];
- $statements[$key] = $row['statement'];
- }
-
- array_multisort(
- $timestamps, SORT_ASC, $ids, SORT_ASC, $usernames,
- SORT_ASC, $statements, SORT_ASC, $entries
- );
-
+ $entries = PMA_getEntries($data, $filter_ts_from, $filter_ts_to, $filter_users);
}
// Export as file download
if (isset($_REQUEST['report_export'])
&& $_REQUEST['export_type'] == 'sqldumpfile'
) {
- @ini_set('url_rewriter.tags', '');
-
- $dump = "# " . sprintf(
- __('Tracking report for table `%s`'), htmlspecialchars($_REQUEST['table'])
- )
- . "\n" . "# " . date('Y-m-d H:i:s') . "\n";
- foreach ($entries as $entry) {
- $dump .= $entry['statement'];
- }
- $filename = 'log_' . htmlspecialchars($_REQUEST['table']) . '.sql';
- PMA_downloadHeader($filename, 'text/x-sql', strlen($dump));
-
- echo $dump;
- exit();
+ PMA_exportAsFileDownload($entries);
}
@@ -152,7 +68,7 @@ if (isset($_REQUEST['report_export'])
* Gets tables informations
*/
-echo '
';
+$html = '
';
/**
* Actions
@@ -160,277 +76,34 @@ echo '
';
// Create tracking version
if (isset($_REQUEST['submit_create_version'])) {
- $tracking_set = '';
-
- if ($_REQUEST['alter_table'] == true) {
- $tracking_set .= 'ALTER TABLE,';
- }
- if ($_REQUEST['rename_table'] == true) {
- $tracking_set .= 'RENAME TABLE,';
- }
- if ($_REQUEST['create_table'] == true) {
- $tracking_set .= 'CREATE TABLE,';
- }
- if ($_REQUEST['drop_table'] == true) {
- $tracking_set .= 'DROP TABLE,';
- }
- if ($_REQUEST['create_index'] == true) {
- $tracking_set .= 'CREATE INDEX,';
- }
- if ($_REQUEST['drop_index'] == true) {
- $tracking_set .= 'DROP INDEX,';
- }
- if ($_REQUEST['insert'] == true) {
- $tracking_set .= 'INSERT,';
- }
- if ($_REQUEST['update'] == true) {
- $tracking_set .= 'UPDATE,';
- }
- if ($_REQUEST['delete'] == true) {
- $tracking_set .= 'DELETE,';
- }
- if ($_REQUEST['truncate'] == true) {
- $tracking_set .= 'TRUNCATE,';
- }
- $tracking_set = rtrim($tracking_set, ',');
-
- $versionCreated = PMA_Tracker::createVersion(
- $GLOBALS['db'],
- $GLOBALS['table'],
- $_REQUEST['version'],
- $tracking_set,
- PMA_Table::isView($GLOBALS['db'], $GLOBALS['table'])
- );
- if ($versionCreated) {
- $msg = PMA_Message::success(
- sprintf(
- __('Version %1$s was created, tracking for %2$s is active.'),
- htmlspecialchars($_REQUEST['version']),
- htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table'])
- )
- );
- $msg->display();
- }
+ PMA_createTrackingVersion();
}
// Deactivate tracking
if (isset($_REQUEST['submit_deactivate_now'])) {
- $deactivated = PMA_Tracker::deactivateTracking(
- $GLOBALS['db'], $GLOBALS['table'], $_REQUEST['version']
- );
- if ($deactivated) {
- $msg = PMA_Message::success(
- sprintf(
- __('Tracking for %1$s was deactivated at version %2$s.'),
- htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table']),
- htmlspecialchars($_REQUEST['version'])
- )
- );
- $msg->display();
- }
+ PMA_deactivateTracking();
}
// Activate tracking
if (isset($_REQUEST['submit_activate_now'])) {
- $activated = PMA_Tracker::activateTracking(
- $GLOBALS['db'], $GLOBALS['table'], $_REQUEST['version']
- );
- if ($activated) {
- $msg = PMA_Message::success(
- sprintf(
- __('Tracking for %1$s was activated at version %2$s.'),
- htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table']),
- htmlspecialchars($_REQUEST['version'])
- )
- );
- $msg->display();
- }
+ PMA_activateTracking();
}
// Export as SQL execution
if (isset($_REQUEST['report_export']) && $_REQUEST['export_type'] == 'execution') {
- foreach ($entries as $entry) {
- $sql_result = $GLOBALS['dbi']->query("/*NOTRACK*/\n" . $entry['statement']);
- }
- $msg = PMA_Message::success(__('SQL statements executed.'));
- $msg->display();
+ $sql_result = PMA_exportAsSQLExecution($entries);
}
// Export as SQL dump
if (isset($_REQUEST['report_export']) && $_REQUEST['export_type'] == 'sqldump') {
- $new_query = "# "
- . __(
- 'You can execute the dump by creating and using a temporary database. '
- . 'Please ensure that you have the privileges to do so.'
- )
- . "\n"
- . "# " . __('Comment out these two lines if you do not need them.') . "\n"
- . "\n"
- . "CREATE database IF NOT EXISTS pma_temp_db; \n"
- . "USE pma_temp_db; \n"
- . "\n";
-
- foreach ($entries as $entry) {
- $new_query .= $entry['statement'];
- }
- $msg = PMA_Message::success(
- __('SQL statements exported. Please copy the dump or execute it.')
- );
- $msg->display();
-
- $db_temp = $db;
- $table_temp = $table;
-
- $db = $table = '';
- include_once './libraries/sql_query_form.lib.php';
-
- echo PMA_getHtmlForSqlQueryForm($new_query, 'sql');
-
- $db = $db_temp;
- $table = $table_temp;
+ PMA_exportAsSQLDump($entries);
}
/*
* Schema snapshot
*/
if (isset($_REQUEST['snapshot'])) {
- echo '';
- $data = PMA_Tracker::getTrackedData(
- $_REQUEST['db'], $_REQUEST['table'], $_REQUEST['version']
- );
-
- // Get first DROP TABLE/VIEW and CREATE TABLE/VIEW statements
- $drop_create_statements = $data['ddlog'][0]['statement'];
-
- if (strstr($data['ddlog'][0]['statement'], 'DROP TABLE')
- || strstr($data['ddlog'][0]['statement'], 'DROP VIEW')
- ) {
- $drop_create_statements .= $data['ddlog'][1]['statement'];
- }
- // Print SQL code
- echo PMA_Util::getMessage(
- sprintf(
- __('Version %s snapshot (SQL code)'),
- htmlspecialchars($_REQUEST['version'])
- ),
- $drop_create_statements
- );
-
- // Unserialize snapshot
- $temp = unserialize($data['schema_snapshot']);
- $columns = $temp['COLUMNS'];
- $indexes = $temp['INDEXES'];
- echo '' . __('Structure') . '
';
- echo '';
- echo '';
- echo '';
- echo '| ' . __('Column') . ' | ';
- echo '' . __('Type') . ' | ';
- echo '' . __('Collation') . ' | ';
- echo '' . __('Null') . ' | ';
- echo '' . __('Default') . ' | ';
- echo '' . __('Extra') . ' | ';
- echo '' . __('Comment') . ' | ';
- echo '
';
- echo '';
- echo '';
- $style = 'odd';
- foreach ($columns as $field_index => $field) {
- echo '';
- if ($field['Key'] == 'PRI') {
- echo '| ' . htmlspecialchars($field['Field']) . ' | ';
- } else {
- echo '' . htmlspecialchars($field['Field']) . ' | ';
- }
- echo "\n";
- echo '' . htmlspecialchars($field['Type']) . ' | ';
- echo '' . htmlspecialchars($field['Collation']) . ' | ';
- echo '' . (($field['Null'] == 'YES') ? __('Yes') : __('No')) . ' | ';
- echo '';
- if (isset($field['Default'])) {
- $extracted_columnspec = PMA_Util::extractColumnSpec($field['Type']);
- if ($extracted_columnspec['type'] == 'bit') {
- // here, $field['Default'] contains something like b'010'
- echo PMA_Util::convertBitDefaultValue($field['Default']);
- } else {
- echo htmlspecialchars($field['Default']);
- }
- } else {
- if ($field['Null'] == 'YES') {
- echo 'NULL';
- } else {
- echo '' . _pgettext('None for default', 'None') . '';
- }
- }
- echo ' | ';
- echo '' . htmlspecialchars($field['Extra']) . ' | ';
- echo '' . htmlspecialchars($field['Comment']) . ' | ';
- echo '
';
-
- if ($style == 'even') {
- $style = 'odd';
- } else {
- $style = 'even';
- }
- }
-
- echo '';
- echo '
';
-
- if (count($indexes) > 0) {
- echo '' . __('Indexes') . '
';
- echo '';
- echo '';
- echo '';
- echo '| ' . __('Keyname') . ' | ';
- echo '' . __('Type') . ' | ';
- echo '' . __('Unique') . ' | ';
- echo '' . __('Packed') . ' | ';
- echo '' . __('Column') . ' | ';
- echo '' . __('Cardinality') . ' | ';
- echo '' . __('Collation') . ' | ';
- echo '' . __('Null') . ' | ';
- echo '' . __('Comment') . ' | ';
- echo '
';
- echo '';
-
- $style = 'odd';
- foreach ($indexes as $indexes_index => $index) {
- if ($index['Non_unique'] == 0) {
- $str_unique = __('Yes');
- } else {
- $str_unique = __('No');
- }
- if ($index['Packed'] != '') {
- $str_packed = __('Yes');
- } else {
- $str_packed = __('No');
- }
-
- echo '';
- echo '| ' . htmlspecialchars($index['Key_name']) . ' | ';
- echo '' . htmlspecialchars($index['Index_type']) . ' | ';
- echo '' . $str_unique . ' | ';
- echo '' . $str_packed . ' | ';
- echo '' . htmlspecialchars($index['Column_name']) . ' | ';
- echo '' . htmlspecialchars($index['Cardinality']) . ' | ';
- echo '' . htmlspecialchars($index['Collation']) . ' | ';
- echo '' . htmlspecialchars($index['Null']) . ' | ';
- echo '' . htmlspecialchars($index['Comment']) . ' | ';
- echo '
';
-
- if ($style == 'even') {
- $style = 'odd';
- } else {
- $style = 'even';
- }
- }
- echo '';
- echo '
';
- } // endif
- echo '
';
+ $html .= PMA_getHtmlForSchemaSnapshot($url_query);
}
// end of snapshot report
@@ -440,483 +113,46 @@ if (isset($_REQUEST['snapshot'])) {
if (isset($_REQUEST['report'])
&& (isset($_REQUEST['delete_ddlog']) || isset($_REQUEST['delete_dmlog']))
) {
-
- if (isset($_REQUEST['delete_ddlog'])) {
-
- // Delete ddlog row data
- $delete_id = $_REQUEST['delete_ddlog'];
-
- // Only in case of valable id
- if ($delete_id == (int)$delete_id) {
- unset($data['ddlog'][$delete_id]);
-
- $successfullyDeleted = PMA_Tracker::changeTrackingData(
- $_REQUEST['db'], $_REQUEST['table'],
- $_REQUEST['version'], 'DDL', $data['ddlog']
- );
- if ($successfullyDeleted) {
- $msg = PMA_Message::success(
- __('Tracking data definition successfully deleted')
- );
- } else {
- $msg = PMA_Message::rawError(__('Query error'));
- }
- $msg->display();
- }
- }
-
- if (isset($_REQUEST['delete_dmlog'])) {
-
- // Delete dmlog row data
- $delete_id = $_REQUEST['delete_dmlog'];
-
- // Only in case of valable id
- if ($delete_id == (int)$delete_id) {
- unset($data['dmlog'][$delete_id]);
-
- $successfullyDeleted = PMA_Tracker::changeTrackingData(
- $_REQUEST['db'], $_REQUEST['table'],
- $_REQUEST['version'], 'DML', $data['dmlog']
- );
- if ($successfullyDeleted) {
- $msg = PMA_Message::success(
- __('Tracking data manipulation successfully deleted')
- );
- } else {
- $msg = PMA_Message::rawError(__('Query error'));
- }
- $msg->display();
- }
- }
+ PMA_deleteTrackingReportRows($data);
}
if (isset($_REQUEST['report']) || isset($_REQUEST['report_export'])) {
- echo '';
-
- echo '' . __('Tracking statements') . ' '
- . htmlspecialchars($data['tracking']) . '
';
- echo '
';
-
- echo '';
- echo '';
- echo '';
- echo "
\n";
+ $html .= PMA_getHtmlForTrackingReport(
+ $url_query, $data, $url_params, $selection_schema, $selection_data,
+ $selection_both, $filter_ts_to, $filter_ts_from, $filter_users
+ );
} // end of report
/*
* List selectable tables
*/
-
-$sql_query = " SELECT DISTINCT db_name, table_name FROM " .
- PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) . "." .
- PMA_Util::backquote($GLOBALS['cfg']['Server']['tracking']) .
- " WHERE db_name = '" . PMA_Util::sqlAddSlashes($GLOBALS['db']) . "' " .
- " ORDER BY db_name, table_name";
-
-$sql_result = PMA_queryAsControlUser($sql_query);
-
-if ($GLOBALS['dbi']->numRows($sql_result) > 0) {
- echo '';
+$selectable_tables_sql_result = PMA_getSQLResultForSelectableTables();
+if ($GLOBALS['dbi']->numRows($selectable_tables_sql_result) > 0) {
+ $html .= PMA_getHtmlForSelectableTables(
+ $selectable_tables_sql_result, $url_query
+ );
}
-echo '
';
+$html .= '
';
/*
* List versions of current table
*/
-
-$sql_query = " SELECT * FROM " .
- PMA_Util::backquote($GLOBALS['cfg']['Server']['pmadb']) . "." .
- PMA_Util::backquote($GLOBALS['cfg']['Server']['tracking']) .
- " WHERE db_name = '" . PMA_Util::sqlAddSlashes($_REQUEST['db']) . "' ".
- " AND table_name = '" . PMA_Util::sqlAddSlashes($_REQUEST['table']) ."' ".
- " ORDER BY version DESC ";
-
-$sql_result = PMA_queryAsControlUser($sql_query);
-
-$last_version = 0;
-$maxversion = $GLOBALS['dbi']->fetchArray($sql_result);
-$last_version = $maxversion['version'];
-
+$sql_result = PMA_getListOfVersionsOfTable();
+$last_version = PMA_getTableLastVersionNumber($sql_result);
if ($last_version > 0) {
- echo '';
- echo '';
- echo '';
- echo '| ' . __('Database') . ' | ';
- echo '' . __('Table') . ' | ';
- echo '' . __('Version') . ' | ';
- echo '' . __('Created') . ' | ';
- echo '' . __('Updated') . ' | ';
- echo '' . __('Status') . ' | ';
- echo '' . __('Show') . ' | ';
- echo '
';
- echo '';
- echo '';
-
- $style = 'odd';
- $GLOBALS['dbi']->dataSeek($sql_result, 0);
- while ($version = $GLOBALS['dbi']->fetchArray($sql_result)) {
- if ($version['tracking_active'] == 1) {
- $version_status = __('active');
- } else {
- $version_status = __('not active');
- }
- if ($version['version'] == $last_version) {
- if ($version['tracking_active'] == 1) {
- $tracking_active = true;
- } else {
- $tracking_active = false;
- }
- }
- echo '';
- echo '| ' . htmlspecialchars($version['db_name']) . ' | ';
- echo '' . htmlspecialchars($version['table_name']) . ' | ';
- echo '' . htmlspecialchars($version['version']) . ' | ';
- echo '' . htmlspecialchars($version['date_created']) . ' | ';
- echo '' . htmlspecialchars($version['date_updated']) . ' | ';
- echo '' . $version_status . ' | ';
- echo '' . __('Tracking report') . '';
- echo '| ' . __('Structure snapshot') . '';
- echo ' | ';
- echo '
';
-
- if ($style == 'even') {
- $style = 'odd';
- } else {
- $style = 'even';
- }
- }
-
- echo '';
- echo '
';
-
- if ($tracking_active) {
- echo '';
- echo '';
- echo '
';
- } else {
- echo '';
- echo '';
- echo '
';
- }
+ $html .= PMA_getHtmlForTableVersionDetails(
+ $sql_result, $last_version, $url_params, $url_query
+ );
}
-echo '';
-
-echo '
';
+?>
\ No newline at end of file