Compare commits

...

41 Commits

Author SHA1 Message Date
vdumas
f805c2f834 N°6203 - Add complementary name to CMDB classes 2023-05-22 18:30:33 +02:00
vdumas
976c9c1523 N°6311 - Add a Log on User Local, LDAP and External 2023-05-20 18:54:10 +02:00
vdumas
22a14a2de2 N°6323 - n:n Add filter on lnk ExtKey aligned to PrefillSearchForm 2023-05-20 17:21:10 +02:00
vdumas
bd04b2a24c N°6203 - Standard DM overcard, complementary name 2023-05-20 17:20:16 +02:00
Eric Espie
0188108a68 Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-17 10:16:18 +02:00
Pierre Goiffon
4038d4d925 N°6314 N°1150 Fix cannot reopen or close a UR in the user portal
Since introduction of MultipleChoicesField and SelectObjectField value checks with b71cd218, when a portal user was trying to do an action on a resolved UR the agent_id field was rejected. The reason for the rejection is the corresponding SelectObjectField query is containing the portal user scopes (query generated in ObjectFormManager::Build), and of course the agent_id set isn't in this scope...

As a workaround we are disabling those checks for read only fields (values are set server side and possibly in a different context than the portal user)
2023-05-16 17:54:24 +02:00
acognet
ea88aa85df N°4517 - PHP 8.1 compatibility : Replace htmlentities(***, ENT_QUOTES,'UTF-8') with utils::EscapeHtml 2023-05-16 17:45:58 +02:00
Eric Espie
8f2814a590 Undo... Move event declaration where the object is defined 2023-05-16 15:59:50 +02:00
Eric Espie
ad25d8823f Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-16 15:52:02 +02:00
Eric Espie
59cd489e72 N°6252 - Make designer itop 3.1 compatible - Fix deprecated calls of opendir() with null value 2023-05-16 14:06:01 +02:00
Pierre Goiffon
4b9b0a91a9 N°6307 Fix alert when caselog filled and submitting form
Regression was introduced in 8dc7d68b
Temporary fix, as the alert is caused by activity-panel.js which is disabled only if another handler is registered before 🤯 As a temporary fix I add an empty handler at the same place as the one that was there before, but we need a better way to disable activity-panel.js handler !
2023-05-16 12:14:26 +02:00
vdumas
a21def5329 N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
vdumas
55f8efe0db N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
Eric Espie
0cc28b42b2 Move EventService init to Metamodel::Startup() for unit tests 2023-05-16 10:23:01 +02:00
Anne-Catherine
a98fab8f66 N°6240 - Improve display of picture in read or edit mode (#484) 2023-05-15 16:12:26 +02:00
vdumas
bf21481bf6 N°6212 - Replace 'case logs' by 'logs' in naming of ActionEmail for TriggerOnObjectMention 2023-05-15 12:32:31 +02:00
Eric Espie
cda51c67c5 Fix warning 2023-05-15 11:46:25 +02:00
Eric Espie
64264eb7ed Move event declaration where the object is defined (Fix undefined constant) 2023-05-15 10:42:58 +02:00
Eric Espie
940118967c Move event declaration where the object is defined 2023-05-15 09:33:53 +02:00
vdumas
a647c235c3 N°1350 - Audit domains: add description as complementary name 2023-05-15 09:27:02 +02:00
vdumas
752ac5a469 N°5971 - Allow changing the Org of a Person having Portal User without allowed orgs 2023-05-15 08:54:19 +02:00
vdumas
c61faf453c N°5971 - Prevent changing the Org of a Person having Portal User without the new Org allowed 2023-05-12 18:24:46 +02:00
Pierre Goiffon
b71cd2182f N°1150 Add value checks in MultipleChoicesField and SelectObjectField
Now current value is checked against list of possible values
2023-05-12 10:59:33 +02:00
Pierre Goiffon
7c594db4b9 N°1150 AtributeCustomFields : FromJSONToValue is now delegated to the handler
Method was always returning null before
2023-05-12 10:58:43 +02:00
Pierre Goiffon
cca27674d0 ItopDataTestCase : extract UR params to a method to allow overrides 2023-05-12 10:58:43 +02:00
Eric Espie
f426626b9a N°6299 - DBUpdate regression in 3.1 - Fix unit tests 2023-05-12 09:09:39 +02:00
Eric Espie
5dc80f31f5 N°6299 - DBUpdate regression in 3.1 when setting a field with same value in the concerned object 2023-05-11 16:16:49 +02:00
denis.flaven@combodo.com
7fe565c4fb Merge branch 'feature/6133-add-extra-files-to-backup-and-restore' into develop 2023-05-11 14:41:02 +02:00
denis.flaven@combodo.com
08ebac1b5c Fix PR remarks 2023-05-10 17:42:50 +02:00
vdumas
380723056a N°5559 - Obsolete replica locks object edition - fix default param value 2023-05-10 10:24:12 +02:00
Stephen Abello
2f20cf52f3 N°6219 Fix issues in dictionary entries 2023-05-10 10:08:18 +02:00
Stephen Abello
5219541b74 N°6154 Fix some classes names hardcoded in dict entries and calls MetaModel GetName instead of hardcoded dict entry 2023-05-10 10:03:02 +02:00
Pierre Goiffon
4695511b46 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-05-09 15:37:05 +02:00
odain
0b5bf7bf6f N°6299 - DBUpdate regression in 3.1 when setting a field with same value in the concerned object - fix temporarly ci until fix comes 2023-05-09 14:51:26 +02:00
Pierre Goiffon
cdcc069099 Fix typo in exception message
Regression introduced in fe179079 in support/3.0 branch and upwards
2023-05-09 14:02:02 +02:00
denis.flaven@combodo.com
001194835f Config file may not exist during unattended setup/backup 2023-04-06 10:50:47 +02:00
denis.flaven@combodo.com
955aefc05b Ignore non-existing files. 2023-04-04 14:26:31 +02:00
denis.flaven@combodo.com
034ca26d01 Do NOT backup unsafe files. 2023-04-03 11:54:01 +02:00
denis.flaven@combodo.com
d03bd706e2 Support of extra files (configurable) in the backup. 2023-03-29 16:53:38 +02:00
denis.flaven@combodo.com
d3f8e1c472 Revert "N°5619 - Hide newsroom menu when no provider"
This reverts commit 647b669eb9.
2022-11-14 18:28:18 +01:00
denis.flaven@combodo.com
647b669eb9 N°5619 - Hide newsroom menu when no provider 2022-11-14 18:18:10 +01:00
52 changed files with 1007 additions and 150 deletions

View File

@@ -35,15 +35,16 @@ class AuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -2955,6 +2955,9 @@ JS
$oPage->add_ready_script(<<<JS
// Try to release concurrent lock when leaving the page
$(window).on('unload',function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
window.onbeforeunload = function() {
// N°6307 just an empty function to disable activity-panel.js handler
};
// Leave handler for the current form (check if in a modal or not)
// Note: We use a self-invoking function to avoid making unique vars. names to avoid collision (this can be called multiple time if modal forms are displayed)
@@ -5874,6 +5877,11 @@ JS
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE);
}
final protected function FireEventBeforeObjectCreate()
{
$this->FireEvent(EVENT_DB_BEFORE_CREATE);
}
/**
* @return void
* @throws \CoreException
@@ -5891,6 +5899,16 @@ JS
/// UPDATE
///
/**
* @return void
* @throws \CoreException
*/
final protected function FireEventBeforeObjectUpdate()
{
$this->FireEvent(EVENT_DB_BEFORE_UPDATE);
}
/**
* @param array $aChanges
*

View File

@@ -203,6 +203,23 @@
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_BEFORE_CREATE" _delta="define">
<description>An object is about to be created into the database. The object can be modified.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnInsert</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CREATE_DONE" _delta="define">
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
<sources>
@@ -220,6 +237,23 @@
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_BEFORE_UPDATE" _delta="define">
<description>An object is about to be updated into the database. The object can be modified.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
<description>An object has been updated into the database and reloaded.</description>
<sources>

View File

@@ -15,10 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once(APPROOT.'core/cmdbobject.class.inc.php');
require_once(APPROOT.'application/utils.inc.php');
@@ -103,6 +100,3 @@ else
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));

View File

@@ -801,8 +801,8 @@ abstract class AttributeDefinition
/**
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
*
* @param $proposedValue
* @param $oHostObj
* @param mixed $proposedValue
* @param \DBObject $oHostObj
*
* @return mixed
*/
@@ -992,17 +992,21 @@ abstract class AttributeDefinition
}
/**
* Helper to form a value, given JSON decoded data
* Helper to form a value, given JSON decoded data. This way the attribute itself handles the transformation from the JSON structure to the expected data (the one that
* needs to be used in the {@see \DBObject::Set()} method).
*
* Note that for CSV and XML this isn't done yet (no delegation to the attribute but switch/case inside controllers) :/
*
* @see GetForJSON for the reverse operation
*
* @param string $json JSON encoded value
*
* @return mixed JSON decoded data
* @return mixed JSON decoded data, depending on the attribute type
*
* @see GetForJSON for the reverse operation
*/
public function FromJSONToValue($json)
{
// Passthrough in most of the cases
// Pass-through in most of the cases
return $json;
}
@@ -8607,7 +8611,7 @@ class AttributeImage extends AttributeBlob
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
// Important: If you change this, mind updating edit_image.js as well
return '<div class="'.$sCssClasses.'" style="max-width: '.$sMaxWidthPx.'; max-height: '.$sMaxHeightPx.'; aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
return '<div class="'.$sCssClasses.'" style="max-width: min('.$sMaxWidthPx.',100%); max-height: min('.$sMaxHeightPx.',100%); aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
}
/**
@@ -8621,7 +8625,7 @@ class AttributeImage extends AttributeBlob
* @since 2.7.0 change visibility to protected
*/
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
return '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
return '<img src="'.$sUrl.'" style="max-width: min('.$iMaxWidthPx.',100%); max-height: min('.$iMaxHeightPx.',100%)">';
}
/**
@@ -13048,6 +13052,7 @@ class AttributeCustomFields extends AttributeDefinition
public function GetHandler($aValues = null)
{
$sHandlerClass = $this->Get('handler_class');
/** @var \TemplateFieldsHandler $oHandler */
$oHandler = new $sHandlerClass($this->GetCode());
if (!is_null($aValues))
{
@@ -13072,36 +13077,50 @@ class AttributeCustomFields extends AttributeDefinition
/**
* Makes the string representation out of the values given by the form defined in GetDisplayForm
*/
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
{
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix) {
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
if ($aRawData != null) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
} else{
}
else {
return null;
}
}
public function MakeRealValue($proposedValue, $oHostObject)
{
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue))
{
public function MakeRealValue($proposedValue, $oHostObject) {
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue)) {
if (false === $oHostObject->IsNew()) {
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
$aRequestTemplateValues = $proposedValue->GetValues();
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
}
}
if (is_null($proposedValue->GetHostObject())) {
// the object might not be set : for example in \AttributeCustomFields::FromJSONToValue we don't have the object available :(
$proposedValue->SetHostObject($oHostObject);
}
return $proposedValue;
}
elseif (is_string($proposedValue))
{
if (is_string($proposedValue)) {
$aValues = json_decode($proposedValue, true);
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
}
elseif (is_array($proposedValue))
{
if (is_array($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $proposedValue);
}
elseif (is_null($proposedValue))
{
if (is_null($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
}
throw new Exception('Unexpected type for the value of a custom fields attribute: '.gettype($proposedValue));
}
@@ -13388,11 +13407,13 @@ class AttributeCustomFields extends AttributeDefinition
/**
* @inheritDoc
*
* @return ?\ormCustomFieldsValue
* @return ?\ormCustomFieldsValue with empty host object as we don't have it here (most consumers don't have an object in their context, for example in \RestUtils::GetObjectSetFromKey)
* The host object will be set in {@see MakeRealValue}
* All the necessary checks will be done in {@see CheckValue}
*/
public function FromJSONToValue($json)
{
return null;
return ormCustomFieldsValue::FromJSONToValue($json, $this);
}
public function Equals($val1, $val2)

View File

@@ -519,6 +519,15 @@ abstract class CMDBObject extends DBObject
utils::PopArchiveMode();
}
}
public function DBUpdate()
{
if (count($this->ListChanges()) === 0) {
$this->InitPreviousValuesForUpdatedAttributes();
return $this->GetKey();
}
return parent::DBUpdate(); // TODO: Change the autogenerated stub
}
}

