mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 11:08:45 +02:00
Merge branch 'support/2.7' into develop
# Conflicts: # README.md # composer.json # composer.lock # core/cmdbsource.class.inc.php # core/dbobject.class.php # datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php # datamodels/2.x/combodo-db-tools/dbtools.php # datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php # datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php # datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php # dictionaries/zh_cn.dictionary.itop.core.php # dictionaries/zh_cn.dictionary.itop.ui.php # lib/composer/InstalledVersions.php # lib/composer/autoload_classmap.php # lib/composer/autoload_static.php # lib/composer/installed.php # lib/composer/platform_check.php # pages/ajax.render.php # pages/csvimport.php # setup/ajax.dataloader.php # setup/index.php # setup/setuputils.class.inc.php # test/application/UtilsTest.php
This commit is contained in:
@@ -223,13 +223,11 @@ class DatabaseAnalyzer
|
||||
{
|
||||
$oFixSearch->AddCondition($sAttCode, $sValue, '=');
|
||||
}
|
||||
$aFixit[] = $oFixSearch->MakeSelectQuery().';';
|
||||
$aFixit[] = $oFixSearch->MakeSelectQuery([], [], null, null, 0, 0, false, false).';';
|
||||
$aFixit[] = "";
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private function GetUniquenessRuleMessage($sUniquenessRuleId)
|
||||
@@ -327,13 +325,8 @@ class DatabaseAnalyzer
|
||||
{
|
||||
$sField = MetaModel::DBGetClassField($sClass);
|
||||
$sRootField = MetaModel::DBGetClassField($sRootClass);
|
||||
$sSelWrongRecs = <<<SQL
|
||||
SELECT `$sTable`.`$sKeyField` AS id
|
||||
FROM `$sTable`
|
||||
JOIN `$sRootTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField`
|
||||
WHERE `$sTable`.`$sField` != `$sRootTable`.`$sRootField`
|
||||
SQL;
|
||||
// Copy the finalclass of the root table
|
||||
$sSelWrongRecs = "SELECT `$sTable`.`$sKeyField` AS id FROM `$sTable` JOIN `$sRootTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sField` != `$sRootTable`.`$sRootField`";
|
||||
// Copy the final class of the root table
|
||||
$sFixItRequest = "UPDATE `$sTable`,`$sRootTable` SET `$sTable`.`$sField` = `$sRootTable`.`$sRootField` WHERE `$sTable`.`$sKeyField` = `$sRootTable`.`$sRootKey`";
|
||||
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-FinalClass', $sField, $sTable, $sRootTable), $sClass, $aErrorsAndFixes);
|
||||
}
|
||||
@@ -361,75 +354,79 @@ SQL;
|
||||
|
||||
// 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`";
|
||||
$sFrom = "FROM `$sTable`";
|
||||
$sJoin = "LEFT JOIN `$sRemoteTable` AS `{$sRemoteTable}_1` ON `$sTable`.`$sExtKeyField` = `{$sRemoteTable}_1`.`$sRemoteKey`";
|
||||
|
||||
$sFilter = $sFilter." WHERE `{$sRemoteTable}_1`.`$sRemoteKey` IS NULL";
|
||||
$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";
|
||||
$sSelWrongRecs = "$sSelect $sFrom $sJoin $sFilter";
|
||||
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidExtKey', $sAttCode, $sTable, $sExtKeyField);
|
||||
$this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
$aFixIt = [];
|
||||
// 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:";
|
||||
$iOffset = 0;
|
||||
$iStep = 100;
|
||||
do
|
||||
{
|
||||
$aIds = array_slice(array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']), $iOffset, $iStep);
|
||||
$sIds = implode(', ', $aIds);
|
||||
$sDelete = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)";
|
||||
$aFixIt[] = $sDelete;
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['cleanup'][] = $sDelete;
|
||||
$iOffset += $iStep;
|
||||
}
|
||||
while (count($aIds) == $iStep);
|
||||
$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);
|
||||
if ($oAttDef->IsNullAllowed()) {
|
||||
$aFixIt[] = "-- Fix inconsistant values: remove the external key";
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey) {
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = 0 WHERE `$sTable`.`$sExtKeyField` = '$sKey'";
|
||||
}
|
||||
} else {
|
||||
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter, $sJoin);
|
||||
foreach ($aAdditionalFixIt as $sFixIt)
|
||||
{
|
||||
$aFixIt[] = $sFixIt;
|
||||
}
|
||||
|
||||
$aFixIt[] = "-- Alternate fix: remove inconsistant entries:";
|
||||
$sIds = implode(', ', array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']));
|
||||
$aFixIt[] = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)";
|
||||
|
||||
$aFixIt[] = "-- Alternate fix: update 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`";
|
||||
$sFrom = "FROM `$sTable`";
|
||||
$sFilter = "WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$sSelWrongRecs = "$sSelect $sFrom $sFilter";
|
||||
$sFixItRequest = "$sDelete $sFrom $sFilter";
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-MissingExtKey', $sAttCode, $sTable, $sExtKeyField);
|
||||
$this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
$aFixIt = [];
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['count']) && ($aErrorsAndFixes[$sClass][$sErrorDesc]['count'] > 0))
|
||||
{
|
||||
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter);
|
||||
foreach ($aAdditionalFixIt as $sFixIt)
|
||||
{
|
||||
$aFixIt[] = $sFixIt;
|
||||
}
|
||||
$aFixIt[] = "-- Alternate fix: remove inconsistant entries:";
|
||||
$aFixIt[] = $sFixItRequest;
|
||||
$aFixIt[] = "-- Alternate fix: replace XXX with the appropriate value";
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField)
|
||||
private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField, $sFilter, $sJoin = '')
|
||||
{
|
||||
$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";
|
||||
$aFixIt[] = "UPDATE ticket JOIN contact AS c ON ticket.caller_id=c.id $sJoin SET ticket.org_id=c.org_id $sFilter";
|
||||
}
|
||||
return $aFixIt;
|
||||
}
|
||||
@@ -452,7 +449,8 @@ SQL;
|
||||
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode);
|
||||
if (!is_null($aAllowedValues) && count($aAllowedValues) > 0)
|
||||
{
|
||||
$sExpectedValues = implode(",", CMDBSource::Quote(array_keys($aAllowedValues), true));
|
||||
$aAllowedValues = array_keys($aAllowedValues);
|
||||
$sExpectedValues = implode(",", CMDBSource::Quote($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
|
||||
@@ -469,15 +467,18 @@ SQL;
|
||||
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['fixit']))
|
||||
{
|
||||
$aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'];
|
||||
$aFixIt[] = "-- Alternative: Replace 'XXX' with the appropriate value";
|
||||
$aFixIt[] = "-- Alternative: Replace enums with the appropriate value";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aFixIt = array("-- Replace 'XXX' with the appropriate value");
|
||||
$aFixIt = ["-- Replace enums with the appropriate value"];
|
||||
}
|
||||
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey)
|
||||
{
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = 'XXX' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'";
|
||||
foreach ($aAllowedValues as $sAllowedValue) {
|
||||
$aFixIt[] = "-- Replace $sKey by $sAllowedValue";
|
||||
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = '$sAllowedValue' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'";
|
||||
}
|
||||
}
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSec
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\HtmlFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
@@ -43,14 +44,15 @@ const MAX_RESULTS = 10;
|
||||
* @param ApplicationContext $oAppContext
|
||||
*
|
||||
* @return \iTopWebPage
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws MySQLException
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppContext)
|
||||
{
|
||||
$iShowId = intval(utils::ReadParam('show_id', '0'));
|
||||
$sClassSelection = utils::ReadParam('class_selection', '');
|
||||
$bVerbose = utils::ReadParam('verbose', 0);
|
||||
if (!empty($sClassSelection)) {
|
||||
$aClassSelection = explode(",", $sClassSelection);
|
||||
} else {
|
||||
@@ -118,7 +120,7 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
$oForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
|
||||
|
||||
if (!empty($sErrorLabelSelection) || !empty($sClassSelection)) {
|
||||
if (!empty($sClassSelection)) {
|
||||
$oForm = FormUIBlockFactory::MakeStandard();
|
||||
$oP->AddUiBlock($oForm);
|
||||
$oInput = InputUIBlockFactory::MakeForHidden('show_id', '0');
|
||||
@@ -138,10 +140,9 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
if (!empty($aResults)) {
|
||||
if ($iShowId == 3) {
|
||||
// Report
|
||||
DisplayInconsistenciesReport($aResults);
|
||||
DisplayInconsistenciesReport($aResults, $bVerbose);
|
||||
}
|
||||
|
||||
|
||||
if ($iShowId == 0) {
|
||||
// Error List
|
||||
$oPanel = PanelUIBlockFactory::MakeForWarning(Dict::S('DBTools:ErrorsFound'));
|
||||
@@ -152,23 +153,23 @@ function DisplayDBInconsistencies(iTopWebPage &$oP, ApplicationContext &$oAppCon
|
||||
// Detail List
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:ErrorsFound'));
|
||||
$oP->AddUiBlock($oFieldSet);
|
||||
$oFieldSet->AddSubBlock(DisplayErrorDetails($aResults));
|
||||
$oFieldSet->AddSubBlock(DisplayErrorDetails($aResults, $bVerbose));
|
||||
}
|
||||
}
|
||||
|
||||
return $oP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aResults
|
||||
* @param bool $bVerbose
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
function DisplayInconsistenciesReport($aResults)
|
||||
function DisplayInconsistenciesReport($aResults, $bVerbose = false)
|
||||
{
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
|
||||
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults, $bVerbose);
|
||||
|
||||
$sZipReport = "{$sReportFile}.zip";
|
||||
$oArchive = new ZipArchive();
|
||||
@@ -217,13 +218,15 @@ function DisplayErrorList($aResults)
|
||||
return DataTableUIBlockFactory::MakeForForm('', $aColumns, $aRows);
|
||||
}
|
||||
|
||||
function DisplayErrorDetails($aResults)
|
||||
function DisplayErrorDetails($aResults, $bVerbose)
|
||||
{
|
||||
$oBlock = UIContentBlockUIBlockFactory::MakeStandard();
|
||||
|
||||
$oBlock->AddSubBlock(HtmlFactory::MakeParagraph(Dict::S('DBTools:Disclaimer')));
|
||||
$oBlock->AddSubBlock(HtmlFactory::MakeParagraph(Dict::S('DBTools:Indication')));
|
||||
|
||||
foreach ($aResults as $sClass => $aErrorList) {
|
||||
foreach ($aErrorList as $sErrorLabel => $aError) {
|
||||
|
||||
$sErrorTitle = Dict::Format('DBTools:DetailedErrorTitle', MetaModel::GetName($sClass).' ('.$sClass.')', $aError['count'], $sErrorLabel);
|
||||
$oCollapsible = CollapsibleSectionUIBlockFactory::MakeStandard($sErrorTitle);
|
||||
$oBlock->AddSubBlock($oCollapsible);
|
||||
@@ -231,7 +234,7 @@ function DisplayErrorDetails($aResults)
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLquery'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForCode($aError['query']);
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
|
||||
if (isset($aError['fixit'])) {
|
||||
@@ -240,30 +243,32 @@ function DisplayErrorDetails($aResults)
|
||||
|
||||
$aQueries = $aError['fixit'];
|
||||
foreach ($aQueries as $sFixQuery) {
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForCode($sFixQuery);
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
}
|
||||
}
|
||||
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLresult'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
if ($bVerbose) {
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLresult'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
|
||||
$sQueryResult = '';
|
||||
$iCount = count($aError['res']);
|
||||
$iMaxCount = MAX_RESULTS;
|
||||
foreach ($aError['res'] as $aRes) {
|
||||
$iMaxCount--;
|
||||
if ($iMaxCount < 0) {
|
||||
$sQueryResult .= 'Displayed '.MAX_RESULTS."/$iCount results.<br>";
|
||||
break;
|
||||
$sQueryResult = '';
|
||||
$iCount = count($aError['res']);
|
||||
$iMaxCount = MAX_RESULTS;
|
||||
foreach ($aError['res'] as $aRes) {
|
||||
$iMaxCount--;
|
||||
if ($iMaxCount < 0) {
|
||||
$sQueryResult .= 'Displayed '.MAX_RESULTS."/$iCount results.<br>";
|
||||
break;
|
||||
}
|
||||
foreach ($aRes as $sKey => $sValue) {
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
}
|
||||
$sQueryResult .= '<br>';
|
||||
}
|
||||
foreach ($aRes as $sKey => $sValue) {
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
}
|
||||
$sQueryResult .= '<br>';
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sQueryResult);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
}
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForCode($sQueryResult);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'DBTools:Class' => 'Class',
|
||||
'DBTools:Title' => 'Database integrity check',
|
||||
'DBTools:ErrorsFound' => 'Errors Found',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES',
|
||||
'DBTools:Error' => 'Error',
|
||||
'DBTools:Count' => 'Count',
|
||||
'DBTools:SQLquery' => 'SQL query',
|
||||
|
||||
@@ -23,6 +23,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'DBTools:Class' => 'Classe',
|
||||
'DBTools:Title' => 'Contrôle de l\'intégrité de la base de données',
|
||||
'DBTools:ErrorsFound' => 'Erreurs trouvées',
|
||||
'DBTools:Indication' => 'Important : après correction il est nécessaire de relancer l\'analyse car d\'autres inconsistances peuvent être générées par les modifications',
|
||||
'DBTools:Disclaimer' => 'ATTENTION : EFFECTUEZ UNE SAUVEGARDE DE LA BASE AVANT D\'APPLIQUER LES CORRECTIONS',
|
||||
'DBTools:Error' => 'Erreur',
|
||||
'DBTools:Count' => 'Nombre',
|
||||
'DBTools:SQLquery' => 'Requête SQL',
|
||||
@@ -43,7 +45,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'DBTools:Inconsistencies' => 'Incohérences de base de données',
|
||||
'DBTools:DetailedErrorTitle' => '%2$s erreur(s) dans la classe %1$s : %3$s',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la tableit `%2$s`',
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la table `%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`)',
|
||||
|
||||
@@ -74,11 +74,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => '该操作无法回退, 请确认是否继续还原.',
|
||||
'DBTools:LostAttachments:Button:Busy' => '请稍后...',
|
||||
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~',
|
||||
'DBTools:LostAttachments:Step:Analyze' => '首先, 通过分析数据库来搜索丢失或误挪动的附件.',
|
||||
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => '分析结果:',
|
||||
'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:None' => '非常好! 所有附件都是正常的.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => '某些附件 (%1$d) 看起来放错了位置. 请检查下面的列表并选择要挪动的文件.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => '文件名',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => '当前位置',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => '移动到...',
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace Combodo\iTop\DBTools\Service;
|
||||
|
||||
use CoreException;
|
||||
use Dict;
|
||||
use DictExceptionMissingString;
|
||||
use MetaModel;
|
||||
|
||||
@@ -20,29 +21,31 @@ class DBAnalyzerUtils
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public static function GenerateReport($aResults)
|
||||
public static function GenerateReport($aResults, $bVerbose = false)
|
||||
{
|
||||
$sDBToolsFolder = str_replace("\\", '/', APPROOT.'log/');
|
||||
$sReportFile = 'dbtools-report';
|
||||
|
||||
$fReport = fopen($sDBToolsFolder.$sReportFile.'.log', 'w');
|
||||
fwrite($fReport, 'Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n");
|
||||
fwrite($fReport, '-- Database Maintenance tools: '.date('Y-m-d H:i:s')."\r\n");
|
||||
fwrite($fReport, "-- ".Dict::S('DBTools:Disclaimer')."\r\n");
|
||||
fwrite($fReport, "-- ".Dict::S('DBTools:Indication')."\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");
|
||||
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");
|
||||
fwrite($fReport, '-- Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, '-- Error: '.$sErrorLabel."\r\n");
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, 'Query: '.$sQuery."\r\n");
|
||||
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
{
|
||||
fwrite($fReport, "\r\nFix it (indication):\r\n\r\n");
|
||||
fwrite($fReport, "\r\n-- Fix it (indication):\r\n\r\n");
|
||||
$aFixitQueries = $aError['fixit'];
|
||||
foreach ($aFixitQueries as $sFixitQuery)
|
||||
{
|
||||
@@ -51,31 +54,26 @@ class DBAnalyzerUtils
|
||||
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;
|
||||
if ($bVerbose) {
|
||||
$sQueryResult = '';
|
||||
$aIdList = [];
|
||||
foreach ($aError['res'] as $aRes) {
|
||||
$sQueryResult .= " - ";
|
||||
foreach ($aRes as $sKey => $sValue) {
|
||||
$sQueryResult .= "'$sKey'='$sValue' ";
|
||||
if ($sKey == 'id') {
|
||||
$aIdList[] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sQueryResult .= "\r\n";
|
||||
|
||||
fwrite($fReport, "-- Result: ".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, "\r\n-- Ids: ".$sIdList."\r\n");
|
||||
}
|
||||
fwrite($fReport, "Result: \r\n".$sQueryResult);
|
||||
$sIdList = '('.implode(',', $aIdList).')';
|
||||
fwrite($fReport, 'Ids: '.$sIdList."\r\n");
|
||||
}
|
||||
}
|
||||
fclose($fReport);
|
||||
|
||||
|
||||
$sReportFile = $sDBToolsFolder.$sReportFile;
|
||||
|
||||
return $sReportFile;
|
||||
return $sDBToolsFolder.$sReportFile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Attachment+' => '',
|
||||
'Class:Attachment/Attribute:expire' => '过期',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => '临时 id',
|
||||
'Class:Attachment/Attribute:temp_id' => '临时id',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Item class~~',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
|
||||
@@ -86,8 +86,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Change/Attribute:category/Value:hardware+' => '硬件',
|
||||
'Class:Change/Attribute:category/Value:network' => '网络',
|
||||
'Class:Change/Attribute:category/Value:network+' => '网络',
|
||||
'Class:Change/Attribute:category/Value:other' => '其他',
|
||||
'Class:Change/Attribute:category/Value:other+' => '其他',
|
||||
'Class:Change/Attribute:category/Value:other' => '其它',
|
||||
'Class:Change/Attribute:category/Value:other+' => '其它',
|
||||
'Class:Change/Attribute:category/Value:software' => '软件',
|
||||
'Class:Change/Attribute:category/Value:software+' => '软件',
|
||||
'Class:Change/Attribute:category/Value:system' => '系统',
|
||||
|
||||
@@ -150,8 +150,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:PhysicalDevice/Attribute:status/Value:obsolete+' => '废弃',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:production' => '生产',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:production+' => '生产',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock' => '闲置',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock+' => '闲置',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock' => '库存',
|
||||
'Class:PhysicalDevice/Attribute:status/Value:stock+' => '库存',
|
||||
'Class:PhysicalDevice/Attribute:brand_id' => '品牌',
|
||||
'Class:PhysicalDevice/Attribute:brand_id+' => '',
|
||||
'Class:PhysicalDevice/Attribute:brand_name' => '品牌名称',
|
||||
@@ -653,8 +653,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:VirtualDevice/Attribute:status/Value:obsolete+' => '废弃',
|
||||
'Class:VirtualDevice/Attribute:status/Value:production' => '生产',
|
||||
'Class:VirtualDevice/Attribute:status/Value:production+' => '生产',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock' => '闲置',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock+' => '闲置',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock' => '库存',
|
||||
'Class:VirtualDevice/Attribute:status/Value:stock+' => '库存',
|
||||
'Class:VirtualDevice/Attribute:logicalvolumes_list' => '逻辑卷',
|
||||
'Class:VirtualDevice/Attribute:logicalvolumes_list+' => '该设备使用的所有逻辑卷',
|
||||
));
|
||||
|
||||
@@ -144,9 +144,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:Incident/Attribute:cumulatedpending' => 'Pendências acumuladas',
|
||||
'Class:Incident/Attribute:cumulatedpending+' => '',
|
||||
'Class:Incident/Attribute:tto' => 'TTO',
|
||||
'Class:Incident/Attribute:tto+' => '',
|
||||
'Class:Incident/Attribute:tto+' => 'Tempo de atribuição',
|
||||
'Class:Incident/Attribute:ttr' => 'TTR',
|
||||
'Class:Incident/Attribute:ttr+' => '',
|
||||
'Class:Incident/Attribute:ttr+' => 'Tempo de resolução',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline' => 'Prazo determinado TTO',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO passou',
|
||||
|
||||
@@ -493,6 +493,10 @@
|
||||
{
|
||||
ShowErrorDialog('{{ 'UI:ObjectDoesNotExist'|dict_s|escape('js') }}', '{{ 'Error:HTTP:404'|dict_s|escape('js') }}');
|
||||
}
|
||||
else if(oXHR.status === 0 && oXHR.readyState === 0)
|
||||
{
|
||||
//DO NOTHING the ajax call has been aborted
|
||||
}
|
||||
else if(oXHR.responseText !== undefined && IsJSONString(oXHR.responseText) === true)
|
||||
{
|
||||
var oData = JSON.parse(oXHR.responseText);
|
||||
|
||||
@@ -147,9 +147,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:UserRequest/Attribute:cumulatedpending' => 'Pendências acumuladas',
|
||||
'Class:UserRequest/Attribute:cumulatedpending+' => '',
|
||||
'Class:UserRequest/Attribute:tto' => 'TTO',
|
||||
'Class:UserRequest/Attribute:tto+' => '',
|
||||
'Class:UserRequest/Attribute:tto+' => 'Tempo de atribuição',
|
||||
'Class:UserRequest/Attribute:ttr' => 'TTR',
|
||||
'Class:UserRequest/Attribute:ttr+' => '',
|
||||
'Class:UserRequest/Attribute:ttr+' => 'Tempo de resolução',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'Prazo determinado TTO',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO passou',
|
||||
|
||||
@@ -171,8 +171,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => 'bug修复',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => '硬件维修',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => '硬件维修',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:other' => '其他',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:other+' => '其他',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:other' => '其它',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:other+' => '其它',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:software patch' => '软件补丁',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:software patch+' => '软件补丁',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:system update' => '系统更新',
|
||||
@@ -197,7 +197,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:parent_change_id+' => '',
|
||||
'Class:UserRequest/Attribute:parent_change_ref' => '变更编号',
|
||||
'Class:UserRequest/Attribute:parent_change_ref+' => '',
|
||||
'Class:UserRequest/Attribute:parent_incident_ref' => 'Parent incident ref~~',
|
||||
'Class:UserRequest/Attribute:parent_incident_ref' => '父级事件编号',
|
||||
'Class:UserRequest/Attribute:parent_incident_ref+' => '~~',
|
||||
'Class:UserRequest/Attribute:related_request_list' => '子需求',
|
||||
'Class:UserRequest/Attribute:related_request_list+' => '该父级需求相关的所有子需求',
|
||||
|
||||
@@ -153,9 +153,9 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:UserRequest/Attribute:cumulatedpending' => 'Pendências acumuladas',
|
||||
'Class:UserRequest/Attribute:cumulatedpending+' => '',
|
||||
'Class:UserRequest/Attribute:tto' => 'TTO',
|
||||
'Class:UserRequest/Attribute:tto+' => '',
|
||||
'Class:UserRequest/Attribute:tto+' => 'Tempo de atribuição',
|
||||
'Class:UserRequest/Attribute:ttr' => 'TTR',
|
||||
'Class:UserRequest/Attribute:ttr+' => '',
|
||||
'Class:UserRequest/Attribute:ttr+' => 'Tempo de resolução',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'Prazo determinado TTO',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO passou',
|
||||
|
||||
Reference in New Issue
Block a user