SetCurrentTab('DBTools:Inconsistencies'); $bRunAnalysis = intval(utils::ReadParam('run_analysis', '0')); if ($bRunAnalysis) { $oDBAnalyzer = new DatabaseAnalyzer(0); $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) { $sReportFile = DBAnalyzerUtils::GenerateReport($aResults); $sZipReport = "{$sReportFile}.zip"; $oArchive = new ZipArchive(); $oArchive->open($sZipReport, ZipArchive::CREATE); $oArchive->addFile($sReportFile.'.log', basename($sReportFile.'.log')); $oArchive->close(); header('Content-Description: File Transfer'); header('Content-Type: multipart/x-zip'); header('Content-Disposition: inline; filename="'.basename($sZipReport).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: '.filesize($sZipReport)); readfile($sZipReport); unlink($sZipReport); 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('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(); }