View File

@@ -483,7 +483,7 @@ class Config
'synchro_obsolete_replica_locks_object' => [
'type' => 'bool',
'description' => 'Obsolete synchro replicas prevent object modification by any mean (eg. anonymization)',
'default' => 'true',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,

View File

@@ -23,10 +23,22 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class CustomFieldsHandler
{
abstract class CustomFieldsHandler {
/** @var string $sAttCode */
protected $sAttCode;
/** @var array{
* legacy: int,
* extradata_id: string,
* _template_name: string,
* template_id: string,
* template_data: string,
* user_data: array<string, mixed>,
* current_template_id: string,
* current_template_data: string,
* } $aValues same as {@see \ormCustomFieldsValue::$aCurrentValues}
*/
protected $aValues;
/** @var \Combodo\iTop\Form\Form $oForm */
protected $oForm;
/**
@@ -118,6 +130,21 @@ abstract class CustomFieldsHandler
return null;
}
/**
* @param \stdClass|null $json
* @param string $sAttCode
*
* @return \ormCustomFieldsValue|null
*
* @since 3.1.0 N°1150 Method creation
*/
public function FromJSONToValue(?stdClass $json, string $sAttCode): ?ormCustomFieldsValue
{
// Default impl doing nothing, to avoid errors on children not having this method
return null;
}
/**
* @param DBObject $oHostObject
*

View File

@@ -1983,7 +1983,7 @@ abstract class DBObject implements iDisplay
/** @var \AttributeExternalKey $oAtt */
$sTargetClass = $oAtt->GetTargetClass();
if (false === MetaModel::IsObjectInDB($sTargetClass, $toCheck)) {
return "Target object not found (".$sTargetClass.".::".$toCheck.")";
return "Target object not found ({$sTargetClass}::{$toCheck})";
}
}
if ($oAtt->IsHierarchicalKey())
@@ -3066,6 +3066,8 @@ abstract class DBObject implements iDisplay
$this->DoComputeValues();
$this->OnInsert();
$this->FireEventBeforeObjectCreate();
// If not automatically computed, then check that the key is given by the caller
if (!MetaModel::IsAutoIncrementKey($sRootClass)) {
if (empty($this->m_iKey)) {
@@ -3085,7 +3087,7 @@ abstract class DBObject implements iDisplay
}
$this->ComputeStopWatchesDeadline(true);
$iTransactionRetry = 1;
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled) {
@@ -3276,6 +3278,8 @@ abstract class DBObject implements iDisplay
$this->ComputeStopWatchesDeadline(false);
$this->OnUpdate();
$this->FireEventBeforeObjectUpdate();
// Freeze the changes at this point
$this->InitPreviousValuesForUpdatedAttributes();
$aChanges = $this->ListChanges();
@@ -3637,9 +3641,10 @@ abstract class DBObject implements iDisplay
* @uses m_aOrigValues
* @uses m_aPreviousValuesForUpdatedAttributes
* @since 2.7.0 N°2293
* @since 3.1.0 N°6299 - change visibility
* @throws \Exception
*/
private function InitPreviousValuesForUpdatedAttributes()
protected final function InitPreviousValuesForUpdatedAttributes()
{
$aChanges= $this->ListChanges();
if (empty($aChanges))
@@ -5961,7 +5966,22 @@ abstract class DBObject implements iDisplay
}
/**
*
* @api
*
* @param string $sWarning Warning message displayed when objet is redisplayed
*
* @return void
* @since 3.1.0
*/
final public function AddCheckWarning(string $sWarning)
{
$this->m_aCheckWarnings[] = $sWarning;
}
/**
* @api
*
* @param string $sIssue
* @param bool $bIsSecurityIssue
*
@@ -6037,6 +6057,14 @@ abstract class DBObject implements iDisplay
{
}
/**
* @return void
* @since 3.1.0
*/
protected function FireEventBeforeObjectCreate()
{
}
/**
* @return void
* @since 3.1.0
@@ -6053,6 +6081,16 @@ abstract class DBObject implements iDisplay
* @return void
* @since 3.1.0
*/
protected function FireEventBeforeObjectUpdate()
{
}
/**
* @param array $aChanges
*
* @return void
* @since 3.1.0
*/
protected function FireEventUpdateDone(array $aChanges): void
{
}
@@ -6062,6 +6100,8 @@ abstract class DBObject implements iDisplay
///
/**
* @param \DeletionPlan $oDeletionPlan
*
* @return void
* @since 3.1.0
*/

View File

@@ -400,7 +400,7 @@ class DBObjectSearch extends DBSearch
}
/**
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurences will be replaced by the last. Instead use:
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurrences will be replaced by the last. Instead use:
* * {@see \DBObjectSearch::AddConditionExpression()} in loops to add conditions one by one
* * {@see \DBObjectSearch::AddConditionForInOperatorUsingParam()} for IN/NOT IN queries with lots of params at once
*

View File

@@ -17,7 +17,10 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once APPROOT.'core/modulehandler.class.inc.php';
require_once APPROOT.'core/querymodifier.class.inc.php';
@@ -6343,36 +6346,37 @@ abstract class MetaModel
{
self::$m_sEnvironment = $sEnvironment;
if (!defined('MODULESROOT'))
{
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
try {
if (!defined('MODULESROOT')) {
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config)
{
self::LoadConfig($config, $bAllowCache);
}
else
{
self::LoadConfig(new Config($config), $bAllowCache);
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config) {
self::LoadConfig($config, $bAllowCache);
} else {
self::LoadConfig(new Config($config), $bAllowCache);
}
if ($bModelOnly) {
return;
}
}
if ($bModelOnly)
{
return;
CMDBSource::SelectDB(self::$m_sDBName);
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
finally {
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
}
CMDBSource::SelectDB(self::$m_sDBName);
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
/**

View File

@@ -35,10 +35,11 @@ class ormCustomFieldsValue
* _template_name: string,
* template_id: string,
* template_data: string,
* user_data: string,
* user_data: array<string, mixed>,
* current_template_id: string,
* current_template_data: string,
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data, user_data.
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data.
* The user_data key contains an array with field code as key and field value as value
* Warning, current_* are mandatory for data to be saved in a DBUpdate() call !
*/
protected $aCurrentValues;
@@ -48,13 +49,31 @@ class ormCustomFieldsValue
* @param string $sAttCode
* @param array $aCurrentValues
*/
public function __construct(DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
public function __construct(?DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
{
$this->oHostObject = $oHostObject;
$this->sAttCode = $sAttCode;
$this->aCurrentValues = $aCurrentValues;
}
/**
* @return \DBObject|null
*/
public function GetHostObject(): ?DBObject
{
return $this->oHostObject;
}
/**
* @param \DBObject|null $oHostObject
*
* @return void
*/
public function SetHostObject(?DBObject $oHostObject): void
{
$this->oHostObject = $oHostObject;
}
public function GetValues()
{
return $this->aCurrentValues;
@@ -62,6 +81,7 @@ class ormCustomFieldsValue
/**
* Wrapper used when the only thing you have is the value...
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm($sFormPrefix = null)
@@ -96,6 +116,19 @@ class ormCustomFieldsValue
return $this->GetHandler()->GetAsJSON($this->aCurrentValues);
}
/**
* @param string|null $json
* @param \AttributeDefinition $oAttDef
*
* @return \ormCustomFieldsValue
*
* @since 3.1.0 N°1150 Method creation
*/
public static function FromJSONToValue(?stdClass $json, AttributeCustomFields $oAttDef)
{
return $oAttDef->GetHandler()->FromJSONToValue($json, $oAttDef->GetCode());
}
/**
* @return \CustomFieldsHandler
* @throws \Exception
@@ -103,6 +136,7 @@ class ormCustomFieldsValue
*/
final protected function GetHandler()
{
/** @var \AttributeCustomFields $oAttDef */
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
return $oAttDef->GetHandler($this->GetValues());

View File

@@ -235,15 +235,26 @@ abstract class User extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values" => null, "sql" => "login", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>"EN US", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum('enabled,disabled'), "styled_values"=>['enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null)], "sql"=>"status", "default_value"=>"enabled", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql" => "language", "default_value" => "EN US", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum('enabled,disabled'),
"styled_values" => [
'enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),
'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null),
],
"sql" => "status",
"default_value" => "enabled",
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list",
array("linked_class" => "URP_UserProfile", "ext_key_to_me" => "userid", "ext_key_to_remote" => "profileid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class" => "URP_UserOrg", "ext_key_to_me" => "userid", "ext_key_to_remote" => "allowed_org_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeCaseLog("log", array("sql" => 'log', "is_null_allowed" => true, "default_value" => '', "allowed_values" => null, "depends_on" => array(), "always_load_in_tables" => false)));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list', 'log')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'email', 'language', 'status', 'org_id')); // Criteria of the std search form
@@ -640,23 +651,23 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// When set, this token allows for password reset
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values" => null, "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list', 'log')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form

View File

@@ -64,11 +64,22 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
word-break: break-word;
white-space: inherit;
}
& pre {
white-space: break-spaces;
}
}
}
/* We need the rule to keep picture inside the column */
&[data-attribute-type="AttributeImage"] {
> .ibo-field--value {
display: grid;
> span {
display: inherit;
}
}
}
}
/* Large field = Label on top, value below */

View File

@@ -61,6 +61,7 @@ class UserExternal extends User
'fieldset:User:profiles' => array('profile_list'),
),
'allowed_org_list',
'log',
)
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list

View File

@@ -309,6 +309,9 @@
<item id="allowed_org_list">
<rank>80</rank>
</item>
<item id="log">
<rank>90</rank>
</item>
</items>
</details>
<search>

View File

@@ -108,6 +108,7 @@ class UserLocal extends UserInternal
'fieldset:UserLocal:password:expiration' => array('expiration', 'password_renewed_date',),
),
'allowed_org_list',
'log',
)
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'org_id')); // Attributes to be displayed for a list

