/** * Constructs a DisplayBlock object from a DBObjectSet already in memory * @param $oSet DBObjectSet * @return DisplayBlock The DisplayBlock object, or null if the creation failed */ public static function FromObjectSet(DBObjectSet $oSet, $sStyle, $aParams = array()) { $oDummyFilter = new DBObjectSearch($oSet->GetClass()); $aKeys = array(); $oSet->OptimizeColumnLoad(array('id')); // No need to load all the columns just to get the id while ($oObject = $oSet->Fetch()) { $aKeys[] = $oObject->GetKey(); } $oSet->Rewind(); if (count($aKeys) > 0) { $oDummyFilter->AddCondition('id', $aKeys, 'IN'); } else { $oDummyFilter->AddCondition('id', 0, '='); } $oBlock = new DisplayBlock($oDummyFilter, $sStyle, false, $aParams); // DisplayBlocks built this way are synchronous return $oBlock; }
$oFilter = new DBObjectSearch($sClassName); $aParams = array(); foreach ($aFullTextNeedles as $sSearchText) { $oFilter->AddCondition_FullText($sSearchText); } } // Skip abstract classes if (MetaModel::IsAbstract($sClassName)) { continue; } if ($iTune > 0) { $fStartedClass = microtime(true); } $oSet = new DBObjectSet($oFilter, array(), $aParams); if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('attributes', $aAccelerators[$sClassName])) { $oSet->OptimizeColumnLoad(array($oFilter->GetClassAlias() => $aAccelerators[$sClassName]['attributes'])); } $sFullTextJS = addslashes($sFullText); $bEnableEnlarge = array_key_exists($sClassName, $aAccelerators) && array_key_exists('query', $aAccelerators[$sClassName]); if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('enable_enlarge', $aAccelerators[$sClassName])) { $bEnableEnlarge &= $aAccelerators[$sClassName]['enable_enlarge']; } $sEnlargeTheSearch = <<<EOF \t\t\t\$('.search-class-{$sClassName} button').attr('disabled', 'disabled'); \t\t\t\$('.search-class-{$sClassName} h2').append(' <img id="indicator" src="../images/indicator.gif">'); \t\t\tvar oParams = {operation: 'full_text_search_enlarge', class: '{$sClassName}', text: '{$sFullTextJS}'}; \t\t\t\$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) { \t\t\t\t\$('.search-class-{$sClassName}').html(data); \t\t\t}); EOF;
/** * Prepare the given object set with the list of fields as read into $this->aStatusInfo['fields'] */ protected function OptimizeColumnLoad(DBObjectSet $oSet) { $aColumnsToLoad = array(); foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) { $sClass = $aFieldSpec['sClass']; $sAlias = $aFieldSpec['sAlias']; $sAttCode = $aFieldSpec['sAttCode']; if (!array_key_exists($sAlias, $aColumnsToLoad)) { $aColumnsToLoad[$sAlias] = array(); } // id is not a real attribute code and, moreover, is always loaded if ($sAttCode != 'id') { // Extended attributes are not recognized by DBObjectSet::OptimizeColumnLoad if (($iPos = strpos($sAttCode, '->')) === false) { $aColumnsToLoad[$sAlias][] = $sAttCode; $sClass = '???'; } else { $sExtKeyAttCode = substr($sAttCode, 0, $iPos); $sRemoteAttCode = substr($sAttCode, $iPos + 2); // Load the external key to avoid an object reload! $aColumnsToLoad[$sAlias][] = $sExtKeyAttCode; // Load the external field (if any) to avoid getting the remote object (see DBObject::Get that does the same) $oExtFieldAtt = MetaModel::FindExternalField($sClass, $sExtKeyAttCode, $sRemoteAttCode); if (!is_null($oExtFieldAtt)) { $aColumnsToLoad[$sAlias][] = $oExtFieldAtt->GetCode(); } } } } // Add "always loaded attributes" // $aSelectedClasses = $this->oSearch->GetSelectedClasses(); foreach ($aSelectedClasses as $sAlias => $sClass) { foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef->AlwaysLoadInTables()) { $aColumnsToLoad[$sAlias][] = $sAttCode; } } } $oSet->OptimizeColumnLoad($aColumnsToLoad); }
public function GetNextChunk(&$aStatus) { $sRetCode = 'run'; $iPercentage = 0; $oSet = new DBObjectSet($this->oSearch); $aSelectedClasses = $this->oSearch->GetSelectedClasses(); $aAliases = array_keys($aSelectedClasses); $oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']); $aAliasByField = array(); $aColumnsToLoad = array(); // Prepare the list of aliases / columns to load foreach ($this->aStatusInfo['fields'] as $sExtendedAttCode) { if (preg_match('/^([^\\.]+)\\.(.+)$/', $sExtendedAttCode, $aMatches)) { $sAlias = $aMatches[1]; $sAttCode = $aMatches[2]; } else { $sAlias = reset($aAliases); $sAttCode = $sExtendedAttCode; } if (!in_array($sAlias, $aAliases)) { throw new Exception("Invalid alias '{$sAlias}' for the column '{$sExtendedAttCode}'. Availables aliases: '" . implode("', '", $aAliases) . "'"); } if (!array_key_exists($sAlias, $aColumnsToLoad)) { $aColumnsToLoad[$sAlias] = array(); } if ($sAttCode != 'id') { // id is not a real attribute code and, moreover, is always loaded $aColumnsToLoad[$sAlias][] = $sAttCode; } $aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode); } $iCount = 0; $sData = ''; $oSet->OptimizeColumnLoad($aColumnsToLoad); $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); while ($aRow = $oSet->FetchAssoc()) { set_time_limit($iLoopTimeLimit); $sFirstAlias = reset($aAliases); $sHilightClass = $aRow[$sFirstAlias]->GetHilightClass(); if ($sHilightClass != '') { $sData .= "<tr class=\"{$sHilightClass}\">"; } else { $sData .= "<tr>"; } foreach ($aAliasByField as $aAttCode) { $sField = ''; switch ($aAttCode['attcode']) { case 'id': $sField = $aRow[$aAttCode['alias']]->GetHyperlink(); break; default: $sField = $aRow[$aAttCode['alias']]->GetAsHtml($aAttCode['attcode']); } $sValue = $sField === '' ? ' ' : $sField; $sData .= "<td>{$sValue}</td>"; } $sData .= "</tr>"; $iCount++; } set_time_limit($iPreviousTimeLimit); $this->aStatusInfo['position'] += $this->iChunkSize; if ($this->aStatusInfo['total'] == 0) { $iPercentage = 100; } else { $iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total'])); } if ($iCount < $this->iChunkSize) { $sRetCode = 'done'; } $aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage); return $sData; }
/** * Helper to remove selected objects without calling any handler * Surpasses BulkDelete as it can handle abstract classes, but has the other limitation as it bypasses standard objects handlers * * @param string $oFilter Scope of objects to wipe out * @return The count of deleted objects */ public static function PurgeData($oFilter) { $sTargetClass = $oFilter->GetClass(); $oSet = new DBObjectSet($oFilter); $oSet->OptimizeColumnLoad(array($sTargetClass => array('finalclass'))); $aIdToClass = $oSet->GetColumnAsArray('finalclass', true); $aIds = array_keys($aIdToClass); if (count($aIds) > 0) { $aQuotedIds = CMDBSource::Quote($aIds); $sIdList = implode(',', $aQuotedIds); $aTargetClasses = array_merge(self::EnumChildClasses($sTargetClass, ENUM_CHILD_CLASSES_ALL), self::EnumParentClasses($sTargetClass, ENUM_PARENT_CLASSES_EXCLUDELEAF)); foreach ($aTargetClasses as $sSomeClass) { $sTable = MetaModel::DBGetTable($sSomeClass); $sPKField = MetaModel::DBGetKey($sSomeClass); $sDeleteSQL = "DELETE FROM `{$sTable}` WHERE `{$sPKField}` IN ({$sIdList})"; CMDBSource::DeleteFrom($sDeleteSQL); } } return count($aIds); }
public function GetNextChunk(&$aStatus) { $sRetCode = 'run'; $iPercentage = 0; $oSet = new DBObjectSet($this->oSearch); $aSelectedClasses = $this->oSearch->GetSelectedClasses(); $oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']); $aAliasByField = array(); $aColumnsToLoad = array(); // Prepare the list of aliases / columns to load foreach ($this->aStatusInfo['fields'] as $sExtendedAttCode) { if (preg_match('/^([^\\.]+)\\.(.+)$/', $sExtendedAttCode, $aMatches)) { $sAlias = $aMatches[1]; $sAttCode = $aMatches[2]; } else { $sAlias = reset($aSelectedClasses); $sAttCode = $sExtendedAttCode; } if (!array_key_exists($sAlias, $aSelectedClasses)) { throw new Exception("Invalid alias '{$sAlias}' for the column '{$sExtendedAttCode}'. Availables aliases: '" . implode("', '", array_keys($aSelectedClasses)) . "'"); } if (!array_key_exists($sAlias, $aColumnsToLoad)) { $aColumnsToLoad[$sAlias] = array(); } if ($sAttCode != 'id') { // id is not a real attribute code and, moreover, is always loaded $aColumnsToLoad[$sAlias][] = $sAttCode; } $aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode); } $iCount = 0; $sData = ''; $oSet->OptimizeColumnLoad($aColumnsToLoad); $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); while ($aRow = $oSet->FetchAssoc()) { set_time_limit($iLoopTimeLimit); $sData .= "<tr>"; foreach ($aAliasByField as $aAttCode) { $sField = ''; $oObj = $aRow[$aAttCode['alias']]; if ($oObj == null) { $sData .= "<td x:str>{$sField}</td>"; continue; } switch ($aAttCode['attcode']) { case 'id': $sField = $oObj->GetName(); $sData .= "<td>{$sField}</td>"; break; default: $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $aAttCode['attcode']); $oFinalAttDef = $oAttDef->GetFinalAttDef(); if (get_class($oFinalAttDef) == 'AttributeDateTime') { $iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($aAttCode['attcode'])); $sData .= '<td>' . date('Y-m-d', $iDate) . '</td>'; // Add the first column directly $sField = date('H:i:s', $iDate); // Will add the second column below $sData .= "<td>{$sField}</td>"; } else { if ($oAttDef instanceof AttributeCaseLog) { $rawValue = $oObj->Get($aAttCode['attcode']); $sField = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8')); // Trick for Excel: treat the content as text even if it begins with an equal sign $sData .= "<td x:str>{$sField}</td>"; } else { $rawValue = $oObj->Get($aAttCode['attcode']); // Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings // let's fix this and make sure we render an empty string if the key == 0 if ($oAttDef instanceof AttributeFriendlyName) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); if ($sKeyAttCode != 'id') { if ($oObj->Get($sKeyAttCode) == 0) { $sValue = ''; } } } if ($this->aStatusInfo['localize']) { $sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8'); } else { $sField = htmlentities($rawValue, ENT_QUOTES, 'UTF-8'); } $sData .= "<td>{$sField}</td>"; } } } } $sData .= "</tr>"; $iCount++; } set_time_limit($iPreviousTimeLimit); $this->aStatusInfo['position'] += $this->iChunkSize; if ($this->aStatusInfo['total'] == 0) { $iPercentage = 100; } else { $iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total'])); } if ($iCount < $this->iChunkSize) { $sRetCode = 'done'; } $aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage); return $sData; }
/** * Interprets the results posted by a normal or paginated list (in multiple selection mode) * @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected * @return Array An arry of object IDs corresponding to the objects selected in the set */ public static function ReadMultipleSelection($oFullSetFilter) { $aSelectedObj = utils::ReadParam('selectObject', array()); $sSelectionMode = utils::ReadParam('selectionMode', ''); if ($sSelectionMode != '') { // Paginated selection $aExceptions = utils::ReadParam('storedSelection', array()); if ($sSelectionMode == 'positive') { // Only the explicitely listed items are selected $aSelectedObj = $aExceptions; } else { // All items of the set are selected, except the one explicitely listed $aSelectedObj = array(); $oFullSet = new DBObjectSet($oFullSetFilter); $sClassAlias = $oFullSetFilter->GetClassAlias(); $oFullSet->OptimizeColumnLoad(array($sClassAlias => array('friendlyname'))); // We really need only the IDs but it does not work since id is not a real field while ($oObj = $oFullSet->Fetch()) { if (!in_array($oObj->GetKey(), $aExceptions)) { $aSelectedObj[] = $oObj->GetKey(); } } } } return $aSelectedObj; }
public function GetNextChunk(&$aStatus) { $sRetCode = 'run'; $iPercentage = 0; $hFile = fopen($this->aStatusInfo['tmp_file'], 'ab'); $oSet = new DBObjectSet($this->oSearch); $aSelectedClasses = $this->oSearch->GetSelectedClasses(); $aAliases = array_keys($aSelectedClasses); $oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']); $aAliasByField = array(); $aColumnsToLoad = array(); // Prepare the list of aliases / columns to load foreach ($this->aStatusInfo['fields'] as $sExtendedAttCode) { if (preg_match('/^([^\\.]+)\\.(.+)$/', $sExtendedAttCode, $aMatches)) { $sAlias = $aMatches[1]; $sAttCode = $aMatches[2]; } else { $sAlias = reset($aAliases); $sAttCode = $sExtendedAttCode; } if (!in_array($sAlias, $aAliases)) { throw new Exception("Invalid alias '{$sAlias}' for the column '{$sExtendedAttCode}'. Availables aliases: '" . implode("', '", $aAliases) . "'"); } if (!array_key_exists($sAlias, $aColumnsToLoad)) { $aColumnsToLoad[$sAlias] = array(); } if ($sAttCode != 'id') { // id is not a real attribute code and, moreover, is always loaded $aColumnsToLoad[$sAlias][] = $sAttCode; } $aAliasByField[$sExtendedAttCode] = array('alias' => $sAlias, 'attcode' => $sAttCode); } $iCount = 0; $oSet->OptimizeColumnLoad($aColumnsToLoad); $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); while ($aRow = $oSet->FetchAssoc()) { set_time_limit($iLoopTimeLimit); $aData = array(); foreach ($aAliasByField as $aAttCode) { $sField = ''; switch ($aAttCode['attcode']) { case 'id': $sField = $aRow[$aAttCode['alias']]->GetKey(); break; default: $value = $aRow[$aAttCode['alias']]->Get($aAttCode['attcode']); if ($value instanceof ormCaseLog) { // Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it! $sField = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText())); } else { if ($value instanceof DBObjectSet) { $oAttDef = MetaModel::GetAttributeDef(get_class($aRow[$aAttCode['alias']]), $aAttCode['attcode']); $sField = $oAttDef->GetAsCSV($value, '', '', $aRow[$aAttCode['alias']]); } else { $oAttDef = MetaModel::GetAttributeDef(get_class($aRow[$aAttCode['alias']]), $aAttCode['attcode']); $sField = $oAttDef->GetEditValue($value, $aRow[$aAttCode['alias']]); } } } $aData[] = $sField; } fwrite($hFile, json_encode($aData) . "\n"); $iCount++; } set_time_limit($iPreviousTimeLimit); $this->aStatusInfo['position'] += $this->iChunkSize; if ($this->aStatusInfo['total'] == 0) { $iPercentage = 100; $sRetCode = 'done'; // Next phase (GetFooter) will be to build the xlsx file } else { $iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total'])); } if ($iCount < $this->iChunkSize) { $sRetCode = 'done'; } $aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage); return ''; // The actual XLSX file is built in GetFooter(); }
public function Run() { $sCode = 'error'; $iPercentage = 100; $sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState); $fTime = microtime(true); try { switch ($this->sState) { case 'new': $oIDSet = new DBObjectSet($this->oSearch); $oIDSet->OptimizeColumnLoad(array('id')); $this->aObjectsIDs = array(); while ($oObj = $oIDSet->Fetch()) { $this->aObjectsIDs[] = $oObj->GetKey(); } $sCode = 'retrieving-data'; $iPercentage = 5; $sMessage = Dict::S('ExcelExporter:RetrievingData'); $this->iPosition = 0; $this->aStatistics['objects_count'] = count($this->aObjectsIDs); $this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime; // The first line of the file is the "headers" specifying the label and the type of each column $this->GetFieldsList($oIDSet, $this->bAdvancedMode); $sRow = json_encode($this->aTableHeaders); $hFile = @fopen($this->GetDataFile(), 'ab'); if ($hFile === false) { throw new Exception('ExcelExporter: Failed to open temporary data file: "' . $this->GetDataFile() . '" for writing.'); } fwrite($hFile, $sRow . "\n"); fclose($hFile); // Next state $this->sState = 'retrieving-data'; break; case 'retrieving-data': $oCurrentSearch = clone $this->oSearch; $aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize); $oCurrentSearch->AddCondition('id', $aIDs, 'IN'); $hFile = @fopen($this->GetDataFile(), 'ab'); if ($hFile === false) { throw new Exception('ExcelExporter: Failed to open temporary data file: "' . $this->GetDataFile() . '" for writing.'); } $oSet = new DBObjectSet($oCurrentSearch); $this->GetFieldsList($oSet, $this->bAdvancedMode); while ($aObjects = $oSet->FetchAssoc()) { $aRow = array(); foreach ($this->aAuthorizedClasses as $sAlias => $sClassName) { $oObj = $aObjects[$sAlias]; if ($this->bAdvancedMode) { $aRow[] = $oObj->GetKey(); } foreach ($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef) { $value = $oObj->Get($sAttCodeEx); if ($value instanceof ormCaseLog) { // Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it! $sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText())); } else { $sExcelVal = $oAttDef->GetEditValue($value, $oObj); } $aRow[] = $sExcelVal; } } $sRow = json_encode($aRow); fwrite($hFile, $sRow . "\n"); } fclose($hFile); if ($this->iPosition + $this->iChunkSize > count($this->aObjectsIDs)) { // Next state $this->sState = 'building-excel'; $sCode = 'building-excel'; $iPercentage = 80; $sMessage = Dict::S('ExcelExporter:BuildingExcelFile'); } else { $sCode = 'retrieving-data'; $this->iPosition += $this->iChunkSize; $iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs))); $sMessage = Dict::S('ExcelExporter:RetrievingData'); } break; case 'building-excel': $hFile = @fopen($this->GetDataFile(), 'rb'); if ($hFile === false) { throw new Exception('ExcelExporter: Failed to open temporary data file: "' . $this->GetDataFile() . '" for reading.'); } $sHeaders = fgets($hFile); $aHeaders = json_decode($sHeaders, true); $aData = array(); while ($sLine = fgets($hFile)) { $aRow = json_decode($sLine); $aData[] = $aRow; } fclose($hFile); @unlink($this->GetDataFile()); $fStartExcel = microtime(true); $writer = new XLSXWriter(); $writer->setAuthor(UserRights::GetUserFriendlyName()); $writer->writeSheet($aData, 'Sheet1', $aHeaders); $fExcelTime = microtime(true) - $fStartExcel; $this->aStatistics['excel_build_duration'] = $fExcelTime; $fTime = microtime(true); $writer->writeToFile($this->GetExcelFilePath()); $fExcelSaveTime = microtime(true) - $fTime; $this->aStatistics['excel_write_duration'] = $fExcelSaveTime; // Next state $this->sState = 'done'; $sCode = 'done'; $iPercentage = 100; $sMessage = Dict::S('ExcelExporter:Done'); break; case 'done': $this->sState = 'done'; $sCode = 'done'; $iPercentage = 100; $sMessage = Dict::S('ExcelExporter:Done'); break; } } catch (Exception $e) { $sCode = 'error'; $sMessage = $e->getMessage(); } $this->aStatistics['total_duration'] += microtime(true) - $fTime; $peak_memory = memory_get_peak_usage(true); if ($peak_memory > $this->aStatistics['peak_memory_usage']) { $this->aStatistics['peak_memory_usage'] = $peak_memory; } return array('code' => $sCode, 'message' => $sMessage, 'percentage' => $iPercentage); }