From 2741a446ea9e4b1418cdf8fd84b3d4aad298af58 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 13 Dec 2019 17:28:08 +0100 Subject: [PATCH] Integrate database integrity module --- .../cs.dict.combodo-db-tools.php | 87 +++ .../da.dict.combodo-db-tools.php | 87 +++ .../datamodel.combodo-db-tools.xml | 11 + .../db_analyzer.class.inc.php | 476 ++++++++++++++++ datamodels/2.x/combodo-db-tools/dbtools.php | 536 ++++++++++++++++++ .../de.dict.combodo-db-tools.php | 84 +++ datamodels/2.x/combodo-db-tools/default.css | 18 + datamodels/2.x/combodo-db-tools/default.scss | 30 + .../en.dict.combodo-db-tools.php | 88 +++ .../es_cr.dict.combodo-db-tools.php | 87 +++ .../fr.dict.combodo-db-tools.php | 83 +++ .../hu.dict.combodo-db-tools.php | 87 +++ .../it.dict.combodo-db-tools.php | 87 +++ .../ja.dict.combodo-db-tools.php | 87 +++ .../module.combodo-db-tools.php | 59 ++ .../nl.dict.combodo-db-tools.php | 87 +++ .../pt_br.dict.combodo-db-tools.php | 87 +++ .../ru.dict.combodo-db-tools.php | 87 +++ .../src/Service/DBToolsUtils.php | 173 ++++++ .../tr.dict.combodo-db-tools.php | 87 +++ .../zh_cn.dict.combodo-db-tools.php | 87 +++ 21 files changed, 2515 insertions(+) create mode 100644 datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/datamodel.combodo-db-tools.xml create mode 100644 datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php create mode 100644 datamodels/2.x/combodo-db-tools/dbtools.php create mode 100644 datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/default.css create mode 100644 datamodels/2.x/combodo-db-tools/default.scss create mode 100644 datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/src/Service/DBToolsUtils.php create mode 100644 datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php create mode 100644 datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php diff --git a/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php new file mode 100644 index 000000000..f6719c0b6 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('CS CZ', 'Czech', 'Čeština', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php new file mode 100644 index 000000000..9adde2565 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('DA DA', 'Danish', 'Dansk', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('DA DA', 'Danish', 'Dansk', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/datamodel.combodo-db-tools.xml b/datamodels/2.x/combodo-db-tools/datamodel.combodo-db-tools.xml new file mode 100644 index 000000000..5d7cdadbd --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/datamodel.combodo-db-tools.xml @@ -0,0 +1,11 @@ + + + + + 30 + System + $pages/exec.php?exec_module=combodo-db-tools&exec_page=dbtools.php&c[menu]=DBToolsMenu + 1 + + + diff --git a/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php new file mode 100644 index 000000000..6a3162a92 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php @@ -0,0 +1,476 @@ + +// + +class DatabaseAnalyzer +{ + var $iTimeLimitPerOperation; + + public function __construct($iTimeLimitPerOperation = null) + { + $this->iTimeLimitPerOperation = $iTimeLimitPerOperation; + } + + /** + * @param $sSelWrongRecs + * @param $sFixItRequest + * @param $sErrorDesc + * @param $sClass + * @param $aErrorsAndFixes + * @param array $aValueNames + * + * @throws \MySQLException + */ + private function ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, &$aErrorsAndFixes, $aValueNames = array()) + { + if (!is_null($this->iTimeLimitPerOperation)) + { + set_time_limit($this->iTimeLimitPerOperation); + } + + $aWrongRecords = CMDBSource::QueryToArray($sSelWrongRecs); + if (count($aWrongRecords) > 0) + { + foreach($aWrongRecords as $aRes) + { + if (!isset($aErrorsAndFixes[$sClass][$sErrorDesc])) + { + $aErrorsAndFixes[$sClass][$sErrorDesc] = array( + 'count' => 1, + 'query' => $sSelWrongRecs, + ); + if (!empty($sFixItRequest)) + { + $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = array($sFixItRequest); + } + } + else + { + $aErrorsAndFixes[$sClass][$sErrorDesc]['count'] += 1; + } + if (empty($aValueNames)) + { + $aValues = array('id' => $aRes['id']); + } + else + { + $aValues = array(); + foreach ($aValueNames as $sValueName) + { + $aValues[$sValueName] = $aRes[$sValueName]; + } + } + + if (isset($aRes['value'])) + { + $value = $aRes['value']; + $aValues['value'] = $value; + if (!isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value])) + { + $aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value] = 1; + } + else + { + $aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value] += 1; + } + } + $aErrorsAndFixes[$sClass][$sErrorDesc]['res'][] = $aValues; + } + } + } + + /** + * @param $aClassSelection + * + * @return array + * @throws \CoreException + * @throws \MySQLException + * @throws \Exception + */ + public function CheckIntegrity($aClassSelection) + { + // Getting and setting time limit are not symetric: + // www.php.net/manual/fr/function.set-time-limit.php#72305 + $iPreviousTimeLimit = ini_get('max_execution_time'); + + $aErrorsAndFixes = array(); + + if (empty($aClassSelection)) + { + $aClassSelection = MetaModel::GetClasses(); + } + else + { + $aClasses = $aClassSelection; + foreach($aClasses as $sClass) + { + $aExpectedClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); + $aClassSelection = array_merge($aClassSelection, $aExpectedClasses); + } + $aClassSelection = array_unique($aClassSelection); + } + + foreach($aClassSelection as $sClass) + { + // Check uniqueness rules + if (method_exists('MetaModel', 'GetUniquenessRules')) + { + $this->CheckUniquenessRules($sClass, $aErrorsAndFixes); + } + + if (!MetaModel::HasTable($sClass)) + { + continue; + } + + $sRootClass = MetaModel::GetRootClass($sClass); + $sTable = MetaModel::DBGetTable($sClass); + $sKeyField = MetaModel::DBGetKey($sClass); + + if (!MetaModel::IsStandaloneClass($sClass)) + { + if (!MetaModel::IsRootClass($sClass)) + { + $sRootTable = MetaModel::DBGetTable($sRootClass); + $sRootKey = MetaModel::DBGetKey($sRootClass); + + $this->CheckRecordsInRootTable($sTable, $sKeyField, $sRootTable, $sRootKey, $sClass, $aErrorsAndFixes); + $this->CheckRecordsInChildTable($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes); + } + } + + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + // Skip this attribute if not defined in this table + if (!MetaModel::IsAttributeOrigin($sClass, $sAttCode)) + { + continue; + } + + if ($oAttDef->IsExternalKey()) + { + $this->CheckExternalKeys($oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, $aErrorsAndFixes); + } + elseif ($oAttDef->IsDirectField() && !($oAttDef instanceof AttributeTagSet)) + { + $this->CheckEnums($sClass, $sAttCode, $oAttDef, $sTable, $sKeyField, $aErrorsAndFixes); + } + } + } + $this->CheckUsers($aErrorsAndFixes); + + if (!is_null($this->iTimeLimitPerOperation)) + { + set_time_limit($iPreviousTimeLimit); + } + return $aErrorsAndFixes; + } + + /** + * @param $sClass + * @param $sUniquenessRuleId + * @param $aUniquenessRuleProperties + * @param $aErrorsAndFixes + * + * @throws \CoreException + * @throws \MissingQueryArgument + * @throws \MySQLException + * @throws \OQLException + * @throws \Exception + */ + private function CheckUniquenessRule($sClass, $sUniquenessRuleId, $aUniquenessRuleProperties, &$aErrorsAndFixes) + { + $sOqlUniquenessQuery = "SELECT $sClass"; + if (!(empty($sUniquenessFilter = $aUniquenessRuleProperties['filter']))) + { + $sOqlUniquenessQuery .= ' WHERE '.$sUniquenessFilter; + } + $oUniquenessQuery = DBObjectSearch::FromOQL($sOqlUniquenessQuery); + + $aValueNames = array(); + $aGroupByExpr = array(); + foreach ($aUniquenessRuleProperties['attributes'] as $sAttributeCode) + { + $oExpr = Expression::FromOQL("$sClass.$sAttributeCode"); + $aGroupByExpr[$sAttributeCode] = $oExpr; + $aValueNames[] = $sAttributeCode; + } + + $aSelectExpr = array(); + + $sSQLUniquenessQuery = $oUniquenessQuery->MakeGroupByQuery(array(), $aGroupByExpr, false, $aSelectExpr); + + $sSQLUniquenessQuery .= ' having count(*) > 1'; + + $sErrorDesc = $this->GetUniquenessRuleMessage($sUniquenessRuleId); + + $this->ExecQuery($sSQLUniquenessQuery, '', $sErrorDesc, $sClass, $aErrorsAndFixes, $aValueNames); + if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['res'])) + { + $aFixit = array("-- In order to get the duplicates, run the following queries:"); + foreach ($aErrorsAndFixes[$sClass][$sErrorDesc]['res'] as $aValues) + { + $oFixSearch = new DBObjectSearch($sClass); + foreach ($aValues as $sAttCode => $sValue) + { + $oFixSearch->AddCondition($sAttCode, $sValue, '='); + } + $aFixit[] = $oFixSearch->MakeSelectQuery().';'; + $aFixit[] = ""; + } + $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixit; + } + + return; + } + + private function GetUniquenessRuleMessage($sUniquenessRuleId) + { + // we could add also a specific message if user is admin ("dict key is missing") + return Dict::Format('Core:UniquenessDefaultError', $sUniquenessRuleId); + } + + /** + * @param $sClass + * @param array $aErrorsAndFixes + * + * @throws \CoreException + */ + private function CheckUniquenessRules($sClass, &$aErrorsAndFixes) + { + if (method_exists('MetaModel', 'GetUniquenessRules')) + { + $aUniquenessRules = MetaModel::GetUniquenessRules($sClass); + foreach ($aUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties) + { + if ($aUniquenessRuleProperties['disabled'] === true) + { + continue; + } + $this->CheckUniquenessRule($sClass, $sUniquenessRuleId, $aUniquenessRuleProperties, $aErrorsAndFixes); + } + } + } + + /** + * Check that any record found here has its counterpart in the root table + * + * @param $sTable + * @param $sKeyField + * @param $sRootTable + * @param $sRootKey + * @param $sClass + * @param array $aErrorsAndFixes + * + * @throws \MySQLException + */ + private function CheckRecordsInRootTable($sTable, $sKeyField, $sRootTable, $sRootKey, $sClass, &$aErrorsAndFixes) + { + $sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id"; + $sDelete = "DELETE `$sTable`"; + $sFilter = "FROM `$sTable` LEFT JOIN `$sRootTable` ON `$sTable`.`$sKeyField` = `$sRootTable`.`$sRootKey` WHERE `$sRootTable`.`$sRootKey` IS NULL"; + $sSelectWrongRecs = "$sSelect $sFilter"; + $sFixItRequest = "$sDelete $sFilter"; + $this->ExecQuery($sSelectWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-OrphanRecord', $sTable, $sRootTable), $sClass, $aErrorsAndFixes); + } + + /** + * Check that any record found in the root table and referring to a child class + * has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy) + * + * @param $sRootClass + * @param $sClass + * @param $sRootTable + * @param $sRootKey + * @param $sTable + * @param $sKeyField + * @param array $aErrorsAndFixes + * + * @throws \CoreException + * @throws \MySQLException + */ + private function CheckRecordsInChildTable($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, &$aErrorsAndFixes) + { + $sFinalClassField = MetaModel::DBGetClassField($sRootClass); + $aExpectedClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); + $sExpectedClasses = implode(",", CMDBSource::Quote($aExpectedClasses, true)); + $sSelect = "SELECT DISTINCT `$sRootTable`.`$sRootKey` AS id"; + $sDelete = "DELETE `$sRootTable`"; + $sFilter = "FROM `$sRootTable` LEFT JOIN `$sTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses)"; + $sSelWrongRecs = "$sSelect $sFilter"; + $sFixItRequest = "$sDelete $sFilter"; + $this->ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-OrphanRecord', $sRootTable, $sTable), $sRootClass, $aErrorsAndFixes); + } + + /** + * Check that any external field is pointing to an existing object + * + * @param \AttributeDefinition $oAttDef + * @param $sTable + * @param $sKeyField + * @param $sAttCode + * @param $sClass + * @param array $aErrorsAndFixes + * + * @throws \CoreException + * @throws \MySQLException + */ + private function CheckExternalKeys(AttributeDefinition $oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, &$aErrorsAndFixes) + { + $sRemoteClass = $oAttDef->GetTargetClass(); + $sRemoteTable = MetaModel::DBGetTable($sRemoteClass); + $sRemoteKey = MetaModel::DBGetKey($sRemoteClass); + + $aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc()) + $sExtKeyField = current($aCols); // get the first column for an external key + + // Note: a class/table may have an external key on itself + $sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sExtKeyField` AS value"; + $sFilter = "FROM `$sTable` LEFT JOIN `$sRemoteTable` AS `{$sRemoteTable}_1` ON `$sTable`.`$sExtKeyField` = `{$sRemoteTable}_1`.`$sRemoteKey`"; + + $sFilter = $sFilter." WHERE `{$sRemoteTable}_1`.`$sRemoteKey` IS NULL"; + // Exclude the records pointing to 0/null from the errors (separate test below) + $sFilter .= " AND `$sTable`.`$sExtKeyField` IS NOT NULL"; + $sFilter .= " AND `$sTable`.`$sExtKeyField` != 0"; + + $sSelWrongRecs = "$sSelect $sFilter"; + + $sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidExtKey', $sAttCode, $sTable, $sExtKeyField); + $this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes); + // Fix it request needs the values of the enum to generate the requests + if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values'])) + { + $aFixIt = array(); + $aFixIt[] = "-- Remove inconsistant entries:"; + $sIds = implode(', ', array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values'])); + $aFixIt[] = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)"; + $aFixIt[] = ""; + $aFixIt[] = "-- Or fix inconsistant values: Replace XXX with the appropriate value"; + foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) + { + $aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` = '$sKey'"; + } + $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt; + } + + if (!$oAttDef->IsNullAllowed()) + { + $sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id"; + $sDelete = "DELETE `$sTable`"; + $sFilter = "FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0"; + $sSelWrongRecs = "$sSelect $sFilter"; + $sFixItRequest = "$sDelete $sFilter"; + $sErrorDesc = Dict::Format('DBAnalyzer-Integrity-MissingExtKey', $sAttCode, $sTable, $sExtKeyField); + $this->ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, $aErrorsAndFixes); + if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['count']) && ($aErrorsAndFixes[$sClass][$sErrorDesc]['count'] > 0)) + { + $aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit']; + $aFixIt[] = "-- Alternate fix"; + $aFixIt[] = "-- Replace XXX with the appropriate value"; + $aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0"; + $aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField); + foreach ($aAdditionalFixIt as $sFixIt) + { + $aFixIt[] = $sFixIt; + } + $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt; + } + } + } + + private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField) + { + $aFixIt = array(); + if ($sTable == 'ticket' && $sExtKeyField == 'org_id') + { + $aFixIt[] = "-- Alternate fix: set the ticket org to the caller org"; + $aFixIt[] = "UPDATE ticket AS t JOIN contact AS c ON t.caller_id=c.id SET t.org_id=c.org_id WHERE t.org_id IS NULL OR t.org_id = 0"; + } + return $aFixIt; + } + + /** + * Check that the values fit the allowed values + * + * @param $sClass + * @param $sAttCode + * @param \AttributeDefinition $oAttDef + * @param $sTable + * @param $sKeyField + * @param array $aErrorsAndFixes + * + * @throws \MySQLException + * @throws \Exception + */ + private function CheckEnums($sClass, $sAttCode, AttributeDefinition $oAttDef, $sTable, $sKeyField, &$aErrorsAndFixes) + { + $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode); + if (!is_null($aAllowedValues) && count($aAllowedValues) > 0) + { + $sExpectedValues = implode(",", CMDBSource::Quote(array_keys($aAllowedValues), true)); + + $aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc()) + $sMyAttributeField = current($aCols); // get the first column for the moment + $sFilter = "FROM `$sTable` WHERE `$sTable`.`$sMyAttributeField` NOT IN ($sExpectedValues)"; + $sDelete = "DELETE `$sTable`"; + $sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sMyAttributeField` AS value"; + $sSelWrongRecs = "$sSelect $sFilter"; + $sFixItRequest = "$sDelete $sFilter"; + $sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidValue', $sAttCode, $sTable, $sMyAttributeField); + $this->ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, $aErrorsAndFixes); + // Fix it request needs the values of the enum to generate the requests + if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values'])) + { + if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'])) + { + $aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit']; + $aFixIt[] = "-- Alternative: Replace 'XXX' with the appropriate value"; + } + else + { + $aFixIt = array("-- Replace 'XXX' with the appropriate value"); + } + foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) + { + $aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = 'XXX' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'"; + } + $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt; + } + } + } + + /** + * @param $aErrorsAndFixes + * + * @throws \CoreException + * @throws \MySQLException + */ + private function CheckUsers(&$aErrorsAndFixes) + { +// Check user accounts without profile + $sUserTable = MetaModel::DBGetTable('User'); + $sLinkTable = MetaModel::DBGetTable('URP_UserProfile'); + $sSelect = "SELECT DISTINCT u.id AS id, u.`login` AS value"; + $sFilter = "FROM `$sUserTable` AS u LEFT JOIN `$sLinkTable` AS l ON l.userid = u.id WHERE l.id IS NULL"; + $sSelWrongRecs = "$sSelect $sFilter"; + $sFixit = "-- Remove the corresponding user(s)"; + $this->ExecQuery($sSelWrongRecs, $sFixit, Dict::S('DBAnalyzer-Integrity-UsersWithoutProfile'), 'User', $aErrorsAndFixes); + } + + +} diff --git a/datamodels/2.x/combodo-db-tools/dbtools.php b/datamodels/2.x/combodo-db-tools/dbtools.php new file mode 100644 index 000000000..75744aced --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/dbtools.php @@ -0,0 +1,536 @@ + +// + +@include_once('../../approot.inc.php'); +require_once(APPROOT.'application/startup.inc.php'); + +require_once('db_analyzer.class.inc.php'); + +const MAX_RESULTS = 10; + +/** + * @param iTopWebPage $oP + * @param ApplicationContext $oAppContext + * + * @return \iTopWebPage + * @throws CoreException + * @throws DictExceptionMissingString + * @throws MySQLException + */ +function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppContext) +{ + $iShowId = intval(utils::ReadParam('show_id', '0')); + $sErrorLabelSelection = utils::ReadParam('error_selection', ''); + $sClassSelection = utils::ReadParam('class_selection', ''); + if (!empty($sClassSelection)) + { + $aClassSelection = explode(",", $sClassSelection); + } + else + { + $aClassSelection = array(); + } + $sClassSelection = utils::ReadParam('class_selection', ''); + + $oP->SetCurrentTab(Dict::S('DBTools:Inconsistencies')); + + $bRunAnalysis = intval(utils::ReadParam('run_analysis', '0')); + if ($bRunAnalysis) + { + $oDBAnalyzer = new DatabaseAnalyzer(); + $aResults = $oDBAnalyzer->CheckIntegrity($aClassSelection); + if (empty($aResults)) + { + $oP->p('
'.Dict::S('DBTools:NoError').'
'); + } + } + + $oP->add('
'); + $oP->add("
"); + $oP->add(''); + + $oP->add("\n"); + + $oP->add("
"); + $sChecked = ($iShowId == 0) ? 'checked' : ''; + $oP->add("'); + $oP->add(""); + $sChecked = ($iShowId == 1) ? 'checked' : ''; + $oP->add("'); + $oP->add(""); + $sChecked = ($iShowId == 3) ? 'checked' : ''; + $oP->add("'); + $oP->add("

\n"); + + $oP->add("\n"); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add($oAppContext->GetForForm()); + $oP->add("
\n"); + $oP->add('
'); + + + if (!empty($sErrorLabelSelection) || !empty($sClassSelection)) + { + $oP->add("
"); + $oP->add("
"); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add("\n"); + $oP->add("
\n"); + } + + if (!empty($aResults)) + { + + if ($iShowId == 3) + { + DisplayInconsistenciesReport($aResults); + } + + $oP->p(Dict::S('DBTools:ErrorsFound')); + + $oP->add(''); + $bTable = true; + foreach($aResults as $sClass => $aErrorList) + { + foreach($aErrorList as $sErrorLabel => $aError) + { + if (!empty($sErrorLabelSelection) && ($sErrorLabel != $sErrorLabelSelection)) + { + continue; + } + + if (!$bTable) + { + $oP->add('
'); + $oP->add('
'.Dict::S('DBTools:Class').''.Dict::S('DBTools:Count').''.Dict::S('DBTools:Error').'
'); + $bTable = true; + } + + $oP->add(''); + + + $oP->add(''); + $iCount = $aError['count']; + $oP->add(''); + $oP->add(''); + $oP->add(''); + + if ($iShowId > 0) + { + $oP->add('
ClassCountError
'.MetaModel::GetName($sClass).' ('.$sClass.')'.$iCount.''.$sErrorLabel.'
'); + $bTable = false; + $oP->p(Dict::S('DBTools:SQLquery')); + $sQuery = $aError['query']; + $oP->add('
'); + $oP->add(''.$sQuery.''); + $oP->add('
'); + + if (isset($aError['fixit'])) + { + $oP->p(Dict::S('DBTools:FixitSQLquery')); + $aQueries = $aError['fixit']; + $oP->add('
'); + foreach($aQueries as $sFixQuery) + { + $oP->add('
'.$sFixQuery.'
'); + } + $oP->add('
'); + } + + $oP->p(Dict::S('DBTools:SQLresult')); + $sQueryResult = ''; + $iCount = count($aError['res']); + $iMaxCount = MAX_RESULTS; + foreach($aError['res'] as $aRes) + { + $iMaxCount--; + if ($iMaxCount < 0) + { + $sQueryResult .= 'Displayed '.MAX_RESULTS."/$iCount results.
"; + break; + } + foreach($aRes as $sKey => $sValue) + { + $sQueryResult .= "'$sKey'='$sValue' "; + } + $sQueryResult .= '
'; + } + $oP->add('
'); + $oP->add(''.$sQueryResult.''); + $oP->add('
'); + } + } + } + $oP->add(''); + } + return $oP; +} + +/** + * @param $aResults + * + * @return mixed + * @throws CoreException + * @throws DictExceptionMissingString + */ +function DisplayInconsistenciesReport($aResults) +{ + $sDBToolsFolder = str_replace("\\", '/', APPROOT.'log/'); + $sReportFile = 'dbtools-report-'.date('Y-m-d-H-i-s'); + + $fReport = fopen($sDBToolsFolder.$sReportFile.'.txt', 'w'); + fwrite($fReport, 'Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n"); + foreach($aResults as $sClass => $aErrorList) + { + fwrite($fReport, ''); + foreach($aErrorList as $sErrorLabel => $aError) + { + fwrite($fReport, "\r\n----------\r\n"); + fwrite($fReport, 'Class: '.MetaModel::GetName($sClass).' ('.$sClass.")\r\n"); + $iCount = $aError['count']; + fwrite($fReport, 'Count: '.$iCount."\r\n"); + fwrite($fReport, 'Error: '.$sErrorLabel."\r\n"); + $sQuery = $aError['query']; + fwrite($fReport, 'Query: '.$sQuery."\r\n"); + + if (isset($aError['fixit'])) + { + fwrite($fReport, "\r\nFix it (indication):\r\n\r\n"); + $aFixitQueries = $aError['fixit']; + foreach($aFixitQueries as $sFixitQuery) + { + fwrite($fReport, "$sFixitQuery\r\n"); + } + fwrite($fReport, "\r\n"); + } + + $sQueryResult = ''; + $aIdList = array(); + foreach($aError['res'] as $aRes) + { + foreach($aRes as $sKey => $sValue) + { + $sQueryResult .= "'$sKey'='$sValue' "; + if ($sKey == 'id') + { + $aIdList[] = $sValue; + } + } + $sQueryResult .= "\r\n"; + + } + fwrite($fReport, "Result: \r\n".$sQueryResult); + $sIdList = '('.implode(',', $aIdList).')'; + fwrite($fReport, 'Ids: '.$sIdList."\r\n"); + } + } + fclose($fReport); + + $oArchive = new ZipArchive(); + $oArchive->open($sDBToolsFolder.$sReportFile.'.zip', ZipArchive::CREATE); + $oArchive->addFile($sDBToolsFolder.$sReportFile.'.txt', $sReportFile.'.txt'); + $oArchive->close(); + unlink($sDBToolsFolder.$sReportFile.'.txt'); + $sReportFile = $sDBToolsFolder.$sReportFile.'.zip'; + + + header('Content-Description: File Transfer'); + header('Content-Type: multipart/x-zip'); + header('Content-Disposition: inline; filename="'.basename($sReportFile).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Length: '.filesize($sReportFile)); + readfile($sReportFile); + unlink($sReportFile); + exit(0); +} + +/** + * @param iTopWebPage $oP + * @param ApplicationContext $oAppContext + * + * @return \iTopWebPage + * @throws CoreException + * @throws MySQLException + * @throws \Exception + */ +function DisplayLostAttachments(iTopWebPage &$oP, ApplicationContext &$oAppContext) +{ + // Retrieve parameters + $sStepName = utils::ReadParam('step_name'); + $aRecordsToClean = utils::ReadParam('dbt-cbx', array(), false, 'raw_data'); + + $iRestoredItemsCount = 0; + $iRecordsToCleanCount = count($aRecordsToClean); + $aErrorsReport = array(); + + $bDoAnalyze = in_array($sStepName, array('analyze', 'restore')); + $bDoRestore = in_array($sStepName, array('restore')); + + // Build HTML + $oP->SetCurrentTab(Dict::S('DBTools:LostAttachments')); + + $oP->add('
'); + $oP->add('
'); + + $oP->add('
'.Dict::S('DBTools:LostAttachments:Disclaimer').'
'); + $oP->add('
'); + $oP->add('
'); + $oP->add(''); + $oP->add(''); + + // Step 1: Analyze DB + $oP->add('

1.'.Dict::S('DBTools:LostAttachments:Step:Analyze').'

'); + + // Step 2: Display results + if($bDoAnalyze) + { + // Check if we have to restore some items first + if($bDoRestore) + { + foreach($aRecordsToClean as $sRecordToClean) + { + utils::PushArchiveMode(false); // For iTop < 2.5, the application can be wrongly set to archive mode true when it fails from retrieving an object. See r5340. + try + { + // Retrieve attachment + $aLocationParts = explode('::', $sRecordToClean); + /** @var \DBObject $oOriginObject */ + $oOriginObject = MetaModel::GetObject($aLocationParts[0], $aLocationParts[1], true, true); + /** @var \ormDocument $oOrmDocument */ + $oOrmDocument = $oOriginObject->Get('contents'); + + // Retrieve target object + $sTargetClass = $oOriginObject->Get('item_class'); + $sTargetId = $oOriginObject->Get('item_id'); + /** @var \DBObject $oTargetObject */ + $oTargetObject = MetaModel::GetObject($sTargetClass, $sTargetId, true, true); + + // Put it on the target object + /** @var \Attachment $oAttachment */ + $oAttachment = MetaModel::NewObject('Attachment'); + $oAttachment->Set('item_class', $sTargetClass); + $oAttachment->Set('item_id', $sTargetId); + $oAttachment->Set('item_org_id', $oTargetObject->Get('org_id')); + $oAttachment->Set('contents', $oOrmDocument); + $oAttachment->DBInsert(); + + // Put history entry + $sHistoryEntry = Dict::Format('DBTools:LostAttachments:History', $oOrmDocument->GetFileName()); + CMDBObject::SetTrackInfo(UserRights::GetUserFriendlyName()); + $oChangeOp = MetaModel::NewObject('CMDBChangeOpPlugin'); + /** @var \Change $oChange */ + $oChange = CMDBObject::GetCurrentChange(); + $oChangeOp->Set('change', $oChange->GetKey()); + $oChangeOp->Set('objclass', $sTargetClass); + $oChangeOp->Set('objkey', $sTargetId); + $oChangeOp->Set('description', $sHistoryEntry); + $oChangeOp->DBInsert(); + + // Remove origin object (should only be done for InlineImage) + $oOriginObject->DBDelete(); + + $iRestoredItemsCount++; + } + catch(Exception $e) + { + $aErrorsReport[] = 'Could not restore attachment from '.$sRecordToClean.', cause: '.$e->getMessage(); + } + utils::PopArchiveMode(); + } + } + + // Search attachments stored as inline images + $sInlineImageDBTable = MetaModel::DBGetTable('InlineImage'); + $sSelWrongRecs = 'SELECT id, secret, "InlineImage" AS current_class, id AS current_id, item_class AS target_class, item_id AS target_id, contents_filename AS filename FROM '.$sInlineImageDBTable.' WHERE contents_mimetype NOT LIKE "image/%"'; + $aWrongRecords = CMDBSource::QueryToArray($sSelWrongRecs); + + $oP->add('
'); + $oP->add('

2.'.Dict::S('DBTools:LostAttachments:Step:AnalyzeResults').'

'); + + if(empty($aWrongRecords)) + { + $oP->add('
'.Dict::S('DBTools:LostAttachments:Step:AnalyzeResults:None').'
'); + } + else + { + $oP->add('
'.Dict::Format('DBTools:LostAttachments:Step:AnalyzeResults:Some', count($aWrongRecords)).'
'); + + // Display errors as table + $oP->add(''); + $oP->add(''); + + foreach($aWrongRecords as $iIndex => $aWrongRecord) + { + + $sCurrentClass = $aWrongRecord['current_class']; + $sCurrentId = $aWrongRecord['current_id']; + $sRecordToClean = Dict::S('DBTools:LostAttachments:StoredAsInlineImage'); + + $sTargetClass = $aWrongRecord['target_class']; + $sTargetId = $aWrongRecord['target_id']; + $sTargetLocation = ''.$sTargetClass.'::'.$sTargetId.''; + + $sFilename = ''.$aWrongRecord['filename'].''; + + $sRowClass = ($iIndex % 2 === 0) ? 'odd' : 'even'; // (Starts at 0, not 1) + $oP->add(''); + } + + $oP->add('
'.Dict::S('DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename').''.Dict::S('DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation').''.Dict::S('DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation').'
'.$sFilename.''.$sRecordToClean.''.$sTargetLocation.'
'); + $oP->add('
'); + + // JS to handle checkboxes and button + $oP->add_ready_script( +<< 0 ) + { + $('.dbt-lostattachments .dbt-toggler-cbx').prop('checked', false); + } + }); +EOF + ); + } + + $oP->add('
'); + } + + // Step 3: Restore results + if($bDoRestore) + { + $oP->add('
'); + $oP->add('

3.'.Dict::S('DBTools:LostAttachments:Step:RestoreResults').'

'); + + $oP->add('
'.Dict::Format('DBTools:LostAttachments:Step:RestoreResults:Results', $iRestoredItemsCount, $iRecordsToCleanCount).'
'); + + if(!empty($aErrorsReport)) + { + foreach($aErrorsReport as $sErrorReport) + { + $oP->add('
'.$sErrorReport.'
'); + } + } + + $oP->add('
'); + } + + $oP->add($oAppContext->GetForForm()); + $oP->add('
'); + $oP->add('
'); + + $oP->add('
'); + $oP->add('
'); + + // Buttons disabling on click + $sConfirmText = Dict::S('DBTools:LostAttachments:Button:Restore:Confirm'); + $sButtonBusyText = Dict::S('DBTools:LostAttachments:Button:Busy'); + $oP->add_ready_script( +<<add_saas('env-'.utils::GetCurrentEnvironment().'/combodo-db-tools/default.scss'); + + $oP->add( +<< +

$sPageTitle

+ +EOF + ); + $oP->AddTabContainer('db-tools'); + $oP->SetCurrentTabContainer('db-tools'); + + // DB Inconsistences + $oP = DisplayDBInconsistencies($oP, $oAppContext); + + // Lost attachments + $oP = DisplayLostAttachments($oP, $oAppContext); +} +catch (Exception $e) +{ + $oP->p(''.$e->getMessage().''); +} + +if (isset($oP)) +{ + $oP->output(); +} diff --git a/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php new file mode 100644 index 000000000..6884af762 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php @@ -0,0 +1,84 @@ + +// +// Copyright 2018 David Gümbel, ITOMIG GmbH, david.guembel @ itomig DE +// Database inconsistencies +Dict::Add('DE DE', 'German', 'Deutsch', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools', + 'DBTools:Class' => 'Klasse', + 'DBTools:Title' => 'Datenbank-Pflege-Tools', + 'DBTools:ErrorsFound' => 'Fehler gefunden', + 'DBTools:Error' => 'Fehler', + 'DBTools:Count' => 'Anzahl', + 'DBTools:SQLquery' => 'SQL Query', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL Ergebnis', + 'DBTools:NoError' => 'Die Datenbank ist OK', + 'DBTools:HideIds' => 'Fehler', + 'DBTools:ShowIds' => 'Fehler und Werte', + 'DBTools:ShowReport' => 'Report', + 'DBTools:IntegrityCheck' => 'Integritätscheck', + 'DBTools:FetchCheck' => 'Fetch Check (dauert länger)', + + 'DBTools:Analyze' => 'Analysiere', + 'DBTools:Details' => 'Details anzeigen', + 'DBTools:ShowAll' => 'Alle Fehler anzeigen', + + 'DBTools:Inconsistencies' => 'Datenbank-Inkonsistenzen', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Verwaister Eintrag in `%1$s`, er sollte eine Entsprechung in Tabelle `%2$s` haben', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Ungültiger Externer Key %1$s (Spalte: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Fehlender Externer Key %1$s (Spalte: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-InvalidValue' => 'Ungültiger Wert für %1$s (Spalte: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Manche Benutzerkonten haben keinerlei zugewiesenes Profi', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch-Count-Fehler in `%1$s`, %2$d Einträge geholt (fetched) / %3$d gezählt', +)); + +// Database Info +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'DBTools:DatabaseInfo' => 'Datenbank-Information', + 'DBTools:Base' => 'Datenbank', + 'DBTools:Size' => 'Größe', +)); + +// Lost attachments +Dict::Add('DE DE', 'German', 'Deutsch', array( + 'DBTools:LostAttachments' => 'Verlorene Attachments', + 'DBTools:LostAttachments:Disclaimer' => 'Hier können Sie Ihre Datenbank nach verlorenen oder falsch platzierten Attachments durchsuchen. Dies ist kein Recovery-Tool - es stellt keine gelöschten Daten wieder her.', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analysieren', + 'DBTools:LostAttachments:Button:Restore' => 'Wiederherstellen', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'Diese Aktion kann nicht rückgängig gemacht werden, bitte bestätigen Sie dass Sie die ausgewählten Dateien wiederherstellen möchten.', + 'DBTools:LostAttachments:Button:Busy' => 'Bitte warten...', + + 'DBTools:LostAttachments:Step:Analyze' => 'Suche zunächst nach verlorenen / falsch platzierten Attachments, mittels einer Analyse der Datenbank', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyseergebnisse:', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Toll! Alles scheint am richtigen Ort zu sein.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Manche Attachments scheinen am falschen Ort zu sein. Werfen Sie einen Blick auf die folgende Liste und wählen Sie diejenigen aus, die Sie gerne verschieben möchten.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Dateiname', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Derzeitiger Ort', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Verschieben nach...', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore-Ergebnisse:', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d Attachments wurden wiederhergestellt.', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Als Inline-Bild gespeichert', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" mit DB-Tools wiederhergestellt' +)); diff --git a/datamodels/2.x/combodo-db-tools/default.css b/datamodels/2.x/combodo-db-tools/default.css new file mode 100644 index 000000000..df91037d5 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/default.css @@ -0,0 +1,18 @@ +.db-tools-tab-content .dbt-lostattachments .dbt-steps .dbt-step { + margin-top: 30px; + /* This is to avoid long exception message on restore errors */ +} +.db-tools-tab-content .dbt-lostattachments .dbt-steps .dbt-step .dbt-step-description { + padding: 0; + margin: 0px 0px 5px 0px; +} +.db-tools-tab-content .dbt-lostattachments .dbt-steps .dbt-step .dbt-step-number { + margin-right: 0.3em; +} +.db-tools-tab-content .dbt-lostattachments .dbt-steps .dbt-step .listResults tr > *:first-child { + text-align: center; +} +.db-tools-tab-content .dbt-lostattachments .dbt-steps .dbt-step .message_error { + max-height: 150px; + overflow-y: auto; +} diff --git a/datamodels/2.x/combodo-db-tools/default.scss b/datamodels/2.x/combodo-db-tools/default.scss new file mode 100644 index 000000000..f33e73c81 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/default.scss @@ -0,0 +1,30 @@ +.db-tools-tab-content{ + .dbt-lostattachments{ + .dbt-steps{ + .dbt-step{ + margin-top: 30px; + + .dbt-step-description{ + padding: 0; + margin: 0px 0px 5px 0px; + } + + .dbt-step-number{ + margin-right: 0.3em; + } + + .listResults{ + tr>*:first-child{ + text-align: center; + } + } + + /* This is to avoid long exception message on restore errors */ + .message_error{ + max-height: 150px; + overflow-y: auto; + } + } + } + } +} \ No newline at end of file diff --git a/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php new file mode 100644 index 000000000..ddad8345c --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php @@ -0,0 +1,88 @@ + + */ + +// Database inconsistencies +Dict::Add('EN US', 'English', 'English', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'Database integrity', + 'DBTools:Class' => 'Class', + 'DBTools:Title' => 'Database Maintenance Tools', + 'DBTools:ErrorsFound' => 'Errors Found', + 'DBTools:Error' => 'Error', + 'DBTools:Count' => 'Count', + 'DBTools:SQLquery' => 'SQL query', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)', + 'DBTools:SQLresult' => 'SQL result', + 'DBTools:NoError' => 'The database is OK', + 'DBTools:HideIds' => 'Error List', + 'DBTools:ShowIds' => 'Detailed view', + 'DBTools:ShowReport' => 'Report', + 'DBTools:IntegrityCheck' => 'Integrity check', + 'DBTools:FetchCheck' => 'Fetch Check (long)', + + 'DBTools:Analyze' => 'Analyze', + 'DBTools:Details' => 'Show Details', + 'DBTools:ShowAll' => 'Show All Errors', + + 'DBTools:Inconsistencies' => 'Database inconsistencies', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted', +)); + +// Database Info +Dict::Add('EN US', 'English', 'English', array( + 'DBTools:DatabaseInfo' => 'Database Information', + 'DBTools:Base' => 'Base', + 'DBTools:Size' => 'Size', +)); + +// Lost attachments +Dict::Add('EN US', 'English', 'English', array( + 'DBTools:LostAttachments' => 'Lost attachments', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze', + 'DBTools:LostAttachments:Button:Restore' => 'Restore', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools' +)); diff --git a/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php new file mode 100644 index 000000000..002c819c1 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php new file mode 100644 index 000000000..6a2bb8fed --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php @@ -0,0 +1,83 @@ + +// +// Database inconsistencies +Dict::Add('FR FR', 'French', 'Français', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'Intégrité base de données', + 'DBTools:Class' => 'Classe', + 'DBTools:Title' => 'Outils maintenance base de données', + 'DBTools:ErrorsFound' => 'Erreurs trouvées', + 'DBTools:Error' => 'Erreur', + 'DBTools:Count' => 'Nombre', + 'DBTools:SQLquery' => 'Requête SQL', + 'DBTools:FixitSQLquery' => 'Requête SQL pour nettoyer la base (indication)', + 'DBTools:SQLresult' => 'Résultat SQL', + 'DBTools:NoError' => 'La base de données est OK', + 'DBTools:HideIds' => 'Erreurs', + 'DBTools:ShowIds' => 'Détails des erreurs', + 'DBTools:ShowReport' => 'Rapport', + 'DBTools:IntegrityCheck' => 'Contrôle d\'intégrité', + 'DBTools:FetchCheck' => 'Contrôle de récupération (long)', + + 'DBTools:Analyze' => 'Analyser', + 'DBTools:Details' => 'Afficher détails', + 'DBTools:ShowAll' => 'Afficher toutes les erreurs', + + 'DBTools:Inconsistencies' => 'Incohérences de base de données', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la tableit `%2$s`', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Clef externe invalide %1$s (colonne: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Clef externe manquante %1$s (colonne: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-InvalidValue' => 'Valeur invalide pour %1$s (colonne: `%2$s.%3$s`)', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Certains comptes utilisateurs n\'ont aucun profile', + 'DBAnalyzer-Fetch-Count-Error' => 'Erreur de récupération dans `%1$s`, %2$d enregistrements récupérés / %3$d comptés', +)); + +// Database Info +Dict::Add('FR FR', 'French', 'Français', array( + 'DBTools:DatabaseInfo' => 'Information Base de Données', + 'DBTools:Base' => 'Base', + 'DBTools:Size' => 'Taille', +)); + +// Lost attachments +Dict::Add('FR FR', 'French', 'Français', array( + 'DBTools:LostAttachments' => 'Pièces jointes perdues', + 'DBTools:LostAttachments:Disclaimer' => 'Ici vous pouvez retrouver des pièces jointes perdues ou égarées dans votre base de données. Ceci n\'est PAS un outil de récupération des données, il ne récupère pas les données effacées.', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyser', + 'DBTools:LostAttachments:Button:Restore' => 'Restaurer', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'Cet action ne peut être annuler, veuillez confirmer que vous voulez restaurer les fichiers sélectionnés.', + 'DBTools:LostAttachments:Button:Busy' => 'Patientez ...', + + 'DBTools:LostAttachments:Step:Analyze' => 'Tout d\'abord, scannez la base de données à la recherche de pièces jointes perdues/égarées.', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Résultat de l\'analyse :', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Parfait ! Il semble que tout soit en ordre.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Certaines pièces jointes (%1$d) semblent être au mauvais endroit. Examinez la liste suivante et cochez celles que vous souhaitez déplacer.', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Nom de fichier', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Emplacement actuel', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Déplacer vers ...', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Résultats de la restauration :', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d pièces jointes ont été restaurées.', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stockée comme "InlineImage"', + 'DBTools:LostAttachments:History' => 'Pièce jointe "%1$s" restaurée avec l\'outil de BDD' +)); diff --git a/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php new file mode 100644 index 000000000..fb1dac12d --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('HU HU', 'Hungarian', 'Magyar', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php new file mode 100644 index 000000000..7d5ea3f5f --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('IT IT', 'Italian', 'Italiano', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('IT IT', 'Italian', 'Italiano', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php new file mode 100644 index 000000000..d6dfe83ef --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('JA JP', 'Japanese', '日本語', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('JA JP', 'Japanese', '日本語', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php new file mode 100644 index 000000000..523b2bdd7 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php @@ -0,0 +1,59 @@ + +// + +// +// iTop module definition file +// + +SetupWebPage::AddModule( + __FILE__, // Path to the current file, all other file names are relative to the directory containing this file + 'combodo-db-tools/1.0.8', + array( + // Identification + // + 'label' => 'Database maintenance tools', + 'category' => 'business', + + // Setup + // + 'dependencies' => array( + ), + 'mandatory' => false, + 'visible' => true, + + // Components + // + 'datamodel' => array( + 'model.combodo-db-tools.php', + 'src/Service/DBToolsUtils.php' + ), + 'webservice' => array(), + 'data.struct' => array(), + 'data.sample' => array(), + + // Documentation + // + 'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any + 'doc.more_information' => '', // hyperlink to more information, if any + + // Default settings + // + 'settings' => array(), + ) +); diff --git a/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php new file mode 100644 index 000000000..dae102a31 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('NL NL', 'Dutch', 'Nederlands', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php new file mode 100644 index 000000000..3e0a35f22 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php new file mode 100644 index 000000000..f210b1315 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('RU RU', 'Russian', 'Русский', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('RU RU', 'Russian', 'Русский', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/src/Service/DBToolsUtils.php b/datamodels/2.x/combodo-db-tools/src/Service/DBToolsUtils.php new file mode 100644 index 000000000..8335eea9e --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/src/Service/DBToolsUtils.php @@ -0,0 +1,173 @@ +fetch_assoc(); + $sSize = $aRow['sz']; + return (int)$sSize; + } + + return 0; + } + /** + * @return int + * @throws \CoreException + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + */ + public final static function GetDBDataSize() + { + $sSchema = CMDBSource::DBName(); + + $sReq = <<fetch_assoc(); + $sSize = $aRow['sz']; + return (int)$sSize; + } + + return 0; + } + /** + * @return int + * @throws \CoreException + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + */ + public final static function GetDBIndexSize() + { + $sSchema = CMDBSource::DBName(); + + $sReq = <<fetch_assoc(); + $sSize = $aRow['sz']; + return (int)$sSize; + } + + return 0; + } + + public final static function GetDatamodelVersion() + { + $oFilter = DBObjectSearch::FromOQL('SELECT ModuleInstallation AS mi WHERE mi.name="datamodel"'); + $oSet = new DBObjectSet($oFilter, array('installed' => false)); // Most recent first + $oSet->SetLimit(1); + /** @var \DBObject $oModuleInstallation */ + $oModuleInstallation = $oSet->Fetch(); + return $oModuleInstallation->Get('version'); + } + + public static function GetPreviousInstallations($iLimitCount = 10) + { + $oFilter = DBObjectSearch::FromOQL('SELECT ModuleInstallation AS mi WHERE mi.parent_id=0 AND mi.name!="datamodel"'); + $oSet = new DBObjectSet($oFilter, array('installed' => false)); // Most recent first + $oSet->SetLimit($iLimitCount); + $aRawValues = $oSet->ToArrayOfValues(); + $aValues = array(); + foreach ($aRawValues as $aRawValue) + { + $aValue = array(); + foreach ($aRawValue as $sAliasAttCode => $sValue) + { + // remove 'mi.' from AttCode + $sAttCode = substr($sAliasAttCode, 3); + $aValue[$sAttCode] = $sValue; + } + + $aValues[] = $aValue; + } + return $aValues; + } + + public static function GetDBTablesInfo() + { + $sSchema = CMDBSource::DBName(); + + $sReq = <<fetch_all(MYSQLI_ASSOC); + } + + return array(); + } + + public static function GetDBVariables() + { + $sReq = 'SHOW variables'; + $oResult = CMDBSource::Query($sReq); + if ($oResult !== false) + { + return $oResult->fetch_all(MYSQLI_ASSOC); + } + return array(); + } + + public static function GetDBStatus() + { + $sReq = 'SHOW status'; + $oResult = CMDBSource::Query($sReq); + if ($oResult !== false) + { + return $oResult->fetch_all(MYSQLI_ASSOC); + } + return array(); + } +} diff --git a/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php new file mode 100644 index 000000000..56d2b10f6 --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('TR TR', 'Turkish', 'Türkçe', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +)); diff --git a/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php new file mode 100644 index 000000000..6a2d97c6d --- /dev/null +++ b/datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php @@ -0,0 +1,87 @@ + + */ +// Database inconsistencies +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + // Dictionary entries go here + 'Menu:DBToolsMenu' => 'DB Tools~~', + 'DBTools:Class' => 'Class~~', + 'DBTools:Title' => 'Database Maintenance Tools~~', + 'DBTools:ErrorsFound' => 'Errors Found~~', + 'DBTools:Error' => 'Error~~', + 'DBTools:Count' => 'Count~~', + 'DBTools:SQLquery' => 'SQL query~~', + 'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~', + 'DBTools:SQLresult' => 'SQL result~~', + 'DBTools:NoError' => 'The database is OK~~', + 'DBTools:HideIds' => 'Error List~~', + 'DBTools:ShowIds' => 'Detailed view~~', + 'DBTools:ShowReport' => 'Report~~', + 'DBTools:IntegrityCheck' => 'Integrity check~~', + 'DBTools:FetchCheck' => 'Fetch Check (long)~~', + + 'DBTools:Analyze' => 'Analyze~~', + 'DBTools:Details' => 'Show Details~~', + 'DBTools:ShowAll' => 'Show All Errors~~', + + 'DBTools:Inconsistencies' => 'Database inconsistencies~~', + + 'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~', + 'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~', + 'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~', +)); + +// Database Info +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'DBTools:DatabaseInfo' => 'Database Information~~', + 'DBTools:Base' => 'Base~~', + 'DBTools:Size' => 'Size~~', +)); + +// Lost attachments +Dict::Add('ZH CN', 'Chinese', '简体中文', array( + 'DBTools:LostAttachments' => 'Lost attachments~~', + 'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~', + + 'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~', + 'DBTools:LostAttachments:Button:Restore' => 'Restore~~', + 'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~', + 'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~', + + 'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~', + + 'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~', + 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~', + + 'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~', + 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~', + + 'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~', + 'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~' +));