View File

@@ -187,6 +187,12 @@ class DBRestore extends DBBackup
@chmod($sConfigFile, 0770); // Allow overwriting the file
rename($sDataDir.'/config-itop.php', $sConfigFile);
@chmod($sConfigFile, 0440); // Read-only
$aExtraFiles = $this->ListExtraFiles($sDataDir);
foreach($aExtraFiles as $sSourceFilePath => $sDestinationFilePath) {
SetupUtils::builddir(dirname($sDestinationFilePath));
rename($sSourceFilePath, $sDestinationFilePath);
}
try {
SetupUtils::rrmdir($sDataDir);
@@ -211,4 +217,31 @@ class DBRestore extends DBBackup
$oRestoreMutex->Unlock();
}
}
/**
* List the 'extra files' found in the decompressed archive
* (i.e. files other than config-itop.php, delta.xml, itop-dump.sql or production-modules/*
* @param string $sDataDir
* @return string[]
*/
protected function ListExtraFiles(string $sDataDir)
{
$aExtraFiles = [];
$aStandardFiles = ['config-itop.php', 'itop-dump.sql', 'production-modules', 'delta.xml'];
$oDirectoryIterator = new RecursiveDirectoryIterator($sDataDir, FilesystemIterator::CURRENT_AS_FILEINFO|FilesystemIterator::SKIP_DOTS);
$oIterator = new RecursiveIteratorIterator($oDirectoryIterator);
foreach ($oIterator as $oFileInfo)
{
if (in_array($oFileInfo->getFilename(), $aStandardFiles)) {
continue;
}
if (strncmp($oFileInfo->getPathname(), $sDataDir.'/production-modules', strlen($sDataDir.'/production-modules')) == 0) {
continue;
}
$aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir));
}
return $aExtraFiles;
}
}

View File

@@ -14,6 +14,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server.svg</icon>
@@ -269,6 +272,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -518,6 +525,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server.svg</icon>
@@ -689,6 +700,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server.svg</icon>
@@ -978,6 +993,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="networkdevicetype_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-switch.svg</icon>
@@ -1254,6 +1273,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server.svg</icon>
@@ -1557,6 +1580,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -1719,6 +1745,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -1850,6 +1879,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -2049,6 +2081,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-automatic.svg</icon>
@@ -2180,6 +2215,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-database.svg</icon>
@@ -2311,6 +2349,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server-custom.svg</icon>
@@ -2442,6 +2483,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-desktop.svg</icon>
@@ -2550,6 +2594,9 @@
<attribute id="name"/>
<attribute id="system_name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-software-other.svg</icon>
@@ -2657,6 +2704,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="middleware_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-automatic.svg</icon>
@@ -2776,6 +2827,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-database-custom.svg</icon>
@@ -2893,6 +2947,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="webserver_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-web.svg</icon>
@@ -3020,6 +3078,10 @@
<attribute id="name"/>
<attribute id="version"/>
</attributes>
<complementary_attributes>
<attribute id="vendor"></attribute>
<attribute id="type"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-software.svg</icon>
@@ -3284,6 +3346,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="osversion_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-bandage.svg</icon>
@@ -3374,6 +3439,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="software_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-bandage.svg</icon>
@@ -3464,6 +3532,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-licence.svg</icon>
@@ -3650,6 +3721,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"/>
<attribute id="osversion_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-licence.svg</icon>
@@ -3793,6 +3868,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"/>
<attribute id="software_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-software-license.svg</icon>
@@ -4020,6 +4099,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="osfamily_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon/>
@@ -4208,6 +4290,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="brand_name"></attribute>
<attribute id="type"></attribute>
</complementary_attributes>
</naming>
<style>
<icon/>
@@ -5061,6 +5147,10 @@
<attribute id="ip"/>
<attribute id="ip_mask"/>
</attributes>
<complementary_attributes>
<attribute id="org_name"></attribute>
<attribute id="subnet_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-network.svg</icon>
@@ -5545,6 +5635,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="ipaddress"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-globe-cable.svg</icon>
@@ -6412,6 +6505,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="owner_name"></attribute>
<attribute id="type"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>

View File

