/** * Display a form for modifying several objects at once * The form will be submitted to the current page, with the specified additional values */ public static function DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, $sCustomOperation, $sCancelUrl, $aExcludeAttributes = array(), $aContextData = array()) { if (count($aSelectedObj) > 0) { $iAllowedCount = count($aSelectedObj); $sSelectedObj = implode(',', $aSelectedObj); $sOQL = "SELECT {$sClass} WHERE id IN (" . $sSelectedObj . ")"; $oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL)); // Compute the distribution of the values for each field to determine which of the "scalar" fields are homogenous $aList = MetaModel::ListAttributeDefs($sClass); $aValues = array(); foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar()) { $aValues[$sAttCode] = array(); } } while ($oObj = $oSet->Fetch()) { foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { $currValue = $oObj->Get($sAttCode); if ($oAttDef instanceof AttributeCaseLog) { $currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory... } if (is_object($currValue)) { continue; } // Skip non scalar values... if (!array_key_exists($currValue, $aValues[$sAttCode])) { $aValues[$sAttCode][$currValue] = array('count' => 1, 'display' => $oObj->GetAsHTML($sAttCode)); } else { $aValues[$sAttCode][$currValue]['count']++; } } } } // Now create an object that has values for the homogenous values only $oDummyObj = new $sClass(); // @@ What if the class is abstract ? $aComments = array(); function MyComparison($a, $b) { if ($a['count'] == $b['count']) { return 0; } return $a['count'] > $b['count'] ? -1 : 1; } $iFormId = cmdbAbstractObject::GetNextFormId(); // Identifier that prefixes all the form fields $sReadyScript = ''; $aDependsOn = array(); $sFormPrefix = '2_'; foreach ($aList as $sAttCode => $oAttDef) { $aPrerequisites = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aPrerequisites) > 0) { // When 'enabling' a field, all its prerequisites must be enabled too $sFieldList = "['{$sFormPrefix}" . implode("','{$sFormPrefix}", $aPrerequisites) . "']"; $oP->add_ready_script("\$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, {$sFieldList}, true); } );\n"); } $aDependents = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aDependents) > 0) { // When 'disabling' a field, all its dependent fields must be disabled too $sFieldList = "['{$sFormPrefix}" . implode("','{$sFormPrefix}", $aDependents) . "']"; $oP->add_ready_script("\$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, {$sFieldList}, false); } );\n"); } if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { if ($oAttDef->GetEditClass() == 'One Way Password') { $sTip = "Unknown values"; $sReadyScript .= "\$('#multi_values_{$sAttCode}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"; $oDummyObj->Set($sAttCode, null); $aComments[$sAttCode] = '<input type="checkbox" id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; $aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_' . $sAttCode . '"> ? </div>'; $sReadyScript .= 'ToogleField(false, \'' . $iFormId . '_' . $sAttCode . '\');' . "\n"; } else { $iCount = count($aValues[$sAttCode]); if ($iCount == 1) { // Homogenous value reset($aValues[$sAttCode]); $aKeys = array_keys($aValues[$sAttCode]); $currValue = $aKeys[0]; // The only value is the first key //echo "<p>current value for $sAttCode : $currValue</p>"; $oDummyObj->Set($sAttCode, $currValue); $aComments[$sAttCode] = ''; if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) { $aComments[$sAttCode] .= '<input type="checkbox" checked id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; } $aComments[$sAttCode] .= '<div class="mono_value">1</div>'; } else { // Non-homogenous value $aMultiValues = $aValues[$sAttCode]; uasort($aMultiValues, 'MyComparison'); $iMaxCount = 5; $sTip = "<p><b>" . Dict::Format('UI:BulkModify_Count_DistinctValues', $iCount) . "</b><ul>"; $index = 0; foreach ($aMultiValues as $sCurrValue => $aVal) { $sDisplayValue = empty($aVal['display']) ? '<i>' . Dict::S('Enum:Undefined') . '</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']); $sTip .= "<li>" . Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count']) . "</li>"; $index++; if ($iMaxCount == $index) { $sTip .= "<li>" . Dict::Format('UI:BulkModify:N_MoreValues', count($aMultiValues) - $iMaxCount) . "</li>"; break; } } $sTip .= "</ul></p>"; $sTip = addslashes($sTip); $sReadyScript .= "\$('#multi_values_{$sAttCode}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"; $oDummyObj->Set($sAttCode, null); $aComments[$sAttCode] = ''; if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) { $aComments[$sAttCode] .= '<input type="checkbox" id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; } $aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_' . $sAttCode . '">' . $iCount . '</div>'; } $sReadyScript .= 'ToogleField(' . ($iCount == 1 ? 'true' : 'false') . ', \'' . $iFormId . '_' . $sAttCode . '\');' . "\n"; } } } $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); if ($sStateAttCode != '' && $oDummyObj->GetState() == '') { // Hmmm, it's not gonna work like this ! Set a default value for the "state" // Maybe we should use the "state" that is the most common among the objects... $aMultiValues = $aValues[$sStateAttCode]; uasort($aMultiValues, 'MyComparison'); foreach ($aMultiValues as $sCurrValue => $aVal) { $oDummyObj->Set($sStateAttCode, $sCurrValue); break; } //$oStateAtt = MetaModel::GetAttributeDef($sClass, $sStateAttCode); //$oDummyObj->Set($sStateAttCode, $oStateAtt->GetDefaultValue()); } $oP->add("<div class=\"page_header\">\n"); $oP->add("<h1>" . $oDummyObj->GetIcon() . " " . Dict::Format('UI:Modify_M_ObjectsOf_Class_OutOf_N', $iAllowedCount, $sClass, $iAllowedCount) . "</h1>\n"); $oP->add("</div>\n"); $oP->add("<div class=\"wizContainer\">\n"); $sDisableFields = json_encode($aExcludeAttributes); $aParams = array('fieldsComments' => $aComments, 'noRelations' => true, 'custom_operation' => $sCustomOperation, 'custom_button' => Dict::S('UI:Button:PreviewModifications'), 'selectObj' => $sSelectedObj, 'preview_mode' => true, 'disabled_fields' => $sDisableFields, 'disable_plugins' => true); $aParams = $aParams + $aContextData; // merge keeping associations $oDummyObj->DisplayModifyForm($oP, $aParams); $oP->add("</div>\n"); $oP->add_ready_script($sReadyScript); $oP->add_ready_script(<<<EOF \$('.wizContainer button.cancel').unbind('click'); \$('.wizContainer button.cancel').click( function() { window.location.href = '{$sCancelUrl}'; } ); EOF ); } else { $oP->p("No object selected !, nothing to do"); } }
/** * Builds an object that contains the values that are common to all the objects * in the set. If for a given attribute, objects in the set have various values * then the resulting object will contain null for this value. * @param $aValues Hash Output: the distribution of the values, in the set, for each attribute * @return DBObject The object with the common values */ public function ComputeCommonObject(&$aValues) { $sClass = $this->GetClass(); $aList = MetaModel::ListAttributeDefs($sClass); $aValues = array(); foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar()) { $aValues[$sAttCode] = array(); } } $this->Rewind(); while ($oObj = $this->Fetch()) { foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { $currValue = $oObj->Get($sAttCode); if (is_object($currValue)) { continue; } // Skip non scalar values... if (!array_key_exists($currValue, $aValues[$sAttCode])) { $aValues[$sAttCode][$currValue] = array('count' => 1, 'display' => $oObj->GetAsHTML($sAttCode)); } else { $aValues[$sAttCode][$currValue]['count']++; } } } } foreach ($aValues as $sAttCode => $aMultiValues) { if (count($aMultiValues) > 1) { uasort($aValues[$sAttCode], 'HashCountComparison'); } } // Now create an object that has values for the homogenous values only $oCommonObj = new $sClass(); // @@ What if the class is abstract ? $aComments = array(); $iFormId = cmdbAbstractObject::GetNextFormId(); // Identifier that prefixes all the form fields $sReadyScript = ''; $aDependsOn = array(); $sFormPrefix = '2_'; foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { if ($oAttDef->GetEditClass() == 'One Way Password') { $oCommonObj->Set($sAttCode, null); } else { $iCount = count($aValues[$sAttCode]); if ($iCount == 1) { // Homogenous value reset($aValues[$sAttCode]); $aKeys = array_keys($aValues[$sAttCode]); $currValue = $aKeys[0]; // The only value is the first key $oCommonObj->Set($sAttCode, $currValue); } else { // Non-homogenous value $oCommonObj->Set($sAttCode, null); } } } } $this->Rewind(); return $oCommonObj; }