Compare commits

..

3 Commits

Author SHA1 Message Date
v-dumas
8fa7079dc2 N°8514 - Datamodel for electricity (3) 2025-12-19 09:45:19 +01:00
v-dumas
7ee95864b3 N°8514 - CMDB datamodel for electricity (2) 2025-12-17 15:11:24 +01:00
v-dumas
5619a9cb75 N°8514 - CMDB datamodel for electricity 2025-12-17 10:56:29 +01:00
32 changed files with 2661 additions and 863 deletions

View File

@@ -537,12 +537,6 @@ class CMDBSource
*/
public static function Query($sSQLQuery)
{
if (self::$sRaisesExceptionMsgWhenSqlQuery) {
$e = new \Exception(self::$sRaisesExceptionMsgWhenSqlQuery);
\IssueLog::Error(__METHOD__, null, [$e->getTraceAsString()]);
throw $e;
}
if (preg_match('/^START TRANSACTION;?$/i', $sSQLQuery)) {
self::StartTransaction();
@@ -562,13 +556,6 @@ class CMDBSource
return self::DBQuery($sSQLQuery);
}
public static ?string $sRaisesExceptionMsgWhenSqlQuery = null;
public static function TriggerExceptionWhenSqlQuery(?string $sMsg)
{
self::$sRaisesExceptionMsgWhenSqlQuery = $sMsg;
}
/**
* Send the query directly to the DB. **Be extra cautious with this !**
*

View File

@@ -2418,7 +2418,6 @@ class Config
public function SetAllowedLoginTypes($aAllowedLoginTypes)
{
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
$this->Set('allowed_login_types', implode('|', $aAllowedLoginTypes));
}
/**

View File

@@ -122,7 +122,9 @@ class DBObjectSet implements iDBObjectSetIterator
*/
public function __destruct()
{
$this->Free();
if (is_object($this->m_oSQLResult)) {
$this->m_oSQLResult->free();
}
}
/**
@@ -709,8 +711,11 @@ class DBObjectSet implements iDBObjectSetIterator
$sSQL = $this->_makeSelectQuery($this->m_aAttToLoad);
// Free previous resultset if any
$this->Free();
if (is_object($this->m_oSQLResult)) {
// Free previous resultset if any
$this->m_oSQLResult->free();
$this->m_oSQLResult = null;
}
try {
$oKPI = new ExecutionKPI();
@@ -866,7 +871,23 @@ class DBObjectSet implements iDBObjectSetIterator
*/
public function CountExceeds($iLimit)
{
$iCount = $this->CountWithLimit($iLimit);
if (is_null($this->m_iNumTotalDBRows)) {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery([], $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, [], $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) {
$aRow = CMDBSource::FetchArray($resQuery);
$iCount = intval($aRow['COUNT']);
CMDBSource::FreeResult($resQuery);
} else {
$iCount = 0;
}
} else {
$iCount = $this->m_iNumTotalDBRows;
}
return ($iCount > $iLimit);
}
@@ -892,8 +913,8 @@ class DBObjectSet implements iDBObjectSetIterator
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) {
$aRow = CMDBSource::FetchArray($resQuery);
$iCount = intval($aRow['COUNT']);
CMDBSource::FreeResult($resQuery);
$iCount = intval($aRow['COUNT']);
} else {
$iCount = 0;
}
@@ -914,14 +935,6 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumLoadedDBRows + count($this->m_aAddedObjects);
}
private function Free()
{
if (is_object($this->m_oSQLResult)) {
CMDBSource::FreeResult($this->m_oSQLResult);
$this->m_oSQLResult = null;
}
}
/**
* Fetch an object (with the given class alias) at the current position in the set and move the cursor to the next position.
*
@@ -942,7 +955,6 @@ class DBObjectSet implements iDBObjectSetIterator
}
if ($this->m_iCurrRow >= $this->CountLoaded()) {
$this->Free();
return null;
}
@@ -950,9 +962,7 @@ class DBObjectSet implements iDBObjectSetIterator
$sRequestedClassAlias = $this->m_oFilter->GetClassAlias();
}
if ($this->m_iCurrRow < count($this->m_aCacheObj)) {
$oRetObj = $this->m_aCacheObj[$this->m_iCurrRow][$sRequestedClassAlias];
} else if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
// Pick the row from the database
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
@@ -962,7 +972,6 @@ class DBObjectSet implements iDBObjectSetIterator
} else {
try {
$oRetObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
$this->m_aCacheObj[$this->m_iCurrRow] = [$sRequestedClassAlias => $oRetObj];
} catch (CoreException $e) {
$this->m_iCurrRow++;
$oRetObj = $this->Fetch($sRequestedClassAlias);
@@ -979,8 +988,6 @@ class DBObjectSet implements iDBObjectSetIterator
return $oRetObj;
}
private $m_aCacheObj = [];
/**
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
*
@@ -999,29 +1006,21 @@ class DBObjectSet implements iDBObjectSetIterator
}
if ($this->m_iCurrRow >= $this->CountLoaded()) {
$this->Free();
return null;
}
if ($this->m_iCurrRow < count($this->m_aCacheObj)) {
$aRetObjects = $this->m_aCacheObj[$this->m_iCurrRow];
} else if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
// Pick the row from the database
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
$aRetObjects = [];
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
$oObj = null;
if (!is_null($aRow[$sClassAlias.'id'])) {
try {
$oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
}
catch (CoreException $e) {
}
if (is_null($aRow[$sClassAlias.'id'])) {
$oObj = null;
} else {
$oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
}
$aRetObjects[$sClassAlias] = $oObj;
}
$this->m_aCacheObj[$this->m_iCurrRow] = $aRetObjects;
} else {
// Pick the row from the objects added *in memory*
$aRetObjects = [];
@@ -1064,10 +1063,6 @@ class DBObjectSet implements iDBObjectSetIterator
$this->Load();
}
if (is_null($this->m_oSQLResult)) {
return;
}
$this->m_iCurrRow = min($iRow, $this->Count());
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
$this->m_oSQLResult->data_seek($this->m_iCurrRow);
@@ -1241,7 +1236,7 @@ class DBObjectSet implements iDBObjectSetIterator
*
* @throws \CoreException
*/
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = []): bool
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = [])
{
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
return $oComparator->SetsAreEquivalent();

View File

@@ -415,7 +415,12 @@ abstract class User extends cmdbAbstractObject
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:CurrentProfilesHaveInsufficientRights');
}
$oAddon->ResetCache();
Session::Set('profile_list', $aCurrentProfiles);
if (is_null($aCurrentProfiles)) {
Session::IsSet('profile_list');
} else {
Session::Set('profile_list', $aCurrentProfiles);
}
}
// Prevent an administrator to remove their own admin profile
if (UserRights::IsAdministrator($this)) {

View File

@@ -323,38 +323,18 @@
<value id="production">
<code>production</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="stock">
<code>stock</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-neutral-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-neutral-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>40</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>
@@ -6917,29 +6897,14 @@
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
// Menu : ConfigManagement : PowerSupply
//
Dict::Add('EN US', 'English', 'English', [
'Menu:ConfigManagement:PowerSupply' => 'Power supplies',
]);
//
// Class: Inverter
//
Dict::Add('EN US', 'English', 'English', [
'Class:Inverter' => 'Inverter',
'Class:Inverter+' => '',
'Class:Inverter/Attribute:rack_id' => 'Rack',
'Class:Inverter/Attribute:rack_id+' => '',
'Class:Inverter/Attribute:autonomy' => 'Autonomy (min)',
'Class:Inverter/Attribute:autonomy+' => 'Estimated autonomy at nominal load',
'Class:Inverter/Attribute:power' => 'Power (kVA)',
'Class:Inverter/Attribute:power+' => 'Apparent power deliverable in kVA',
'Class:Inverter/Attribute:maintenance_date' => 'Last maintenance',
'Class:Inverter/Attribute:maintenance_date+' => '',
'Class:Inverter/Attribute:battery_date' => 'Batterys\' date',
'Class:Inverter/Attribute:battery_date+' => 'Indicates the date of batteries\' installation',
'Class:Inverter/Attribute:powerconnection_id' => 'Power supply',
'Class:Inverter/Attribute:powerconnection_id+' => '',
'Class:Inverter/Attribute:supply_type' => 'Source type',
'Class:Inverter/Attribute:supply_type+' => 'Type of power supplied',
'Class:Inverter/Attribute:nb_u' => 'Number of units (U)',
'Class:Inverter/Attribute:nb_u+' => 'Number of units consumed in the rack by this equipment',
'Class:Inverter/Attribute:position' => 'Position',
'Class:Inverter/Attribute:position+' => 'Position in the rack',
'Class:Inverter/Attribute:powerstarts_list' => 'Power start',
'Class:Inverter/Attribute:powerstarts_list+' => 'Downstream power start',
'Class:Inverter/Attribute:pdus_list' => 'PDUs',
'Class:Inverter/Attribute:pdus_list+' => 'Downstream PDUs',
'Class:Inverter/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:Inverter/Attribute:stss_list_a+' => 'Downstream ATS/STS (source A)',
'Class:Inverter/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:Inverter/Attribute:stss_list_b+' => 'Downstream ATS/STS (source B)',
'Class:Inverter/Attribute:obsolescence_flag' => 'Obsolete',
'Class:Inverter/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:Inverter/Attribute:rack_id_friendlyname' => 'Rack',
'Class:Inverter/Attribute:rack_id_friendlyname+' => 'Full name',
'Class:Inverter/Attribute:rack_id_obsolescence_flag' => 'Rack->Obsolete',
'Class:Inverter/Attribute:rack_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:Inverter/Attribute:powerconnection_id_friendlyname' => 'Power supply',
'Class:Inverter/Attribute:powerconnection_id_friendlyname+' => 'Full name',
'Class:Inverter/Attribute:powerconnection_id_finalclass_recall' => 'Power supply->CI sub-class',
'Class:Inverter/Attribute:powerconnection_id_finalclass_recall+' => 'Name of the final class',
'Class:Inverter/Attribute:powerconnection_id_obsolescence_flag' => 'Power supply->Obsolete',
'Class:Inverter/Attribute:powerconnection_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'Inverter:baseinfo' => 'General informations',
'Inverter:moreinfo' => 'Additional informations',
'Inverter:technicalinfo' => 'Technical informations',
'Inverter:Date' => 'Dates',
'Inverter:otherinfo' => 'Other informations',
]);
//
// Class: PDU
//
Dict::Add('EN US', 'English', 'English', [
'Class:PDU' => 'PDU',
'Class:PDU+' => 'Power distribution unit. A type of Power Connection.',
'Class:PDU/Attribute:rack_id' => 'Rack',
'Class:PDU/Attribute:rack_id+' => '',
'Class:PDU/Attribute:rack_name' => 'Rack name',
'Class:PDU/Attribute:rack_name+' => '',
'Class:PDU/Attribute:powerconnection_id' => 'Electric supply',
'Class:PDU/Attribute:powerconnection_id+' => '',
'Class:PDU/Attribute:powerstart_name' => 'Power start name',
'Class:PDU/Attribute:powerstart_name+' => '',
'Class:PDU/Attribute:output_number' => 'Number of output',
'Class:PDU/Attribute:output_number+' => '',
'Class:PDU/Attribute:protection' => 'Protection',
'Class:PDU/Attribute:protection+' => '',
'Class:PDU/Attribute:protection/Value:inverter' => 'Uninterruptible',
'Class:PDU/Attribute:protection/Value:inverter+' => '',
'Class:PDU/Attribute:protection/Value:no' => 'No',
'Class:PDU/Attribute:protection/Value:no+' => '',
'Class:PDU/Attribute:protection/Value:sts' => 'ATS/STS',
'Class:PDU/Attribute:protection/Value:sts+' => '',
'Class:PDU/Attribute:nb_u' => 'Number of units (U)',
'Class:PDU/Attribute:nb_u+' => 'Number of units consumed by the equipment in the rack',
'Class:PDU/Attribute:position' => 'Position',
'Class:PDU/Attribute:position+' => 'Position in the rack',
'Class:PDU/Attribute:obsolescence_flag' => 'Obsolete',
'Class:PDU/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:PDU/Attribute:rack_id_friendlyname' => 'Rack',
'Class:PDU/Attribute:rack_id_friendlyname+' => 'Full name',
'Class:PDU/Attribute:rack_id_obsolescence_flag' => 'Rack->Obsolete',
'Class:PDU/Attribute:rack_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:PDU/Attribute:powerconnection_id_friendlyname' => 'Electric supply',
'Class:PDU/Attribute:powerconnection_id_friendlyname+' => 'Full name',
'Class:PDU/Attribute:powerconnection_id_finalclass_recall' => 'Electric supply->CI sub-class',
'Class:PDU/Attribute:powerconnection_id_finalclass_recall+' => 'Name of the final class',
'Class:PDU/Attribute:powerconnection_id_obsolescence_flag' => 'Electric supply->Obsolete',
'Class:PDU/Attribute:powerconnection_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'PDU:baseinfo' => 'General informations',
'PDU:moreinfo' => 'Additional informations',
'PDU:technicalinfo' => 'Technical informations',
'PDU:Date' => 'Dates',
'PDU:otherinfo' => 'Other informations',
]);
//
// Class: PowerConnection
//
Dict::Add('EN US', 'English', 'English', [
'Class:PowerConnection' => 'Power Connection',
'Class:PowerConnection+' => 'Abstract class, grouping all power devices',
'Class:PowerConnection/Attribute:charge_capacity' => 'Maximum load (A)',
'Class:PowerConnection/Attribute:charge_capacity+' => 'Maximum admissible load capacity in amperes',
'Class:PowerConnection/Attribute:power_capacity' => 'Maximum power (kVA)',
'Class:PowerConnection/Attribute:power_capacity+' => 'Maximum admissible active power in kVA',
'Class:PowerConnection/Attribute:power_type' => 'Power type',
'Class:PowerConnection/Attribute:power_type+' => 'Type of power supplied',
'Class:PowerConnection/Attribute:power_type/Value:continuous' => 'Continuous',
'Class:PowerConnection/Attribute:power_type/Value:continuous+' => 'Continuous',
'Class:PowerConnection/Attribute:power_type/Value:single' => 'single phase',
'Class:PowerConnection/Attribute:power_type/Value:single+' => 'single phase',
'Class:PowerConnection/Attribute:power_type/Value:three' => 'three phase',
'Class:PowerConnection/Attribute:power_type/Value:three+' => 'three phase',
'Class:PowerConnection/Attribute:charge_current' => 'Current charge',
'Class:PowerConnection/Attribute:charge_current+' => 'Load currently in use',
'Class:PowerConnection/Attribute:power_current' => 'Currently power',
'Class:PowerConnection/Attribute:power_current+' => 'Active power currently in use',
'Class:PowerConnection/Attribute:power_phase' => 'Phase used',
'Class:PowerConnection/Attribute:power_phase+' => 'Phase used in the case of an initial three-phase to single-phase source',
'Class:PowerConnection/Attribute:management_url' => 'Management URL',
'Class:PowerConnection/Attribute:management_url+' => '',
'Class:PowerConnection/Attribute:managementip' => 'Management IP',
'Class:PowerConnection/Attribute:managementip+' => '',
'Class:PowerConnection/Attribute:voltage' => 'Voltage',
'Class:PowerConnection/Attribute:voltage+' => '',
'Class:PowerConnection/Attribute:obsolescence_flag' => 'Obsolete',
'Class:PowerConnection/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
]);
//
// Class: PowerSource
//
Dict::Add('EN US', 'English', 'English', [
'Class:PowerSource' => 'Power Source',
'Class:PowerSource+' => 'First Power Connection documented in a power circuit,
It has no electrical source documented as an object in the CMDB.',
'Class:PowerSource/Attribute:pdus_list' => 'PDUs',
'Class:PowerSource/Attribute:pdus_list+' => 'All the PDUs using this power source',
'Class:PowerSource/Attribute:inverters_list' => 'Inverter',
'Class:PowerSource/Attribute:inverters_list+' => 'Downstream inverters',
'Class:PowerSource/Attribute:powerstarts_list' => 'Power start',
'Class:PowerSource/Attribute:powerstarts_list+' => 'Downstream power start',
'Class:PowerSource/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:PowerSource/Attribute:stss_list_a+' => 'Downstream ATS/STS (source A)',
'Class:PowerSource/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:PowerSource/Attribute:stss_list_b+' => 'Downstream ATS (source B)',
'Class:PowerSource/Attribute:obsolescence_flag' => 'Obsolete',
'Class:PowerSource/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
'PowerSource:baseinfo' => 'General informations',
'PowerSource:moreinfo' => 'Additional informations',
'PowerSource:technicalinfo' => 'Technical informations',
'PowerSource:Date' => 'Dates',
'PowerSource:otherinfo' => 'Other informations',
]);
//
// Class: PowerStart
//
Dict::Add('EN US', 'English', 'English', [
'Class:PowerStart' => 'Power Start',
'Class:PowerStart+' => '',
'Class:PowerStart/Attribute:powerconnection_id' => 'Source',
'Class:PowerStart/Attribute:powerconnection_id+' => '',
'Class:PowerStart/Attribute:supply_type' => 'Source type',
'Class:PowerStart/Attribute:supply_type+' => 'Type of power supply on the source',
'Class:PowerStart/Attribute:inverters_list' => 'Inverters',
'Class:PowerStart/Attribute:inverters_list+' => 'Dowstream inverters',
'Class:PowerStart/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:PowerStart/Attribute:stss_list_a+' => 'Downstream ATS/STS (source A)',
'Class:PowerStart/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:PowerStart/Attribute:stss_list_b+' => 'Downstream ATS/STS (source B)',
'Class:PowerStart/Attribute:pdus_list' => 'PDUs',
'Class:PowerStart/Attribute:pdus_list+' => 'Downstream PDUs',
'Class:PowerStart/Attribute:powerstarts_list' => 'powerstarts list',
'Class:PowerStart/Attribute:powerstarts_list+' => '',
'Class:PowerStart/Attribute:obsolescence_flag' => 'Obsolete',
'Class:PowerStart/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:PowerStart/Attribute:powerconnection_id_friendlyname' => 'Source',
'Class:PowerStart/Attribute:powerconnection_id_friendlyname+' => 'Full name',
'Class:PowerStart/Attribute:powerconnection_id_finalclass_recall' => 'Source->CI sub-class',
'Class:PowerStart/Attribute:powerconnection_id_finalclass_recall+' => 'Name of the final class',
'Class:PowerStart/Attribute:powerconnection_id_obsolescence_flag' => 'Source->Obsolete',
'Class:PowerStart/Attribute:powerconnection_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'PowerStart:baseinfo' => 'General informations',
'PowerStart:moreinfo' => 'Additional informations',
'PowerStart:technicalinfo' => 'Technical informations',
'PowerStart:Date' => 'Dates',
'PowerStart:otherinfo' => 'Other informations',
]);
//
// Class: STS
//
Dict::Add('EN US', 'English', 'English', [
'Class:STS' => 'ATS / STS',
'Class:STS+' => 'Automatic Transfer Switch / Static Transfer Switch
Redondant power supply for devices with a single electrical input.
It can supply PDU(s)
They are themselves supplied with two different Power Source, through Power Connection ',
'Class:STS/Attribute:powerconnection_source1_id' => 'Source A',
'Class:STS/Attribute:powerconnection_source1_id+' => '',
'Class:STS/Attribute:powerconnection_source2_id' => 'Source B',
'Class:STS/Attribute:powerconnection_source2_id+' => '',
'Class:STS/Attribute:nominal_source' => 'Nominal source',
'Class:STS/Attribute:nominal_source+' => 'Nominal source used by STS',
'Class:STS/Attribute:nominal_source/Value:source1' => 'Source A',
'Class:STS/Attribute:nominal_source/Value:source1+' => '',
'Class:STS/Attribute:nominal_source/Value:source2' => 'Source B',
'Class:STS/Attribute:nominal_source/Value:source2+' => '',
'Class:STS/Attribute:rack' => 'Rack',
'Class:STS/Attribute:rack+' => '',
'Class:STS/Attribute:nb_u' => 'Number of units (U)',
'Class:STS/Attribute:nb_u+' => 'Number of units consumed in the rack by this equipment',
'Class:STS/Attribute:position' => 'Position',
'Class:STS/Attribute:position+' => 'Position in the rack',
'Class:STS/Attribute:redundancy' => 'Configuration of electrical redundancy',
'Class:STS/Attribute:redundancy+' => '',
'Class:STS/Attribute:pdus_list' => 'PDUs',
'Class:STS/Attribute:pdus_list+' => 'Downstream PDUs',
'Class:STS/Attribute:obsolescence_flag' => 'Obsolete',
'Class:STS/Attribute:obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:STS/Attribute:powerconnection_source1_id_friendlyname' => 'Source A',
'Class:STS/Attribute:powerconnection_source1_id_friendlyname+' => 'Full name',
'Class:STS/Attribute:powerconnection_source1_id_finalclass_recall' => 'Source A->CI sub-class',
'Class:STS/Attribute:powerconnection_source1_id_finalclass_recall+' => 'Name of the final class',
'Class:STS/Attribute:powerconnection_source1_id_obsolescence_flag' => 'Source A->Obsolete',
'Class:STS/Attribute:powerconnection_source1_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:STS/Attribute:powerconnection_source2_id_friendlyname' => 'Source B',
'Class:STS/Attribute:powerconnection_source2_id_friendlyname+' => 'Full name',
'Class:STS/Attribute:powerconnection_source2_id_finalclass_recall' => 'Source B->CI sub-class',
'Class:STS/Attribute:powerconnection_source2_id_finalclass_recall+' => 'Name of the final class',
'Class:STS/Attribute:powerconnection_source2_id_obsolescence_flag' => 'Source B->Obsolete',
'Class:STS/Attribute:powerconnection_source2_id_obsolescence_flag+' => 'Computed dynamically on other attributes',
'Class:STS/Attribute:rack_friendlyname' => 'Rack',
'Class:STS/Attribute:rack_friendlyname+' => 'Full name',
'Class:STS/Attribute:rack_obsolescence_flag' => 'Rack->Obsolete',
'Class:STS/Attribute:rack_obsolescence_flag+' => 'Computed dynamically on other attributes',
'STS:baseinfo' => 'General informations',
'STS:moreinfo' => 'Additional informations',
'STS:technicalinfo' => 'Technical informations',
'STS:Date' => 'Dates',
'STS:otherinfo' => 'Other informations',
]);
//
// Class: Model
//
Dict::Add('EN US', 'English', 'English', [
'Class:Model/Attribute:type/Value:PDU+' => 'Power Distribution Unit',
'Class:Model/Attribute:type/Value:PowerStart' => 'Power Start',
'Class:Model/Attribute:type/Value:STS' => 'ATS/STS',
'Class:Model/Attribute:type/Value:Inverter' => 'Inverter',
]);

View File

@@ -0,0 +1,274 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
// Menu : ConfigManagement : PowerSupply
//
Dict::Add('FR FR', 'French', 'Français', [
'Menu:ConfigManagement:PowerSupply' => 'Alimentations électriques',
]);
//
// Class: Inverter
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:Inverter' => 'Onduleur',
'Class:Inverter+' => '',
'Class:Inverter/Attribute:rack_id' => 'Rack',
'Class:Inverter/Attribute:rack_id+' => '',
'Class:Inverter/Attribute:autonomy' => 'Autonomie (min)',
'Class:Inverter/Attribute:autonomy+' => 'Autonomie estimée en charge nominale',
'Class:Inverter/Attribute:power' => 'Puissance (kVA)',
'Class:Inverter/Attribute:power+' => 'Puissance apparente délivrable en kVA',
'Class:Inverter/Attribute:maintenance_date' => 'Dernière maintenance',
'Class:Inverter/Attribute:maintenance_date+' => '',
'Class:Inverter/Attribute:battery_date' => 'Date des batteries',
'Class:Inverter/Attribute:battery_date+' => 'Indique la date d\'installation des batteries',
'Class:Inverter/Attribute:powerconnection_id' => 'Arrivée électrique',
'Class:Inverter/Attribute:powerconnection_id+' => '',
'Class:Inverter/Attribute:supply_type' => 'Type de la source',
'Class:Inverter/Attribute:supply_type+' => '',
'Class:Inverter/Attribute:nb_u' => 'Nombre d\'unités (U)',
'Class:Inverter/Attribute:nb_u+' => 'Nombre d\'unités consommés dans le rack par cet équipement',
'Class:Inverter/Attribute:position' => 'Position',
'Class:Inverter/Attribute:position+' => 'Position dans le rack',
'Class:Inverter/Attribute:powerstarts_list' => 'Départs électriques',
'Class:Inverter/Attribute:powerstarts_list+' => 'Départs électriques en aval',
'Class:Inverter/Attribute:pdus_list' => 'PDUs',
'Class:Inverter/Attribute:pdus_list+' => 'PDUs en aval',
'Class:Inverter/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:Inverter/Attribute:stss_list_a+' => 'ATS/STS en aval (source A)',
'Class:Inverter/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:Inverter/Attribute:stss_list_b+' => 'ATS/STS en aval (source B)',
'Class:Inverter/Attribute:obsolescence_flag' => 'Obsolète',
'Class:Inverter/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:Inverter/Attribute:rack_id_friendlyname' => 'Rack',
'Class:Inverter/Attribute:rack_id_friendlyname+' => 'Nom complet',
'Class:Inverter/Attribute:rack_id_obsolescence_flag' => 'Rack->Obsolète',
'Class:Inverter/Attribute:rack_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:Inverter/Attribute:powerconnection_id_friendlyname' => 'Arrivée électrique',
'Class:Inverter/Attribute:powerconnection_id_friendlyname+' => 'Nom complet',
'Class:Inverter/Attribute:powerconnection_id_finalclass_recall' => 'Arrivée électrique->Sous-classe de CI',
'Class:Inverter/Attribute:powerconnection_id_finalclass_recall+' => 'Nom de la classe instanciable',
'Class:Inverter/Attribute:powerconnection_id_obsolescence_flag' => 'Arrivée électrique->Obsolète',
'Class:Inverter/Attribute:powerconnection_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Inverter:baseinfo' => 'Informations générales',
'Inverter:moreinfo' => 'Informations complémentaires',
'Inverter:Date' => 'Dates',
'Inverter:otherinfo' => 'Autres informations',
'Inverter:technicalinfo' => 'Données techniques',
]);
//
// Class: PDU
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:PDU' => 'PDU',
'Class:PDU+' => 'Unité de distribution d\'alimentation',
'Class:PDU/Attribute:rack_id' => 'Rack',
'Class:PDU/Attribute:rack_id+' => '',
'Class:PDU/Attribute:rack_name' => 'Nom rack',
'Class:PDU/Attribute:rack_name+' => '',
'Class:PDU/Attribute:powerconnection_id' => 'Arrivée électrique',
'Class:PDU/Attribute:powerconnection_id+' => '',
'Class:PDU/Attribute:powerstart_name' => 'Nom arrivée électrique',
'Class:PDU/Attribute:powerstart_name+' => '',
'Class:PDU/Attribute:output_number' => 'Nombre de sortie',
'Class:PDU/Attribute:output_number+' => '',
'Class:PDU/Attribute:protection' => 'Protection',
'Class:PDU/Attribute:protection+' => '',
'Class:PDU/Attribute:protection/Value:inverter' => 'Ondulée',
'Class:PDU/Attribute:protection/Value:inverter+' => '',
'Class:PDU/Attribute:protection/Value:no' => 'Aucune',
'Class:PDU/Attribute:protection/Value:no+' => '',
'Class:PDU/Attribute:protection/Value:sts' => 'ATS/STS',
'Class:PDU/Attribute:protection/Value:sts+' => '',
'Class:PDU/Attribute:nb_u' => 'Nombre d\'unités (U)',
'Class:PDU/Attribute:nb_u+' => 'Nombre d\'unités consommés par l\'équipement dans le rack',
'Class:PDU/Attribute:position' => 'Position',
'Class:PDU/Attribute:position+' => 'Position dans le rack',
'Class:PDU/Attribute:obsolescence_flag' => 'Obsolète',
'Class:PDU/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:PDU/Attribute:rack_id_friendlyname' => 'Rack',
'Class:PDU/Attribute:rack_id_friendlyname+' => 'Nom complet',
'Class:PDU/Attribute:rack_id_obsolescence_flag' => 'Rack->Obsolète',
'Class:PDU/Attribute:rack_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:PDU/Attribute:powerconnection_id_friendlyname' => 'Arrivée électrique',
'Class:PDU/Attribute:powerconnection_id_friendlyname+' => 'Nom complet',
'Class:PDU/Attribute:powerconnection_id_finalclass_recall' => 'Arrivée électrique->Sous-classe de CI',
'Class:PDU/Attribute:powerconnection_id_finalclass_recall+' => 'Nom de la classe instanciable',
'Class:PDU/Attribute:powerconnection_id_obsolescence_flag' => 'Arrivée électrique->Obsolète',
'Class:PDU/Attribute:powerconnection_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'PDU:baseinfo' => 'Informations générales',
'PDU:moreinfo' => 'Informations complémentaires',
'PDU:Date' => 'Dates',
'PDU:otherinfo' => 'Autres informations',
'PDU:technicalinfo' => 'Données techniques',
]);
//
// Class: PowerConnection
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:PowerConnection' => 'Connection électrique',
'Class:PowerConnection+' => '',
'Class:PowerConnection/Attribute:charge_capacity' => 'Charge maximale (A)',
'Class:PowerConnection/Attribute:charge_capacity+' => 'Capacité de charge maximale admissible en ampère',
'Class:PowerConnection/Attribute:power_capacity' => 'Puissance maximale (kVA)',
'Class:PowerConnection/Attribute:power_capacity+' => 'Puissance active maximale admissible en kVA',
'Class:PowerConnection/Attribute:power_type' => 'Type d\'alimentation',
'Class:PowerConnection/Attribute:power_type+' => 'Type d\'alimentation fournie',
'Class:PowerConnection/Attribute:power_type/Value:continuous' => 'continue',
'Class:PowerConnection/Attribute:power_type/Value:continuous+' => 'continue',
'Class:PowerConnection/Attribute:power_type/Value:single' => 'monophasée',
'Class:PowerConnection/Attribute:power_type/Value:single+' => 'monophasée',
'Class:PowerConnection/Attribute:power_type/Value:three' => 'triphasée',
'Class:PowerConnection/Attribute:power_type/Value:three+' => 'triphasée',
'Class:PowerConnection/Attribute:power_phase' => 'Phase utilisée',
'Class:PowerConnection/Attribute:power_phase+' => 'Phase utilisée dans le cas d\'une source initiale triphasée vers monophasée',
'Class:PowerConnection/Attribute:management_url' => 'URL de management',
'Class:PowerConnection/Attribute:management_url+' => '',
'Class:PowerConnection/Attribute:managementip' => 'IP de management',
'Class:PowerConnection/Attribute:managementip+' => '',
'Class:PowerConnection/Attribute:voltage' => 'Voltage',
'Class:PowerConnection/Attribute:voltage+' => '',
'Class:PowerConnection/Attribute:obsolescence_flag' => 'Obsolète',
'Class:PowerConnection/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
]);
//
// Class: PowerSource
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:PowerSource' => 'Arrivée électrique',
'Class:PowerSource+' => '',
'Class:PowerSource/Attribute:pdus_list' => 'PDUs',
'Class:PowerSource/Attribute:pdus_list+' => 'PDU qui utilise cette alimentation',
'Class:PowerSource/Attribute:inverters_list' => 'Onduleurs',
'Class:PowerSource/Attribute:inverters_list+' => 'Onduleurs en aval',
'Class:PowerSource/Attribute:powerstarts_list' => 'Départs électriques',
'Class:PowerSource/Attribute:powerstarts_list+' => 'Départs électriques en aval',
'Class:PowerSource/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:PowerSource/Attribute:stss_list_a+' => 'ATS/STS en aval (source A)',
'Class:PowerSource/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:PowerSource/Attribute:stss_list_b+' => 'ATS/STS en aval (source B)',
'Class:PowerSource/Attribute:obsolescence_flag' => 'Obsolète',
'Class:PowerSource/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'PowerSource:baseinfo' => 'Informations générales',
'PowerSource:moreinfo' => 'Informations complémentaires',
'PowerSource:Date' => 'Dates',
'PowerSource:otherinfo' => 'Autres informations',
'PowerSource:technicalinfo' => 'Données techniques',
]);
//
// Class: PowerStart
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:PowerStart' => 'Départ électrique',
'Class:PowerStart+' => '',
'Class:PowerStart/Attribute:powerconnection_id' => 'Source',
'Class:PowerStart/Attribute:powerconnection_id+' => '',
'Class:PowerStart/Attribute:supply_type' => 'Type de la source',
'Class:PowerStart/Attribute:supply_type+' => 'Type d\'alimentation sur la source',
'Class:PowerStart/Attribute:inverters_list' => 'Onduleurs',
'Class:PowerStart/Attribute:inverters_list+' => 'Onduleurs en aval',
'Class:PowerStart/Attribute:stss_list_a' => 'ATS/STS - source A',
'Class:PowerStart/Attribute:stss_list_a+' => 'ATS/STS en aval (source A)',
'Class:PowerStart/Attribute:stss_list_b' => 'ATS/STS - source B',
'Class:PowerStart/Attribute:stss_list_b+' => 'ATS/STS en aval (source B)',
'Class:PowerStart/Attribute:pdus_list' => 'PDUs',
'Class:PowerStart/Attribute:pdus_list+' => 'PDUs en aval',
'Class:PowerStart/Attribute:powerstarts_list' => 'Départs électriques',
'Class:PowerStart/Attribute:powerstarts_list+' => 'Départs électriques en aval',
'Class:PowerStart/Attribute:obsolescence_flag' => 'Obsolète',
'Class:PowerStart/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:PowerStart/Attribute:powerconnection_id_friendlyname' => 'Source',
'Class:PowerStart/Attribute:powerconnection_id_friendlyname+' => 'Nom complet',
'Class:PowerStart/Attribute:powerconnection_id_finalclass_recall' => 'Source->Sous-classe de CI',
'Class:PowerStart/Attribute:powerconnection_id_finalclass_recall+' => 'Nom de la classe instanciable',
'Class:PowerStart/Attribute:powerconnection_id_obsolescence_flag' => 'Source->Obsolète',
'Class:PowerStart/Attribute:powerconnection_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'PowerStart:baseinfo' => 'Informations générales',
'PowerStart:moreinfo' => 'Informations complémentaires',
'PowerStart:Date' => 'Dates',
'PowerStart:otherinfo' => 'Autres informations',
'PowerStart:technicalinfo' => 'Données techniques',
]);
//
// Class: STS
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:STS' => 'ATS / STS',
'Class:STS+' => 'Système de Transfert Automatique de Source',
'Class:STS/Attribute:powerconnection_source1_id' => 'Source A',
'Class:STS/Attribute:powerconnection_source1_id+' => '',
'Class:STS/Attribute:powerconnection_source2_id' => 'Source B',
'Class:STS/Attribute:powerconnection_source2_id+' => '',
'Class:STS/Attribute:nominal_source' => 'Source nominale',
'Class:STS/Attribute:nominal_source+' => 'Source nominale utilisée par le STS',
'Class:STS/Attribute:nominal_source/Value:source1' => 'Source A',
'Class:STS/Attribute:nominal_source/Value:source1+' => 'Source A',
'Class:STS/Attribute:nominal_source/Value:source2' => 'Source B',
'Class:STS/Attribute:nominal_source/Value:source2+' => 'Source B',
'Class:STS/Attribute:rack' => 'Rack',
'Class:STS/Attribute:rack+' => '',
'Class:STS/Attribute:nb_u' => 'Nombre d\'unités (U)',
'Class:STS/Attribute:nb_u+' => 'Nombre d\'unités consommés dans le rack par cet équipement',
'Class:STS/Attribute:position' => 'Position',
'Class:STS/Attribute:position+' => 'Position dans le rack',
'Class:STS/Attribute:redundancy' => 'Configuration de la redondance électrique',
'Class:STS/Attribute:redundancy+' => '',
'Class:STS/Attribute:pdus_list' => 'PDUs',
'Class:STS/Attribute:pdus_list+' => 'PDUs en aval',
'Class:STS/Attribute:obsolescence_flag' => 'Obsolète',
'Class:STS/Attribute:obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:STS/Attribute:powerconnection_source1_id_friendlyname' => 'Source A',
'Class:STS/Attribute:powerconnection_source1_id_friendlyname+' => 'Nom complet',
'Class:STS/Attribute:powerconnection_source1_id_finalclass_recall' => 'Source A->Sous-classe de CI',
'Class:STS/Attribute:powerconnection_source1_id_finalclass_recall+' => 'Nom de la classe instanciable',
'Class:STS/Attribute:powerconnection_source1_id_obsolescence_flag' => 'Source A->Obsolète',
'Class:STS/Attribute:powerconnection_source1_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:STS/Attribute:powerconnection_source2_id_friendlyname' => 'Source B',
'Class:STS/Attribute:powerconnection_source2_id_friendlyname+' => 'Nom complet',
'Class:STS/Attribute:powerconnection_source2_id_finalclass_recall' => 'Source B->Sous-classe de CI',
'Class:STS/Attribute:powerconnection_source2_id_finalclass_recall+' => 'Nom de la classe instanciable',
'Class:STS/Attribute:powerconnection_source2_id_obsolescence_flag' => 'Source B->Obsolète',
'Class:STS/Attribute:powerconnection_source2_id_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'Class:STS/Attribute:rack_friendlyname' => 'Rack',
'Class:STS/Attribute:rack_friendlyname+' => 'Nom complet',
'Class:STS/Attribute:rack_obsolescence_flag' => 'Rack->Obsolète',
'Class:STS/Attribute:rack_obsolescence_flag+' => 'Calculé dynamiquement en fonction d\'autres attributs de l\'objet',
'STS:baseinfo' => 'Informations générales',
'STS:moreinfo' => 'Informations complémentaires',
'STS:Date' => 'Dates',
'STS:otherinfo' => 'Autres informations',
'STS:technicalinfo' => 'Données techniques',
]);
//
// Class: Model
//
Dict::Add('FR FR', 'French', 'Français', [
'Class:Model/Attribute:type/Value:PDU+' => 'Unité de Distribution d\'Alimentation',
'Class:Model/Attribute:type/Value:PowerStart' => 'Départ Electrique',
'Class:Model/Attribute:type/Value:STS' => 'ATS/STS',
'Class:Model/Attribute:type/Value:Inverter' => 'Onduleur',
]);