@@ -142,6 +142,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:PhysicalDevice' => 'Physical Device',
'Class:PhysicalDevice+' => '',
'Class:PhysicalDevice/ComplementaryName' => '%1$s - %2$s',
'Class:PhysicalDevice/Attribute:serialnumber' => 'Serial number',
'Class:PhysicalDevice/Attribute:serialnumber+' => '',
'Class:PhysicalDevice/Attribute:location_id' => 'Location',
@@ -181,6 +182,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Rack' => 'Rack',
'Class:Rack+' => '',
'Class:Rack/ComplementaryName' => '%1$s - %2$s',
'Class:Rack/Attribute:nb_u' => 'Rack units',
'Class:Rack/Attribute:nb_u+' => '',
'Class:Rack/Attribute:device_list' => 'Devices',
@@ -247,6 +249,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:ConnectableCI' => 'Connectable CI',
'Class:ConnectableCI+' => 'Physical CI',
'Class:ConnectableCI/ComplementaryName' => '%1$s - %2$s',
'Class:ConnectableCI/Attribute:networkdevice_list' => 'Network devices',
'Class:ConnectableCI/Attribute:networkdevice_list+' => 'All network devices connected to this device',
'Class:ConnectableCI/Attribute:physicalinterface_list' => 'Network interfaces',
@@ -260,6 +263,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:DatacenterDevice' => 'Datacenter Device',
'Class:DatacenterDevice+' => '',
'Class:DatacenterDevice/ComplementaryName' => '%1$s - %2$s',
'Class:DatacenterDevice/Attribute:rack_id' => 'Rack',
'Class:DatacenterDevice/Attribute:rack_id+' => '',
'Class:DatacenterDevice/Attribute:rack_name' => 'Rack name',
@@ -298,6 +302,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:NetworkDevice' => 'Network Device',
'Class:NetworkDevice+' => '',
'Class:NetworkDevice/ComplementaryName' => '%1$s - %2$s',
'Class:NetworkDevice/Attribute:networkdevicetype_id' => 'Network type',
'Class:NetworkDevice/Attribute:networkdevicetype_id+' => '',
'Class:NetworkDevice/Attribute:networkdevicetype_name' => 'Network type name',
@@ -319,6 +324,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Server' => 'Server',
'Class:Server+' => '',
'Class:Server/ComplementaryName' => '%1$s - %2$s',
'Class:Server/Attribute:osfamily_id' => 'OS family',
'Class:Server/Attribute:osfamily_id+' => '',
'Class:Server/Attribute:osfamily_name' => 'OS family name',
@@ -346,6 +352,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:StorageSystem' => 'Storage System',
'Class:StorageSystem+' => '',
'Class:StorageSystem/ComplementaryName' => '%1$s - %2$s',
'Class:StorageSystem/Attribute:logicalvolume_list' => 'Logical volumes',
'Class:StorageSystem/Attribute:logicalvolume_list+' => 'All the logical volumes in this storage system',
));
@@ -357,6 +364,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:SANSwitch' => 'SAN Switch',
'Class:SANSwitch+' => '',
'Class:SANSwitch/ComplementaryName' => '%1$s - %2$s',
'Class:SANSwitch/Attribute:datacenterdevice_list' => 'Devices',
'Class:SANSwitch/Attribute:datacenterdevice_list+' => 'All the devices connected to this SAN switch',
));
@@ -368,6 +376,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:TapeLibrary' => 'Tape Library',
'Class:TapeLibrary+' => '',
'Class:TapeLibrary/ComplementaryName' => '%1$s - %2$s',
'Class:TapeLibrary/Attribute:tapes_list' => 'Tapes',
'Class:TapeLibrary/Attribute:tapes_list+' => 'All the tapes in the tape library',
));
@@ -379,6 +388,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:NAS' => 'NAS',
'Class:NAS+' => '',
'Class:NAS/ComplementaryName' => '%1$s - %2$s',
'Class:NAS/Attribute:nasfilesystem_list' => 'Filesystems',
'Class:NAS/Attribute:nasfilesystem_list+' => 'All the file systems in this NAS',
));
@@ -390,6 +400,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:PC' => 'PC',
'Class:PC+' => '',
'Class:PC/ComplementaryName' => '%1$s - %2$s',
'Class:PC/Attribute:osfamily_id' => 'OS family',
'Class:PC/Attribute:osfamily_id+' => '',
'Class:PC/Attribute:osfamily_name' => 'OS family name',
@@ -417,6 +428,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Printer' => 'Printer',
'Class:Printer+' => '',
'Class:Printer/ComplementaryName' => '%1$s - %2$s',
));
//
@@ -426,6 +438,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:PowerConnection' => 'Power Connection',
'Class:PowerConnection+' => '',
'Class:PowerConnection/ComplementaryName' => '%1$s - %2$s',
));
//
@@ -435,6 +448,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:PowerSource' => 'Power Source',
'Class:PowerSource+' => '',
'Class:PowerSource/ComplementaryName' => '%1$s - %2$s',
'Class:PowerSource/Attribute:pdus_list' => 'PDUs',
'Class:PowerSource/Attribute:pdus_list+' => 'All the PDUs using this power source',
));
@@ -446,6 +460,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:PDU' => 'PDU',
'Class:PDU+' => '',
'Class:PDU/ComplementaryName' => '%1$s - %2$s - %3$s',
'Class:PDU/Attribute:rack_id' => 'Rack',
'Class:PDU/Attribute:rack_id+' => '',
'Class:PDU/Attribute:rack_name' => 'Rack name',
@@ -463,6 +478,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Peripheral' => 'Peripheral',
'Class:Peripheral+' => '',
'Class:Peripheral/ComplementaryName' => '%1$s - %2$s',
));
//
@@ -472,6 +488,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Enclosure' => 'Enclosure',
'Class:Enclosure+' => '',
'Class:Enclosure/ComplementaryName' => '%1$s - %2$s - %3$s',
'Class:Enclosure/Attribute:rack_id' => 'Rack',
'Class:Enclosure/Attribute:rack_id+' => '',
'Class:Enclosure/Attribute:rack_name' => 'Rack name',
@@ -609,6 +626,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:MiddlewareInstance' => 'Middleware Instance',
'Class:MiddlewareInstance+' => '',
'Class:MiddlewareInstance/ComplementaryName' => '%1$s - %2$s',
'Class:MiddlewareInstance/Attribute:middleware_id' => 'Middleware',
'Class:MiddlewareInstance/Attribute:middleware_id+' => '',
'Class:MiddlewareInstance/Attribute:middleware_name' => 'Middleware name',
@@ -635,6 +653,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:WebApplication' => 'Web Application',
'Class:WebApplication+' => '',
'Class:WebApplication/ComplementaryName' => '%1$s - %2$s',
'Class:WebApplication/Attribute:webserver_id' => 'Web server',
'Class:WebApplication/Attribute:webserver_id+' => '',
'Class:WebApplication/Attribute:webserver_name' => 'Web server name',
@@ -715,6 +734,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:VirtualMachine' => 'Virtual Machine',
'Class:VirtualMachine+' => '',
'Class:VirtualMachine/ComplementaryName' => '%1$s - %2$s',
'Class:VirtualMachine/Attribute:virtualhost_id' => 'Virtual host',
'Class:VirtualMachine/Attribute:virtualhost_id+' => '',
'Class:VirtualMachine/Attribute:virtualhost_name' => 'Virtual host name',
@@ -877,6 +897,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Software' => 'Software',
'Class:Software+' => '',
'Class:Software/ComplementaryName' => '%1$s - %2$s',
'Class:Software/Attribute:name' => 'Name',
'Class:Software/Attribute:name+' => '',
'Class:Software/Attribute:vendor' => 'Vendor',
@@ -959,6 +980,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Licence' => 'License',
'Class:Licence+' => '',
'Class:Licence/Attribute:name' => 'Name',
'Class:Licence/Attribute:name+' => '',
'Class:Licence/Attribute:documents_list' => 'Documents',
@@ -994,6 +1016,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:OSLicence' => 'OS License',
'Class:OSLicence+' => '',
'Class:OSLicence/ComplementaryName' => '%1$s - %2$s',
'Class:OSLicence/Attribute:osversion_id' => 'OS version',
'Class:OSLicence/Attribute:osversion_id+' => '',
'Class:OSLicence/Attribute:osversion_name' => 'OS version name',
@@ -1011,6 +1034,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:SoftwareLicence' => 'Software License',
'Class:SoftwareLicence+' => '',
'Class:SoftwareLicence/ComplementaryName' => '%1$s - %2$s',
'Class:SoftwareLicence/Attribute:software_id' => 'Software',
'Class:SoftwareLicence/Attribute:software_id+' => '',
'Class:SoftwareLicence/Attribute:software_name' => 'Software name',
@@ -1079,6 +1103,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Model' => 'Model',
'Class:Model+' => '',
'Class:Model/ComplementaryName' => '%1$s - %2$s',
'Class:Model/Attribute:brand_id' => 'Brand',
'Class:Model/Attribute:brand_id+' => '',
'Class:Model/Attribute:brand_name' => 'Brand name',
@@ -1230,14 +1255,16 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Subnet' => 'Subnet',
'Class:Subnet+' => '',
'Class:Subnet/Name' => '%1$s/%2$s',
'Class:Subnet/ComplementaryName' => '%1$s - %2$s',
'Class:Subnet/Attribute:description' => 'Description',
'Class:Subnet/Attribute:description+' => '',
'Class:Subnet/Attribute:subnet_name' => 'Subnet name',
'Class:Subnet/Attribute:subnet_name+' => '',
'Class:Subnet/Attribute:org_id' => 'Owner organization',
'Class:Subnet/Attribute:org_id+' => '',
'Class:Subnet/Attribute:org_name' => 'Name',
'Class:Subnet/Attribute:org_name+' => 'Common name',
'Class:Subnet/Attribute:org_name' => 'Organization name',
'Class:Subnet/Attribute:org_name+' => '',
'Class:Subnet/Attribute:ip' => 'IP',
'Class:Subnet/Attribute:ip+' => '',
'Class:Subnet/Attribute:ip_mask' => 'IP Mask',
@@ -1260,7 +1287,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:VLAN/Attribute:org_id' => 'Organization',
'Class:VLAN/Attribute:org_id+' => '',
'Class:VLAN/Attribute:org_name' => 'Organization name',
'Class:VLAN/Attribute:org_name+' => 'Common name',
'Class:VLAN/Attribute:org_name+' => '',
'Class:VLAN/Attribute:subnets_list' => 'Subnets',
'Class:VLAN/Attribute:subnets_list+' => '',
'Class:VLAN/Attribute:physicalinterfaces_list' => 'Physical network interfaces',
@@ -1465,6 +1492,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Group' => 'Group',
'Class:Group+' => '',
'Class:Group/ComplementaryName' => '%1$s - %2$s',
'Class:Group/Attribute:name' => 'Name',
'Class:Group/Attribute:name+' => '',
'Class:Group/Attribute:status' => 'Status',

View File

