From 973f8cc5f9adf0540c7ade52e863ab69983c3635 Mon Sep 17 00:00:00 2001 From: CaMer0n Date: Wed, 18 May 2011 08:54:07 +0000 Subject: [PATCH] rewrote Admin -> database -> db_verify routines. --- e107_admin/db_verify.php | 757 +++++++++++++++++- e107_languages/English/admin/lan_admin.php | 2 +- .../English/admin/lan_db_verify.php | 3 +- 3 files changed, 716 insertions(+), 46 deletions(-) diff --git a/e107_admin/db_verify.php b/e107_admin/db_verify.php index d636b4bd8..889f8c0d9 100644 --- a/e107_admin/db_verify.php +++ b/e107_admin/db_verify.php @@ -28,19 +28,13 @@ $e_sub_cat = 'database'; require_once("auth.php"); -require_once(e_HANDLER."form_handler.php"); -$frm = new e_form(); -require_once (e_HANDLER."message_handler.php"); -$emessage = &eMessage::getInstance(); - -$sql_data = file_get_contents(e_ADMIN.'sql/core_sql.php'); if (!$sql_data) { - exit(DBLAN_1); + // exit(DBLAN_1); } -$tables['core'] = preg_replace("#\/\*.*?\*\/#mis", '', $sql_data); // Strip any comments as we copy + if (!getperms("0")) { @@ -48,6 +42,700 @@ if (!getperms("0")) exit; } + +$dbv = new db_verify; +// print_a($dbv->tables); + + + + +require_once(e_ADMIN."footer.php"); +exit; + +class db_verify +{ + + var $tables = array(); + var $sqlTables = array(); + var $results = array(); + var $indices = array(0); + + function __construct() + { + + + $ns = e107::getRender(); + + $pref = e107::getPref(); + + $core_data = file_get_contents(e_ADMIN.'sql/core_sql.php'); + $this->tables['core'] = $this->getTables($core_data); + + foreach($pref['e_sql_list'] as $path => $file) + { + $filename = e_PLUGIN.$path.'/'.$file.'.php'; + if(is_readable($filename)) + { + $id = str_replace('_sql','',$file); + $data = file_get_contents($filename); + $this->tables[$id] = $this->getTables($data); + unset($data); + } + else + { + $emessage->add($filename.DBLAN_22, E_MESSAGE_WARNING); + } + } + + if($_POST['verify_table']) + { + foreach($_POST['verify_table'] as $tab) + { + $this->compare($tab); + } + + if(count($this->errors)) + { + $this->renderResults(); + } + else + { + $mes->add("Tables appear to be okay!",E_MESSAGE_SUCCESS); + $text .= "
".$frm->admin_button('back', DBLAN_17, 'back')."
"; + $ns->tablerender("Okay",$mes->render().$text); + } + + + } + else + { + $this->runFix(); + $this->renderTableSelect(); + } + + // $this->sqlTables = $this->sqlTableList(); + + // print_a($this->tables); + // $this->renderTableSelect(); + + // print_a($field); + // print_a($match[2]); + // echo "
".$sql_data."
"; + } + + function compare($selection) + { + + + foreach($this->tables[$selection]['tables'] as $key=>$tbl) + { + //$this->errors[$tbl]['_status'] = 'ok'; // default table status + $rawSqlData = $this->getSqlData($tbl); + if($rawSqlData === FALSE) + { + $this->errors[$tbl]['_status'] = 'missing_table'; + $this->results[$tbl]['_file'] = $selection; + // echo "missing table: $tbl"; + continue; + } + + $sqlDataArr = $this->getTables($rawSqlData); + + $fileFieldData = $this->getFields($this->tables[$selection]['data'][$key]); + $sqlFieldData = $this->getFields($sqlDataArr['data'][0]); + + $fileIndexData = $this->getIndex($this->tables[$selection]['data'][$key]); + $sqlIndexData = $this->getIndex($sqlDataArr['data'][0]); + + // echo "

".$field."

+ //
".print_r($fileIndexData,TRUE)."
".print_r($sqlIndexData,TRUE)."
"; + + + // Check Field Data. + foreach($fileFieldData as $field => $info ) + { + + + $this->results[$tbl][$field]['_status'] = 'ok'; + + if(!is_array($sqlFieldData[$field])) + { + // echo "

".$field."

+ //
".print_r($info,TRUE)."
 - ".print_r($sqlFieldData[$field],TRUE)."
"; + $this->errors[$tbl]['_status'] = 'error'; // table status + $this->results[$tbl][$field]['_status'] = 'missing_field'; // field status + $this->results[$tbl][$field]['_valid'] = $info; + $this->results[$tbl][$field]['_file'] = $selection; + } + elseif(count($off = array_diff_assoc($info,$sqlFieldData[$field]))) + { + $this->errors[$tbl]['_status'] = 'mismatch'; + $this->results[$tbl][$field]['_status'] = 'mismatch'; + $this->results[$tbl][$field]['_diff'] = $off; + $this->results[$tbl][$field]['_valid'] = $info; + $this->results[$tbl][$field]['_invalid'] = $sqlFieldData[$field]; + $this->results[$tbl][$field]['_file'] = $selection; + + } + + + } + + // print_a($fileIndexData); + // print_a($sqlIndexData); + // Check Index data + foreach($fileIndexData as $field => $info ) + { + + if(!is_array($sqlIndexData[$field])) // missing index. + { + // print_a($info); + // print_a($sqlIndexData[$field]); + + $this->errors[$tbl]['_status'] = 'error'; // table status + $this->indices[$tbl][$field]['_status'] = 'missing_index'; // index status + $this->indices[$tbl][$field]['_valid'] = $info; + $this->indices[$tbl][$field]['_file'] = $selection; + } + elseif(count($offin = array_diff_assoc($info,$sqlIndexData[$field]))) // missmatch data + { + // print_a($info); + // print_a($sqlIndexData[$field]); + + $this->errors[$tbl]['_status'] = 'mismatch_index'; + $this->indices[$tbl][$field]['_status'] = 'mismatch'; + $this->indices[$tbl][$field]['_diff'] = $offin; + $this->indices[$tbl][$field]['_valid'] = $info; + $this->indices[$tbl][$field]['_invalid'] = $sqlIndexData[$field]; + $this->indices[$tbl][$field]['_file'] = $selection; + + } + + // TODO Check for additional fields in SQL that should be removed. + // TODO Add support for MYSQL 5 table layout .eg. journal_id INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , + + } + + + unset($data); + + } + + + // print_a($this->results); + //echo "

Missing

"; + //print_a($this->missing); + // print_a($this->tables); + + } + + + function renderResults() + { + + $frm = e107::getForm(); + $ns = e107::getRender(); + $mes = e107::getMessage(); + + $text = " +
+
+ ".DBLAN_16." - $what ".DBLAN_18." + + + + + + + + + + + + + + + + + + + + "; + + $info = array( + 'missing_table' => DBLAN_13, + 'mismatch' => DBLAN_8, + 'missing_field' => DBLAN_11, + 'ok' => ADMIN_TRUE_ICON, + 'missing_index' => DBLAN_25, + ); + + $modes = array( + 'missing_table' => 'create', + 'mismatch' => 'alter', + 'missing_field' => 'insert', + 'missing_index' => 'index', + 'mismatch_index' => '', // TODO + ); + + foreach($this->results as $tabs => $field) + { + + if($this->errors[$tabs]['_status'] == 'missing_table') + { + $text .= " + + + + + + + + "; + } + elseif($this->errors[$tabs] != 'ok') + { + foreach($field as $k=>$f) + { + if($f['_status']=='ok') continue; + + $fstat = $info[$f['_status']]; + + $text .= " + + + + + + + + "; + } + } + + } + + + // Indices + + foreach($this->indices as $tabs => $field) + { + + if($this->errors[$tabs] != 'ok') + { + foreach($field as $k=>$f) + { + if($f['_status']=='ok') continue; + + $fstat = $info[$f['_status']]; + + $text .= " + + + + + + + + "; + } + } + + } + + + + $text .= " + +
".DBLAN_4.": {$k}".DBLAN_5."".DBLAN_6."".DBLAN_7."".DBLAN_19."
{$tabs} ".$info[$this->errors[$tabs]['_status']]." ".$this->fixForm($this->results[$tabs]['_file'],$tabs, 'all', '', 'create') . "
{$tabs}".$k." ".$fstat."".$this->renderNotes($f)." ".$this->fixForm($f['_file'],$tabs, $k, $f['_valid'], $modes[$f['_status']]) . "
{$tabs}".$k." ".$fstat."".$this->renderNotes($f,'index')." ".$this->fixForm($f['_file'],$tabs, $k, $f['_valid'], $modes[$f['_status']]) . "
+
+ "; + $text .= " +
+ ".$frm->admin_button('runfix', DBLAN_21, 'execute', '', array('id'=>false))." + ".$frm->admin_button('check_all', 'jstarget:fix_active', 'action', LAN_CHECKALL, array('id'=>false))." + ".$frm->admin_button('uncheck_all', 'jstarget:fix_active', 'action', LAN_UNCHECKALL, array('id'=>false))." +
+ +
+
+ "; + + + $ns->tablerender(DBLAN_23.' - '.DBLAN_16, $mes->render().$text); + + } + + + function fixForm($file,$table,$field, $newvalue,$mode,$after ='') + { + $frm = e107::getForm(); + $text .= $frm->checkbox("fix[$file][$table][$field]", $mode, false, array('id'=>false)); + + return $text; + } + + + function renderNotes($data,$mode='field') + { + // return "
".print_r($data,TRUE)."
"; + + $v = $data['_valid']; + $i = $data['_invalid']; + + $valid = $this->toMysql($v,$mode); + $invalid = $this->toMysql($i,$mode); + + $text = ""; + if($invalid) + { + $text .= "".DBLAN_9." +
".$invalid."
"; + } + + $text .= "".DBLAN_10." +
".$valid."
"; + + return $text; + } + + + + function toMysql($data,$mode = 'field') + { + + if(!$data) return; + + if($mode == 'index') + { + // print_a($data); + if($data['type']) + { + return $data['type']." (".$data['field'].");"; + } + else + { + return "INDEX `".$data['keyname']."` (".$data['field'].");"; + } + + } + + + if($data['type'] != 'TEXT') + { + return $data['type']."(".$data['value'].") ".$data['attributes']." ".$data['null']." ".$data['default']; + } + else + { + return $data['type']." ".$data['attributes']." ".$data['null']." ".$data['default']; + } + + } + + + + function runFix() + { + $mes = e107::getMessage(); + + if(!isset($_POST['runfix'])) + { + //print_a($_POST); + return; + + } + // print_a($_POST); + + + // $table = + // print_a($_POST['fix']); + // echo "

Select

"; + + + foreach($_POST['fix'] as $j=>$file) + { + + //print_a($this->tables[$j]); + + foreach($file as $table=>$val) + { + foreach($val as $field=>$mode) + { + + $key = array_flip($this->tables[$j]['tables']); + $id = $key[$table]; + + if(substr($mode,0,5)== 'index') + { + $fdata = $this->getIndex($this->tables[$j]['data'][$id]); + $newval = $this->toMysql($fdata[$field],'index'); + } + else + { + $fdata = $this->getFields($this->tables[$j]['data'][$id]); + $newval = $this->toMysql($fdata[$field]); + } + + + switch($mode) + { + case 'alter': + $query = "ALTER TABLE `".MPREFIX.$table."` CHANGE `$field` `$field` $newval"; + break; + + case 'insert': + if($after) $after = " AFTER {$after}"; + $query = "ALTER TABLE `".MPREFIX.$table."` ADD `$field` $newval{$after}"; + break; + + case 'drop': + $query = "ALTER TABLE `".MPREFIX.$table."` DROP `$field` "; + break; + + case 'index': + $query = "ALTER TABLE `".MPREFIX.$table."` ADD $newval "; + break; + + case 'indexdrop': + $query = "ALTER TABLE `".MPREFIX.$table."` DROP INDEX `$field`"; + break; + + case 'create': + $query = "CREATE TABLE `".MPREFIX.$table."` (".$this->tables[$j]['data'][$id].") ENGINE=MyISAM;"; + break; + } + + + //echo "QUery=".$query; + // continue; + if(mysql_query($query)) + { + $mes->add(LAN_UPDATED.' [ '.$query.' ]', E_MESSAGE_SUCCESS); + } + else + { + $mes->add(LAN_UPDATED_FAILED.' [ '.$query.' ]', E_MESSAGE_WARNING); + if(mysql_errno()) + { + $mes->add('     SQL #'.mysql_errno().': '.mysql_error(), E_MESSAGE_WARNING); + } + } + } + + } // + } + + } + + + + + function getTables($sql_data) + { + if(!$sql_data) + { + return; + } + + $ret = array(); + + $sql_data = preg_replace("#\/\*.*?\*\/#mis", '', $sql_data); // remove comments + + $regex = "/CREATE TABLE `?([\w]*)`?\s*?\(([\sa-z0-9_\(\),' `]*)\)\s*(ENGINE|TYPE)\s*?=\s?([\w]*)[\w =]*;/i"; + + $table = preg_match_all($regex,$sql_data,$match); + + $ret['tables'] = $match[1]; + $ret['data'] = $match[2]; + + return $ret; + } + + + function getFields($data) + { + + $regex = "/`?([\w]*)`?\s*(int|varchar|tinyint|smallint|text|char|tinyint) ?(?:\([\s]?([0-9]*)[\s]?\))?[\s]?(unsigned)?[\s]*?(NOT NULL|NULL)?[\s]*(auto_increment|default .*)?[\s]?,/i"; + preg_match_all($regex,$data,$m); + + $ret = array(); + + foreach($m[1] as $k=>$val) + { + $ret[$val] = array( + 'type' => strtoupper($m[2][$k]), + 'value' => $m[3][$k], + 'attributes' => strtoupper($m[4][$k]), + 'null' => strtoupper($m[5][$k]), + 'default' => strtoupper($m[6][$k]) + ); + } + + return $ret; + } + + + function getIndex($data) + { + $regex = "/(?:(PRIMARY|UNIQUE|FULLTEXT))[\s]*?KEY (?: ?`?([\w]*)`?)[\s]* ?(?:\([\s]?`?([\w]*[\s]?)`?\))?,?/i"; + preg_match_all($regex,$data,$m); + + $ret = array(); + + // print_a($m); + + foreach($m[3] as $k=>$val) + { + $ret[$val] = array( + 'type' => strtoupper($m[1][$k]), + 'keyname' => (vartrue($m[2][$k])) ? $m[2][$k] : $m[3][$k], + 'field' => $m[3][$k] + ); + } + + return $ret; + //print_a($ret); + } + + + + function getSqlData($tbl,$prefix='') + { + $mes = e107::getMessage(); + if(!$prefix) + { + $prefix = MPREFIX; + } + mysql_query('SET SQL_QUOTE_SHOW_CREATE = 1'); + $qry = 'SHOW CREATE TABLE `' . $prefix . $tbl . "`"; + $z = mysql_query($qry); + if($z) + { + $row = mysql_fetch_row($z); + return str_replace("`", "", stripslashes($row[1])).';'; + } + else + { + $mes->addDebug('Failed: '.$qry); + // echo "Failed".$qry; + return FALSE; + } + + } + + + + + function renderTableSelect() + { + $frm = e107::getForm(); + $ns = e107::getRender(); + $mes = e107::getMessage(); + + + $text = " +
+
+ ".DBLAN_14." + + + + + + + + + + + "; + + foreach(array_keys($this->tables) as $x) + { + $text .= " + + + + "; + } + + $text .= " + +
".$frm->checkbox_toggle('check-all-verify', 'table_').LAN_CHECKALL.' | '.LAN_UNCHECKALL."
".$frm->checkbox('verify_table[]', $x).$frm->label($x, 'table_'.$x, $x)."
+
+ ".$frm->admin_button('db_verify', DBLAN_15)." + ".$frm->admin_button('db_tools_back', DBLAN_17, 'back')." +
+
+
+ "; + + $ns->tablerender(DBLAN_23.' - '.DBLAN_16, $mes->render().$text); + } + + + + function sqlTableList() + { + + // grab default language lists. + global $mySQLdefaultdb; + + $exclude[] = "banlist"; $exclude[] = "banner"; + $exclude[] = "cache"; $exclude[] = "core"; + $exclude[] = "online"; $exclude[] = "parser"; + $exclude[] = "plugin"; $exclude[] = "user"; + $exclude[] = "upload"; $exclude[] = "userclass_classes"; + $exclude[] = "rbinary"; $exclude[] = "session"; + $exclude[] = "tmp"; $exclude[] = "flood"; + $exclude[] = "stat_info"; $exclude[] = "stat_last"; + $exclude[] = "submit_news"; $exclude[] = "rate"; + $exclude[] = "stat_counter";$exclude[] = "user_extended"; + $exclude[] = "user_extended_struct"; + $exclude[] = "pm_messages"; + $exclude[] = "pm_blocks"; + + $replace = array(); + + $lanlist = explode(",",e_LANLIST); + foreach($lanlist as $lang) + { + if($lang != $pref['sitelanguage']) + { + $replace[] = "lan_".strtolower($lang)."_"; + } + } + + $tables = mysql_list_tables($mySQLdefaultdb); + + while (list($temp) = mysql_fetch_array($tables)) + { + + $prefix = MPREFIX."lan_"; + $match = array(); + if(strpos($temp,$prefix)!==FALSE) + { + $e107tab = str_replace(MPREFIX, "", $temp); + $core = str_replace($replace,"",$e107tab); + if (str_replace($exclude, "", $e107tab)) + { + $tabs[$core] = $e107tab; + + } + } + } + + + return $tabs; + } + + + // ([\w]*)\s*(int|varchar|text|char|tinyint) ?(?:\([\s]?([0-9]*)[\s]?\))? (unsigned)?[\s]*(NOT NULL|NULL)[\s]*(auto_increment|default .*)?[\s]?, +} + + + + +/* //Get any plugin _sql.php files foreach($pref['e_sql_list'] as $path => $file) { @@ -346,18 +1034,18 @@ function check_tables($what) "; } - */ - /* DISABLED for now (show only errors), could be page setting - else - { - $body_txt .= " - OK -   -   - "; - } - */ + + // DISABLED for now (show only errors), could be page setting + // else + // { + // $body_txt .= " + // OK + //   + //   + // "; + // } +// } } // Finished checking one field @@ -370,7 +1058,7 @@ function check_tables($what) ".DBLAN_10."
{$fparams}
- ".fix_form($k, $fname, $fparams, "insert"/*, $prev_fname*/)." + ".fix_form($k, $fname, $fparams, "insert", $prev_fname)." "; $fix_active = TRUE; $xfield_errors++; @@ -554,11 +1242,12 @@ if(isset($_POST['do_fix'])) $query = "CREATE TABLE `".MPREFIX.$table."` ({$newval}"; if (!preg_match('#.*?\s+?(?:TYPE|ENGINE)\s*\=\s*(.*?);#is', $newval)) { - $query .= ') ENGINE=MyISAM;'; + $query .= ') TYPE=MyISAM;'; } break; } + return $query; //FIXME - db handler!!! if(mysql_query($query)) $emessage->add(LAN_UPDATED.' [ '.$query.' ]', E_MESSAGE_SUCCESS); else @@ -573,29 +1262,7 @@ if(isset($_POST['do_fix'])) } -// ---------------------- Main Form and Submit. ------------------------ -if (varset($_POST['db_verify']) || varset($_POST['do_fix'])) -{ - $text = ''; - foreach(array_keys($_POST) as $k) - { - $match = array(); - if (preg_match("/table_(.*)/", $k, $match)) - { - $xx = $match[1]; - $text .= check_tables($xx); - } - } - - if(!$text) $emessage->add(DBLAN_24, E_MESSAGE_WARNING); - else - { - $e107->ns->tablerender(DBLAN_23.' - '.DBLAN_16, $emessage->render().$text); - require_once(e_ADMIN."footer.php"); - exit; - } -} $text = "
@@ -670,11 +1337,13 @@ function fix_form($table,$field, $newvalue,$mode,$after ='') $field = trim($field, '`'); } + $text .= "\n\n"; $text .= $frm->checkbox("fix_active[$field][]", 1, false, array('id'=>false)); $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= ($after) ? "\n" : ""; + $text .= "\n\n"; return $text; } @@ -727,7 +1396,7 @@ function table_list() return $tabs; } - +*/ /** * Handle page DOM within the page header * diff --git a/e107_languages/English/admin/lan_admin.php b/e107_languages/English/admin/lan_admin.php index c8f7d1ed2..dfaede3e9 100644 --- a/e107_languages/English/admin/lan_admin.php +++ b/e107_languages/English/admin/lan_admin.php @@ -352,7 +352,7 @@ define('LAN_UI_PREF_LABEL', 'Settings'); define('LAN_UI_DELETE_LABEL', 'Confirm Delete'); define('LAN_UI_DELETE_WARNING', 'You are about to delete %1$d records. Please confirm to continue.'); -define("LAN_USER_MANAGEALL", "Manage all user access and settings"); +define("LAN_USER_MANAGEALL", "Manage all User, Userclass and Extended User-Field settings"); define("LAN_USER_LIST", "User List"); define("LAN_USER_QUICKADD","Quick Add User"); define("LAN_USER_PRUNE", "Prune Users"); diff --git a/e107_languages/English/admin/lan_db_verify.php b/e107_languages/English/admin/lan_db_verify.php index 40341d6d4..cd3a7529f 100644 --- a/e107_languages/English/admin/lan_db_verify.php +++ b/e107_languages/English/admin/lan_db_verify.php @@ -19,7 +19,7 @@ define("DBLAN_7", "Notes"); define("DBLAN_8", "Mismatch"); define("DBLAN_9", "Currently"); define("DBLAN_10", "should be"); -define("DBLAN_11", "Field missing"); +define("DBLAN_11", "Field missing!"); define("DBLAN_12", "Extra Field!"); define("DBLAN_13", "Table missing!"); define("DBLAN_14", "Choose table(s) to validate"); @@ -33,4 +33,5 @@ define("DBLAN_21", "Fix Selected Items"); define("DBLAN_22", " is not readable"); define("DBLAN_23", "Database Utilities"); define("DBLAN_24", "Please select action."); +define("DBLAN_25", "Index missing!"); ?> \ No newline at end of file