View File

@@ -0,0 +1,16 @@
<?php
// PHP Data Model definition file
// WARNING - WARNING - WARNING
// DO NOT EDIT THIS FILE (unless you know what you are doing)
//
// If you use supply a datamodel.xxxx.xml file with your module
// the this file WILL BE overwritten by the compilation of the
// module (during the setup) if the datamodel.xxxx.xml file
// contains the definition of new classes or menus.
//
// The recommended way to define new classes (for iTop 2.0) is via the XML definition.
// This file remains in the module's template only for the cases where there is:
// - either no new class or menu defined in the XML file
// - or no XML file at all supplied by the module

View File

@@ -0,0 +1,51 @@
<?php
//
// iTop module definition file
//
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-power-mgmt/3.3.0',
[
// Identification
//
'label' => 'Extended Power Management for iTop Datacenter Management',
'category' => 'business',
// Setup
//
'dependencies' => [
'itop-datacenter-mgmt/3.3.0',
],
'mandatory' => false,
'visible' => true, // To prevent auto-install but shall not be listed in the install wizard
'auto_select' => 'SetupInfo::ModuleIsSelected("itop-datacenter-mgmt")',
// Components
//
'datamodel' => [
'model.itop-power-mgmt.php',
],
'webservice' => [
],
'data.struct' => [
// add your 'structure' definition XML files here,
],
'data.sample' => [
// add your sample data XML files here,
],
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
'doc.more_information' => '', // hyperlink to more information, if any
// Default settings
//
'settings' => [
// Module specific settings go here, if any
],
]
);