@@ -37,7 +37,7 @@ Dict::Add('EN US', 'English', 'English', array(
'config-current-line' => 'Editing line: %1$s',
'config-saved-warning-db-password' => 'Successfully recorded, but the backup won\'t work due to unsupported characters in the database password.',
'config-error-transaction' => 'Error: invalid Transaction ID. The configuration was <b>NOT</b> modified.',
'config-error-file-changed' => 'Error: The configuration was changed on the server, the configuration has not been saved',
'config-error-file-changed' => 'Error: The Configuration file has changed since you opened it and cannot be saved. Refresh and apply your changes again.',
'config-not-allowed-in-demo' => 'Sorry, '.ITOP_APPLICATION_SHORT.' is in <b>demonstration mode</b>: the configuration file cannot be edited.',
'config-interactive-not-allowed' => ITOP_APPLICATION_SHORT." interactive edition of the configuration as been disabled. See <code>'config_editor' => 'disabled'</code> in the configuration file.",
));

View File

@@ -19,9 +19,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'config-reverted' => 'Vos modifications ont été écrasées par la version enregistrée.',
'config-parse-error' => 'Ligne %2$d: %1$s.<br/>Le fichier n\'a PAS été modifié.',
'config-current-line' => 'Ligne en édition : %1$s',
'config-saved-warning-db-password' => 'Configuration enregistrée. Les sauvegardes ne fonctionneront pas à cause du format du pot de passe de la base.',
'config-saved-warning-db-password' => 'Configuration enregistrée. Les sauvegardes ne fonctionneront pas à cause du format du mot de passe de la base.',
'config-error-transaction' => "Erreur : La transaction n'est plus valide. Les modifications n'ont <b>PAS</b> été enregistrées.",
'config-error-file-changed' => "Erreur : La configuration a été modifiée sur le serveur par une autre action. Les modifications n'ont <b>PAS</b> été enregistrées.",
'config-error-file-changed' => "Erreur : La configuration a été modifiée depuis que vous l\'avez ouvert. Vos modifications ne peuvent <b>PAS</b> être enregistrées. Rechargez la page et recommencez.",
'config-not-allowed-in-demo' => 'Désolé, '.ITOP_APPLICATION_SHORT.' est en <b>mode démonstration</b> : la configuration ne peut pas être modifiée.',
'config-interactive-not-allowed' => "La modification interactive de la configuration n'est pas autorisée. Voir le paramètre <code>'config_editor' => 'disabled'</code> dans le fichier de configuration.",
));

View File

@@ -14,6 +14,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-rack.svg</icon>
@@ -196,6 +200,11 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
<attribute id="rack_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-enclosure-for-servers.svg</icon>
@@ -383,6 +392,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-electrical.svg</icon>
@@ -545,6 +558,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-electricity.svg</icon>
@@ -702,6 +719,11 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
<attribute id="rack_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-plug-socket.svg</icon>

View File

@@ -14,6 +14,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="phonenumber"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-phone.svg</icon>
@@ -168,6 +172,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="phonenumber"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-telephone.svg</icon>
@@ -315,6 +323,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="phonenumber"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-mobile.svg</icon>
@@ -485,6 +497,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="phonenumber"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-office-phone.svg</icon>
@@ -632,6 +648,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-smartphone-tablet.svg</icon>
@@ -776,6 +795,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-laptop.svg</icon>
@@ -1026,6 +1049,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-print.svg</icon>
@@ -1206,6 +1233,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-electronics.svg</icon>

View File