View File

@@ -76,9 +76,6 @@
<attribute id="finalclass"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -179,32 +176,17 @@
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>
@@ -1151,9 +1133,6 @@ public function PrefillSearchForm(&$aContextParam)
<attribute id="organization_name"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -1205,32 +1184,17 @@ public function PrefillSearchForm(&$aContextParam)
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>
@@ -1573,9 +1537,6 @@ public function PrefillSearchForm(&$aContextParam)
<attribute id="service_name"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -1624,32 +1585,17 @@ public function PrefillSearchForm(&$aContextParam)
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>

View File

@@ -76,9 +76,6 @@
<attribute id="finalclass"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -179,32 +176,17 @@
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>
@@ -1140,9 +1122,6 @@ public function PrefillSearchForm(&$aContextParam)
<attribute id="organization_name"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -1194,32 +1173,17 @@ public function PrefillSearchForm(&$aContextParam)
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>
@@ -1584,9 +1548,6 @@ public function PrefillSearchForm(&$aContextParam)
<attribute id="service_name"/>
</attributes>
</reconciliation>
<obsolescence>
<condition><![CDATA[status='obsolete']]></condition>
</obsolescence>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
@@ -1635,32 +1596,17 @@ public function PrefillSearchForm(&$aContextParam)
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="production">
<code>production</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>10</rank>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>

View File

@@ -1486,29 +1486,14 @@
<value id="draft">
<code>draft</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="published">
<code>published</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>

View File

@@ -43,38 +43,18 @@
<value id="production">
<code>production</code>
<rank>30</rank>
<style>
<main_color>$ibo-lifecycle-active-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-active-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="implementation">
<code>implementation</code>
<rank>20</rank>
<style>
<main_color>$ibo-lifecycle-inactive-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-inactive-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="stock">
<code>stock</code>
<rank>10</rank>
<style>
<main_color>$ibo-lifecycle-neutral-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-neutral-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
<value id="obsolete">
<code>obsolete</code>
<rank>40</rank>
<style>
<main_color>$ibo-lifecycle-frozen-state-primary-color</main_color>
<complementary_color>$ibo-lifecycle-frozen-state-secondary-color</complementary_color>
<decoration_classes/>
</style>
</value>
</values>
<sql>status</sql>

View File

@@ -1,129 +0,0 @@
# PHP static analysis
- [Installation](#installation)
- [Usages](#usages)
- [Analysing a package](#analysing-a-package)
- [Analysing a module](#analysing-a-module)
- [Configuration](#configuration)
- [Adjust local configuration to your needs](#adjust-local-configuration-to-your-needs)
- [Adjust configuration for a particular CI repository / job](#adjust-configuration-for-a-particular-ci-repository--job)
## Installation
- Install dependencies by running `composer install` in this folder
- You should be all set! 🚀
## Usages
### Analysing a package
_Do this if you want to analyse the whole iTop package (iTop core, extensions, third-party libs, ...)_
- Make sure you ran a setup on your iTop as it will analyse the `env-production` folder
- Open a prompt in your iTop folder
- Run the following command
```bash
tests/php-static-analysis/vendor/bin/phpstan analyse \
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
--error-format raw
```
You will then have an output like this listing all errors:
```bash
tests/php-static-analysis/vendor/bin/phpstan analyse \
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
--error-format raw
1049/1049 [============================] 100%
<ITOP>\addons\userrights\userrightsprofile.class.inc.php:552:Call to static method InitSharedClassProperties() on an unknown class SharedObject.
<ITOP>\addons\userrights\userrightsprofile.db.class.inc.php:927:Call to static method GetSharedClassProperties() on an unknown class SharedObject.
<ITOP>\addons\userrights\userrightsprojection.class.inc.php:722:Access to an undefined property UserRightsProjection::$m_aClassProjs.
<ITOP>\application\applicationextension.inc.php:295:Method AbstractPreferencesExtension::ApplyPreferences() should return bool but return statement is missing.
<ITOP>\application\cmdbabstract.class.inc.php:1010:Class utils referenced with incorrect case: Utils.
[...]
```
### Analysing a module
_Do this if you only want to analyse one or more modules within this iTop but not the whole package_
- Make sure you ran a setup on your iTop as it will analyse the `env-production` folder
- Open a prompt in your iTop folder
- Run the following command
```
tests/php-static-analysis/vendor/bin/phpstan analyse \
--configuration ./tests/php-static-analysis/config/for-module.dist.neon \
--error-format raw \
env-production/<MODULE_CODE_1> [env-production/<MODULE_CODE_2> ...]
```
You will then have an output like this listing all errors:
```
tests/php-static-analysis/vendor/bin/phpstan analyse \
--configuration ./tests/php-static-analysis/config/for-module.dist.neon \
--error-format raw \
env-production/authent-ldap env-production/itop-oauth-client
49/49 [============================] 100%
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:79:Undefined variable: $hDS
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:80:Undefined variable: $name
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:80:Undefined variable: $value
<ITOP>\env-production\itop-oauth-client\vendor\composer\InstalledVersions.php:105:Parameter $parser of method Composer\InstalledVersions::satisfies() has invalid type Composer\Semver\VersionParser.
[...]
```
## Configuration
### Adjust local configuration to your needs
#### Define which PHP version to run the analysis for
The way we configured PHPStan in this project changes how it will find the PHP version to run the analysis for. \
By default PHPStan check the information from the composer.json file, but we changed that (via the `config/php-includes/set-php-version-from-process.php` include) so it used the PHP
version currently ran by the CLI.
So all you have to do is either:
- Prepend your command line with the path of the executable of the desired PHP version
- Change the default PHP interpreter in your IDE settings
#### Change some parameters for a local run
If you want to change some particular settings (eg. the memory limit, the rules level, ...) for a local run of the analysis you have 2 choices.
##### Method 1: CLI parameter
For most parameters there is a good chance you can just add the parameter and its value in your command, which will override the one defined in the configuration file. \
Below are some example, but your can find the complete reference [here](https://phpstan.org/user-guide/command-line-usage).
```bash
--memory-limit 1G
--level 5
--error-format raw
[...]
```
**Pros** Quick and easy to try different parameters \
**Cons** Parameters aren't saved, so you'll have to remember them and put them again next time
##### Method 2: Configuration file
Crafting your own configuration file gives you the ability to fine tune any parameters, it's way more powerful but can also quickly lead to crashes if you mess with the symbols discovery (classes, ...). \
But mostly it can be saved, shared, re-used; which is it's main purpose.
It is recommended that you create your configuration file from scratch and that you include the `base.dist.neon` so you are bootstrapped for the symbols discovery. Then you can override any parameter. \
Check [the documentation](https://phpstan.org/config-reference#multiple-files) for more information.
```neon
includes:
- base.dist.neon
parameters:
# Override parameters here
```
#### Analyse only one (or some) folder(s) quicker
It's pretty easy and good news you don't need to create a new configuration file or change an existing one. \
Just adapt and use command lines from the [usages section](#usages) and add the folders you want to analyse at the end of the command, exactly like when analysing modules.
For example if you want to analyse just `<ITOP>/setup` and `<ITOP>/sources`, use something like:
```
tests/php-static-analysis/vendor/bin/phpstan analyse \
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
--error-format raw \
setup sources
```
### Adjust configuration for a particular CI repository / job
TODO

View File

@@ -1,5 +0,0 @@
{
"require": {
"phpstan/phpstan": "^2.1"
}
}

View File

@@ -1,72 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cc6d7580a5e98236d68d8b91de9ddebb",
"packages": [
{
"name": "phpstan/phpstan",
"version": "2.1.33",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
"reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2025-12-05T10:24:31+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

View File

@@ -1,29 +0,0 @@
## Disclaimer
DON'T modify the following files without knowledge and discussing with the team:
- base.dist.neon
- for-package.dist.neon
- for-module.dist.neon
## Purpose of these files
### base.dist.neon
This configuration file contains the common parameters for all analysis, whereas it is a package, a module or something specific. Among others:
- Rules level for analysis
- PHP version to compare
- Necessary files for autoloaders discovery and such
- ...
This file should not be modified for your specific needs, you should always include it and override the desired parameters. \
See how it is done in `for-package.dist.neon` and `for-module.dist.neon` or on the documentation [here](https://phpstan.org/config-reference#multiple-files).
### for-package.dist.neon
This configuration file contains the parameters to analyse a package (iTop core, modules, third-party libs).
### for-module.dist.neon
This configuration file contains the parameters to analyse one or more modules only.
## How / when can I modify these files?
**You CAN'T!** \
Well, unless there is a good reason and you talked about it with the team. But you should never modify them for a specific need on your local environment.
- If you have a particular need for your local environment (eg. increase memory limit, change rules levels, analyse only a specific folder), check the [Configuration section](../#configuration) of the main README.md.
- If you feel like there is need for an adjustment in the default configurations, discuss it with th team and make a PR.

View File

@@ -1,40 +0,0 @@
includes:
- php-includes/set-php-version-from-process.php # Workaround to set PHP version to the on running the CLI
# for an explanation of the baseline concept, see: https://phpstan.org/user-guide/baseline
#baseline HERE DO NOT REMOVE FOR CI
parameters:
level: 0
#phpVersion: null # Explicitly commented as we rather use the detected version from the above include (`php-includes/target-php-version.php`)
editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' # Open in PHPStorm as it's Combodo's default IDE
bootstrapFiles:
- ../../../approot.inc.php
- ../../../bootstrap.inc.php
scanFiles:
# Files necessary as they contain some declarations (constants, classes, functions, ...)
- ../../../approot.inc.php
- ../../../bootstrap.inc.php
excludePaths:
analyse:
# For third-party libs we should analyse them in a dedicated configuration as we can't improve / clean them which would
# prevent us from raising the rules level as we improve / clean our codebase
- ../../../lib # Irrelevant as we only want to analyze our codebase
- ../../../node_modules # Irrelevant as we only want to analyze our codebase
analyseAndScan:
# This file generates "unignorable errors" for the baseline due to its format, so we don't have any other choice than to exclude it.
# But mind that it will prevent PHPStan from warning us about PHP syntax errors in this file.
- ../../../core/oql/build/PHP/Lempar.php
#- ../../../data # Left and commented on purpose to show that we want to analyse the generated cache files
# Note 1: We can't analyse these folders as if a PHP file requires another PHP element declared in an XML file, it won't find it. So we rely only on `env-production`
# Note 2: Only the options selected during the setup will be analysed correctly in `env-production`. For unselected options, we still want to ignore them during the analysis as they would only give a false sentiment of security as their XML PHP classes / snippets / etc would not be tested.
- ../../../data/production-modules (?) # Irrelevent as it will already be in `env-production` (for local run only, not useful in the CI)
- ../../../datamodels # Irrelevent as it will already be in `env-production`
- ../../../extensions # Irrelevent as it will already be in `env-production` (for local run only, not useful in the CI)
- ../../../env-php-unit-tests (?) # Irrelevant as it will either already be in `env-production` or might be desynchronized from `env-production`
- ../../../env-toolkit (?) # Irrelevent as it will either already be in `env-production` or might be desynchronized from `env-production` (for local run only, not useful in the CI)
- ../../../tests (?) # Exclude tests for now
- ../../../toolkit (?) # Exlclude toolkit for now

View File

@@ -1,15 +0,0 @@
includes:
- base.dist.neon
parameters:
paths:
# We just want to analyse the module folder(s), either:
# - Create your own `for-module.neon` file, include this one and override this parameter (see https://phpstan.org/config-reference#multiple-files)
# - Pass the module folder(s) in the commande line (see https://phpstan.org/config-reference#analysed-files)
scanDirectories:
# Unlike for `for-package.dist.neon`, here we need to scan all the folders to discover symbols, but we only want to analyse the module folder.
# We initially thought of doing it through the `excludePaths` param. by excluding everything but the module folder, but it doesn't seem to be possible, because it uses the `fnmatch()` function.
# As a workaround, we list here all the folders to scan.
#
# Scan the whole project and rely on the `excludePaths` param. to filter the unnecessary
- ../../..

View File

@@ -1,7 +0,0 @@
includes:
- base.dist.neon
parameters:
paths:
# We want to analyse almost the whole project, so we do a negative selection between the `paths` and `excludePaths` (see base.dist.neon) parameters
- ../../../

View File

@@ -1,25 +0,0 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
declare(strict_types=1);
/**
* This file is only here to allow setting a specific PHP version to run the analysis for without
* having to explicitly set it in the .neon file. This is the best way we found so far.
*
* @link https://phpstan.org/config-reference#phpversion
*
* Usage: Uses the CLI PHP version by default, which would work fine for
* - The CI as the docker image has the target PHP version in both CLI and web
* - The developer's IDE as PHPStorm also has a default PHP version configured which can be changed on the fly
*/
// Default PHP version to analyse is the one running in CLI
$config = [];
$config['parameters']['phpVersion'] = PHP_VERSION_ID;
return $config;

View File

@@ -2,6 +2,9 @@
Documentation on creating and maintaining tests in iTop.
## Prerequisites
### PHPUnit configuration file
@@ -75,8 +78,7 @@ Example :
$oTagData->DBDelete();
```
> [!WARNING]
> When the condition is met the test is finished and following code will be ignored !
Warning : when the condition is met the test is finished and following code will be ignored !
Another way to do is using try/catch blocks, for example :
```php

View File

@@ -28,14 +28,14 @@ class AjaxPageTest extends ItopDataTestCase
$iLastCompilation = filemtime(APPROOT.'env-production');
// When
$sOutput = $this->CallItopUri(
"pages/exec.php?exec_module=itop-hub-connector&exec_page=ajax.php",
$sOutput = $this->CallItopUrl(
"/pages/exec.php?exec_module=itop-hub-connector&exec_page=ajax.php",
[
'auth_user' => $sLogin,
'auth_pwd' => self::AUTHENTICATION_PASSWORD,
'operation' => "compile",
'authent' => self::AUTHENTICATION_TOKEN,
],
]
);
// Then
@@ -53,4 +53,26 @@ class AjaxPageTest extends ItopDataTestCase
clearstatcache();
$this->assertGreaterThan($iLastCompilation, filemtime(APPROOT.'env-production'), 'The env-production directory should have been rebuilt');
}
protected function CallItopUrl($sUri, ?array $aPostFields = null, bool $bXDebugEnabled = false)
{
$ch = curl_init();
if ($bXDebugEnabled) {
curl_setopt($ch, CURLOPT_COOKIE, 'XDEBUG_SESSION=phpstorm');
}
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
var_dump($sUrl);
curl_setopt($ch, CURLOPT_URL, $sUrl);
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$sOutput = curl_exec($ch);
//echo "$sUrl error code:".curl_error($ch);
curl_close($ch);
return $sOutput;
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -25,15 +24,19 @@
require_once('../../../approot.inc.php');
require_once(APPROOT.'application/startup.inc.php');
$sEnvironment = MetaModel::GetEnvironmentId();
$aEntries = [];
$aEntries = array();
$aCacheUserData = apc_cache_info_compat();
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list'])) {
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
{
$sPrefix = 'itop-'.$sEnvironment.'-query-cache-';
foreach ($aCacheUserData['cache_list'] as $i => $aEntry) {
foreach($aCacheUserData['cache_list'] as $i => $aEntry)
{
$sEntryKey = array_key_exists('info', $aEntry) ? $aEntry['info'] : $aEntry['key'];
if (strpos($sEntryKey, $sPrefix) === 0) {
if (strpos($sEntryKey, $sPrefix) === 0)
{
$aEntries[] = $sEntryKey;
}
}
@@ -41,39 +44,52 @@ if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list'])) {
echo "<pre>";
if (empty($aEntries)) {
if (empty($aEntries))
{
echo "No Data";
return;
}
$sKey = $aEntries[0];
$result = apc_fetch($sKey);
if (!is_object($result)) {
if (!is_object($result))
{
return;
}
$oSQLQuery = $result;
echo "NB Tables before;NB Tables after;";
foreach ($oSQLQuery->m_aContextData as $sField => $oValue) {
foreach($oSQLQuery->m_aContextData as $sField => $oValue)
{
echo $sField.';';
}
echo "\n";
sort($aEntries);
foreach ($aEntries as $sKey) {
foreach($aEntries as $sKey)
{
$result = apc_fetch($sKey);
if (is_object($result)) {
if (is_object($result))
{
$oSQLQuery = $result;
if (isset($oSQLQuery->m_aContextData)) {
if (isset($oSQLQuery->m_aContextData))
{
echo $oSQLQuery->m_iOriginalTableCount.";".$oSQLQuery->CountTables().';';
foreach ($oSQLQuery->m_aContextData as $oValue) {
if (is_array($oValue)) {
foreach($oSQLQuery->m_aContextData as $oValue)
{
if (is_array($oValue))
{
$sVal = json_encode($oValue);
} else {
if (empty($oValue)) {
}
else
{
if (empty($oValue))
{
$sVal = '';
} else {
}
else
{
$sVal = $oValue;
}
}
@@ -85,3 +101,4 @@ foreach ($aEntries as $sKey) {
}
echo "</pre>";

View File

@@ -18,7 +18,6 @@ use ArchivedObjectException;
use CMDBObject;
use CMDBSource;
use Combodo\iTop\Service\Events\EventService;
use Config;
use Contact;
use CoreException;
use CoreUnexpectedValue;
@@ -71,9 +70,6 @@ abstract class ItopDataTestCase extends ItopTestCase
private $aCreatedObjects = [];
private $aEventListeners = [];
protected ?string $sConfigTmpBackupFile = null;
protected ?Config $oiTopConfig = null;
/**
* @var bool When testing with silo, there are some cache we need to update on tearDown. Doing it all the time will cost too much, so it's opt-in !
* @see tearDown
@@ -128,8 +124,6 @@ abstract class ItopDataTestCase extends ItopTestCase
{
parent::setUp();
\IssueLog::Error($this->getName());
$this->PrepareEnvironment();
if (static::USE_TRANSACTION) {
@@ -196,8 +190,6 @@ abstract class ItopDataTestCase extends ItopTestCase
CMDBObject::SetCurrentChange(null);
$this->RestoreConfiguration();
parent::tearDown();
}
@@ -1525,35 +1517,4 @@ abstract class ItopDataTestCase extends ItopTestCase
$oObject->Set($sStopwatchAttCode, $oStopwatch);
}
protected function BackupConfiguration(): void
{
$sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
clearstatcache();
echo sprintf("rights via ls on %s:\n %s \n", $sConfigPath, exec("ls -al $sConfigPath"));
$sFilePermOutput = substr(sprintf('%o', fileperms('/etc/passwd')), -4);
echo sprintf("rights via fileperms on %s:\n %s \n", $sConfigPath, $sFilePermOutput);
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
MetaModel::GetConfig()->WriteToFile($this->sConfigTmpBackupFile);
$this->oiTopConfig = new Config($sConfigPath);
}
protected function RestoreConfiguration(): void
{
if (is_null($this->sConfigTmpBackupFile) || ! is_file($this->sConfigTmpBackupFile)) {
return;
}
if (is_null($this->oiTopConfig)) {
return;
}
//put config back
$sConfigPath = $this->oiTopConfig->GetLoadedFile();
@chmod($sConfigPath, 0770);
$oConfig = new Config($this->sConfigTmpBackupFile);
$oConfig->WriteToFile($sConfigPath);
@chmod($sConfigPath, 0440);
@unlink($this->sConfigTmpBackupFile);
}
}

View File

@@ -8,11 +8,12 @@
namespace Combodo\iTop\Test\UnitTest;
use CMDBSource;
use DateTime;
use DeprecatedCallsLog;
use MySQLTransactionNotClosedException;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use ReflectionMethod;
use SetupUtils;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\KernelInterface;
use const DEBUG_BACKTRACE_IGNORE_ARGS;
@@ -28,7 +29,6 @@ use const DEBUG_BACKTRACE_IGNORE_ARGS;
abstract class ItopTestCase extends KernelTestCase
{
public const TEST_LOG_DIR = 'test';
protected array $aFileToClean = [];
/**
* @var bool
@@ -37,7 +37,7 @@ abstract class ItopTestCase extends KernelTestCase
public const DISABLE_DEPRECATEDCALLSLOG_ERRORHANDLER = true;
public static $DEBUG_UNIT_TEST = false;
protected static $aBackupStaticProperties = [];
public ?array $aLastCurlGetInfo = null;
/**
* @link https://docs.phpunit.de/en/9.6/annotations.html#preserveglobalstate PHPUnit `preserveGlobalState` annotation documentation
*
@@ -175,15 +175,6 @@ abstract class ItopTestCase extends KernelTestCase
}
throw new MySQLTransactionNotClosedException('Some DB transactions were opened but not closed ! Fix the code by adding ROLLBACK or COMMIT statements !', []);
}
foreach ($this->aFileToClean as $sPath) {
if (is_file($sPath)) {
@unlink($sPath);
continue;
}
SetupUtils::tidydir($sPath);
}
}
/**
@@ -640,62 +631,4 @@ abstract class ItopTestCase extends KernelTestCase
fclose($handle);
return array_reverse($aLines);
}
/**
* @param $sUrl
* @param array|null $aPostFields
* @param array|null $aCurlOptions
* @param $bXDebugEnabled
* @return string
*/
protected function CallUrl($sUrl, ?array $aPostFields = [], ?array $aCurlOptions = [], $bXDebugEnabled = false): string
{
$ch = curl_init();
if ($bXDebugEnabled) {
curl_setopt($ch, CURLOPT_COOKIE, "XDEBUG_SESSION=phpstorm");
}
curl_setopt($ch, CURLOPT_URL, $sUrl);
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Force disable of certificate check as most of dev / test env have a self-signed certificate
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt_array($ch, $aCurlOptions);
if ($this->IsArrayOfArray($aPostFields)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($aPostFields));
} else {
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
}
$sOutput = curl_exec($ch);
$info = curl_getinfo($ch);
$this->aLastCurlGetInfo = $info;
$sErrorMsg = curl_error($ch);
$iErrorCode = curl_errno($ch);
curl_close($ch);
\IssueLog::Info(__METHOD__, null, ['url' => $sUrl, 'error' => $sErrorMsg, 'error_code' => $iErrorCode, 'post_fields' => $aPostFields, 'info' => $info]);
return $sOutput;
}
private function IsArrayOfArray(array $aStruct): bool
{
foreach ($aStruct as $k => $v) {
if (is_array($v)) {
return true;
}
}
return false;
}
protected function CallItopUri(string $sUri, ?array $aPostFields = [], ?array $aCurlOptions = [], $bXDebugEnabled = false): string
{
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
return $this->CallUrl($sUrl, $aPostFields, $aCurlOptions, $bXDebugEnabled);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Application;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use MetaModel;
class LoginTest extends ItopDataTestCase
{
protected $sConfigTmpBackupFile;
protected $sConfigPath;
protected $sLoginMode;
protected function setUp(): void
{
parent::setUp();
clearstatcache();
// The test consists in requesting UI.php from outside iTop with a specific configuration
// Hence the configuration file must be tweaked on disk (and restored)
$this->sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
file_put_contents($this->sConfigTmpBackupFile, file_get_contents($this->sConfigPath));
$oConfig = new \Config($this->sConfigPath);
$this->sLoginMode = "unimplemented_loginmode";
$oConfig->AddAllowedLoginTypes($this->sLoginMode);
@chmod($this->sConfigPath, 0770);
$oConfig->WriteToFile();
@chmod($this->sConfigPath, 0444);
}
protected function tearDown(): void
{
if (! is_null($this->sConfigTmpBackupFile) && is_file($this->sConfigTmpBackupFile)) {
//put config back
@chmod($this->sConfigPath, 0770);
file_put_contents($this->sConfigPath, file_get_contents($this->sConfigTmpBackupFile));
@chmod($this->sConfigPath, 0444);
@unlink($this->sConfigTmpBackupFile);
}
parent::tearDown();
}
protected function CallItopUrlByCurl($sUri, ?array $aPostFields = [])
{
$ch = curl_init();
$sUrl = MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
curl_setopt($ch, CURLOPT_URL, $sUrl);
if (0 !== sizeof($aPostFields)) {
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$sOutput = curl_exec($ch);
curl_close($ch);
return $sOutput;
}
}

View File

@@ -143,12 +143,34 @@ class QueryTest extends ItopDataTestCase
{
// compute request url
$url = $oQuery->GetExportUrl();
$aCurlOptions = [
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => self::USER.':'.self::PASSWORD,
];
return $this->CallUrl($url, [], $aCurlOptions);
// open curl
$curl = curl_init();
// curl options
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, self::USER.':'.self::PASSWORD);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Force disable of certificate check as most of dev / test env have a self-signed certificate
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
// execute curl
$result = curl_exec($curl);
if (curl_errno($curl)) {
$info = curl_getinfo($curl);
var_export($info);
var_dump([
'url' => $url,
'app_root_url:' => MetaModel::GetConfig()->Get('app_root_url'),
'GetAbsoluteUrlAppRoot:' => \utils::GetAbsoluteUrlAppRoot(),
]);
}
// close curl
curl_close($curl);
return $result;
}
/** @inheritDoc */

View File

@@ -1,138 +0,0 @@
<?php
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
class DBObjectSetTest extends ItopDataTestCase
{
public const USE_TRANSACTION = true;
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->CreateURs();
}
protected function tearDown(): void
{
CMDBSource::TriggerExceptionWhenSqlQuery(null);
parent::tearDown(); // TODO: Change the autogenerated stub
}
private function CreateURs()
{
$this->CreateTestOrganization();
for ($i = 0; $i < 10; $i++) {
$this->CreateTicket($i);
}
}
public function testCount()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$oSet = new DBObjectSet($oSearch);
$iCount = $oSet->Count();
$oSet->Fetch();
$this->assertEquals($iCount, $oSet->Count());
$this->assertEquals($iCount, $oSet->CountWithLimit(0));
$this->assertTrue($oSet->CountExceeds(0));
//no DB SQL query: exception will be raised after here
CMDBSource::TriggerExceptionWhenSqlQuery(__METHOD__.' :'.__LINE__);
$this->assertEquals($iCount, $oSet->Count(), 'should use cache and not call DB again');
}
public function testRewind()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$oSet = new DBObjectSet($oSearch);
while ($oObj = $oSet->Fetch()) {
$this->assertNotEquals(0, $oObj->GetKey());
}
//no DB SQL query: exception will be raised after here
CMDBSource::TriggerExceptionWhenSqlQuery(__METHOD__.' :'.__LINE__);
$oSet->Rewind();
while ($oObj = $oSet->Fetch()) {
$this->assertNotEquals(0, $oObj->GetKey());
}
}
public function testDBObjectSetComparator()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$DBObjectSet1 = new DBObjectSet($oSearch);
$DBObjectSet3 = new DBObjectSet($oSearch);
$oDBObjectSetComparator = new DBObjectSetComparator($DBObjectSet1, $DBObjectSet3);
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
}
public function testDBObjectSetComparator_CheckCache()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$DBObjectSet1 = new DBObjectSet($oSearch);
$DBObjectSet3 = new DBObjectSet($oSearch);
$oDBObjectSetComparator = new DBObjectSetComparator($DBObjectSet1, $DBObjectSet3);
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
$sMsg = __METHOD__.' :'.__LINE__;
//no DB SQL query: exception will be raised after here
CMDBSource::TriggerExceptionWhenSqlQuery($sMsg);
$oDBObjectSetComparator = new DBObjectSetComparator($DBObjectSet1, $DBObjectSet3);
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
$oDBObjectSetComparator = new DBObjectSetComparator($DBObjectSet1, new DBObjectSet($oSearch));
$this->expectExceptionMessage($sMsg, "should call DB again this time");
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
}
public static function JeffreyProvider()
{
return [
'basic' => [false],
'opt trick' => [true],
];
}
/**
* @dataProvider JeffreyProvider
*/
public function testJeffrey(bool $bTrick)
{
echo '<p>-------</p>';
$this->doesNotPerformAssertions();
$iMax = 100;
$oFilter = DBObjectSearch::FromOQL_AllData('SELECT UserRequest');
$oSet = new DBObjectSet($oFilter);
$oSet->OptimizeColumnLoad([
'UserRequest' => ['ref', 'status', 'title'],
]);
if ($bTrick) {
$oNewSet = DBObjectSet::FromScratch($oSet->GetClass());
while ($oObj = $oSet->Fetch()) {
$oNewSet->AddObject($oObj);
}
$oSet = $oNewSet;
}
echo '<p>Start: '.date('Y-m-d H:i:s').'</p>';
$i = 0;
while ($i < $iMax) {
$oSet->Rewind();
while ($oObj = $oSet->Fetch()) {
// Do nothing
$s = $oObj->Get('title');
}
$i += 1;
}
$peak = memory_get_peak_usage(true) / 1000000;
echo '<p>End: '.date('Y-m-d H:i:s').'</p><p>Peak memory: '.$peak.'</p> \n';
}
}

View File

@@ -12,8 +12,10 @@ class CliResetSessionTest extends ItopDataTestCase
public const USE_TRANSACTION = false;
private $sCookieFile = "";
private $sUrl;
private $sLogin;
private $sPassword = "Iuytrez9876543ç_è-(";
protected $sConfigTmpBackupFile;
/**
* @throws Exception
@@ -22,13 +24,16 @@ class CliResetSessionTest extends ItopDataTestCase
{
parent::setUp();
$this->BackupConfiguration();
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
MetaModel::GetConfig()->WriteToFile($this->sConfigTmpBackupFile);
$this->sLogin = "rest-user-".date('dmYHis');
$this->CreateTestOrganization();
$this->sCookieFile = tempnam(sys_get_temp_dir(), 'jsondata_');
$this->sUrl = \MetaModel::GetConfig()->Get('app_root_url');
$oRestProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => 'REST Services User'], true);
$oAdminProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => 'Administrator'], true);
@@ -42,6 +47,16 @@ class CliResetSessionTest extends ItopDataTestCase
{
parent::tearDown();
if (! is_null($this->sConfigTmpBackupFile) && is_file($this->sConfigTmpBackupFile)) {
//put config back
$sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
@chmod($sConfigPath, 0770);
$oConfig = new Config($this->sConfigTmpBackupFile);
$oConfig->WriteToFile($sConfigPath);
@chmod($sConfigPath, 0444);
unlink($this->sConfigTmpBackupFile);
}
if (!empty($this->sCookieFile)) {
unlink($this->sCookieFile);
}
@@ -135,18 +150,26 @@ class CliResetSessionTest extends ItopDataTestCase
*/
private function SendHTTPRequestWithCookies($sUri, $aPostFields, $sForcedLoginMode = null): string
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->sCookieFile);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->sCookieFile);
$sUrl = "$this->sUrl/$sUri";
if (!is_null($sForcedLoginMode)) {
$sUri .= "?login_mode=$sForcedLoginMode";
$sUrl .= "?login_mode=$sForcedLoginMode";
}
curl_setopt($ch, CURLOPT_URL, $sUrl);
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// Force disable of certificate check as most of dev / test env have a self-signed certificate
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$aCurlOptions = [
CURLOPT_COOKIEJAR => $this->sCookieFile,
CURLOPT_COOKIEFILE => $this->sCookieFile,
CURLOPT_HEADER => 1,
];
$sResponse = $this->CallItopUri($sUri, $aPostFields, $aCurlOptions);
var_dump($this->aLastCurlGetInfo);
$sResponse = curl_exec($ch);
/** $sResponse example
* "HTTP/1.1 200 OK
Date: Wed, 07 Jun 2023 05:00:40 GMT
@@ -154,15 +177,16 @@ class CliResetSessionTest extends ItopDataTestCase
Set-Cookie: itop-2e83d2e9b00e354fdc528621cac532ac=q7ldcjq0rvbn33ccr9q8u8e953; path=/
*/
//var_dump($sResponse);
$iHeaderSize = $this->aLastCurlGetInfo['header_size'] ?? 0;
$iHeaderSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$sBody = substr($sResponse, $iHeaderSize);
//$iHttpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
if (preg_match('/HTTP.* (\d*) /', $sResponse, $aMatches)) {
$sHttpCode = $aMatches[1];
} else {
$sHttpCode = $this->aLastCurlGetInfo['http_code'] ?? -1;
$sHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
}
curl_close($ch);
$this->assertEquals(200, $sHttpCode, "The test logic assumes that the HTTP request is correctly handled");
return $sBody;

View File

@@ -17,6 +17,7 @@ class RestTest extends ItopDataTestCase
public const USE_TRANSACTION = false;
public const CREATE_TEST_ORG = false;
private static $sUrl;
private static $sLogin;
private static $sPassword = "Iuytrez9876543ç_è-(";
@@ -43,6 +44,7 @@ class RestTest extends ItopDataTestCase
{
parent::setUp();
static::$sUrl = MetaModel::GetConfig()->Get('app_root_url');
static::$sLogin = "rest-user-".date('dmYHis');
$this->CreateTestOrganization();
@@ -94,6 +96,7 @@ class RestTest extends ItopDataTestCase
public function testPostJSONDataAsCurlFile()
{
$sCallbackName = 'fooCallback';
$sJsonData = '{"operation": "list_operations"}';
// Test regular JSON result
@@ -294,7 +297,16 @@ JSON;
$aPostFields['callback'] = $sCallbackName;
}
$sJson = $this->CallItopUri('webservices/rest.php', $aPostFields);
curl_setopt($ch, CURLOPT_URL, static::$sUrl."/webservices/rest.php");
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Force disable of certificate check as most of dev / test env have a self-signed certificate
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$sJson = curl_exec($ch);
curl_close($ch);
if (!is_null($sTmpFile)) {
unlink($sTmpFile);