@@ -29,6 +29,7 @@ use Dict;
use Exception;
use IssueLog;
use MetaModel;
use utils;
/**
* Contains static methods to help loading / registering classes of the application.

View File

@@ -803,9 +803,21 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="customer_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>org_id</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="contact_id" xsi:type="AttributeExternalKey">
<sql>contact_id</sql>
<target_class>Contact</target_class>
<filter><![CDATA[SELECT Contact WHERE org_id IN (:this->customer_id, :this->provider_id)]]></filter>
<dependencies>
<attribute id="contract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
@@ -896,9 +908,21 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="customer_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>org_id</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="document_id" xsi:type="AttributeExternalKey">
<sql>document_id</sql>
<target_class>Document</target_class>
<filter><![CDATA[SELECT Document WHERE org_id IN (:this->customer_id, :this->provider_id)]]></filter>
<dependencies>
<attribute id="contract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
@@ -1043,6 +1067,10 @@ public function PrefillSearchForm(&$aContextParam)
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="status"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<image_attribute>icon</image_attribute>
@@ -1234,6 +1262,16 @@ public function PrefillSearchForm(&$aContextParam)
</item>
</items>
</list>
<summary>
<items>
<item id="org_id">
<rank>10</rank>
</item>
<item id="description">
<rank>20</rank>
</item>
</items>
</summary>
</presentation>
</class>
<class id="lnkDocumentToService" _delta="define">
@@ -1435,6 +1473,10 @@ public function PrefillSearchForm(&$aContextParam)
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="service_provider"></attribute>
<attribute id="status"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -1587,6 +1629,19 @@ public function PrefillSearchForm(&$aContextParam)
</item>
</items>
</list>
<summary>
<items>
<item id="service_provider">
<rank>10</rank>
</item>
<item id="request_type">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
</items>
</summary>
</presentation>
</class>
<class id="SLA" _delta="define">
@@ -2080,9 +2135,17 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>customercontract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>customercontract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="service_id" xsi:type="AttributeExternalKey">
<sql>service_id</sql>
<target_class>Service</target_class>
<filter><![CDATA[SELECT Service WHERE org_id = :this->provider_id]]></filter>
<dependencies>
<attribute id="customercontract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>

View File

@@ -255,6 +255,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Service' => 'Service',
'Class:Service+' => '',
'Class:Service/ComplementaryName' => '%1$s - %2$s',
'Class:Service/Attribute:name' => 'Name',
'Class:Service/Attribute:name+' => '',
'Class:Service/Attribute:org_id' => 'Provider',
@@ -330,6 +331,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:ServiceSubcategory' => 'Service Subcategory',
'Class:ServiceSubcategory+' => '',
'Class:ServiceSubcategory/ComplementaryName' => '%1$s - %2$s',
'Class:ServiceSubcategory/Attribute:name' => 'Name',
'Class:ServiceSubcategory/Attribute:name+' => '',
'Class:ServiceSubcategory/Attribute:description' => 'Description',

View File

@@ -773,9 +773,21 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="customer_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>org_id</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="contact_id" xsi:type="AttributeExternalKey">
<sql>contact_id</sql>
<target_class>Contact</target_class>
<filter><![CDATA[SELECT Contact WHERE org_id IN (:this->customer_id, :this->provider_id)]]></filter>
<dependencies>
<attribute id="contract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
@@ -794,6 +806,12 @@ public function PrefillSearchForm(&$aContextParam)
<item id="contract_id">
<rank>20</rank>
</item>
<item id="customer_id">
<rank>30</rank>
</item>
<item id="provider_id">
<rank>40</rank>
</item>
</items>
</details>
<search>
@@ -866,9 +884,21 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="customer_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>org_id</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>contract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="document_id" xsi:type="AttributeExternalKey">
<sql>document_id</sql>
<target_class>Document</target_class>
<filter><![CDATA[SELECT Document WHERE org_id IN (:this->customer_id, :this->provider_id)]]></filter>
<dependencies>
<attribute id="contract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
@@ -1013,6 +1043,10 @@ public function PrefillSearchForm(&$aContextParam)
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="status"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<image_attribute>icon</image_attribute>
@@ -1228,6 +1262,16 @@ public function PrefillSearchForm(&$aContextParam)
</item>
</items>
</list>
<summary>
<items>
<item id="org_id">
<rank>10</rank>
</item>
<item id="description">
<rank>20</rank>
</item>
</items>
</summary>
</presentation>
</class>
<class id="lnkDocumentToService" _delta="define">
@@ -1429,6 +1473,10 @@ public function PrefillSearchForm(&$aContextParam)
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="service_provider"></attribute>
<attribute id="status"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -1581,6 +1629,19 @@ public function PrefillSearchForm(&$aContextParam)
</item>
</items>
</list>
<summary>
<items>
<item id="service_provider">
<rank>10</rank>
</item>
<item id="request_type">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
</items>
</summary>
</presentation>
</class>
<class id="SLA" _delta="define">
@@ -2083,9 +2144,17 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>customercontract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>customercontract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="service_id" xsi:type="AttributeExternalKey">
<sql>service_id</sql>
<target_class>Service</target_class>
<filter><![CDATA[SELECT Service WHERE org_id = :this->provider_id]]></filter>
<dependencies>
<attribute id="customercontract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
@@ -2182,16 +2251,6 @@ public function PrefillSearchForm(&$aContextParam)
</uniqueness_rules>
</properties>
<fields>
<field id="service_id" xsi:type="AttributeExternalKey">
<sql>service_id</sql>
<target_class>Service</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
<field id="service_name" xsi:type="AttributeExternalField">
<extkey_attcode>service_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="providercontract_id" xsi:type="AttributeExternalKey">
<sql>providercontract_id</sql>
<target_class>ProviderContract</target_class>
@@ -2202,6 +2261,24 @@ public function PrefillSearchForm(&$aContextParam)
<extkey_attcode>providercontract_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="provider_id" xsi:type="AttributeExternalField">
<extkey_attcode>providercontract_id</extkey_attcode>
<target_attcode>provider_id</target_attcode>
</field>
<field id="service_id" xsi:type="AttributeExternalKey">
<sql>service_id</sql>
<target_class>Service</target_class>
<filter><![CDATA[SELECT Service WHERE org_id = :this->provider_id]]></filter>
<dependencies>
<attribute id="providercontract_id"/>
</dependencies>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
</field>
<field id="service_name" xsi:type="AttributeExternalField">
<extkey_attcode>service_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
</fields>
<methods/>
<presentation>

View File

@@ -224,6 +224,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Service' => 'Service',
'Class:Service+' => '',
'Class:Service/ComplementaryName' => '%1$s - %2$s',
'Class:Service/Attribute:name' => 'Name',
'Class:Service/Attribute:name+' => '',
'Class:Service/Attribute:org_id' => 'Provider',
@@ -303,6 +304,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:ServiceSubcategory' => 'Service Subcategory',
'Class:ServiceSubcategory+' => '',
'Class:ServiceSubcategory/ComplementaryName' => '%1$s - %2$s',
'Class:ServiceSubcategory/Attribute:name' => 'Name',
'Class:ServiceSubcategory/Attribute:name+' => '',
'Class:ServiceSubcategory/Attribute:description' => 'Description',

View File

@@ -14,6 +14,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-server-storage.svg</icon>
@@ -261,6 +265,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-switch-san.svg</icon>
@@ -504,6 +512,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-tape-library.svg</icon>
@@ -748,6 +760,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="location_name"/>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-nas.svg</icon>

View File

@@ -407,6 +407,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="email"></attribute>
<attribute id="org_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -642,6 +646,13 @@
</rule>
</uniqueness_rules>
</properties>
<event_listeners>
<event_listener id="CheckUsersUpdate">
<event>EVENT_DB_CHECK_TO_WRITE</event>
<callback>CheckUsersOnUpdate</callback>
<rank>1</rank>
</event_listener>
</event_listeners>
<fields>
<field id="picture" xsi:type="AttributeImage">
<display_max_width>96</display_max_width>
@@ -712,6 +723,40 @@
</field>
</fields>
<methods>
<method id="CheckUsersOnUpdate">
<static>false</static>
<access>public</access>
<type>EventListener</type>
<code><![CDATA[ public function CheckUsersOnUpdate(Combodo\iTop\Service\Events\EventData $oEventData)
{
// This method can block the Person modification, by adding a Check Issue
$aChanges = $this->ListChanges();
// Current User may not be allowed to see User class, so we can't use $this->Get('user_list')
$oSearch = new DBObjectSearch('User');
$oSearch->AddCondition('contactid', $this->GetKey(), '=');
$oSearch->AllowAllData();
$oUserSet = new DBObjectSet($oSearch);
// The organization's person was changed and it has associated Users
if (array_key_exists('org_id', $aChanges) && ($oUserSet->Count() > 0)) {
while($oUser = $oUserSet->Fetch())
{
$oAddon = UserRights::GetModuleInstance();
$aOrgs = $oAddon->GetUserOrgs($oUser,'Organization');
$oSet = $oUser->Get('profile_list');
$aProfiles = $oSet->GetColumnAsArray('profile');
// User is not allowed on the new Organization and has 'Portal user' Profile and is enabled
if ((count($aOrgs) > 0) && !in_array($this->Get('org_id'), $aOrgs) && in_array('Portal user',$aProfiles) && ($oUser->Get('status') === 'enabled'))
{ // Let's block the Person modification,
// replace by $this->AddCheckWarning(...) if you don't want to block the modification
$this->AddCheckIssue(Dict::Format('Class:Person/Error:ChangingOrgDenied', $this->Get('org_id_friendlyname')));
}
}
}
}
]]></code>
</method>
<method id="CheckToDelete">
<static>false</static>
<access>public</access>
@@ -1261,6 +1306,11 @@
<db_final_class_field>finalclass</db_final_class_field>
<naming>
<attributes/>
<complementary_attributes>
<attribute id="org_name"></attribute>
<attribute id="status"></attribute>
<attribute id="documenttype_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>

View File

@@ -119,6 +119,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Contact' => 'Contact',
'Class:Contact+' => '',
'Class:Contact/ComplementaryName' => '%1$s - %2$s',
'Class:Contact/Attribute:name' => 'Name',
'Class:Contact/Attribute:name+' => '',
'Class:Contact/Attribute:status' => 'Status',
@@ -187,6 +188,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Person/UniquenessRule:employee_number' => 'there is already a person in \'$this->org_name$\' organization with the same employee number',
'Class:Person/UniquenessRule:name+' => 'The employee name should be unique inside its organization',
'Class:Person/UniquenessRule:name' => 'There is already a person in \'$this->org_name$\' organization with the same name',
'Class:Person/Error:ChangingOrgDenied' => 'Impossible to move this person under organization \'%1$s\' as it would break his access to the User Portal, his associated user not being allowed on this organization',
));
//
@@ -210,6 +212,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Class:Document' => 'Document',
'Class:Document+' => '',
'Class:Document/ComplementaryName' => '%1$s - %2$s - %3$s',
'Class:Document/Attribute:name' => 'Name',
'Class:Document/Attribute:name+' => '',
'Class:Document/Attribute:org_id' => 'Organization',

View File

@@ -196,6 +196,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
\'$this->org_name$\'',
'Class:Person/UniquenessRule:name+' => 'Le nom de l\'employé devrait être unique dans l\'organisation',
'Class:Person/UniquenessRule:name' => 'Il y a déjà une personne avec ce nom dans l\'organisation \'$this->org_name$\'',
'Class:Person/Error:ChangingOrgDenied' => 'Impossible de déplacer cette personne sous l\'organisation \'%1$s\', cela casserait son accès au portail utilisateur, car il n\'a pas le droit de voir cette organisation',
));
//

View File

@@ -192,7 +192,7 @@ if (!class_exists('StructureInstaller'))
// Build the corresponding action and link it to the triggers
if (count($aCreatedTriggerIds) > 0) {
$oAction = MetaModel::NewObject('ActionEmail');
$oAction->Set('name', 'Notification to persons mentioned in case logs');
$oAction->Set('name', 'Notification to persons mentioned in logs');
$oAction->Set('status', 'enabled');
$oAction->Set('from', '$current_contact->email$');
$oAction->Set('to', 'SELECT Person WHERE id = :mentioned->id');

View File

@@ -14,6 +14,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<fields_semantic>
<state_attribute>status</state_attribute>
@@ -153,6 +156,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon/>
@@ -275,6 +281,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-hypervisor.svg</icon>
@@ -418,6 +427,9 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-farm.svg</icon>
@@ -541,6 +553,10 @@
<attributes>
<attribute id="name"/>
</attributes>
<complementary_attributes>
<attribute id="organization_name"></attribute>
<attribute id="virtualhost_name"></attribute>
</complementary_attributes>
</naming>
<style>
<icon>../../images/icons/icons8-virtual-machine.svg</icon>

View File

@@ -46,12 +46,12 @@ Dict::Add('EN US', 'English', 'English', array(
// Modify object
'UI:Links:ModifyObject:Button' => 'Modify',
'UI:Links:ModifyObject:Button+' => 'Modify this object',
'UI:Links:ModifyObject:Modal:Title' => '%5$s',
'UI:Links:ModifyObject:Modal:Title' => 'Modify %5$s',
// Remove
'UI:Links:Remove:Button' => 'Remove',
'UI:Links:Remove:Button+' => 'Remove this %4$s',
'UI:Links:Remove:Modal:Title' => 'Remove a %4$s',
'UI:Links:Remove:Modal:Title' => 'Remove a %4$s from its %1$s',
'UI:Links:Remove:Modal:Message' => 'Do you really want to remove %5$s from %2$s?',
// Delete

View File

@@ -47,7 +47,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
// Modify object
'UI:Links:ModifyObject:Button' => 'Modifier',
'UI:Links:ModifyObject:Button+' => 'Modifier cet objet',
'UI:Links:ModifyObject:Modal:Title' => '%5$s',
'UI:Links:ModifyObject:Modal:Title' => 'Modifier %5$s',
// Remove
'UI:Links:Remove:Button' => 'Retirer',

View File

@@ -71,7 +71,7 @@ $(function()
let sMarkup = `
<input type="hidden" id="do_remove_${this.options.input_name}" name="${this.options.input_name}[remove]" value="0"/>
<div id="preview_${this.options.input_name}" class="${sCssClasses}" data-role="ibo-input-image--image-view" style="max-width: ${this.options.max_width_px}px; max-height: ${this.options.max_height_px}px; aspect-ratio: ${this.options.max_width_px} / ${this.options.max_height_px};">
<img src="${sImageUrl}" data-original-src="${sImageUrl}" data-default-src="${this.options.default_image_url}" style="max-width: ${this.options.max_width_px}px; max-height: ${this.options.max_height_px}px">
<img src="${sImageUrl}" data-original-src="${sImageUrl}" data-default-src="${this.options.default_image_url}" style="max-width: min(${this.options.max_width_px}px,100%); max-height: min(${this.options.max_height_px}px,100%)">
<input id="file_${this.options.input_name}" name="${this.options.input_name}[fcontents]" type="file" />
</div>
<div id="buttons_${this.options.input_name}" class="ibo-input-image--edit-buttons" data-role="ibo-input-image--edit-buttons">

View File

@@ -222,11 +222,12 @@ class DBBackup
*
* @param string $sSourceConfigFile
* @param string $sTmpFolder
* @param bool $bSkipSQLDumpForTesting
*
* @return array list of files to archive
* @throws \Exception
*/
protected function PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder)
protected function PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder, $bSkipSQLDumpForTesting = false)
{
$aRet = array();
if (is_dir($sTmpFolder))
@@ -243,7 +244,7 @@ class DBBackup
{
$sFile = $sTmpFolder.'/config-itop.php';
$this->LogInfo("backup: adding resource '$sSourceConfigFile'");
copy($sSourceConfigFile, $sFile);
@copy($sSourceConfigFile, $sFile); // During unattended install config file may be absent
$aRet[] = $sFile;
}
@@ -264,9 +265,43 @@ class DBBackup
SetupUtils::copydir($sExtraDir, $sFile);
$aRet[] = $sFile;
}
$sDataFile = $sTmpFolder.'/itop-dump.sql';
$this->DoBackup($sDataFile);
$aRet[] = $sDataFile;
if (MetaModel::GetConfig() !== null) // During unattended install config file may be absent
{
$aExtraFiles = MetaModel::GetModuleSetting('itop-backup', 'extra_files', []);
foreach($aExtraFiles as $sExtraFileOrDir)
{
if(!file_exists(APPROOT.'/'.$sExtraFileOrDir)) {
continue; // Ignore non-existing files
}
$sExtraFullPath = utils::RealPath(APPROOT.'/'.$sExtraFileOrDir, APPROOT);
if ($sExtraFullPath === false)
{
throw new Exception("Backup: Aborting, resource '$sExtraFileOrDir'. Considered as UNSAFE because not inside the iTop directory.");
}
if (is_dir($sExtraFullPath))
{
$sFile = $sTmpFolder.'/'.$sExtraFileOrDir;
$this->LogInfo("backup: adding directory '$sExtraFileOrDir'");
SetupUtils::copydir($sExtraFullPath, $sFile);
$aRet[] = $sFile;
}
elseif (file_exists($sExtraFullPath))
{
$sFile = $sTmpFolder.'/'.$sExtraFileOrDir;
$this->LogInfo("backup: adding file '$sExtraFileOrDir'");
@mkdir(dirname($sFile), 0755, true);
copy($sExtraFullPath, $sFile);
$aRet[] = $sFile;
}
}
}
if (!$bSkipSQLDumpForTesting)
{
$sDataFile = $sTmpFolder.'/itop-dump.sql';
$this->DoBackup($sDataFile);
$aRet[] = $sDataFile;
}
return $aRet;
}

View File

@@ -176,6 +176,10 @@ class MFModule
$this->sAutoSelect = 'false';
$this->aFilesToInclude = array('addons' => array(), 'business' => array(), 'webservices' => array(),);
if (is_null($sRootDir)) {
return;
}
// Scan the module's root directory to find the datamodel(*).xml files
if ($hDir = opendir($sRootDir))
{

View File

@@ -10,7 +10,6 @@ use cmdbAbstract;
use cmdbAbstractObject;
use Dict;
use Metamodel;
use Serializable;
/**
* Class DataTableSettings
@@ -167,7 +166,8 @@ class DataTableSettings
$oSettings = new DataTableSettings($aClassAliases);
// Retrieve the class specific settings for each class/alias based on the 'list' ZList
//TODO let the caller pass some other default settings (another Zlist, extre fields...)
$aColumns = array();
$aColumns = [];
$aSortOrder = [];
foreach ($aClassAliases as $sAlias => $sClass) {
if ($aDefaultLists == null) {
$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClass, 'list'));

View File

@@ -17,6 +17,7 @@ use Dict;
use DictExceptionMissingString;
use DisplayBlock;
use Exception;
use MetaModel;
use MySQLException;
use UserRights;
use Utils;
@@ -135,10 +136,10 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock
public function GetDictionaryEntry(string $sKey, DBObject $oDBObject = null)
{
return $this->oAttDef->SearchSpecificLabel($sKey, '', true,
Dict::S("Class:{$this->sObjectClass}"),
MetaModel::GetName($this->sObjectClass),
$this->oDbObject->Get('friendlyname'),
$this->oAttDef->GetLabel(),
Dict::S("Class:{$this->sTargetClass}"),
MetaModel::GetName($this->sTargetClass),
$oDBObject !== null ? $oDBObject->Get('friendlyname') : '{item}');
}

View File

@@ -307,16 +307,16 @@ class BlockDirectLinkSetEditTable extends UIContentBlock
$aRowActions = array();
$sDeleteButtonTooltip = $this->oAttributeLinkedSet->SearchSpecificLabel('UI:Links:Delete:Button+', '', true,
Dict::S("Class:{$this->oAttributeLinkedSet->GetHostClass()}"),
MetaModel::GetName($this->oAttributeLinkedSet->GetHostClass()),
$oHostObject->Get('friendlyname'),
$this->oAttributeLinkedSet->GetLabel(),
Dict::S("Class:{$this->oUILinksDirectWidget->GetLinkedClass()}"));
MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass()));
$sRemoveButtonTooltip = $this->oAttributeLinkedSet->SearchSpecificLabel('UI:Links:Remove:Button+', '', true,
Dict::S("Class:{$this->oAttributeLinkedSet->GetHostClass()}"),
MetaModel::GetName($this->oAttributeLinkedSet->GetHostClass()),
$oHostObject->Get('friendlyname'),
$this->oAttributeLinkedSet->GetLabel(),
Dict::S("Class:{$this->oUILinksDirectWidget->GetLinkedClass()}"));
MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass()));
// until a full link set refactoring (continue using edit_mode property)
switch ($this->oAttributeLinkedSet->GetEditMode()) {

View File

@@ -492,10 +492,10 @@ JS
$aRowActions = array();
$sRemoveButtonTooltip = $this->oAttributeLinkedSetIndirect->SearchSpecificLabel('UI:Links:Remove:Button+', '', true,
Dict::S("Class:{$this->oAttributeLinkedSetIndirect->GetHostClass()}"),
MetaModel::GetName($this->oAttributeLinkedSetIndirect->GetHostClass()),
$oHostObject->Get('friendlyname'),
$this->oAttributeLinkedSetIndirect->GetLabel(),
Dict::S("Class:{$this->oUILinksWidget->GetRemoteClass()}"));
MetaModel::GetName($this->oUILinksWidget->GetRemoteClass()));
if ($this->bIsAllowDelete) {
$aRowActions[] = array(

View File

@@ -208,10 +208,10 @@ JS;
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$oHostObj = MetaModel::GetObject($sClass, $sId);
$sFormTitle = $oLinksetDef->SearchSpecificLabel('UI:Links:Add:Modal:Title', '', true,
Dict::S("Class:{$sClass}"),
MetaModel::GetName($sClass),
$oHostObj->Get('friendlyname'),
$oLinksetDef->GetLabel(),
LinkSetModel::GetTargetClass($oLinksetDef));
MetaModel::GetName(LinkSetModel::GetTargetClass($oLinksetDef)));
$aExtraParams = [
'noRelations' => true,

View File

@@ -20,6 +20,7 @@
namespace Combodo\iTop\Form\Field;
use Closure;
use utils;
/**
* Description of MultipleChoicesField
@@ -213,17 +214,27 @@ abstract class MultipleChoicesField extends Field
/**
* @inheritDoc
*/
public function Validate()
{
public function Validate() {
$this->SetValid(true);
$this->EmptyErrorMessages();
foreach ($this->GetValidators() as $oValidator)
{
foreach ($this->currentValue as $value)
{
if (!preg_match($oValidator->GetRegExp(true), $value))
{
if ($this->GetReadOnly() === false) {
if (count($this->currentValue) > 0) {
foreach ($this->currentValue as $sCode => $value) {
if (utils::IsNullOrEmptyString($value)) {
continue;
}
if (false === array_key_exists($value, $this->aChoices)) {
$this->SetValid(false);
$this->AddErrorMessage("Value ({$value}) is not part of the field possible values list");
}
}
}
}
foreach ($this->GetValidators() as $oValidator) {
foreach ($this->currentValue as $value) {
if (!preg_match($oValidator->GetRegExp(true), $value)) {
$this->SetValid(false);
$this->AddErrorMessage($oValidator->GetErrorMessage());
}

View File

@@ -28,6 +28,7 @@ use DBSearch;
use FieldExpression;
use MetaModel;
use ScalarExpression;
use utils;
/**
* Description of SelectObjectField
@@ -237,19 +238,42 @@ class SelectObjectField extends Field
/**
* @return int
*/
public function GetControlType()
{
public function GetControlType() {
return $this->iControlType;
}
/**
* @return string|null
*/
public function GetSearchEndpoint()
{
public function GetSearchEndpoint() {
return $this->sSearchEndpoint;
}
public function Validate() {
if ($this->GetReadOnly() === false) {
$sCurrentValueForExtKey = $this->currentValue;
if (utils::IsNotNullOrEmptyString($sCurrentValueForExtKey) && ($sCurrentValueForExtKey !== 0)) {
$oSearchForExistingCurrentValue = $this->oSearch->DeepClone();
$oSearchForExistingCurrentValue->AddCondition('id', $sCurrentValueForExtKey, '=');
$oCheckIdAgainstCurrentValueExpression = new BinaryExpression(
new FieldExpression('id', $oSearchForExistingCurrentValue->GetClassAlias()), '=', new ScalarExpression($sCurrentValueForExtKey)
);
$oSearchForExistingCurrentValue->AddConditionExpression($oCheckIdAgainstCurrentValueExpression);
$oSetForExistingCurrentValue = new DBObjectSet($oSearchForExistingCurrentValue);
$iObjectsCount = $oSetForExistingCurrentValue->CountWithLimit(1);
if ($iObjectsCount === 0) {
$this->SetValid(false);
$this->AddErrorMessage("Value $sCurrentValueForExtKey does not match the corresponding filter set");
return $this->GetValid();
}
}
}
return parent::Validate();
}
/**
* Resets current value if not among allowed ones.
* By default, reset is done ONLY when the field is not read-only.

View File

@@ -340,7 +340,7 @@ final class EventService
foreach (self::$aEventDescriptions as $sEvent => $aEventInfo) {
if (is_array($aEventInfo['description']->GetEventSources())) {
foreach ($aEventInfo['description']->GetEventSources() as $sSource) {
if ($sClass == $sSource || $oClass->isSubclassOf($sSource)) {
if (class_exists($sSource) && ($sClass == $sSource || $oClass->isSubclassOf($sSource))) {
$aRes[$sEvent] = $aEventInfo;
}
}

View File

@@ -48,6 +48,7 @@ use Server;
use TagSetFieldData;
use Ticket;
use URP_UserProfile;
use UserRequest;
use VirtualHost;
use VirtualMachine;
use XMLDataLoader;
@@ -286,18 +287,26 @@ class ItopDataTestCase extends ItopTestCase
{
$oFilter = DBSearch::FromOQL($sOQL);
$aRes = $oFilter->ToDataArray(array('id'));
foreach ($aRes as $aRow)
{
foreach ($aRes as $aRow) {
$this->debug($aRow);
$iKey = $aRow['id'];
if (!empty($iKey))
{
if (!empty($iKey)) {
$oObject = MetaModel::GetObject($sClass, $iKey);
$oObject->DBDelete();
}
}
}
protected function GetUserRequestParams($iNum) {
return [
'ref' => 'Ticket_'.$iNum,
'title' => 'BUG 1161_'.$iNum,
//'request_type' => 'incident',
'description' => 'Add aggregate functions',
'org_id' => $this->getTestOrgId(),
];
}
/**
* Create a UserRequest in database
*
@@ -315,18 +324,12 @@ class ItopDataTestCase extends ItopTestCase
* @uses createObject
*/
protected function CreateUserRequest($iNum, $aUserRequestCustomParams = []) {
$aUserRequestDefaultParams = [
'ref' => 'Ticket_'.$iNum,
'title' => 'BUG 1161_'.$iNum,
//'request_type' => 'incident',
'description' => 'Add aggregate functions',
'org_id' => $this->getTestOrgId(),
];
$aUserRequestDefaultParams = $this->GetUserRequestParams($iNum);
$aUserRequestParams = array_merge($aUserRequestDefaultParams, $aUserRequestCustomParams);
/** @var \UserRequest $oTicket */
$oTicket = $this->createObject('UserRequest', $aUserRequestParams);
$oTicket = $this->createObject(UserRequest::class, $aUserRequestParams);
$this->debug("Created {$oTicket->Get('title')} ({$oTicket->Get('ref')})");
return $oTicket;

View File

@@ -104,8 +104,7 @@ class CRUDEventTest extends ItopDataTestCase
$oOrg->DBUpdate();
$this->assertEquals(1, self::$aEventCalls[EVENT_DB_COMPUTE_VALUES]);
$this->assertEquals(1, self::$iEventCalls);
$this->assertEquals(0, self::$iEventCalls);
}
/**

View File

@@ -118,6 +118,12 @@ class DBObjectTest extends ItopDataTestCase
$this->assertCount(0, $oOrg->ListChanges());
$this->assertCount(0, $oOrg->ListPreviousValuesForUpdatedAttributes());
$oOrg->Set('name', $oOrg->Get('name'));
$this->assertCount(0, $oOrg->ListChanges());
$oOrg->DBUpdate();
$this->assertCount(0, $oOrg->ListChanges());
$this->assertCount(0, $oOrg->ListPreviousValuesForUpdatedAttributes());
$oOrg->DBDelete();
$oOrg = MetaModel::NewObject('Organization');
@@ -236,11 +242,15 @@ class DBObjectTest extends ItopDataTestCase
});
$this->assertDBQueryCount(0, function() use (&$oBordeaux, &$oObject){
/** @var DBObject $oObject */
$oObject->Set('location_id', $oBordeaux);
static::assertEquals('IT Department', $oObject->Get('org_id_friendlyname'));
static::assertEquals('IT Department', $oObject->Get('org_name'));
static::assertEquals('Bordeaux', $oObject->Get('location_id_friendlyname'));
});
static::assertEquals('Bordeaux', $oObject->Get('location_id_friendlyname'));
// static::assertEquals('toto', $oObject->EvaluateExpression(\Expression::FromOQL("CONCAT(org_name, '-', location_id_friendlyname)")));
}
/**

View File

@@ -0,0 +1,119 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use DBBackup;
use DBRestore;
use MetaModel;
use SetupUtils;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class DBBackupDataTest extends ItopDataTestCase
{
/**
* @dataProvider prepareFilesToBackupProvider
*/
public function testPrepareFilesToBackup(array $aExtraFiles, bool $bUnsafeFileException)
{
$sTmpDir = sys_get_temp_dir().'/testPrepareFilesToBackup-'.time();
$oBackup = new DBBackup(MetaModel::GetConfig());
MetaModel::GetConfig()->SetModuleSetting('itop-backup', 'extra_files', array_keys($aExtraFiles));
foreach($aExtraFiles as $sExtraFile => $bExists)
{
if ($bExists)
{
@mkdir(dirname(APPROOT.'/'.$sExtraFile), 0755, true);
file_put_contents(APPROOT.'/'.$sExtraFile, 'Hello World!');
}
}
if ($bUnsafeFileException)
{
$this->expectExceptionMessage("Backup: Aborting, resource '$sExtraFile'. Considered as UNSAFE because not inside the iTop directory.");
}
$aFiles = $this->InvokeNonPublicMethod('DBBackup', 'PrepareFilesToBackup', $oBackup, [APPROOT.'/conf/production/config-itop.php', $sTmpDir, true]);
SetupUtils::rrmdir($sTmpDir);
$aExpectedFiles = [
$sTmpDir.'/config-itop.php',
];
foreach($aExtraFiles as $sRelFile => $bExists)
{
if ($bExists)
{
$aExpectedFiles[] = $sTmpDir.'/'.$sRelFile;
}
}
sort($aFiles);
sort($aExpectedFiles);
$this->assertEquals($aFiles, $aExpectedFiles);
// Cleanup
foreach($aExtraFiles as $sExtraFile => $bExists)
{
if ($bExists)
{
unlink(APPROOT.'/'.$sExtraFile);
}
}
}
function prepareFilesToBackupProvider()
{
return [
'no_extra_file' => ['aExtraFiles' => [], false],
'one_extra_file' => ['aExtraFiles' => ['foo.txt' => true], false],
'three_extra_file_and_dir' => ['aExtraFiles' => ['foo.txt' => true, 'gabu/zomeu.xml' => true, 'meuh.html' => true], false],
'two_extra_file_but_only_one_exists' => ['aExtraFiles' => ['foo.txt' => true, 'meuh.html' => false], false],
'one_unsafe_file' => ['aExtraFiles' => ['../foo.txt' => true], true],
];
}
/**
* @dataProvider restoreListExtraFilesProvider
*/
function testRestoreListExtraFiles($aFilesToCreate, $aExpectedRelativeExtraFiles)
{
require_once(APPROOT.'/env-production/itop-backup/dbrestore.class.inc.php');
$sTmpDir = sys_get_temp_dir().'/testRestoreListExtraFiles-'.time();
foreach($aFilesToCreate as $sRelativeName)
{
$sDir = $sTmpDir.'/'.dirname($sRelativeName);
if(!is_dir($sDir))
{
mkdir($sDir, 0755, true);
}
file_put_contents($sTmpDir.'/'.$sRelativeName, 'Hello world.');
}
$aExpectedExtraFiles = [];
foreach($aExpectedRelativeExtraFiles as $sRelativeName)
{
$aExpectedExtraFiles[$sTmpDir.'/'.$sRelativeName] = APPROOT.'/'.$sRelativeName;
}
$oRestore = new DBRestore(MetaModel::GetConfig());
$aExtraFiles = $this->InvokeNonPublicMethod('DBRestore', 'ListExtraFiles', $oRestore, [$sTmpDir]);
asort($aExtraFiles);
asort($aExpectedExtraFiles);
$this->assertEquals($aExpectedExtraFiles, $aExtraFiles);
SetupUtils::rrmdir($sTmpDir);
}
function restoreListExtraFilesProvider()
{
return [
'no extra file' => ['aFilesToCreate' => ['config-itop.php', 'itop-dump.sql', 'delta.xml'], 'aExpectedExtraFiles' => []],
'no extra file (2)' => ['aFilesToCreate' => ['config-itop.php', 'itop-dump.sql', 'delta.xml', 'production-modules/test/module.test.php'], 'aExpectedExtraFiles' => []],
'one extra file' => ['aFilesToCreate' => ['config-itop.php', 'itop-dump.sql', 'delta.xml', 'production-modules/test/module.test.php', 'collectors/ldap/conf/params.local.xml'], 'aExpectedExtraFiles' => ['collectors/ldap/conf/params.local.xml']],
];
}
}