mirror of
https://github.com/Combodo/iTop.git
synced 2026-06-16 12:56:36 +02:00
Compare commits
13 Commits
issue/9678
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0240d12007 | ||
|
|
1123a83490 | ||
|
|
8ccf832bdd | ||
|
|
caa1b8652d | ||
|
|
ed1cfc3b08 | ||
|
|
dd6b03cccc | ||
|
|
3e38e349e5 | ||
|
|
65b58ec4e2 | ||
|
|
6a20e36434 | ||
|
|
3805a322c1 | ||
|
|
0bf773a23c | ||
|
|
28a09068a0 | ||
|
|
db7fd6b3c0 |
@@ -175,6 +175,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
*/
|
||||
protected $sDisplayMode;
|
||||
protected $aFieldsMap;
|
||||
/**
|
||||
* @var array Store posted values in order to be used in GetAttributeFlags
|
||||
* @since 3.3.0
|
||||
*/
|
||||
protected $aPostedValues = [];
|
||||
|
||||
/**
|
||||
* If true, bypass IsActionAllowedOnAttribute when writing this object
|
||||
@@ -524,6 +529,12 @@ JS
|
||||
$sLabel = Dict::S('Tag:Synchronized');
|
||||
$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
|
||||
$aTags[$sSynchroTagId] = ['title' => $sTip, 'css_classes' => 'ibo-object-details--tag--synchronized', 'decoration_classes' => 'fas fa-lock', 'label' => $sLabel];
|
||||
if (UserRights::IsActionAllowed(SynchroReplica::class, UR_ACTION_READ)) {
|
||||
$oDBSearch = DBObjectSearch::FromOQL('SELECT SynchroReplica WHERE dest_class=:sClass AND dest_id=:id');
|
||||
$sFilter = rawurlencode($oDBSearch->serialize(false, ['sClass' => get_class($this),'id' => $this->GetKey()]));
|
||||
$sUrlSearchReplica = 'UI.php?operation=search&filter='.$sFilter;
|
||||
$oPage->add_ready_script("$('#$sSynchroTagId').on('click',function() {window.location = '$sUrlSearchReplica' });");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3986,6 +3997,7 @@ HTML;
|
||||
|
||||
$aErrors = [];
|
||||
$aFinalValues = [];
|
||||
$this->aPostedValues = $aValues; // Store the values for later use (e.g. in GetAttributeFlags)
|
||||
foreach ($this->GetWriteableAttList(array_keys($aValues), $aErrors, $aAttFlags) as $sAttCode => $oAttDef) {
|
||||
$aFinalValues[$sAttCode] = $aValues[$sAttCode];
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,6 @@ JS
|
||||
|
||||
if ($aCounts[$sStateValue] == 0) {
|
||||
$aCounts[$sStateValue] = ['link' => '-', 'label' => $aCounts[$sStateValue]];
|
||||
;
|
||||
} else {
|
||||
$oSingleGroupByValueFilter = $this->m_oFilter->DeepClone();
|
||||
$oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
|
||||
@@ -1947,8 +1946,8 @@ class MenuBlock extends DisplayBlock
|
||||
$sSelectedClassName = MetaModel::GetName($sSelectedClass);
|
||||
|
||||
// Check rights on class
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
|
||||
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
|
||||
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && (($oReflectionClass->IsSubclassOf(cmdbAbstractObject::class) || $sSelectedClass === SynchroReplica::class));
|
||||
$bIsBulkDeleteAllowed = (bool)UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
|
||||
|
||||
// Refine filter on selected class so bullk actions occur on the right class
|
||||
$oSelectedClassFilter = $this->GetFilter()->DeepClone();
|
||||
@@ -1958,7 +1957,15 @@ class MenuBlock extends DisplayBlock
|
||||
// Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets.
|
||||
$sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class';
|
||||
if ($bIsBulkModifyAllowed) {
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||
if ($sSelectedClass === SynchroReplica::class) {
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnlinkAll:', Dict::S('Class:SynchroReplica/Action:unlink_all'), 'unlink');
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnLinkSynchroAll:', Dict::S('Class:SynchroReplica/Action:unlinksynchro_all'), 'unlinksynchro');
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:SynchroAll:', Dict::S('Class:SynchroReplica/Action:synchro_all'), 'synchro');
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:AllowDeleteAll:', Dict::S('Class:SynchroReplica/Action:allowdelete_all'), 'allowdelete');
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:DenyDeleteAll:', Dict::S('Class:SynchroReplica/Action:denydelete_all'), 'denydelete');
|
||||
} else {
|
||||
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||
}
|
||||
}
|
||||
if ($bIsBulkDeleteAllowed) {
|
||||
$this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||
@@ -2478,11 +2485,11 @@ class MenuBlock extends DisplayBlock
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll'): void
|
||||
protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll', $sOperationName = 'modify'): void
|
||||
{
|
||||
$aActions[$sActionIdentifier] = [
|
||||
'label' => Dict::S($sActionLabel),
|
||||
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)),
|
||||
'label' => Dict::S($sActionLabel),
|
||||
'url' => $this->PrepareUrlForStandardMenuAction($sClass, 'operation=select_for_'.$sOperationName.'_all&class='.$sClass.'&filter='.urlencode($sFilter)),
|
||||
] + $this->GetDefaultParamsForMenuAction();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,4 +80,14 @@ $ibo-data-synchro-source--synoptics--cell--arrow--border: 2px solid $ibo-data-sy
|
||||
}
|
||||
.ibo-data-synchro-source--replicas-status--warning{
|
||||
margin: $ibo-data-synchro-source--replicas-status--warning--margin;
|
||||
}
|
||||
|
||||
.ibo-page-header--replica-title{
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
||||
>.ibo-toolbar--button{
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class DataFeatureRemovalController extends Controller
|
||||
|
||||
$bForceCompilation = Session::Get('bForceCompilation', false);
|
||||
try {
|
||||
$this->Compile($aAddedExtensions, $aRemoveExtensionCodes, $bForceCompilation);
|
||||
$this->Compile($aRemoveExtensionCodes, $bForceCompilation);
|
||||
} catch (CoreException $e) {
|
||||
$aParams['DataFeatureRemovalErrorMessage'] = $e->getHtmlDesc();
|
||||
$this->DisplayPage($aParams, 'AnalysisResult');
|
||||
@@ -162,7 +162,7 @@ class DataFeatureRemovalController extends Controller
|
||||
$aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, array_keys($aAddedExtensions), array_keys($aRemovedExtensions));
|
||||
$aHiddenInputs['selected_extensions'] = $this->ConvertIntoSetupFormat($aSelectedExtensions);
|
||||
|
||||
$oRunTimeEnvironment = $this->GetRuntimeEnvironment($aAddedExtensions, $aRemovedExtensions);
|
||||
$oRunTimeEnvironment = $this->GetRuntimeEnvironment($aRemovedExtensions);
|
||||
$aSearchDirs = [$oRunTimeEnvironment->GetBuildDir()];
|
||||
$aSelectedModules = $oRunTimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions, $aSearchDirs);
|
||||
$aHiddenInputs['selected_modules'] = $this->ConvertIntoSetupFormat($aSelectedModules);
|
||||
@@ -213,14 +213,13 @@ class DataFeatureRemovalController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aAddedExtensions
|
||||
* @param array $aRemovedExtensions
|
||||
* @param bool $bForceCompilation
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private function Compile(array $aAddedExtensions, array $aRemovedExtensions, bool $bForceCompilation = true): void
|
||||
private function Compile(array $aRemovedExtensions, bool $bForceCompilation = true): void
|
||||
{
|
||||
$sSourceEnv = MetaModel::GetEnvironment();
|
||||
$sBuildDir = APPROOT."/env-$sSourceEnv-build";
|
||||
@@ -235,15 +234,15 @@ class DataFeatureRemovalController extends Controller
|
||||
null,
|
||||
['sSourceEnv' => $sSourceEnv, 'sBuildDir' => $sBuildDir, 'bIsDirEmpty' => $bIsDirEmpty, glob("$sBuildDir/*")]
|
||||
);
|
||||
$this->GetRuntimeEnvironment($aAddedExtensions, $aRemovedExtensions)->CompileFrom($sSourceEnv);
|
||||
$this->GetRuntimeEnvironment($aRemovedExtensions)->CompileFrom($sSourceEnv);
|
||||
}
|
||||
}
|
||||
|
||||
private function GetRuntimeEnvironment(array $aAddedExtensions, array $aRemovedExtensions): RunTimeEnvironment
|
||||
private function GetRuntimeEnvironment(array $aRemovedExtensions): RunTimeEnvironment
|
||||
{
|
||||
if (is_null($this->oRuntimeEnvironment)) {
|
||||
$sSourceEnv = MetaModel::GetEnvironment();
|
||||
$this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aAddedExtensions, $aRemovedExtensions);
|
||||
$this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aRemovedExtensions);
|
||||
}
|
||||
|
||||
return $this->oRuntimeEnvironment;
|
||||
|
||||
@@ -66,7 +66,7 @@ class DataFeatureRemoverExtensionService
|
||||
public function GetExtensionMap(): iTopExtensionsMap
|
||||
{
|
||||
if (is_null($this->oMap)) {
|
||||
$this->oMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$this->oMap = new iTopExtensionsMap();
|
||||
$this->oMap->LoadInstalledExtensionsFromDatabase(MetaModel::GetConfig());
|
||||
}
|
||||
return $this->oMap;
|
||||
|
||||
@@ -163,7 +163,7 @@ final class CoreUpdater
|
||||
$oRuntimeEnv->LoadData($aAvailableModules, false /* no sample data*/);
|
||||
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDataLoad');
|
||||
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
// Default choices = as before
|
||||
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
|
||||
@@ -20,7 +20,7 @@ function DisplayStatus(WebPage $oPage)
|
||||
if (is_dir($sPath)) {
|
||||
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
||||
}
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(ITOP_DEFAULT_ENV);
|
||||
$oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs);
|
||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||
|
||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
@@ -154,7 +154,7 @@ function DoInstall(WebPage $oPage)
|
||||
if (is_dir($sPath)) {
|
||||
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
|
||||
}
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(ITOP_DEFAULT_ENV);
|
||||
$oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs);
|
||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||
|
||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
|
||||
@@ -193,7 +193,7 @@ function collect_configuration()
|
||||
}
|
||||
|
||||
// iTop Installation Options, i.e. "Extensions"
|
||||
$oExtensionMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionMap = new iTopExtensionsMap();
|
||||
$oExtensionMap->LoadChoicesFromDatabase($oConfig);
|
||||
$aConfiguration['itop_extensions'] = [];
|
||||
foreach ($oExtensionMap->GetChoices() as $oExtension) {
|
||||
|
||||
@@ -41,7 +41,7 @@ function GetExtensionInfoComponent(iTopExtension $oExtension): UIBlock
|
||||
}
|
||||
|
||||
try {
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::S('iTopHub:InstalledExtensions')));
|
||||
|
||||
@@ -208,7 +208,7 @@ class HubController
|
||||
// Record the installation so that the "about box" knows about the installed modules
|
||||
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
|
||||
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
|
||||
// Default choices = as before
|
||||
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
|
||||
|
||||
@@ -460,6 +460,18 @@
|
||||
<extkey_attcode>parent_incident_id</extkey_attcode>
|
||||
<target_attcode>ref</target_attcode>
|
||||
</field>
|
||||
<field id="parent_request_id" xsi:type="AttributeExternalKey">
|
||||
<filter><![CDATA[SELECT UserRequest WHERE id != :this->id AND status NOT IN ('rejected','resolved','closed')]]></filter>
|
||||
<dependencies/>
|
||||
<sql>parent_request_id</sql>
|
||||
<target_class>UserRequest</target_class>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
</field>
|
||||
<field id="parent_request_ref" xsi:type="AttributeExternalField">
|
||||
<extkey_attcode>parent_request_id</extkey_attcode>
|
||||
<target_attcode>ref</target_attcode>
|
||||
</field>
|
||||
<field id="parent_problem_id" xsi:type="AttributeExternalKey">
|
||||
<sql>parent_problem_id</sql>
|
||||
<target_class>Problem</target_class>
|
||||
@@ -987,6 +999,9 @@
|
||||
<attribute id="parent_incident_id">
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="parent_request_id">
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="parent_change_id">
|
||||
<read_only/>
|
||||
</attribute>
|
||||
@@ -1301,90 +1316,20 @@
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildRequestLog()
|
||||
{
|
||||
if (!MetaModel::IsValidClass('UserRequest')) return true; // Do nothing
|
||||
|
||||
$oLog = $this->Get('public_log');
|
||||
$sLogPublic = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPublic != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('public_log',$sLogPublic);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
$oLog = $this->Get('private_log');
|
||||
$sLogPrivate = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPrivate != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('private_log',$sLogPrivate);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
if (MetaModel::IsValidClass('UserRequest')) {
|
||||
return $this->UpdateChildTicketLog('UserRequest', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] );
|
||||
}
|
||||
return true;
|
||||
}]]></code>
|
||||
</method>
|
||||
|
||||
<method id="UpdateChildIncidentLog">
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildIncidentLog()
|
||||
{
|
||||
$oLog = $this->Get('public_log');
|
||||
$sLogPublic = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPublic != '')
|
||||
{
|
||||
$sOQL = "SELECT Incident WHERE parent_incident_id=:ticket";
|
||||
$oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oIncident = $oChildIncidentSet->Fetch())
|
||||
{
|
||||
$oIncident->set('public_log',$sLogPublic);
|
||||
$oIncident->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
$oLog = $this->Get('private_log');
|
||||
$sLogPrivate = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPrivate != '')
|
||||
{
|
||||
$sOQL = "SELECT Incident WHERE parent_incident_id=:ticket";
|
||||
$oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oIncident = $oChildIncidentSet->Fetch())
|
||||
{
|
||||
$oIncident->set('private_log',$sLogPrivate);
|
||||
$oIncident->DBUpdate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return $this->UpdateChildTicketLog('Incident', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log']);
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="ComputeImpactedItems">
|
||||
@@ -1558,6 +1503,9 @@
|
||||
<item id="parent_incident_id">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="parent_request_id">
|
||||
<rank>15</rank>
|
||||
</item>
|
||||
<item id="parent_problem_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
|
||||
@@ -44,6 +44,8 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'UI-IncidentManagementOverview-OpenIncidentByStatus' => 'Open incidents by status',
|
||||
'UI-IncidentManagementOverview-OpenIncidentByAgent' => 'Open incidents by agent',
|
||||
'UI-IncidentManagementOverview-OpenIncidentByCustomer' => 'Open incidents by customer',
|
||||
'Class:Incident/Method:UpdateChildTicketWith:public_log' => '<i><u>Public log entry from parent Incident %2$s:</u></i><br><br>',
|
||||
'Class:Incident/Method:UpdateChildTicketWith:private_log' => '<i>Private log entry from parent Incident [[Incident:%1$s]]:</i><br><br>',
|
||||
]);
|
||||
|
||||
// Dictionnay conventions
|
||||
@@ -193,6 +195,10 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'Class:Incident/Attribute:parent_incident_id+' => '',
|
||||
'Class:Incident/Attribute:parent_incident_ref' => 'Parent incident ref',
|
||||
'Class:Incident/Attribute:parent_incident_ref+' => '',
|
||||
'Class:Incident/Attribute:parent_request_id' => 'Parent request',
|
||||
'Class:Incident/Attribute:parent_request_id+' => '',
|
||||
'Class:Incident/Attribute:parent_request_ref' => 'Parent request ref',
|
||||
'Class:Incident/Attribute:parent_request_ref+' => '',
|
||||
'Class:Incident/Attribute:parent_change_id' => 'Parent change',
|
||||
'Class:Incident/Attribute:parent_change_id+' => '',
|
||||
'Class:Incident/Attribute:parent_change_ref' => 'Parent change ref',
|
||||
|
||||
@@ -179,15 +179,19 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'Class:Incident/Attribute:pending_reason+' => '',
|
||||
'Class:Incident/Attribute:parent_incident_id' => 'Incident parent',
|
||||
'Class:Incident/Attribute:parent_incident_id+' => '',
|
||||
'Class:Incident/Attribute:parent_incident_ref' => 'Référence incident parent',
|
||||
'Class:Incident/Attribute:parent_incident_ref' => 'Réf. incident parent',
|
||||
'Class:Incident/Attribute:parent_incident_ref+' => '',
|
||||
'Class:Incident/Attribute:parent_request_id' => 'Demande parente',
|
||||
'Class:Incident/Attribute:parent_request_id+' => '',
|
||||
'Class:Incident/Attribute:parent_request_ref' => 'Réf. demande parente',
|
||||
'Class:Incident/Attribute:parent_request_ref+' => '',
|
||||
'Class:Incident/Attribute:parent_change_id' => 'Changement parent',
|
||||
'Class:Incident/Attribute:parent_change_id+' => '',
|
||||
'Class:Incident/Attribute:parent_change_ref' => 'Ref Changement parent',
|
||||
'Class:Incident/Attribute:parent_change_ref' => 'Réf. changement parent',
|
||||
'Class:Incident/Attribute:parent_change_ref+' => '',
|
||||
'Class:Incident/Attribute:parent_problem_id' => 'Problème lié',
|
||||
'Class:Incident/Attribute:parent_problem_id+' => '',
|
||||
'Class:Incident/Attribute:parent_problem_ref' => 'Référence problème lié',
|
||||
'Class:Incident/Attribute:parent_problem_ref' => 'Réf. problème lié',
|
||||
'Class:Incident/Attribute:parent_problem_ref+' => '',
|
||||
'Class:Incident/Attribute:related_request_list' => 'Requêtes filles',
|
||||
'Class:Incident/Attribute:related_request_list+' => '',
|
||||
|
||||
@@ -1451,44 +1451,17 @@
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildRequestLog()
|
||||
{
|
||||
return $this->UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log' ]);
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="UpdateChildIncidentLog">
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildIncidentLog()
|
||||
{
|
||||
$oLog = $this->Get('public_log');
|
||||
$sLogPublic = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPublic != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('public_log',$sLogPublic);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
$oLog = $this->Get('private_log');
|
||||
$sLogPrivate = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPrivate != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('private_log',$sLogPrivate);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return $this->UpdateChildTicketLog('Incident', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log']);
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="ComputeImpactedItems">
|
||||
@@ -1526,6 +1499,7 @@
|
||||
parent::OnUpdate();
|
||||
$this->Set('last_update', time());
|
||||
$this->UpdateChildRequestLog();
|
||||
$this->UpdateChildIncidentLog();
|
||||
}]]></code>
|
||||
</method>
|
||||
</methods>
|
||||
|
||||
@@ -37,6 +37,8 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Open requests by customer',
|
||||
'Class:UserRequest:KnownErrorList' => 'Known Errors',
|
||||
'Class:UserRequest:KnownErrorList+' => 'Known Errors related to Functional CI linked to the current ticket',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => '<i><u>Public log automatic copy from parent User Request %2$s:</u></i><br><br>',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => '<i>Private log automatic copy from parent User Request [[UserRequest:%1$s]]:</i><br><br>',
|
||||
]);
|
||||
|
||||
// Dictionnay conventions
|
||||
|
||||
@@ -42,6 +42,8 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par client',
|
||||
'Class:UserRequest:KnownErrorList' => 'Erreurs connues',
|
||||
'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => '<i><u>Copie automatique du log public de la demande parente %2$s:</u></i><br><br>',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => '<i>Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:</i><br><br>',
|
||||
]);
|
||||
|
||||
// Dictionnay conventions
|
||||
|
||||
@@ -1486,44 +1486,8 @@
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildRequestLog()
|
||||
{
|
||||
$oLog = $this->Get('public_log');
|
||||
$sLogPublic = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPublic != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('public_log',$sLogPublic);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
$oLog = $this->Get('private_log');
|
||||
$sLogPrivate = $oLog->GetModifiedEntry('html');
|
||||
if ($sLogPrivate != '')
|
||||
{
|
||||
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
|
||||
$oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
|
||||
array(),
|
||||
array(
|
||||
'ticket' => $this->GetKey(),
|
||||
)
|
||||
);
|
||||
while($oRequest = $oChildRequestSet->Fetch())
|
||||
{
|
||||
$oRequest->set('private_log',$sLogPrivate);
|
||||
$oRequest->DBUpdate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
{
|
||||
return $this->UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] );
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="ComputeImpactedItems">
|
||||
|
||||
@@ -41,6 +41,8 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me',
|
||||
'Class:Problem:KnownProblemList' => 'Known problems',
|
||||
'Tickets:Related:OpenIncidents' => 'Open incidents',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => '<i><u>Public log automatic copy from parent User Request %2$s:</u></i><br><br>',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => '<i>Private log automatic copy from parent User Request [[UserRequest:%1$s]]:</i><br><br>',
|
||||
]);
|
||||
|
||||
// Dictionnay conventions
|
||||
|
||||
@@ -42,6 +42,8 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par organisation',
|
||||
'Class:UserRequest:KnownErrorList' => 'Erreurs connues',
|
||||
'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => '<i><u>Copie automatique du log public de la demande parente %2$s:</u></i><br><br>',
|
||||
'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => '<i>Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:</i><br><br>',
|
||||
'Menu:UserRequest:MyWorkOrders' => 'Tâches qui me sont assignées',
|
||||
'Menu:UserRequest:MyWorkOrders+' => '',
|
||||
'Class:Problem:KnownProblemList' => 'Problèmes connus',
|
||||
|
||||
@@ -351,6 +351,63 @@
|
||||
}]]></code>
|
||||
<arguments/>
|
||||
</method>
|
||||
<method id="UpdateChildTicketLog">
|
||||
<comment><![CDATA[/**
|
||||
*
|
||||
* Remove the current user associated Person from the contacts_list of this Ticket
|
||||
* No error if there is no associated Person or if the Person is not in the list
|
||||
* @return bool Return true
|
||||
* @param string $sChildClass The class name of the child ticket (e.g. 'Incident', 'UserRequest', etc.)
|
||||
* @param string $sChildParentAttCode The external key in the child class pointing to the parent ticket (e.g. 'parent_request_id')
|
||||
* @param array $aLogAttCodes An array of parent caselog attribute codes and for each the corresponding child log attribute code to update (e.g. ['public_log' => 'private_log'])
|
||||
* So in the example parent.public_log will be copied into each child.private_log
|
||||
* with a prefix using a dictionary entry like 'Class:<parent_ticket_class>/Method:UpdateChildTicketWith:<caselog_attcode>' with one placeholder %1$s for the parent ticket ref
|
||||
* resulting in an entry like "Copy of public log entry from parent Incident I-000123: <log entry from parent.public_log>" in the child private_log
|
||||
*
|
||||
*/]]>
|
||||
</comment>
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
<code><![CDATA[ public function UpdateChildTicketLog($sChildClass, $sChildParentAttCode, $aLogAttCodes)
|
||||
{
|
||||
if (!MetaModel::IsValidClass($sChildClass) || (!MetaModel::IsValidAttCode($sChildClass, $sChildParentAttCode))) {
|
||||
ErrorLog::Debug("Invalid class ($sChildClass) or attribute code ($sChildParentAttCode) provided to UpdateChildTicketLog","DataModel");
|
||||
return true; // Do nothing
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sChildClass, $sChildParentAttCode);
|
||||
if (!$oAttDef instanceof AttributeExternalKey || ($oAttDef->GetTargetClass() !== get_class($this))) {
|
||||
ErrorLog::Debug("Attribute $sChildParentAttCode should be an external key of class $sChildClass, pointing to class ".get_class($this),"DataModel");
|
||||
return true; // Do nothing
|
||||
}
|
||||
|
||||
$sParentClass = get_class($this);
|
||||
$aChildEntries = [];
|
||||
foreach ($aLogAttCodes as $sParentAttCode => $sChildAttCode) {
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $sParentAttCode) && MetaModel::GetAttributeDef($sParentClass, $sParentAttCode) instanceof AttributeCaseLog
|
||||
&& MetaModel::IsValidAttCode($sChildClass, $sChildAttCode) && MetaModel::GetAttributeDef($sChildClass, $sChildAttCode) instanceof AttributeCaseLog
|
||||
&& (!utils::IsNullOrEmptyString($this->Get($sParentAttCode)->GetModifiedEntry('html')))) {
|
||||
$aChildEntries[$sChildAttCode] = Dict::Format('Class:'.$sParentClass.'/Method:UpdateChildTicketWith:'.$sParentAttCode, $this->GetKey(), $this->Get('ref')).$this->Get($sParentAttCode)->GetModifiedEntry('html');
|
||||
}
|
||||
}
|
||||
if ($aChildEntries == []) {
|
||||
return true; // nothing to update
|
||||
}
|
||||
|
||||
$sOQL = "SELECT $sChildClass WHERE $sChildParentAttCode = :ticket_id";
|
||||
$oChildSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket_id' => $this->GetKey()]);
|
||||
while ($oChild = $oChildSet->Fetch()) {
|
||||
if (is_object($oChild)) { // Seems that empty set, maybe in case of OQL syntax error, can return a single empty object instead of no object at all
|
||||
foreach ($aChildEntries as $sAttCode => $sEntry) {
|
||||
$oChild->Set($sAttCode, $sEntry);
|
||||
}
|
||||
$oChild->DBUpdate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}]]></code>
|
||||
</method>
|
||||
</methods>
|
||||
<presentation>
|
||||
<details>
|
||||
|
||||
@@ -976,6 +976,39 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Poslední varování',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Datum vytvoření',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum poslední úpravy',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Uživatelské předvolby',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Uživatel',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Předvolby',
|
||||
|
||||
@@ -975,6 +975,39 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Advarsler',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Oprettelses Dato',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Sidste Ændrings Dato',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Bruger Indstillinger',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Bruger',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Inst.',
|
||||
|
||||
@@ -972,6 +972,39 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Letzte Warnung',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Erzeugungs-Datum',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum der letzten Modifikation',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Benutzer-Voreinstellungen',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Benutzer',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Voreinstellungen',
|
||||
|
||||
@@ -982,7 +982,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Core:SynchroReplica:PublicData' => 'Public Data',
|
||||
'Core:SynchroReplica:PrivateDetails' => 'Private Details',
|
||||
'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'List of Replica',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s',
|
||||
'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)',
|
||||
'Core:SynchroAtt:attcode' => 'Attribute',
|
||||
'Core:SynchroAtt:attcode+' => 'Field of the object',
|
||||
@@ -1036,65 +1036,99 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Rows separator',
|
||||
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Attributes separator',
|
||||
'Class:SynchroLog' => 'Synchro Log',
|
||||
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source',
|
||||
'Class:SynchroLog/Attribute:start_date' => 'Start Date',
|
||||
'Class:SynchroLog/Attribute:end_date' => 'End Date',
|
||||
'Class:SynchroLog/Attribute:status' => 'Status',
|
||||
'Class:SynchroLog/Attribute:status/Value:completed' => 'Completed',
|
||||
'Class:SynchroLog/Attribute:status/Value:error' => 'Error',
|
||||
'Class:SynchroLog/Attribute:status/Value:running' => 'Still Running',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_seen' => 'Nb replica seen',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_total' => 'Nb replica total',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_deleted' => 'Nb objects deleted',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_deleted_errors' => 'Nb of errors while deleting',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted' => 'Nb objects obsoleted',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted_errors' => 'Nb of errors while obsoleting',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_created' => 'Nb objects created',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_created_errors' => 'Nb or errors while creating',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_updated' => 'Nb objects updated',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_updated_errors' => 'Nb errors while updating',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_reconciled_errors' => 'Nb of errors during reconciliation',
|
||||
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source',
|
||||
'Class:SynchroLog/Attribute:start_date' => 'Start Date',
|
||||
'Class:SynchroLog/Attribute:end_date' => 'End Date',
|
||||
'Class:SynchroLog/Attribute:status' => 'Status',
|
||||
'Class:SynchroLog/Attribute:status/Value:completed' => 'Completed',
|
||||
'Class:SynchroLog/Attribute:status/Value:error' => 'Error',
|
||||
'Class:SynchroLog/Attribute:status/Value:running' => 'Still Running',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_seen' => 'Nb replica seen',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_total' => 'Nb replica total',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_deleted' => 'Nb objects deleted',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_deleted_errors' => 'Nb of errors while deleting',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted' => 'Nb objects obsoleted',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted_errors' => 'Nb of errors while obsoleting',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_created' => 'Nb objects created',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_created_errors' => 'Nb or errors while creating',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_updated' => 'Nb objects updated',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_updated_errors' => 'Nb errors while updating',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_reconciled_errors' => 'Nb of errors during reconciliation',
|
||||
'Class:SynchroLog/Attribute:stats_nb_replica_disappeared_no_action' => 'Nb replica disappeared',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_new_updated' => 'Nb objects updated',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_new_unchanged' => 'Nb objects unchanged',
|
||||
'Class:SynchroLog/Attribute:last_error' => 'Last error',
|
||||
'Class:SynchroLog/Attribute:traces' => 'Traces',
|
||||
'Class:SynchroReplica' => 'Synchro Replica',
|
||||
'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source',
|
||||
'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)',
|
||||
'Class:SynchroReplica/Attribute:dest_class' => 'Destination type',
|
||||
'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen',
|
||||
'Class:SynchroReplica/Attribute:status' => 'Status',
|
||||
'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified',
|
||||
'Class:SynchroReplica/Attribute:status/Value:new' => 'New',
|
||||
'Class:SynchroReplica/Attribute:status/Value:obsolete' => 'Obsolete',
|
||||
'Class:SynchroReplica/Attribute:status/Value:orphan' => 'Orphan',
|
||||
'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized',
|
||||
'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?',
|
||||
'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error',
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date',
|
||||
'Class:appUserPreferences' => 'User Preferences',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'User',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_new_updated' => 'Nb objects updated',
|
||||
'Class:SynchroLog/Attribute:stats_nb_obj_new_unchanged' => 'Nb objects unchanged',
|
||||
'Class:SynchroLog/Attribute:last_error' => 'Last error',
|
||||
'Class:SynchroLog/Attribute:traces' => 'Traces',
|
||||
'Class:SynchroReplica' => 'Synchro Replica',
|
||||
'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source',
|
||||
'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)',
|
||||
'Class:SynchroReplica/Attribute:dest_class' => 'Destination type',
|
||||
'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen',
|
||||
'Class:SynchroReplica/Attribute:status' => 'Status',
|
||||
'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified',
|
||||
'Class:SynchroReplica/Attribute:status/Value:new' => 'New',
|
||||
'Class:SynchroReplica/Attribute:status/Value:obsolete' => 'Obsolete',
|
||||
'Class:SynchroReplica/Attribute:status/Value:orphan' => 'Orphan',
|
||||
'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized',
|
||||
'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?',
|
||||
'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error',
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica',
|
||||
|
||||
'Class:appUserPreferences' => 'User Preferences',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'User',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Prefs',
|
||||
'Core:ExecProcess:Code1' => 'Wrong command or command finished with errors (e.g. wrong script name)',
|
||||
'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)',
|
||||
'Core:ExecProcess:Code1' => 'Wrong command or command finished with errors (e.g. wrong script name)',
|
||||
'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)',
|
||||
|
||||
// Attribute Duration
|
||||
'Core:Duration_Seconds' => '%1$ds',
|
||||
'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds',
|
||||
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',
|
||||
'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds',
|
||||
'Core:Duration_Seconds' => '%1$ds',
|
||||
'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds',
|
||||
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',
|
||||
'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds',
|
||||
|
||||
// Explain working time computing
|
||||
'Core:ExplainWTC:ElapsedTime' => 'Time elapsed (stored as "%1$s")',
|
||||
'Core:ExplainWTC:StopWatch-TimeSpent' => 'Time spent for "%1$s"',
|
||||
'Core:ExplainWTC:StopWatch-Deadline' => 'Deadline for "%1$s" at %2$d%%',
|
||||
'Core:ExplainWTC:ElapsedTime' => 'Time elapsed (stored as "%1$s")',
|
||||
'Core:ExplainWTC:StopWatch-TimeSpent' => 'Time spent for "%1$s"',
|
||||
'Core:ExplainWTC:StopWatch-Deadline' => 'Deadline for "%1$s" at %2$d%%',
|
||||
|
||||
// Bulk export
|
||||
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"',
|
||||
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".',
|
||||
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"',
|
||||
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".',
|
||||
'Core:BulkExport:ExportFormatPrompt' => 'Export format:',
|
||||
'Core:BulkExportOf_Class' => '%1$s Export',
|
||||
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s',
|
||||
|
||||
@@ -965,7 +965,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Core:SynchroReplica:PublicData' => 'Public Data',
|
||||
'Core:SynchroReplica:PrivateDetails' => 'Private Details',
|
||||
'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'List of Replica',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s',
|
||||
'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)',
|
||||
'Core:SynchroAtt:attcode' => 'Attribute',
|
||||
'Core:SynchroAtt:attcode+' => 'Field of the object',
|
||||
@@ -1058,6 +1058,39 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'User Preferences',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'User',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Prefs',
|
||||
|
||||
@@ -963,6 +963,39 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Advertencias',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Fecha de Creación',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Fecha Última Modificación',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Preferencias de Usuario',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Usuario',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Preferencias',
|
||||
|
||||
@@ -928,7 +928,7 @@ Plusieurs champs peuvent ainsi être modifiés lors d\'une "mise à jour" en app
|
||||
'Core:SynchroReplica:PublicData' => 'Données synchronisées',
|
||||
'Core:SynchroReplica:PrivateDetails' => 'Informations internes',
|
||||
'Core:SynchroReplica:BackToDataSource' => 'Retourner aux détails de la source de données: %1$s',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'Liste des réplica',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'Liste des réplicas de %1$s',
|
||||
'Core:SynchroAttExtKey:ReconciliationById' => 'id (clé primaire)',
|
||||
'Core:SynchroAtt:attcode' => 'Champ',
|
||||
'Core:SynchroAtt:attcode+' => '',
|
||||
@@ -1021,6 +1021,40 @@ Plusieurs champs peuvent ainsi être modifiés lors d\'une "mise à jour" en app
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Avertissements',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification',
|
||||
'Class:SynchroReplica/Action:delete+' => 'Supprimer le réplica',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Délier',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Séparer le réplica de son objet lié',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Délier & Synchroniser',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Délier le réplica avec son objet cible et executer la synchronisation sur ce réplica',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchroniser',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Exécuter la synchronisation sur ce réplica',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Permettre la suppression de l\'objet lié à ce réplica',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'L\'objet lié à un réplica supprimé est supprimé',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Interdire la suppression de l\objet lié à ce réplica',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'L\'objet lié à un réplica supprimé n\'est pas supprimé',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Délier les réplicas et les objets',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Séparer les réplicas de leurs objets liés',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Délier & Synchroniser les réplicas et les objets',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Séparer les réplicas le leurs objets cilbles et exécuter la synchronisation sur les réplicas',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchroniser les objets Réplica',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Exécuter la synchronisation avec ces réplicas',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Autoriser la suppression des objets liés aux Réplicas',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'L\'objet lié à un réplica supprimé est supprimé',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Interdire la suppression des objets liés aux Réplicas',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'L\'objet lié à un réplica supprimé n\'est pas supprimé',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Délier les réplicas des objets',
|
||||
'UI:UnlinkAllPageTitle' => 'Séparer les réplicas de leurs objets liés',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Délier & synchroniser les réplicas',
|
||||
'UI:UnlinkSynchroAllPageTitle' => 'Délier & synchroniser les réplicas ',
|
||||
'UI:SynchroAllTabTitle' => 'Synchroniser les réplica',
|
||||
'UI:SynchroAllPageTitle' => 'Synchroniser les réplica',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Autoriser la suppression des objets liés aux réplicas',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Autoriser la suppression des objets liés aux réplicas',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Interdire la suppression des objets liés aux réplicas',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Interdire la suppression des objets liés aux réplicas',
|
||||
|
||||
'Class:appUserPreferences' => 'Préférences utilisateur',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Utilisateur',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Préférences',
|
||||
|
||||
@@ -970,6 +970,39 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Figyelmeztetések',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Létrehozás dátuma',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Utolsó módosítás dátuma',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Felhasználói beállítások',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Felhasználónév',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Beállítások',
|
||||
|
||||
@@ -970,6 +970,39 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Avvisi',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Data di creazione',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Data di ultima modifica',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Preferenze utente',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Utente',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Prefs',
|
||||
|
||||
@@ -974,6 +974,39 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => '警告',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => '作成日',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => '最終修正日',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'ユーザプリファレンス',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'ユーザ',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'プリファレンス',
|
||||
|
||||
@@ -972,6 +972,39 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Waarschuwingen',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Datum van aanmaken',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum van de laatste aanpassing',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Gebruikersvoorkeuren',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Gebruiker',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Voorkeuren',
|
||||
|
||||
@@ -969,6 +969,39 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Ostrzeżenia',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Data utworzenia',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Data ostatniej zmiany',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Preferencje użytkownika',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Użytkownik',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Preferencje',
|
||||
|
||||
@@ -970,6 +970,39 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Alertas',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Data de criação',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Última data modificação',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Preferências de usuário',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Usuário',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Preferências',
|
||||
|
||||
@@ -882,7 +882,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'Core:SynchroReplica:PublicData' => 'Public Data~~',
|
||||
'Core:SynchroReplica:PrivateDetails' => 'Private Details~~',
|
||||
'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s~~',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'List of Replica~~',
|
||||
'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s~~',
|
||||
'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)~~',
|
||||
'Core:SynchroAtt:attcode' => 'Атрибут',
|
||||
'Core:SynchroAtt:attcode+' => 'Поле объекта',
|
||||
@@ -975,6 +975,40 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Предупреждения',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Дата создания',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Дата последнего изменения',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
|
||||
'Class:appUserPreferences' => 'Предпочтения пользователя',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Пользователь',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Предпочтения',
|
||||
|
||||
@@ -993,6 +993,39 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Upozornenia',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Dátum vytvorenia',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Dátum poslednej úpravy',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Užívateľské preferencie',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Užívateľ',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Preferencie',
|
||||
|
||||
@@ -975,6 +975,39 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => 'Uyarılar',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Yaratılış Tarihi',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Son değiştirilme tarih',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:appUserPreferences' => 'Kullanıcı Tercihleri',
|
||||
'Class:appUserPreferences/Attribute:userid' => 'Kullanıcı',
|
||||
'Class:appUserPreferences/Attribute:preferences' => 'Tercihler',
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -19,4 +19,5 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'Plus d\'informations',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Forcer la désinstallation',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Plus d\'actions',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -21,4 +21,5 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~',
|
||||
'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~',
|
||||
'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~',
|
||||
'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~',
|
||||
]);
|
||||
|
||||
@@ -1075,6 +1075,40 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'Class:SynchroReplica/Attribute:status_last_warning' => '告警',
|
||||
'Class:SynchroReplica/Attribute:info_creation_date' => '创建日期',
|
||||
'Class:SynchroReplica/Attribute:info_last_modified' => '最后修改日期',
|
||||
'Class:SynchroReplica/Action:delete+' => 'delete replica~~',
|
||||
'Class:SynchroReplica/Action:unlink' => 'Unlink~~',
|
||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro' => 'Synchro~~',
|
||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~',
|
||||
'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~',
|
||||
|
||||
'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~',
|
||||
'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~',
|
||||
|
||||
'Class:appUserPreferences' => '用户偏好',
|
||||
'Class:appUserPreferences/Attribute:userid' => '用户',
|
||||
'Class:appUserPreferences/Attribute:preferences' => '偏好设置',
|
||||
|
||||
@@ -14,7 +14,10 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException($err);
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
@@ -131,12 +131,14 @@ return array(
|
||||
'Collator' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
|
||||
'Combodo\\iTop\\Application\\Branding' => $baseDir . '/sources/Application/Branding.php',
|
||||
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => $baseDir . '/sources/Application/EventRegister/ApplicationEvents.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\BulkHelper' => $baseDir . '/sources/Application/Helper/BulkHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => $baseDir . '/sources/Application/Helper/ImportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => $baseDir . '/sources/Application/Helper/SynchroReplicaHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
'Combodo\\iTop\\Application\\Search\\AjaxSearchException' => $baseDir . '/sources/Application/Search/ajaxsearchexception.class.inc.php',
|
||||
|
||||
@@ -28,12 +28,12 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'T' =>
|
||||
'T' =>
|
||||
array (
|
||||
'Twig\\' => 5,
|
||||
'TheNetworg\\OAuth2\\Client\\' => 25,
|
||||
),
|
||||
'S' =>
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' => 34,
|
||||
'Symfony\\Polyfill\\Php83\\' => 23,
|
||||
@@ -85,7 +85,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'ScssPhp\\ScssPhp\\' => 16,
|
||||
'Sabberworm\\CSS\\' => 15,
|
||||
),
|
||||
'P' =>
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
@@ -96,310 +96,310 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'PhpParser\\' => 10,
|
||||
'Pelago\\Emogrifier\\' => 18,
|
||||
),
|
||||
'L' =>
|
||||
'L' =>
|
||||
array (
|
||||
'League\\OAuth2\\Client\\' => 21,
|
||||
),
|
||||
'G' =>
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
'GuzzleHttp\\Promise\\' => 19,
|
||||
'GuzzleHttp\\' => 11,
|
||||
),
|
||||
'F' =>
|
||||
'F' =>
|
||||
array (
|
||||
'Firebase\\JWT\\' => 13,
|
||||
),
|
||||
'E' =>
|
||||
'E' =>
|
||||
array (
|
||||
'Egulias\\EmailValidator\\' => 23,
|
||||
),
|
||||
'D' =>
|
||||
'D' =>
|
||||
array (
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Twig\\' =>
|
||||
'Twig\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/twig/twig/src',
|
||||
),
|
||||
'TheNetworg\\OAuth2\\Client\\' =>
|
||||
'TheNetworg\\OAuth2\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src',
|
||||
),
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' =>
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/runtime/Internal',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php83\\' =>
|
||||
'Symfony\\Polyfill\\Php83\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php83',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' =>
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Icu\\' =>
|
||||
'Symfony\\Polyfill\\Intl\\Icu\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-icu',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Grapheme\\' =>
|
||||
'Symfony\\Polyfill\\Intl\\Grapheme\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme',
|
||||
),
|
||||
'Symfony\\Polyfill\\Ctype\\' =>
|
||||
'Symfony\\Polyfill\\Ctype\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\Service\\' =>
|
||||
'Symfony\\Contracts\\Service\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/service-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\Cache\\' =>
|
||||
'Symfony\\Contracts\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/cache-contracts',
|
||||
),
|
||||
'Symfony\\Component\\Yaml\\' =>
|
||||
'Symfony\\Component\\Yaml\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/yaml',
|
||||
),
|
||||
'Symfony\\Component\\VarExporter\\' =>
|
||||
'Symfony\\Component\\VarExporter\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/var-exporter',
|
||||
),
|
||||
'Symfony\\Component\\VarDumper\\' =>
|
||||
'Symfony\\Component\\VarDumper\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
||||
),
|
||||
'Symfony\\Component\\Validator\\' =>
|
||||
'Symfony\\Component\\Validator\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/validator',
|
||||
),
|
||||
'Symfony\\Component\\String\\' =>
|
||||
'Symfony\\Component\\String\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/string',
|
||||
),
|
||||
'Symfony\\Component\\Stopwatch\\' =>
|
||||
'Symfony\\Component\\Stopwatch\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/stopwatch',
|
||||
),
|
||||
'Symfony\\Component\\Security\\Csrf\\' =>
|
||||
'Symfony\\Component\\Security\\Csrf\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/security-csrf',
|
||||
),
|
||||
'Symfony\\Component\\Security\\Core\\' =>
|
||||
'Symfony\\Component\\Security\\Core\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/security-core',
|
||||
),
|
||||
'Symfony\\Component\\Runtime\\' =>
|
||||
'Symfony\\Component\\Runtime\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/runtime',
|
||||
),
|
||||
'Symfony\\Component\\Routing\\' =>
|
||||
'Symfony\\Component\\Routing\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/routing',
|
||||
),
|
||||
'Symfony\\Component\\PropertyInfo\\' =>
|
||||
'Symfony\\Component\\PropertyInfo\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/property-info',
|
||||
),
|
||||
'Symfony\\Component\\PropertyAccess\\' =>
|
||||
'Symfony\\Component\\PropertyAccess\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/property-access',
|
||||
),
|
||||
'Symfony\\Component\\PasswordHasher\\' =>
|
||||
'Symfony\\Component\\PasswordHasher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/password-hasher',
|
||||
),
|
||||
'Symfony\\Component\\OptionsResolver\\' =>
|
||||
'Symfony\\Component\\OptionsResolver\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/options-resolver',
|
||||
),
|
||||
'Symfony\\Component\\Mime\\' =>
|
||||
'Symfony\\Component\\Mime\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/mime',
|
||||
),
|
||||
'Symfony\\Component\\Mailer\\' =>
|
||||
'Symfony\\Component\\Mailer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/mailer',
|
||||
),
|
||||
'Symfony\\Component\\HttpKernel\\' =>
|
||||
'Symfony\\Component\\HttpKernel\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/http-kernel',
|
||||
),
|
||||
'Symfony\\Component\\HttpFoundation\\' =>
|
||||
'Symfony\\Component\\HttpFoundation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/http-foundation',
|
||||
),
|
||||
'Symfony\\Component\\Form\\' =>
|
||||
'Symfony\\Component\\Form\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/form',
|
||||
),
|
||||
'Symfony\\Component\\Finder\\' =>
|
||||
'Symfony\\Component\\Finder\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
||||
),
|
||||
'Symfony\\Component\\Filesystem\\' =>
|
||||
'Symfony\\Component\\Filesystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/filesystem',
|
||||
),
|
||||
'Symfony\\Component\\EventDispatcher\\' =>
|
||||
'Symfony\\Component\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
|
||||
),
|
||||
'Symfony\\Component\\ErrorHandler\\' =>
|
||||
'Symfony\\Component\\ErrorHandler\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/error-handler',
|
||||
),
|
||||
'Symfony\\Component\\Dotenv\\' =>
|
||||
'Symfony\\Component\\Dotenv\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/dotenv',
|
||||
),
|
||||
'Symfony\\Component\\DependencyInjection\\' =>
|
||||
'Symfony\\Component\\DependencyInjection\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/dependency-injection',
|
||||
),
|
||||
'Symfony\\Component\\CssSelector\\' =>
|
||||
'Symfony\\Component\\CssSelector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/css-selector',
|
||||
),
|
||||
'Symfony\\Component\\Console\\' =>
|
||||
'Symfony\\Component\\Console\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/console',
|
||||
),
|
||||
'Symfony\\Component\\Config\\' =>
|
||||
'Symfony\\Component\\Config\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/config',
|
||||
),
|
||||
'Symfony\\Component\\Cache\\' =>
|
||||
'Symfony\\Component\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/cache',
|
||||
),
|
||||
'Symfony\\Bundle\\WebProfilerBundle\\' =>
|
||||
'Symfony\\Bundle\\WebProfilerBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/web-profiler-bundle',
|
||||
),
|
||||
'Symfony\\Bundle\\TwigBundle\\' =>
|
||||
'Symfony\\Bundle\\TwigBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/twig-bundle',
|
||||
),
|
||||
'Symfony\\Bundle\\FrameworkBundle\\' =>
|
||||
'Symfony\\Bundle\\FrameworkBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/framework-bundle',
|
||||
),
|
||||
'Symfony\\Bundle\\DebugBundle\\' =>
|
||||
'Symfony\\Bundle\\DebugBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/debug-bundle',
|
||||
),
|
||||
'Symfony\\Bridge\\Twig\\' =>
|
||||
'Symfony\\Bridge\\Twig\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/twig-bridge',
|
||||
),
|
||||
'Soundasleep\\' =>
|
||||
'Soundasleep\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/soundasleep/html2text/src',
|
||||
),
|
||||
'ScssPhp\\ScssPhp\\' =>
|
||||
'ScssPhp\\ScssPhp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/scssphp/scssphp/src',
|
||||
),
|
||||
'Sabberworm\\CSS\\' =>
|
||||
'Sabberworm\\CSS\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src',
|
||||
),
|
||||
'Psr\\Log\\' =>
|
||||
'Psr\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/src',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-factory/src',
|
||||
1 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'Psr\\Http\\Client\\' =>
|
||||
'Psr\\Http\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-client/src',
|
||||
),
|
||||
'Psr\\EventDispatcher\\' =>
|
||||
'Psr\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
|
||||
),
|
||||
'Psr\\Container\\' =>
|
||||
'Psr\\Container\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/container/src',
|
||||
),
|
||||
'Psr\\Cache\\' =>
|
||||
'Psr\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/cache/src',
|
||||
),
|
||||
'PhpParser\\' =>
|
||||
'PhpParser\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser',
|
||||
),
|
||||
'Pelago\\Emogrifier\\' =>
|
||||
'Pelago\\Emogrifier\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/pelago/emogrifier/src',
|
||||
),
|
||||
'League\\OAuth2\\Client\\' =>
|
||||
'League\\OAuth2\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/oauth2-google/src',
|
||||
1 => __DIR__ . '/..' . '/league/oauth2-client/src',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
),
|
||||
'GuzzleHttp\\Promise\\' =>
|
||||
'GuzzleHttp\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
|
||||
),
|
||||
'GuzzleHttp\\' =>
|
||||
'GuzzleHttp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
|
||||
),
|
||||
'Firebase\\JWT\\' =>
|
||||
'Firebase\\JWT\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
|
||||
),
|
||||
'Egulias\\EmailValidator\\' =>
|
||||
'Egulias\\EmailValidator\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/egulias/email-validator/src',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'C' =>
|
||||
'C' =>
|
||||
array (
|
||||
'Console' =>
|
||||
'Console' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/pear/console_getopt',
|
||||
),
|
||||
),
|
||||
'A' =>
|
||||
'A' =>
|
||||
array (
|
||||
'Archive_Tar' =>
|
||||
'Archive_Tar' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/pear/archive_tar',
|
||||
),
|
||||
@@ -532,12 +532,14 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Collator' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
|
||||
'Combodo\\iTop\\Application\\Branding' => __DIR__ . '/../..' . '/sources/Application/Branding.php',
|
||||
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => __DIR__ . '/../..' . '/sources/Application/EventRegister/ApplicationEvents.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\BulkHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/BulkHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ImportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ImportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SynchroReplicaHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
'Combodo\\iTop\\Application\\Search\\AjaxSearchException' => __DIR__ . '/../..' . '/sources/Application/Search/ajaxsearchexception.class.inc.php',
|
||||
|
||||
@@ -36,7 +36,8 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
125
pages/UI.php
125
pages/UI.php
@@ -5,12 +5,13 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\BulkHelper;
|
||||
use Combodo\iTop\Application\Helper\SearchHelper;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\Helper\SynchroReplicaHelper;
|
||||
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
|
||||
use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
@@ -577,18 +578,35 @@ try {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'select_for_modify_all': // Select the list of objects to be modified (bulk modify)
|
||||
UI::OperationSelectForModifyAll($oP);
|
||||
BulkHelper::OperationSelectForModifyAll($oP);
|
||||
break;
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'form_for_modify_all': // Form to modify multiple objects (bulk modify)
|
||||
UI::OperationFormForModifyAll($oP, $oAppContext);
|
||||
BulkHelper::OperationFormForModifyAll($oP, $oAppContext);
|
||||
break;
|
||||
|
||||
case 'form_for_unlink_all': // Form to modify multiple objects (bulk modify)
|
||||
SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'unlink');
|
||||
break;
|
||||
case 'form_for_unlinksynchro_all': // Form to modify multiple objects (bulk modify)
|
||||
SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'unlinksynchro');
|
||||
break;
|
||||
case 'form_for_synchro_all': // Form to modify multiple objects (bulk modify)
|
||||
SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'synchro');
|
||||
break;
|
||||
|
||||
case 'form_for_allowdelete_all': // Form to modify multiple objects (bulk modify)
|
||||
SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'allowdelete');
|
||||
break;
|
||||
case 'form_for_denydelete_all': // Form to modify multiple objects (bulk modify)
|
||||
SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'denydelete');
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'preview_or_modify_all': // Preview or apply bulk modify
|
||||
UI::OperationPreviewOrModifyAll($oP, $oAppContext);
|
||||
BulkHelper::OperationPreviewOrModifyAll($oP, $oAppContext);
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -627,7 +645,7 @@ try {
|
||||
'title' => Dict::S('UI:BulkDeleteTitle'),
|
||||
];
|
||||
$oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_DELETE);
|
||||
DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams);
|
||||
BulkHelper::DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams);
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1337,100 +1355,3 @@ try {
|
||||
$sOperationToLog = $operation ?? 'N/A';
|
||||
IssueLog::Debug('UI.php operation='.$sOperationToLog.', error='.$e->getMessage()."\n".$sErrorStackTrace, LogChannels::CONSOLE);
|
||||
}
|
||||
|
||||
class UI
|
||||
{
|
||||
/**
|
||||
* Operation select_for_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationSelectForModifyAll(iTopWebPage $oP): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$oP->set_title(Dict::S('UI:ModifyAllPageTitle'));
|
||||
$sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
if (empty($sFilter)) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'filter'));
|
||||
}
|
||||
$oFilter = DBObjectSearch::unserialize($sFilter); //TODO : check that the filter is valid
|
||||
// Add user filter
|
||||
$oFilter->UpdateContextFromUser();
|
||||
$oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_MODIFY);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sClassName = MetaModel::GetName($sClass);
|
||||
|
||||
$aDisplayParams = [
|
||||
'icon' => MetaModel::GetClassIcon($sClass, false),
|
||||
'title' => Dict::Format('UI:Modify_ObjectsOf_Class', $sClassName),
|
||||
];
|
||||
DisplayMultipleSelectionForm($oP, $oFilter, 'form_for_modify_all', $oChecker, [], $aDisplayParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation form_for_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
* @param \ApplicationContext $oAppContext
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationFormForModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$sClass = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
// Add user filter
|
||||
$oFullSetFilter->UpdateContextFromUser();
|
||||
$aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
|
||||
$aContext = ['filter' => utils::EscapeHtml($sFilter)];
|
||||
cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, [], $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation preview_or_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
* @param \ApplicationContext $oAppContext
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
|
||||
$oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid
|
||||
// Add user filter
|
||||
$oFilter->UpdateContextFromUser();
|
||||
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$bPreview = utils::ReadParam('preview_mode', '');
|
||||
$sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data');
|
||||
if (empty($sClass) || empty($sSelectedObj)) { // TO DO: check that the class name is valid !
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj'));
|
||||
}
|
||||
$aSelectedObj = explode(',', $sSelectedObj);
|
||||
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
|
||||
$aContext = [
|
||||
'filter' => utils::EscapeHtml($sFilter),
|
||||
'selectObj' => $sSelectedObj,
|
||||
];
|
||||
cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
|
||||
*/
|
||||
class iTopExtensionsMap
|
||||
{
|
||||
private static array $aInstancesByEnvironment = [];
|
||||
|
||||
/**
|
||||
* The list of all discovered extensions
|
||||
* @var array $aExtensions
|
||||
@@ -32,36 +30,25 @@ class iTopExtensionsMap
|
||||
* The list of directories browsed using the ReadDir method when building the map
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $aScannedDirs;
|
||||
protected $aScannedDirs;
|
||||
|
||||
/** @var bool $bHasXmlInstallationFile : false when legacy 1.x package with no installation.xml */
|
||||
protected $bHasXmlInstallationFile = true;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetExtensionsMap(string $sFromEnvironment = ITOP_DEFAULT_ENV, ?string $sAppRootForTests = null): iTopExtensionsMap
|
||||
{
|
||||
if (!is_null($sAppRootForTests)) {
|
||||
return new iTopExtensionsMap($sFromEnvironment, $sAppRootForTests);
|
||||
}
|
||||
|
||||
if (!isset(self::$aInstancesByEnvironment[$sFromEnvironment])) {
|
||||
self::$aInstancesByEnvironment[$sFromEnvironment] = new iTopExtensionsMap($sFromEnvironment);
|
||||
}
|
||||
|
||||
return self::$aInstancesByEnvironment[$sFromEnvironment];
|
||||
}
|
||||
//extension dirs apart from package
|
||||
protected array $aExtraDirs = [];
|
||||
|
||||
/**
|
||||
* The list of all discovered extensions
|
||||
*
|
||||
* @param string $sFromEnvironment The environment to scan
|
||||
* @param string|null $sAppRootForTests
|
||||
* @param array $aExtraDirs extensions dir to scan
|
||||
* @param array $aExtraDirs extensions dir to scan
|
||||
* @param string|null $sAppRootForTests
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return void
|
||||
*/
|
||||
private function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, ?string $sAppRootForTests = null)
|
||||
public function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, array $aExtraDirs = [], ?string $sAppRootForTests = null)
|
||||
{
|
||||
$this->aExtensions = [];
|
||||
$this->aExtensionsByCode = [];
|
||||
@@ -69,6 +56,18 @@ class iTopExtensionsMap
|
||||
|
||||
$sAppRoot = $sAppRootForTests ?? APPROOT;
|
||||
$this->ScanDisk($sFromEnvironment, $sAppRoot);
|
||||
|
||||
$this->aExtraDirs = $aExtraDirs;
|
||||
if (is_dir($sAppRoot.'extensions')) {
|
||||
$this->aExtraDirs [] = $sAppRoot.'extensions';
|
||||
}
|
||||
if (is_dir($sAppRoot.'data/'.$sFromEnvironment.'-modules')) {
|
||||
$this->aExtraDirs [] = $sAppRoot.'data/'.$sFromEnvironment.'-modules';
|
||||
}
|
||||
|
||||
foreach ($aExtraDirs as $sDir) {
|
||||
$this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
|
||||
}
|
||||
$this->CheckDependencies($sAppRoot);
|
||||
}
|
||||
|
||||
@@ -84,15 +83,13 @@ class iTopExtensionsMap
|
||||
if (!$this->ReadInstallationWizard($sAppRoot.'/datamodels/2.x')) {
|
||||
$this->bHasXmlInstallationFile = false;
|
||||
//no installation xml found in 2.x: let's read all extensions in 2.x first
|
||||
if (!$this->ReadDir($sAppRoot.'datamodels/2.x', iTopExtension::SOURCE_WIZARD, bIsRootDir: true)) {
|
||||
if (!$this->ReadDir($sAppRoot.'datamodels/2.x', iTopExtension::SOURCE_WIZARD)) {
|
||||
//nothing found in 2.x : fallback read in 1.x (flat structure)
|
||||
$this->ReadDir($sAppRoot.'datamodels/1.x', iTopExtension::SOURCE_WIZARD, bIsRootDir: true);
|
||||
$this->ReadDir($sAppRoot.'datamodels/1.x', iTopExtension::SOURCE_WIZARD);
|
||||
}
|
||||
} else {
|
||||
$this->aScannedDirs[] = $sAppRoot.'datamodels/2.x';
|
||||
}
|
||||
$this->ReadDir($sAppRoot.'extensions', iTopExtension::SOURCE_MANUAL, bIsRootDir: true);
|
||||
$this->ReadDir($sAppRoot.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE, bIsRootDir: true);
|
||||
$this->ReadDir($sAppRoot.'extensions', iTopExtension::SOURCE_MANUAL);
|
||||
$this->ReadDir($sAppRoot.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,14 +274,14 @@ class iTopExtensionsMap
|
||||
*
|
||||
* @return boolean false if we cannot open dir
|
||||
*/
|
||||
protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null, bool $bIsRootDir = false)
|
||||
protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
|
||||
{
|
||||
if (!is_readable($sSearchDir)) {
|
||||
return false;
|
||||
}
|
||||
$hDir = opendir($sSearchDir);
|
||||
if ($hDir !== false) {
|
||||
if ($bIsRootDir) {
|
||||
if ($sParentExtensionId == null) {
|
||||
// We're not recursing, let's add the directory to the list of scanned dirs
|
||||
$this->aScannedDirs[] = $sSearchDir;
|
||||
}
|
||||
@@ -481,8 +478,16 @@ class iTopExtensionsMap
|
||||
*/
|
||||
protected function CheckDependencies(string $sAppRoot)
|
||||
{
|
||||
$aSearchDirs = [];
|
||||
|
||||
if (is_dir($sAppRoot.'/datamodels/2.x')) {
|
||||
$aSearchDirs[] = $sAppRoot.'/datamodels/2.x';
|
||||
} elseif (is_dir($sAppRoot.'/datamodels/1.x')) {
|
||||
$aSearchDirs[] = $sAppRoot.'/datamodels/1.x';
|
||||
}
|
||||
$aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs);
|
||||
try {
|
||||
ModuleDiscovery::GetModulesOrderedByDependencies($this->aScannedDirs, true);
|
||||
ModuleDiscovery::GetModulesOrderedByDependencies($aSearchDirs, true);
|
||||
} catch (MissingDependencyException $e) {
|
||||
// Some modules have missing dependencies
|
||||
// Let's check what is the impact at the "extensions" level
|
||||
@@ -531,7 +536,7 @@ class iTopExtensionsMap
|
||||
public function GetAllExtensionsToDisplayInSetup(bool $bKeepExtensionsHavingMissingDependencies = false, bool $bRemoteExtensionsShouldBeMandatory = true): array
|
||||
{
|
||||
// all extensions are loaded at first screen when no installation xml: flat display
|
||||
// otherwhile wizard screen displays choice screens along extension tree (cf installation.xml)
|
||||
//otherwhile wizard screen displays choice screens along extension tree (cf installation.xml)
|
||||
$aRes = [];
|
||||
foreach ($this->GetAllExtensionsWithPreviouslyInstalled() as $oExtension) {
|
||||
/** @var \iTopExtension $oExtension */
|
||||
@@ -591,7 +596,7 @@ class iTopExtensionsMap
|
||||
{
|
||||
$oExtension = $this->GetFromExtensionCode($sExtensionCode);
|
||||
if (!is_null($oExtension)) {
|
||||
$oExtension->MarkAsChosen($bMark);
|
||||
$oExtension->bMarkedAsChosen = $bMark;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +609,7 @@ class iTopExtensionsMap
|
||||
{
|
||||
$oExtension = $this->GetFromExtensionCode($sExtensionCode);
|
||||
if (!is_null($oExtension)) {
|
||||
return $oExtension->IsMarkedAsChosen();
|
||||
return $oExtension->bMarkedAsChosen;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -631,9 +636,8 @@ class iTopExtensionsMap
|
||||
public function GetChoices()
|
||||
{
|
||||
$aResult = [];
|
||||
/** @var \iTopExtension $oExtension */
|
||||
foreach ($this->aExtensions as $oExtension) {
|
||||
if ($oExtension->IsMarkedAsChosen()) {
|
||||
if ($oExtension->bMarkedAsChosen) {
|
||||
$aResult[] = $oExtension;
|
||||
}
|
||||
}
|
||||
@@ -741,6 +745,11 @@ class iTopExtensionsMap
|
||||
return array_merge($aDbChoices, $aAddedExtensions);
|
||||
}
|
||||
|
||||
public function GetExtraDirs(): array
|
||||
{
|
||||
return $this->aExtraDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the given module name is "chosen" since it is part of a "chosen" extension (in the specified source dir)
|
||||
* @param string $sModuleNameToFind
|
||||
@@ -750,17 +759,13 @@ class iTopExtensionsMap
|
||||
public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSourceOnly = iTopExtension::SOURCE_REMOTE)
|
||||
{
|
||||
foreach ($this->GetAllExtensions() as $oExtension) {
|
||||
if ($oExtension->IsMarkedAsChosen() &&
|
||||
if (($oExtension->sSource == $sInSourceOnly) &&
|
||||
($oExtension->bMarkedAsChosen == true) &&
|
||||
(array_key_exists($sModuleNameToFind, $oExtension->aModuleVersion))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetScannedModulesRootDirs(): array
|
||||
{
|
||||
return $this->aScannedDirs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,22 @@
|
||||
|
||||
namespace Combodo\iTop\Setup\FeatureRemoval;
|
||||
|
||||
use Config;
|
||||
use iTopExtensionsMap;
|
||||
use RunTimeEnvironment;
|
||||
use SetupUtils;
|
||||
|
||||
class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
|
||||
{
|
||||
protected array $aExtensionsToRemoveByCode;
|
||||
protected array $aExtensionCodesToAddByCode;
|
||||
|
||||
/**
|
||||
* Toolset for building a run-time environment
|
||||
*
|
||||
* @param string $sSourceEnv: environment from which setup is inspired to simulate extension removal and usee CompileFrom...
|
||||
*/
|
||||
public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToAdd = [], array $aExtensionCodesToRemove = [])
|
||||
public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToRemove = [])
|
||||
{
|
||||
parent::__construct($sSourceEnv, false);
|
||||
$this->aExtensionCodesToAddByCode = $aExtensionCodesToAdd;
|
||||
$this->aExtensionsToRemoveByCode = $aExtensionCodesToRemove;
|
||||
$this->Prepare($sSourceEnv, $this->sBuildEnv);
|
||||
}
|
||||
@@ -36,18 +34,13 @@ class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
|
||||
SetupUtils::copydir(APPROOT."/data/$sSourceEnv-modules", APPROOT."/data/$sBuildEnv-modules");
|
||||
SetupUtils::copydir(APPROOT."/conf/$sSourceEnv", APPROOT."/conf/$sBuildEnv");
|
||||
|
||||
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
list($aExtraDirs, ) = $this->GetDirsToCompile($sSourceDir, $sSourceEnv);
|
||||
$this->DeclareExtensionAsRemoved($this->aExtensionsToRemoveByCode);
|
||||
}
|
||||
|
||||
$this->InitExtensionMap($aExtraDirs, $oSourceConfig);
|
||||
$this->GetExtensionMap()->DeclareExtensionAsRemoved($this->aExtensionsToRemoveByCode);
|
||||
|
||||
foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) {
|
||||
if (array_key_exists($oExtension->sCode, $this->aExtensionCodesToAddByCode)) {
|
||||
$oExtension->MarkAsChosen();
|
||||
}
|
||||
}
|
||||
private function DeclareExtensionAsRemoved(array $aExtensionCodes): void
|
||||
{
|
||||
$oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv);
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
|
||||
}
|
||||
|
||||
public function Cleanup(): void
|
||||
|
||||
@@ -17,11 +17,6 @@ class SetupAudit extends AbstractSetupAudit
|
||||
$this->sEnvAfter = $sEnvAfter ?? "$sEnvBefore-build";
|
||||
}
|
||||
|
||||
public function GetEnvAfter(): string
|
||||
{
|
||||
return $this->sEnvAfter;
|
||||
}
|
||||
|
||||
public function ComputeClasses(): void
|
||||
{
|
||||
if ($this->bClassesInitialized) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once(APPROOT.'/setup/parameters.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/setup/modulediscovery.class.inc.php');
|
||||
@@ -57,7 +60,7 @@ class iTopExtension
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bMarkedAsChosen;
|
||||
public $bMarkedAsChosen;
|
||||
/**
|
||||
* If null, check if at least one module cannot be uninstalled
|
||||
* @var bool|null
|
||||
@@ -180,14 +183,4 @@ class iTopExtension
|
||||
{
|
||||
return $this->sSource === self::SOURCE_REMOTE;
|
||||
}
|
||||
|
||||
public function IsMarkedAsChosen(): bool
|
||||
{
|
||||
return $this->bMarkedAsChosen;
|
||||
}
|
||||
|
||||
public function MarkAsChosen(bool $bMarkedAsChosen = true): void
|
||||
{
|
||||
$this->bMarkedAsChosen = $bMarkedAsChosen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,37 +71,15 @@ class RunTimeEnvironment
|
||||
return $this->oExtensionsMap;
|
||||
}
|
||||
|
||||
protected function GetDirsToCompile(string $sSourceDir, string $sSourceEnv): array
|
||||
public function InitExtensionMap($aExtraDirs, $oSourceConfig)
|
||||
{
|
||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
||||
if (!is_dir($sSourceDirFull)) {
|
||||
throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)");
|
||||
}
|
||||
$aDirsToCompile = [$sSourceDirFull];
|
||||
if (is_dir(APPROOT.'extensions')) {
|
||||
$aDirsToCompile[] = APPROOT.'extensions';
|
||||
}
|
||||
$sExtraDir = utils::GetDataPath().$this->sBuildEnv.'-modules/';
|
||||
if (is_dir($sExtraDir)) {
|
||||
$aDirsToCompile[] = $sExtraDir;
|
||||
}
|
||||
|
||||
$aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile);
|
||||
$aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs);
|
||||
return [$aExtraDirs, $aDirsToCompile];
|
||||
}
|
||||
|
||||
public function InitExtensionMap(array $aExtraDirs, Config $oSourceConfig)
|
||||
{
|
||||
if (is_null($this->oExtensionsMap)) {
|
||||
// Actually read the modules available for the build environment,
|
||||
// but get the selection from the source environment and finally
|
||||
// mark as (automatically) chosen all the "remote" modules present in the
|
||||
// build environment (data/<build-env>-modules)
|
||||
// The actual choices will be recorded by RecordInstallation below
|
||||
$this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sBuildEnv);
|
||||
$this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig);
|
||||
}
|
||||
// Actually read the modules available for the build environment,
|
||||
// but get the selection from the source environment and finally
|
||||
// mark as (automatically) chosen all the "remote" modules present in the
|
||||
// build environment (data/<build-env>-modules)
|
||||
// The actual choices will be recorded by RecordInstallation below
|
||||
$this->oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv, $aExtraDirs);
|
||||
$this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,7 +168,7 @@ class RunTimeEnvironment
|
||||
MetaModel::Startup($oConfig, $bModelOnly, $bUseCache, false, $this->sBuildEnv);
|
||||
|
||||
if ($this->oExtensionsMap === null) {
|
||||
$this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sBuildEnv);
|
||||
$this->oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,12 +444,32 @@ class RunTimeEnvironment
|
||||
|
||||
/**
|
||||
* Get the installed modules (only the installed ones)
|
||||
* @return \MFModule[]
|
||||
*/
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir): array
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
|
||||
{
|
||||
list($aExtraDirs, $aDirsToCompile) = $this->GetDirsToCompile($sSourceDir, $sSourceEnv);
|
||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
||||
if (!is_dir($sSourceDirFull)) {
|
||||
throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)");
|
||||
}
|
||||
$aDirsToCompile = [$sSourceDirFull];
|
||||
if (is_dir(APPROOT.'extensions')) {
|
||||
$aDirsToCompile[] = APPROOT.'extensions';
|
||||
}
|
||||
$sExtraDir = utils::GetDataPath().$this->sBuildEnv.'-modules/';
|
||||
if (is_dir($sExtraDir)) {
|
||||
$aDirsToCompile[] = $sExtraDir;
|
||||
}
|
||||
|
||||
$aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile);
|
||||
$aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs);
|
||||
|
||||
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
|
||||
|
||||
// Actually read the modules available for the build environment,
|
||||
// but get the selection from the source environment and finally
|
||||
// mark as (automatically) chosen all the "remote" modules present in the
|
||||
// build environment (data/<build-env>-modules)
|
||||
// The actual choices will be recorded by RecordInstallation below
|
||||
$this->InitExtensionMap($aExtraDirs, $oSourceConfig);
|
||||
$this->GetExtensionMap()->LoadChoicesFromDatabase($oSourceConfig);
|
||||
foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) {
|
||||
@@ -479,10 +477,12 @@ class RunTimeEnvironment
|
||||
$this->GetExtensionMap()->MarkAsChosen($oExtension->sCode);
|
||||
}
|
||||
}
|
||||
|
||||
$aModulesToLoad = $this->GetModulesToLoad($this->sFinalEnv, $aDirsToCompile);
|
||||
$aAvailableModules = $this->AnalyzeInstallation($oSourceConfig, $aDirsToCompile, true, $aModulesToLoad);
|
||||
|
||||
// Do load the required modules
|
||||
//
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
|
||||
$aRet = [];
|
||||
@@ -1419,6 +1419,8 @@ class RunTimeEnvironment
|
||||
* @param array $aSelectedExtensionCodes
|
||||
* @param array $aRemovedExtensionCodes
|
||||
* @param array $aSelectedModules
|
||||
* @param string $sSourceDir
|
||||
* @param string $sExtensionDir
|
||||
* @param boolean $bUseSymbolicLinks
|
||||
*
|
||||
* @return void
|
||||
@@ -1426,13 +1428,33 @@ class RunTimeEnvironment
|
||||
* @throws \CoreException
|
||||
*
|
||||
*/
|
||||
public function DoCompile(array $aSelectedExtensionCodes, array $aRemovedExtensionCodes, array $aSelectedModules, bool $bUseSymbolicLinks = false): void
|
||||
public function DoCompile(array $aSelectedExtensionCodes, array $aRemovedExtensionCodes, array $aSelectedModules, string $sSourceDir, string $sExtensionDir, bool $bUseSymbolicLinks = false): void
|
||||
{
|
||||
SetupLog::Info('Compiling data model.');
|
||||
|
||||
$sEnvironment = $this->sBuildEnv;
|
||||
$sBuildPath = $this->GetBuildDir();
|
||||
|
||||
$sSourcePath = APPROOT.$sSourceDir;
|
||||
$aDirsToScan = [$sSourcePath];
|
||||
$sExtensionsPath = APPROOT.$sExtensionDir;
|
||||
if (is_dir($sExtensionsPath)) {
|
||||
// if the extensions dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtensionsPath;
|
||||
}
|
||||
$sExtraPath = APPROOT.'/data/'.$sEnvironment.'-modules/';
|
||||
if (is_dir($sExtraPath)) {
|
||||
// if the extra dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtraPath;
|
||||
}
|
||||
|
||||
if (!is_dir($sSourcePath)) {
|
||||
$sErrorMessage = "Failed to find the source directory '$sSourcePath', please check the rights of the web server";
|
||||
$e = new CoreException($sErrorMessage);
|
||||
IssueLog::Exception($sErrorMessage, $e);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!is_dir($sBuildPath)) {
|
||||
if (!mkdir($sBuildPath)) {
|
||||
$sErrorMessage = "Failed to create directory '$sBuildPath', please check the rights of the web server";
|
||||
@@ -1449,7 +1471,7 @@ class RunTimeEnvironment
|
||||
SetupUtils::tidydir($sBuildPath);
|
||||
}
|
||||
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->GetFinalEnv());
|
||||
$oExtensionsMap = new iTopExtensionsMap($this->GetFinalEnv(), $aDirsToScan);
|
||||
// Removed modules are stored as static for FindModules()
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes);
|
||||
|
||||
@@ -1459,7 +1481,7 @@ class RunTimeEnvironment
|
||||
$aNoCodeExtensionLabelsThatBreakSetup = [];
|
||||
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
|
||||
if (in_array($oExtension->sCode, $aSelectedExtensionCodes)) {
|
||||
$oExtension->MarkAsChosen();
|
||||
$oExtension->bMarkedAsChosen = true;
|
||||
}
|
||||
|
||||
if (empty($oExtension->sCode)) {
|
||||
@@ -1471,7 +1493,7 @@ class RunTimeEnvironment
|
||||
$aNoCodeExtensionSourceDirs [$sExtensionLabel] = $oExtension->sSourceDir;
|
||||
}
|
||||
|
||||
if ($oExtension->IsMarkedAsChosen()) {
|
||||
if ($oExtension->bMarkedAsChosen) {
|
||||
$aNoCodeExtensionLabelsThatBreakSetup[] = $sExtensionLabel;
|
||||
$bSetupFailure = true;
|
||||
}
|
||||
@@ -1489,7 +1511,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
|
||||
$oFactory = new ModelFactory($oExtensionsMap->GetScannedModulesRootDirs());
|
||||
$oFactory = new ModelFactory($aDirsToScan);
|
||||
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
$oFactory->LoadModule($oDictModule);
|
||||
@@ -1678,29 +1700,21 @@ class RunTimeEnvironment
|
||||
}
|
||||
|
||||
$aExtensionDirs = [];
|
||||
$aFromSelectedExtensionModules = [];
|
||||
foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) {
|
||||
if ($oExtension->IsMarkedAsChosen() && is_dir($oExtension->sSourceDir)) {
|
||||
if ($oExtension->bMarkedAsChosen && is_dir($oExtension->sSourceDir)) {
|
||||
$aExtensionDirs [] = $oExtension->sSourceDir;
|
||||
$aFromSelectedExtensionModules = array_merge($aFromSelectedExtensionModules, $oExtension->aModules);
|
||||
}
|
||||
}
|
||||
|
||||
SetupLog::Info(__METHOD__, null, ['ext_dirs' => $aExtensionDirs]);
|
||||
$aModuleIdsToLoad = InstallationChoicesToModuleConverter::GetInstance()->GetModules($aChoices, $aSearchDirs, $sInstallFilePath, $aExtensionDirs);
|
||||
$aModulesToLoad = [];
|
||||
|
||||
foreach ($aModuleIdsToLoad as $sModuleId) {
|
||||
$oModule = new Module($sModuleId);
|
||||
$sModuleName = $oModule->GetModuleName();
|
||||
$aModulesToLoad[] = $sModuleName;
|
||||
}
|
||||
|
||||
foreach ($aFromSelectedExtensionModules as $sModuleName) {
|
||||
if (! in_array($sModuleName, $aModulesToLoad)) {
|
||||
$aModulesToLoad[] = $sModuleName;
|
||||
}
|
||||
}
|
||||
return $aModulesToLoad;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ class DataAuditSequencer extends StepSequencer
|
||||
$aSelectedExtensionCodes,
|
||||
$aRemovedExtensionCodes,
|
||||
$aSelectedModules,
|
||||
$sSourceDir,
|
||||
$sExtensionDir,
|
||||
$bUseSymbolicLinks
|
||||
);
|
||||
return $this->ComputeNextStep($sStep);
|
||||
|
||||
@@ -1602,7 +1602,7 @@ JS
|
||||
}
|
||||
$oProductionEnv = new RunTimeEnvironment($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV));
|
||||
$aRemovedExtensionCodes = json_decode($oWizard->GetParameter('removed_extensions'), true) ?? [];
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV));
|
||||
$oExtensionsMap = new iTopExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV), $aDirsToScan);
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes);
|
||||
|
||||
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad);
|
||||
|
||||
@@ -79,7 +79,7 @@ class InstallationFileService
|
||||
public function GetItopExtensionsMap(): iTopExtensionsMap
|
||||
{
|
||||
if (is_null($this->oItopExtensionsMap)) {
|
||||
$this->oItopExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sTargetEnvironment);
|
||||
$this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment);
|
||||
}
|
||||
return $this->oItopExtensionsMap;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ class InstallationFileService
|
||||
public function ProcessDefaultModules(): void
|
||||
{
|
||||
$sProductionModuleDir = APPROOT.'data/'.$this->sTargetEnvironment.'-modules/';
|
||||
$oConfig = new Config(APPCONF.$this->sTargetEnvironment.'/'.ITOP_CONFIG_FILE);
|
||||
$oConfig = new Config(APPCONF.$this->sTargetEnvironment.'/'.ITOP_CONFIG_FILE, false);
|
||||
|
||||
$aAvailableModules = $this->GetProductionEnv()->AnalyzeInstallation($oConfig, $this->GetExtraDirs());
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class WizStepModulesChoice extends AbstractWizStepInstall
|
||||
{
|
||||
parent::__construct($oWizard, $sCurrentState);
|
||||
$this->bChoicesFromDatabase = false;
|
||||
$this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$this->oExtensionsMap = new iTopExtensionsMap();
|
||||
$sPreviousSourceDir = $this->oWizard->GetParameter('previous_version_dir', '');
|
||||
$sConfigPath = null;
|
||||
if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/'.ITOP_DEFAULT_ENV.'/config-itop.php')) {
|
||||
|
||||
179
sources/Application/Helper/BulkHelper.php
Normal file
179
sources/Application/Helper/BulkHelper.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Application\Helper;
|
||||
|
||||
use ActionChecker;
|
||||
use ApplicationContext;
|
||||
use ApplicationException;
|
||||
use cmdbAbstractObject;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use DBObjectSearch;
|
||||
use DBSearch;
|
||||
use Dict;
|
||||
use DisplayBlock;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
class BulkHelper
|
||||
{
|
||||
/**
|
||||
* Operation select_for_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationSelectForModifyAll(iTopWebPage $oP, $sTitleTab = 'UI:ModifyAllPageTitle', $sTitleCode = 'UI:Modify_ObjectsOf_Class', $sNextOperation = 'form_for_modify_all'): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$oP->set_title(Dict::S($sTitleTab));
|
||||
$sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
if (empty($sFilter)) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'filter'));
|
||||
}
|
||||
$oFilter = DBObjectSearch::unserialize($sFilter); //TODO : check that the filter is valid
|
||||
// Add user filter
|
||||
$oFilter->UpdateContextFromUser();
|
||||
$oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_MODIFY);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sClassName = MetaModel::GetName($sClass);
|
||||
|
||||
$aDisplayParams = [
|
||||
'icon' => MetaModel::GetClassIcon($sClass, false),
|
||||
'title' => Dict::Format($sTitleCode, $sClassName),
|
||||
];
|
||||
self::DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, [], $aDisplayParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation form_for_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
* @param \ApplicationContext $oAppContext
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationFormForModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$sClass = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
// Add user filter
|
||||
$oFullSetFilter->UpdateContextFromUser();
|
||||
$aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
$sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink();
|
||||
$aContext = ['filter' => utils::EscapeHtml($sFilter)];
|
||||
cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, [], $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation preview_or_modify_all
|
||||
*
|
||||
* @param iTopWebPage $oP
|
||||
* @param \ApplicationContext $oAppContext
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
|
||||
$oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid
|
||||
// Add user filter
|
||||
$oFilter->UpdateContextFromUser();
|
||||
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$bPreview = utils::ReadParam('preview_mode', '');
|
||||
$sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data');
|
||||
if (empty($sClass) || empty($sSelectedObj)) { // TO DO: check that the class name is valid !
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj'));
|
||||
}
|
||||
$aSelectedObj = explode(',', $sSelectedObj);
|
||||
$sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink();
|
||||
$aContext = [
|
||||
'filter' => utils::EscapeHtml($sFilter),
|
||||
'selectObj' => $sSelectedObj,
|
||||
];
|
||||
cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a form (checkboxes) to select the objects for which to apply a given action
|
||||
* Only the objects for which the action is valid can be checked. By default all valid objects are checked
|
||||
*
|
||||
* @param WebPage $oP WebPage The page for output
|
||||
* @param \DBSearch $oFilter DBSearch The filter that defines the list of objects
|
||||
* @param string $sNextOperation string The next operation (code) to be executed when the form is submitted
|
||||
* @param ActionChecker $oChecker ActionChecker The helper class/instance used to check for which object the action is valid
|
||||
* @param array $aExtraFormParams
|
||||
* @param array $aDisplayParams
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 $aDisplayParams parameter
|
||||
*
|
||||
*/
|
||||
public static function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sNextOperation, ActionChecker $oChecker, array $aExtraFormParams = [], array $aDisplayParams = [])
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iBulkActionAllowed = $oChecker->IsAllowed();
|
||||
$aExtraParams = ['selection_type' => 'multiple', 'selection_mode' => true, 'display_limit' => false, 'menu' => false];
|
||||
if ($iBulkActionAllowed == UR_ALLOWED_DEPENDS) {
|
||||
$aExtraParams['selection_enabled'] = $oChecker->GetAllowedIDs();
|
||||
} else {
|
||||
if (UR_ALLOWED_NO) {
|
||||
throw new ApplicationException(Dict::Format('UI:ActionNotAllowed'));
|
||||
}
|
||||
}
|
||||
|
||||
$oForm = new Form();
|
||||
$oForm->SetAction(utils::GetAbsoluteUrlAppRoot().'pages/UI.php');
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sNextOperation));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $oFilter->GetClass()));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', utils::HtmlEntities($oFilter->Serialize())));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId()));
|
||||
foreach ($aExtraFormParams as $sName => $sValue) {
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sName, $sValue));
|
||||
}
|
||||
$oForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
//by default all the elements are selected
|
||||
$aExtraParams['selectionMode'] = 'negative';
|
||||
if (array_key_exists('icon', $aDisplayParams) || array_key_exists('title', $aDisplayParams)) {
|
||||
$aExtraParams['surround_with_panel'] = true;
|
||||
if (array_key_exists('icon', $aDisplayParams)) {
|
||||
$aExtraParams['panel_icon'] = $aDisplayParams['icon'];
|
||||
}
|
||||
if (array_key_exists('title', $aDisplayParams)) {
|
||||
$aExtraParams['panel_title'] = $aDisplayParams['title'];
|
||||
}
|
||||
}
|
||||
$oForm->AddSubBlock($oDisplayBlock->GetDisplay($oP, 1, $aExtraParams));
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar--button');
|
||||
$oForm->AddSubBlock($oToolbarButtons);
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel')->SetOnClickJsCode('window.history.back()'));
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), 'next', Dict::S('UI:Button:Next'), true));
|
||||
|
||||
$oP->AddUiBlock($oForm);
|
||||
}
|
||||
}
|
||||
112
sources/Application/Helper/SynchroReplicaHelper.php
Normal file
112
sources/Application/Helper/SynchroReplicaHelper.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Application\Helper;
|
||||
|
||||
use ApplicationContext;
|
||||
use ApplicationException;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use DBObjectSearch;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use iTopWebPage;
|
||||
use MetaModel;
|
||||
use SynchroReplica;
|
||||
use utils;
|
||||
|
||||
class SynchroReplicaHelper
|
||||
{
|
||||
public static function OperationUnlinkAll(iTopWebPage $oP, ApplicationContext $oAppContext, $sOperation = 'unlink'): void
|
||||
{
|
||||
$oP->DisableBreadCrumb();
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$sFilter = utils::ReadPostedParam('filter', '', 'raw_data');
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
// Add user filter
|
||||
$oFullSetFilter->UpdateContextFromUser();
|
||||
$aSelectObject = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
if (empty($sClass) || empty($aSelectObject)) { // TO DO: check that the class name is valid !
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]'));
|
||||
}
|
||||
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
|
||||
$aHeaders = [
|
||||
'object' => ['label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')],
|
||||
'status' => [
|
||||
'label' => Dict::S('UI:BulkModifyStatus'),
|
||||
'description' => Dict::S('UI:BulkModifyStatus+'),
|
||||
],
|
||||
'errors' => [
|
||||
'label' => Dict::S('UI:BulkModifyErrors'),
|
||||
'description' => Dict::S('UI:BulkModifyErrors+'),
|
||||
],
|
||||
];
|
||||
$aRows = [];
|
||||
|
||||
$sHeaderTitle = Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectObject), MetaModel::GetName($sClass));
|
||||
$sClassIcon = MetaModel::GetClassIcon($sClass, false);
|
||||
|
||||
// Not in preview mode, do the update for real
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
|
||||
if (!utils::IsTransactionValid($sTransactionId, false)) {
|
||||
throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated'));
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
|
||||
// Avoid too many events
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
$aErrors = [];
|
||||
foreach ($aSelectObject as $iId) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
/** @var \cmdbAbstractObject $oObj */
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$bResult = true;
|
||||
try {
|
||||
if (in_array($sOperation, ['unlink', 'unlinksynchro'])) {
|
||||
$oReplica->UnLink();
|
||||
}
|
||||
if (in_array($sOperation, ['synchro', 'unlinksynchro'])) {
|
||||
$oStatLog = $oReplica->ReSynchro();
|
||||
$aErrors = $oStatLog->GetTraces();
|
||||
}
|
||||
if ($sOperation == 'allowdelete') {
|
||||
$oReplica->Set('status_dest_creator', 1);
|
||||
$oReplica->DBUpdate();
|
||||
}
|
||||
if ($sOperation == 'denydelete') {
|
||||
$oReplica->Set('status_dest_creator', 0);
|
||||
$oReplica->DBUpdate();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$bResult = false;
|
||||
$aErrors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped');
|
||||
|
||||
$aErrorsToDisplay = array_map(function ($sError) {
|
||||
return utils::HtmlEntities($sError);
|
||||
}, $aErrors);
|
||||
$aRows[] = [
|
||||
'object' => $oReplica->GetHyperlink(),
|
||||
'status' => $sStatus,
|
||||
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
|
||||
];
|
||||
}
|
||||
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
$oTable = DataTableUIBlockFactory::MakeForForm('BulkModify', $aHeaders, $aRows);
|
||||
$oTable->AddOption("bFullscreen", true);
|
||||
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, '');
|
||||
$oPanel->SetIcon($sClassIcon);
|
||||
$oPanel->SetTitle($sHeaderTitle);
|
||||
$oPanel->AddCSSClass('ibo-datatable-panel');
|
||||
$oPanel->AddSubBlock($oTable);
|
||||
|
||||
$oP->AddUiBlock($oPanel);
|
||||
$oP->AddSubBlock(ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done')))->SetOnClickJsCode("window.location.href='$sCancelUrl'")->AddCSSClass('mt-5');
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ class Toggler extends Input
|
||||
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/input/input-toggler';
|
||||
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/input/input-toggler';
|
||||
|
||||
public function __construct(?string $sId = null)
|
||||
protected string $sTooltip = '';
|
||||
public function __construct(?string $sId = null, string $sTooltip = '')
|
||||
{
|
||||
parent::__construct($sId);
|
||||
$this->SetType('checkbox');
|
||||
$this->SetTooltip($sTooltip);
|
||||
}
|
||||
|
||||
public function SetIsToggled(bool $bIsToggled): static
|
||||
@@ -33,4 +35,13 @@ class Toggler extends Input
|
||||
{
|
||||
return $this->IsChecked();
|
||||
}
|
||||
|
||||
public function GetTooltip(): string
|
||||
{
|
||||
return $this->sTooltip;
|
||||
}
|
||||
public function SetTooltip($sTooltip): void
|
||||
{
|
||||
$this->sTooltip = $sTooltip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,13 +182,14 @@ class ExtensionDetails extends UIContentBlock
|
||||
$sName = 'aSelectedExtensions['.$this->GetCode().']';
|
||||
$this->oToggler = new Toggler();
|
||||
$this->oToggler->SetName($sName);
|
||||
$this->oToggler->SetTooltip(Dict::Format('UI:Layout:ExtensionsDetails:TogglerTooltip', $this->GetLabel()));
|
||||
$this->oToggler->AddCSSClass('toggler-install');
|
||||
}
|
||||
|
||||
protected function InitializePopoverMenu()
|
||||
{
|
||||
$this->oPopoverMenu = new PopoverMenu();
|
||||
$oPopoverOpenButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Layout:ExtensionsDetails:MoreActions'));
|
||||
$oPopoverOpenButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Layout:ExtensionsDetails:MoreActions'), 'dropdown-menu-'.$this->GetCode());
|
||||
$this->oPopoverMenu->SetTogglerFromBlock($oPopoverOpenButton);
|
||||
$this->oMoreActions = new UIContentBlock();
|
||||
$this->oMoreActions->AddSubBlock($this->oPopoverMenu);
|
||||
|
||||
@@ -1794,9 +1794,11 @@ JS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bReturnOutput
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function output_dict_entries()
|
||||
protected function output_dict_entries($bReturnOutput = false)
|
||||
{
|
||||
if ($this->sContentType != 'text/plain' && $this->sContentType != 'application/json' && $this->sContentType != 'application/javascript') {
|
||||
/** @var \iBackofficeDictEntriesExtension $oExtensionInstance */
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace Combodo\iTop\Controller;
|
||||
|
||||
use Combodo\iTop\Application\WebPage\AjaxPage;
|
||||
use ApplicationContext;
|
||||
use ApplicationMenu;
|
||||
use AttributeLinkedSet;
|
||||
@@ -20,8 +21,7 @@ use CMDBObjectSet;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\AjaxPage;
|
||||
use Combodo\iTop\Application\WebPage\JsonPage;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
@@ -34,6 +34,7 @@ use FunctionExpression;
|
||||
use IssueLog;
|
||||
use iTopExtension;
|
||||
use iTopExtensionsMap;
|
||||
use Combodo\iTop\Application\WebPage\JsonPage;
|
||||
use LogChannels;
|
||||
use MetaModel;
|
||||
use ormSet;
|
||||
@@ -962,7 +963,7 @@ EOF
|
||||
$oPage->add('<ul style="margin: 0;">');
|
||||
|
||||
require_once(APPROOT.'setup/extensionsmap.class.inc.php');
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
|
||||
$aChoices = $oExtensionsMap->GetChoices();
|
||||
foreach ($aChoices as $oExtension) {
|
||||
|
||||
@@ -114,4 +114,8 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\BulkHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
@@ -41,26 +44,26 @@ try {
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'oql':
|
||||
$iSourceId = utils::ReadParam('datasource', null);
|
||||
if ($iSourceId != null) {
|
||||
$oSource = MetaModel::GetObject(SynchroDataSource::class, $iSourceId);
|
||||
$oBackButton = ButtonUIBlockFactory::MakeLinkNeutral(ApplicationContext::MakeObjectUrl(SynchroDataSource::class, $iSourceId), Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetName()), 'fas fa-chevron-left');
|
||||
$oP->AddUiBlock($oBackButton);
|
||||
$oP->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::Format('Core:SynchroReplica:ListOfReplicas', $oSource->GetName())));
|
||||
}
|
||||
|
||||
$sOQL = utils::ReadParam('oql', null, false, 'raw_data');
|
||||
if ($sOQL == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql'));
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oBlock1 = new DisplayBlock($oFilter, 'search', false, ['menu' => false, 'table_id' => '1']);
|
||||
$oBlock1 = new DisplayBlock($oFilter, 'search', false, ['menu' => true, 'table_id' => '1']);
|
||||
$oBlock1->Display($oP, 0);
|
||||
$oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>');
|
||||
$iSourceId = utils::ReadParam('datasource', null);
|
||||
if ($iSourceId != null) {
|
||||
$oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId);
|
||||
$oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>');
|
||||
}
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, ['menu' => false]);
|
||||
$oBlock->Display($oP, 1);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
@@ -69,6 +72,81 @@ try {
|
||||
$sDelete = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?'.$_SERVER['QUERY_STRING'];
|
||||
header("Location: $sDelete");
|
||||
break;
|
||||
|
||||
case 'unlinksynchro':
|
||||
$iId = utils::ReadParam('id', null);
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oReplica->UnLink();
|
||||
|
||||
$oStatLog = $oReplica->ReSynchro();
|
||||
$oP->add(implode('<br>', $oStatLog->GetTraces()));
|
||||
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'unlink':
|
||||
$iId = utils::ReadParam('id', null);
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oReplica->UnLink();
|
||||
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'synchro':
|
||||
$iId = utils::ReadParam('id', null);
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oStatLog = $oReplica->ReSynchro();
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'allowdelete':
|
||||
$iId = utils::ReadParam('id', null);
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oStatLog = $oReplica->Set('status_dest_creator', 1);
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'denydelete': // Select the list of objects to be modified (bulk modify)
|
||||
$iId = utils::ReadParam('id', null);
|
||||
if ($iId == null) {
|
||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||
}
|
||||
$oReplica = MetaModel::GetObject(SynchroReplica::class, $iId);
|
||||
$oStatLog = $oReplica->Set('status_dest_creator', 0);
|
||||
$oReplica->DisplayDetails($oP);
|
||||
break;
|
||||
|
||||
case 'select_for_unlink_all': // Select the list of objects to be modified (bulk modify)
|
||||
BulkHelper::OperationSelectForModifyAll($oP, 'UI:UnlinkAllTabTitle', 'UI:UnlinkAllPageTitle', 'form_for_unlink_all');
|
||||
break;
|
||||
|
||||
case 'select_for_unlinksynchro_all': // Select the list of objects to be modified (bulk modify)
|
||||
BulkHelper::OperationSelectForModifyAll($oP, 'UI:UnlinkSynchroAllTabTitle', 'UI:UnlinkSynchroAllPageTitle', 'form_for_unlinksynchro_all');
|
||||
break;
|
||||
|
||||
case 'select_for_synchro_all': // Select the list of objects to be modified (bulk modify)
|
||||
BulkHelper::OperationSelectForModifyAll($oP, 'UI:SynchroAllTabTitle', 'UI:SynchroAllPageTitle', 'form_for_synchro_all');
|
||||
break;
|
||||
|
||||
case 'select_for_allowdelete_all': // Select the list of objects to be modified (bulk modify)
|
||||
BulkHelper::OperationSelectForModifyAll($oP, 'UI:AllowDeleteAllTabTitle', 'UI:AllowDeleteAllPageTitle', 'form_for_allowdelete_all');
|
||||
break;
|
||||
|
||||
case 'select_for_denydelete_all': // Select the list of objects to be modified (bulk modify)
|
||||
BulkHelper::OperationSelectForModifyAll($oP, 'UI:DenyDeleteAllTabTitle', 'UI:DenyDeleteAllPageTitle', 'form_for_denydelete_all');
|
||||
break;
|
||||
}
|
||||
} catch (CoreException $e) {
|
||||
$oP->p('<b>An error occured while running the query:</b>');
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
|
||||
|
||||
class SynchroDataSource extends cmdbAbstractObject
|
||||
{
|
||||
@@ -2108,6 +2114,12 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function InitExtendedData($oSource)
|
||||
{
|
||||
$sSQLTable = $oSource->GetDataTable();
|
||||
$this->m_aExtendedData = $this->LoadExtendedDataFromTable($sSQLTable);
|
||||
}
|
||||
|
||||
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
|
||||
{
|
||||
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
||||
@@ -2189,6 +2201,17 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
$this->Set('status_last_error', $sText);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassociate the replica from the destination object and set the status to "new" to be synchronized with the next operation
|
||||
*/
|
||||
public function UnLink()
|
||||
{
|
||||
$this->Set('dest_id', '');
|
||||
$this->Set('dest_class', '');
|
||||
$this->Set('status', 'new');
|
||||
$this->DBWrite();
|
||||
}
|
||||
|
||||
public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog)
|
||||
{
|
||||
$oStatLog->AddTrace(">>> Beginning of SynchroReplica::Synchro, replica status is '".$this->Get('status')."'.", $this);
|
||||
@@ -2365,6 +2388,86 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
$oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return \SynchroLog
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreWarning
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @throws \SynchroExceptionNotStarted
|
||||
*/
|
||||
public function ReSynchro(): SynchroLog
|
||||
{
|
||||
$oDataSource = MetaModel::GetObject(SynchroDataSource::class, $this->Get('sync_source_id'));
|
||||
|
||||
$oStatLog = new SynchroLog();
|
||||
$oStatLog->Set('sync_source_id', $oDataSource->GetKey());
|
||||
$oStatLog->Set('start_date', time());
|
||||
$oStatLog->Set('status', 'running');
|
||||
$oStatLog->AddTrace('Manual synchro');
|
||||
|
||||
// Get the list of SQL columns
|
||||
$aAttCodesExpected = [];
|
||||
$aAttCodesToReconcile = [];
|
||||
$aAttCodesToUpdate = [];
|
||||
$sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)';
|
||||
$oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), [] /* order by*/, ['source_id' => $oDataSource->GetKey()] /* aArgs */);
|
||||
while ($oSyncAtt = $oSetAtt->Fetch()) {
|
||||
if ($oSyncAtt->Get('update')) {
|
||||
$aAttCodesToUpdate[$oSyncAtt->Get('attcode')] = $oSyncAtt;
|
||||
}
|
||||
if ($oSyncAtt->Get('reconcile')) {
|
||||
$aAttCodesToReconcile[$oSyncAtt->Get('attcode')] = $oSyncAtt;
|
||||
}
|
||||
$aAttCodesExpected[$oSyncAtt->Get('attcode')] = $oSyncAtt;
|
||||
}
|
||||
|
||||
// Get the list of attributes, determine reconciliation keys and update targets
|
||||
//
|
||||
if ($oDataSource->Get('reconciliation_policy') == 'use_attributes') {
|
||||
$aReconciliationKeys = $aAttCodesToReconcile;
|
||||
} elseif ($oDataSource->Get('reconciliation_policy') == 'use_primary_key') {
|
||||
// Override the settings made at the attribute level !
|
||||
$aReconciliationKeys = ['primary_key' => null];
|
||||
}
|
||||
|
||||
if (count($aAttCodesToUpdate) == 0) {
|
||||
$oStatLog->AddTrace('No attribute to update');
|
||||
throw new SynchroExceptionNotStarted('There is no attribute to update');
|
||||
}
|
||||
if (count($aReconciliationKeys) == 0) {
|
||||
$oStatLog->AddTrace('No attribute for reconciliation');
|
||||
throw new SynchroExceptionNotStarted('No attribute for reconciliation');
|
||||
}
|
||||
|
||||
$aAttributesToUpdate = [];
|
||||
foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($oDataSource->GetTargetClass(), $sAttCode);
|
||||
if ($oAttDef->IsWritable()) {
|
||||
$aAttributesToUpdate[$sAttCode] = $oSyncAtt;
|
||||
}
|
||||
}
|
||||
// Create a change used for logging all the modifications/creations happening during the synchro
|
||||
$oChange = MetaModel::NewObject('CMDBChange');
|
||||
$oChange->Set('date', time());
|
||||
$sUserString = CMDBChange::GetCurrentUserName();
|
||||
$oChange->Set('userinfo', $sUserString.' '.Dict::S('Core:SyncDataExchangeComment'));
|
||||
$oChange->Set('origin', CMDBChangeOrigin::SYNCHRO_DATA_SOURCE);
|
||||
$oChange->DBInsert();
|
||||
CMDBObject::SetCurrentChange($oChange);
|
||||
|
||||
$this->InitExtendedData($oDataSource);
|
||||
|
||||
$this->Synchro($oDataSource, $aReconciliationKeys, $aAttributesToUpdate, $oChange, $oStatLog);
|
||||
$this->DBUpdate();
|
||||
|
||||
return $oStatLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the destination object with the Extended data found in the synchro_data_XXXX table
|
||||
*
|
||||
@@ -2681,13 +2784,117 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
// Object's details
|
||||
//$this->DisplayBareHeader($oPage, $bEditMode);
|
||||
$this->DisplayBareHeader($oPage, $bEditMode);
|
||||
|
||||
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
|
||||
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
|
||||
$oPage->SetCurrentTab('UI:PropertiesTab');
|
||||
$this->DisplayBareProperties($oPage, $bEditMode);
|
||||
}
|
||||
|
||||
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('title-for-replica', ['ibo-page-header--replica-title']);
|
||||
$oPage->AddSubBlock($oBlock);
|
||||
|
||||
$sId = $this->GetKey();
|
||||
$oTitle = TitleUIBlockFactory::MakeNeutral(Dict::S('Class:SynchroReplica'));
|
||||
$oBlock->AddSubBlock($oTitle);
|
||||
$oActionsToolbar = ToolbarUIBlockFactory::MakeForButton(MenuBlock::ACTIONS_TOOLBAR_ID_PREFIX.$sId);
|
||||
$oActionsToolbar->AddCSSClass('ibo-panel--toolbar');
|
||||
$oBlock->AddSubBlock($oActionsToolbar);
|
||||
|
||||
$sClass = get_class($this);
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
|
||||
$aActions = [];
|
||||
//Delete
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE)) {
|
||||
$aActions['UI:Menu:Delete'] = [
|
||||
'label' => Dict::S('UI:Menu:Delete'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}",
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:delete+'),
|
||||
];
|
||||
}
|
||||
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
|
||||
if (count($aActions) > 0) {
|
||||
$sSeparator = '<hr class="menu-separator"/>';
|
||||
$aActions['sep_0'] = ['label' => $sSeparator, 'url' => ''];
|
||||
}
|
||||
$sUrl = "{$sRootUrl}synchro/replica.php?operation=unlink&class=$sClass&id=$sId{$sContext}";
|
||||
$aActions['Class:SynchroReplica/Action:unlink'] = [
|
||||
'label' => Dict::S('Class:SynchroReplica/Action:unlink'),
|
||||
'url' => $sUrl,
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:unlink+'),
|
||||
];
|
||||
|
||||
$sUrl = "{$sRootUrl}synchro/replica.php?operation=unlinksynchro&class=$sClass&id=$sId{$sContext}";
|
||||
$aActions['Class:SynchroReplica/Action:unlinksynchro'] = [
|
||||
'label' => Dict::S('Class:SynchroReplica/Action:unlinksynchro'),
|
||||
'url' => $sUrl,
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:unlinksynchro+'),
|
||||
];
|
||||
|
||||
$sUrl = "{$sRootUrl}synchro/replica.php?operation=synchro&class=$sClass&id=$sId{$sContext}";
|
||||
$aActions['Class:SynchroReplica/Action:synchro'] = [
|
||||
'label' => Dict::S('Class:SynchroReplica/Action:synchro'),
|
||||
'url' => $sUrl,
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:synchro+'),
|
||||
];
|
||||
|
||||
if ($this->Get('status_dest_creator') == 1) {
|
||||
$sUrl = "{$sRootUrl}synchro/replica.php?operation=denydelete&class=$sClass&id=$sId{$sContext}";
|
||||
$aActions['Class:SynchroReplica/Action:denydelete'] = [
|
||||
'label' => Dict::S('Class:SynchroReplica/Action:denydelete'),
|
||||
'url' => $sUrl,
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:denydelete+'),
|
||||
];
|
||||
} else {
|
||||
$sUrl = "{$sRootUrl}synchro/replica.php?operation=allowdelete&class=$sClass&id=$sId{$sContext}";
|
||||
$aActions['Class:SynchroReplica/Action:allowdelete'] = [
|
||||
'label' => Dict::S('Class:SynchroReplica/Action:allowdelete'),
|
||||
'url' => $sUrl,
|
||||
'tooltip' => Dict::S('Class:SynchroReplica/Action:allowdelete+'),
|
||||
];
|
||||
}
|
||||
}
|
||||
if (count($aActions) > 0) {
|
||||
$sRegularActionsMenuTogglerId = "ibo-regular-actions-menu-toggler-{$sId}";
|
||||
$sRegularActionsPopoverMenuId = "ibo-regular-actions-popover-{$sId}";
|
||||
|
||||
$oActionButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Menu:Actions'), 'UI:Menu:Actions', '', false, $sRegularActionsMenuTogglerId)
|
||||
->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
|
||||
$oRegularActionsMenu = $oPage->GetPopoverMenu($sRegularActionsPopoverMenuId, $aActions)
|
||||
->SetTogglerJSSelector("#$sRegularActionsMenuTogglerId")
|
||||
->SetContainer(PopoverMenu::ENUM_CONTAINER_BODY);
|
||||
|
||||
$oActionsToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oRegularActionsMenu);
|
||||
|
||||
$oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class');
|
||||
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
||||
}
|
||||
|
||||
$sUrl = "{$sRootUrl}pages/$sUIPage?operation=display&class=$sClass&id=$sId{$sContext}";
|
||||
$oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh');
|
||||
$oActionButton->SetIconClass('fas fa-sync-alt')
|
||||
->SetOnClickJsCode('window.location.href=\''.$sUrl.'\'')
|
||||
->SetTooltip(Dict::S('UI:Button:Refresh'))
|
||||
->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = [])
|
||||
{
|
||||
if ($bEditMode) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% block iboInput %}
|
||||
<span class="ibo-toggler--wrapper">
|
||||
{{ parent() }}
|
||||
<span class="ibo-toggler--slider"></span>
|
||||
<a class="ibo-toggler--slider" title="{{ oUIBlock.GetTooltip() }}" href="#"></a>
|
||||
<input class="ibo-toggler--hidden" type="hidden" name="{{ oUIBlock.GetName() }}" value="{% if oUIBlock.IsChecked() %}on{% else %}off{% endif %}"/>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
@@ -108,7 +108,7 @@ class UnitTestRunTimeEnvironment extends RunTimeEnvironment
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir): array
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
|
||||
{
|
||||
\SetupLog::Info(__METHOD__);
|
||||
$aRet = parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
||||
|
||||
@@ -6,18 +6,15 @@ require_once(dirname(__DIR__, 6).'/approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
|
||||
$aParams = [
|
||||
"exec_module" => "combodo-data-feature-removal",
|
||||
"exec_page" => "index.php",
|
||||
'exec_env' => 'production',
|
||||
];
|
||||
|
||||
new ContextTag(ContextTag::TAG_SETUP);
|
||||
$sToken = SetupUtils::CreateSetupToken();
|
||||
|
||||
function GetLastestInstallFile(): ?string
|
||||
{
|
||||
$aFiles = glob(APPROOT.'/log/install-*.xml');
|
||||
if ($aFiles === false) {
|
||||
return null;
|
||||
}
|
||||
rsort($aFiles);
|
||||
$iLatestCtime = 0;
|
||||
$sLastFilePath = null;
|
||||
@@ -34,36 +31,59 @@ function GetLastestInstallFile(): ?string
|
||||
return $sLastFilePath;
|
||||
}
|
||||
|
||||
$aRemovedExtensions = [
|
||||
'itop-container-mgmt' => 'Containerization',
|
||||
];
|
||||
|
||||
$sPath = GetLastestInstallFile();
|
||||
if (is_null($sPath)) {
|
||||
throw new Exception("$sPath no installation XM. Launch a setup....");
|
||||
throw new Exception("$sPath no installation XML. Launch a setup....");
|
||||
}
|
||||
$aParams = new XMLParameters($sPath);
|
||||
$aSelectedModules = array_filter($aParams->Get('selected_modules', []), static function ($element) {
|
||||
global $aRemovedExtensions;
|
||||
return ! array_key_exists($element, $aRemovedExtensions);
|
||||
});
|
||||
$aSelectedModules = $aParams->Get('selected_modules', []);
|
||||
$aSelectedExtensions = $aParams->Get('selected_extensions', []);
|
||||
|
||||
$aSelectedExtensions = array_filter($aParams->Get('selected_extensions', []), static function ($element) {
|
||||
global $aRemovedExtensions;
|
||||
return ! array_key_exists($element, $aRemovedExtensions);
|
||||
});
|
||||
$sAddedExtensions = utils::ReadParam('added_extensions', '', false, 'raw');
|
||||
$aAddedExtensions = [];
|
||||
if (mb_strlen($sAddedExtensions) > 0) {
|
||||
$aAddedExtensions = explode(',', $sAddedExtensions);
|
||||
}
|
||||
$oExtensionMap = new iTopExtensionsMap();
|
||||
foreach ($aAddedExtensions as $iIndex => $sExtensionCode) {
|
||||
if (mb_strlen($sExtensionCode) <= 0) {
|
||||
unset($aAddedExtensions[$iIndex]);
|
||||
continue;
|
||||
}
|
||||
$oExtension = $oExtensionMap->GetFromExtensionCode($sExtensionCode);
|
||||
$aSelectedExtensions[] = $oExtension->sCode;
|
||||
foreach ($oExtension->aModules as $sModuleCode) {
|
||||
if (!in_array($sModuleCode, $aSelectedModules)) {
|
||||
$aSelectedModules[] = $sModuleCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sRemovedExtensions = utils::ReadParam('removed_modules', '', false, 'raw');
|
||||
$aRemovedExtensionsAndModules = [];
|
||||
if (mb_strlen($sRemovedExtensions) > 0) {
|
||||
$aRemovedExtensionsAndModules = explode(',', $sRemovedExtensions);
|
||||
}
|
||||
|
||||
$aSelectedModules = array_filter($aSelectedModules, fn ($element) => !in_array($element, $aRemovedExtensionsAndModules));
|
||||
$aSelectedExtensions = array_filter($aSelectedExtensions, fn ($element) => !in_array($element, $aRemovedExtensionsAndModules));
|
||||
$aRemovedExtensionsAndModules = array_filter($aRemovedExtensionsAndModules, fn ($element) => !is_null($oExtensionMap->GetFromExtensionCode($element)));
|
||||
|
||||
$aRemovedExtensions = array_combine($aRemovedExtensionsAndModules, $aRemovedExtensionsAndModules);
|
||||
$aAddedExtensions = array_combine($aAddedExtensions, $aAddedExtensions);
|
||||
|
||||
$aPostParams = [
|
||||
"auth_user" => 'admin',
|
||||
"auth_pwd" => 'admin',
|
||||
'auth_user' => 'admin',
|
||||
'auth_pwd' => 'admin',
|
||||
'login_mode' => 'form',
|
||||
'operation' => 'AnalysisResult',
|
||||
'authent' => $sToken,
|
||||
'selected_modules' => utils::HtmlEntities(json_encode($aSelectedModules)),
|
||||
'selected_extensions' => utils::HtmlEntities(json_encode($aSelectedExtensions)),
|
||||
'removed_extensions' => utils::HtmlEntities(json_encode($aRemovedExtensions)),
|
||||
'force-uninstall' => "on",
|
||||
'use_symbolic_links' => "",
|
||||
'added_extensions' => utils::HtmlEntities(json_encode($aAddedExtensions)),
|
||||
'force-uninstall' => 'on',
|
||||
'use_symbolic_links' => '',
|
||||
];
|
||||
|
||||
$sHiddenPostedInput = "";
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
// Copyright (c) 2010-2024 Combodo SAS
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\iTopTickets;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ormCaseLog;
|
||||
use MetaModel;
|
||||
|
||||
class UpdateChildTicketLogTest extends ItopDataTestCase
|
||||
{
|
||||
public function testUpdateChildTicketLog_PublicLogOnTwoChild(): void
|
||||
{
|
||||
//Given a parent ticket with two child ticket
|
||||
list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(2);
|
||||
$this->assertCount(2, $aChildrenTree[$iParentTicket], 'The test setup should create exactly two child tickets.');
|
||||
$sParentPublicLogEntry = 'This is a public log entry for the parent ticket.';
|
||||
$oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket);
|
||||
|
||||
// When I enter a public_log entry for the parent ticket
|
||||
$oParentTicket->Set('public_log', $sParentPublicLogEntry);
|
||||
$oParentTicket->DBUpdate();
|
||||
|
||||
// Then the log should be copied to all descendants and contain parent references recursively
|
||||
$this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry);
|
||||
}
|
||||
|
||||
public function testUpdateChildTicketLog_PrivateAndPublicLog(): void
|
||||
{
|
||||
//Given a parent ticket with two child ticket
|
||||
list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(3);
|
||||
$sParentPublicLogEntry = 'This is a public log entry for the parent ticket.';
|
||||
$sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.';
|
||||
|
||||
// When I enter both a public_log and a private_log entry for the parent ticket
|
||||
$oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket);
|
||||
$oParentTicket->Set('public_log', $sParentPublicLogEntry);
|
||||
$oParentTicket->Set('private_log', $sParentPrivateLogEntry);
|
||||
$oParentTicket->DBUpdate();
|
||||
|
||||
// Then both logs should be copied to all descendants and keep ancestor references recursively
|
||||
$this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry);
|
||||
$this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry);
|
||||
}
|
||||
|
||||
public function testUpdateChildTicketLog_PrivateLogOnMultipleLevels(): void
|
||||
{
|
||||
//Given a parent ticket with two child ticket
|
||||
list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(1, 4);
|
||||
$sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.';
|
||||
|
||||
// When I enter both a public_log and a private_log entry for the parent ticket
|
||||
$oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket);
|
||||
$oParentTicket->Set('private_log', $sParentPrivateLogEntry);
|
||||
$oParentTicket->DBUpdate();
|
||||
|
||||
// Then the private log should be copied on each level with parent + grand-parent +... references
|
||||
$this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry);
|
||||
}
|
||||
|
||||
private function AssertLogContainsParentsRefOrKeyRecursively(int $iParentTicket, array $aChildrenTree, string $sLogAttCode, string $sExpectedEntry, array $aAncestors = []): void
|
||||
{
|
||||
$oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket);
|
||||
$aAncestorsToFind = array_merge($aAncestors, [[
|
||||
'id' => (string) $iParentTicket,
|
||||
'ref' => (string) $oParentTicket->Get('ref'),
|
||||
]]);
|
||||
|
||||
foreach ($aChildrenTree as $iChildOrIndex => $aChildNodeOrId) {
|
||||
if (is_array($aChildNodeOrId)) {
|
||||
$iChildTicket = (int) $iChildOrIndex;
|
||||
$aGrandChildrenTree = $aChildNodeOrId;
|
||||
} else {
|
||||
$iChildTicket = (int) $aChildNodeOrId;
|
||||
$aGrandChildrenTree = [];
|
||||
}
|
||||
|
||||
$oChildTicket = MetaModel::GetObject('UserRequest', $iChildTicket);
|
||||
$sLastLogEntry = $oChildTicket->Get($sLogAttCode)->GetLatestEntry();
|
||||
$this->assertNotEmpty($sLastLogEntry, "The $sLogAttCode entry was not copied to child ticket #$iChildTicket.");
|
||||
$this->assertStringContainsString($sExpectedEntry, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain the original parent entry.");
|
||||
foreach ($aAncestorsToFind as $aAncestor) {
|
||||
$bContainsAncestorRef = strpos($sLastLogEntry, $aAncestor['ref']) !== false;
|
||||
$bContainsAncestorId = strpos($sLastLogEntry, $aAncestor['id']) !== false;
|
||||
$this->assertTrue(
|
||||
$bContainsAncestorRef || $bContainsAncestorId,
|
||||
"The $sLogAttCode entry on child ticket #$iChildTicket does not contain ancestor ref '{$aAncestor['ref']}' nor ancestor id '{$aAncestor['id']}'."
|
||||
);
|
||||
}
|
||||
|
||||
if ($aGrandChildrenTree !== []) {
|
||||
$this->AssertLogContainsParentsRefOrKeyRecursively($iChildTicket, $aGrandChildrenTree, $sLogAttCode, $sExpectedEntry, $aAncestorsToFind);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function GivenUserRequests(int $iCount, int $iLevel = 2): array
|
||||
{
|
||||
$iOrg = $this->GivenObjectInDB('Organization', [
|
||||
'name' => 'Test Organization for Log Update',
|
||||
]);
|
||||
// Given a parent ticket
|
||||
$iParentTicket = $this->GivenObjectInDB('UserRequest', [
|
||||
'title' => 'Parent Ticket for Log Update Test',
|
||||
'description' => 'This is the parent ticket for testing log updates.',
|
||||
'org_id' => $iOrg,
|
||||
'ref' => 'R-00001',
|
||||
]);
|
||||
|
||||
$iRemainingLevels = max(0, $iLevel - 1);
|
||||
$iRefCounter = 1;
|
||||
$aChildrenTree = [
|
||||
$iParentTicket => $this->GivenChildTicketsRecursive($iParentTicket, $iCount, $iRemainingLevels, $iOrg, $iRefCounter),
|
||||
];
|
||||
|
||||
return [$iParentTicket, $aChildrenTree];
|
||||
}
|
||||
|
||||
private function GivenChildTicketsRecursive(int $iParentTicket, int $iCount, int $iRemainingLevels, int $iOrg, int &$iRefCounter): array
|
||||
{
|
||||
if ($iRemainingLevels <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aChildren = [];
|
||||
for ($i = 1; $i <= $iCount; $i++) {
|
||||
$iRefCounter++;
|
||||
$sRef = sprintf('R-%05d', $iRefCounter);
|
||||
$iChildTicket = $this->GivenObjectInDB('UserRequest', [
|
||||
'title' => "Child Ticket $sRef for Log Update Test",
|
||||
'description' => "This is child ticket $sRef for testing log updates.",
|
||||
'org_id' => $iOrg,
|
||||
'parent_request_id' => $iParentTicket,
|
||||
'ref' => $sRef,
|
||||
]);
|
||||
|
||||
if ($iRemainingLevels > 1) {
|
||||
$aChildren[$iChildTicket] = $this->GivenChildTicketsRecursive($iChildTicket, $iCount, $iRemainingLevels - 1, $iOrg, $iRefCounter);
|
||||
} else {
|
||||
$aChildren[] = $iChildTicket;
|
||||
}
|
||||
}
|
||||
|
||||
return $aChildren;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ class ExtensionsMapTest extends ItopTestCase
|
||||
|
||||
public function testGetAllExtensionsWithPreviouslyInstalledDoesNotCrash()
|
||||
{
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
$aExtensions = $oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled();
|
||||
$this->assertGreaterThan(0, count($aExtensions));
|
||||
}
|
||||
@@ -84,7 +84,7 @@ class ExtensionsMapTest extends ItopTestCase
|
||||
|
||||
private function GiveExtensionMapWithAllTypeOfExtensions(): iTopExtensionsMap
|
||||
{
|
||||
$oExtensionsMap = iTopExtensionsMap::GetExtensionsMap();
|
||||
$oExtensionsMap = new iTopExtensionsMap();
|
||||
$this->SetNonPublicProperty($oExtensionsMap, 'aInstalledExtensions', []);
|
||||
$this->SetNonPublicProperty($oExtensionsMap, 'aExtensions', []);
|
||||
|
||||
@@ -153,7 +153,9 @@ class ExtensionsMapTest extends ItopTestCase
|
||||
|
||||
public function testiTopExtensionsMapInit()
|
||||
{
|
||||
$oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: __DIR__."/ressources/");
|
||||
$oiTopExtensionsMap = new iTopExtensionsMap(sAppRootForTests:__DIR__."/ressources");
|
||||
|
||||
//file_put_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json', json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT));
|
||||
|
||||
$sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json');
|
||||
$sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected);
|
||||
@@ -164,19 +166,6 @@ class ExtensionsMapTest extends ItopTestCase
|
||||
$this->assertEquals($sExpected, $actual);
|
||||
}
|
||||
|
||||
public function testGetScannedModulesRootDirs()
|
||||
{
|
||||
$sAppRootForTest = __DIR__."/resources2/";
|
||||
$oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: $sAppRootForTest);
|
||||
|
||||
$aExpectedDirs = [
|
||||
"{$sAppRootForTest}datamodels/2.x",
|
||||
"{$sAppRootForTest}extensions",
|
||||
"{$sAppRootForTest}data/production-modules",
|
||||
];
|
||||
self::assertEquals($aExpectedDirs, $oiTopExtensionsMap->GetScannedModulesRootDirs());
|
||||
}
|
||||
|
||||
public function SerializeExtensionMap(iTopExtensionsMap $oiTopExtensionsMap): array
|
||||
{
|
||||
$aRes = [];
|
||||
|
||||
@@ -9,15 +9,30 @@ use RunTimeEnvironment;
|
||||
|
||||
class RunTimeEnvironmentTest extends ItopTestCase
|
||||
{
|
||||
private ?string $sFixtureRootDir = null;
|
||||
private ?string $sBuildDirToCleanup = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('/setup/runtimeenv.class.inc.php');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (($this->sFixtureRootDir !== null) && is_dir($this->sFixtureRootDir)) {
|
||||
self::RecurseRmdir($this->sFixtureRootDir);
|
||||
}
|
||||
if (($this->sBuildDirToCleanup !== null) && is_dir($this->sBuildDirToCleanup)) {
|
||||
self::RecurseRmdir($this->sBuildDirToCleanup);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testDoCompileDoesNotThrowWhenUnselectedExtensionCodeIsMissing(): void
|
||||
{
|
||||
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-code-');
|
||||
[$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-');
|
||||
|
||||
$sInvalidExtensionXml = <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -26,20 +41,20 @@ class RunTimeEnvironmentTest extends ItopTestCase
|
||||
<description>Test extension without code</description>
|
||||
<version>1.0.0</version>
|
||||
<mandatory>false</mandatory>
|
||||
<more_info_url/>
|
||||
<more_info_url></more_info_url>
|
||||
</extension>
|
||||
XML;
|
||||
file_put_contents(APPROOT.$sExtensionsDirRelative.'/extension.xml', $sInvalidExtensionXml);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken);
|
||||
|
||||
//early DOMFormatException to avoid any real compilation
|
||||
$this->expectException(DOMFormatException::class);
|
||||
$oRunTimeEnvironment->DoCompile([], [], []);
|
||||
$oRunTimeEnvironment->DoCompile([], [], [], $sSourceDirRelative, $sExtensionsDirRelative);
|
||||
}
|
||||
|
||||
public function testDoCompileThrowsWhenSelectedExtensionCodeIsMissing(): void
|
||||
{
|
||||
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-code-');
|
||||
[$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-');
|
||||
|
||||
$sInvalidExtensionXml = <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -48,21 +63,21 @@ XML;
|
||||
<description>Test extension without code</description>
|
||||
<version>1.0.0</version>
|
||||
<mandatory>false</mandatory>
|
||||
<more_info_url/>
|
||||
<more_info_url></more_info_url>
|
||||
</extension>
|
||||
XML;
|
||||
file_put_contents(APPROOT.$sExtensionsDirRelative.'/extension.xml', $sInvalidExtensionXml);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken);
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code (Broken extension)");
|
||||
|
||||
$oRunTimeEnvironment->DoCompile([""], [], []);
|
||||
$oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative);
|
||||
}
|
||||
|
||||
public function testDoCompileThrowsWhenSelectedExtensionCodeAndLabelAreMissing(): void
|
||||
{
|
||||
[$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-');
|
||||
[$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-label-');
|
||||
|
||||
$sInvalidExtensionXml = <<<XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -70,34 +85,36 @@ XML;
|
||||
<description>Test extension without code and label</description>
|
||||
<version>1.0.0</version>
|
||||
<mandatory>false</mandatory>
|
||||
<more_info_url/>
|
||||
<more_info_url></more_info_url>
|
||||
</extension>
|
||||
XML;
|
||||
$sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative;
|
||||
file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment);
|
||||
$oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken);
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)");
|
||||
|
||||
$oRunTimeEnvironment->DoCompile([""], [], [], false);
|
||||
$oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative, false);
|
||||
}
|
||||
|
||||
private function CreateFixtureContext(string $sEnvPrefix): array
|
||||
private function CreateFixtureContext(string $sTokenPrefix): array
|
||||
{
|
||||
$sEnvironment = str_replace('.', '-', uniqid($sEnvPrefix, true));
|
||||
$sExtensionsDirRelative = 'data/'.$sEnvironment.'-modules';
|
||||
$sToken = str_replace('.', '-', uniqid($sTokenPrefix, true));
|
||||
$this->sFixtureRootDir = APPROOT.'data/'.$sToken;
|
||||
$sSourceDirRelative = 'data/'.$sToken.'/source';
|
||||
$sExtensionsDirRelative = 'data/'.$sToken.'/extensions';
|
||||
|
||||
mkdir(APPROOT.$sSourceDirRelative, 0777, true);
|
||||
mkdir(APPROOT.$sExtensionsDirRelative, 0777, true);
|
||||
$this->aFileToClean[] = APPROOT.$sExtensionsDirRelative;
|
||||
|
||||
return [$sEnvironment, $sExtensionsDirRelative];
|
||||
return [$sToken, $sSourceDirRelative, $sExtensionsDirRelative];
|
||||
}
|
||||
|
||||
private function CreateRunTimeEnvironment(string $sEnvironment): RunTimeEnvironment
|
||||
private function CreateRunTimeEnvironment(string $sToken): RunTimeEnvironment
|
||||
{
|
||||
$oRunTimeEnvironment = new RunTimeEnvironment($sEnvironment, false);
|
||||
$this->aFileToClean[] = $oRunTimeEnvironment->GetBuildDir();
|
||||
$oRunTimeEnvironment = new RunTimeEnvironment('test-'.$sToken, false);
|
||||
$this->sBuildDirToCleanup = $oRunTimeEnvironment->GetBuildDir();
|
||||
|
||||
return $oRunTimeEnvironment;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,11 @@ use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||
use Combodo\iTop\Test\UnitTest\Service\UnitTestRunTimeEnvironment;
|
||||
use MetaModel;
|
||||
use SetupUtils;
|
||||
|
||||
class SetupAuditTest extends ItopCustomDatamodelTestCase
|
||||
{
|
||||
public const ENVT = 'php-unit-extensionremoval-tests';
|
||||
|
||||
private ?string $sDirPathToRemoveFromExtensionFolder = null;
|
||||
|
||||
public function GetDatamodelDeltaAbsPath(): string
|
||||
{
|
||||
//no delta: empty path provided
|
||||
@@ -47,14 +44,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
|
||||
$this->RequireOnceItopFile('/setup/feature_removal/DryRemovalRuntimeEnvironment.php');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
if (!is_null($this->sDirPathToRemoveFromExtensionFolder) && is_dir($this->sDirPathToRemoveFromExtensionFolder)) {
|
||||
SetupUtils::rrmdir($this->sDirPathToRemoveFromExtensionFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetTestEnvironment(): string
|
||||
{
|
||||
return self::ENVT;
|
||||
@@ -62,11 +51,7 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
|
||||
|
||||
public function testComputeDryRemoval()
|
||||
{
|
||||
$this->sDirPathToRemoveFromExtensionFolder = APPROOT.'/extensions/finalclass_ext3';
|
||||
SetupUtils::copydir(__DIR__.'/other_features/finalclass_ext3', $this->sDirPathToRemoveFromExtensionFolder);
|
||||
clearstatcache();
|
||||
|
||||
$oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment($this->GetTestEnvironment(), ['finalclass_ext3' => 'Ext For Test3'], ['nominal_ext1', 'finalclass_ext2']);
|
||||
$oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment($this->GetTestEnvironment(), ['nominal_ext1', 'finalclass_ext2']);
|
||||
$oDryRemovalRuntimeEnvt->CompileFrom($this->GetTestEnvironment());
|
||||
|
||||
$oSetupAudit = new SetupAudit($this->GetTestEnvironment());
|
||||
@@ -82,19 +67,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
|
||||
"FinalClassFeature2Module1MyFinalClassFromLocation" => 0,
|
||||
];
|
||||
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit());
|
||||
|
||||
$aClassesAfter = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($oSetupAudit->GetEnvAfter());
|
||||
$expected = [
|
||||
"FinalClassFeature3Module1MyClass",
|
||||
"FinalClassFeature3Module1MyFinalClassFromLocation",
|
||||
];
|
||||
foreach ($expected as $sAddedClass) {
|
||||
$this->assertContains(
|
||||
$sAddedClass,
|
||||
$aClassesAfter,
|
||||
"After DryRemoval compilation DM should contain classes coming from finalclass_ext3 extension"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetRemovedClassesFromSetupWizard()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension format="1.0">
|
||||
<extension_code>finalclass_ext3</extension_code>
|
||||
<company>Combodo SARL</company>
|
||||
<author><![CDATA[Odain]]></author>
|
||||
<label><![CDATA[Ext For Test3]]></label>
|
||||
<description><![CDATA[Ext For Test]]></description>
|
||||
<version>6.6.6</version>
|
||||
<modules type="array">
|
||||
<module>
|
||||
<id>finalclass_ext3_module1</id>
|
||||
<version>tags/6.6.6</version>
|
||||
</module>
|
||||
</modules>
|
||||
<release_date>2023-07-19</release_date>
|
||||
<version_description><![CDATA[
|
||||
]]></version_description>
|
||||
<itop_version_min>3.2.0</itop_version_min>
|
||||
<status></status>
|
||||
<mandatory>false</mandatory>
|
||||
<more_info_url></more_info_url>
|
||||
</extension>
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
|
||||
<classes>
|
||||
<class id="FinalClassFeature3Module1MyFinalClassFromLocation" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>FinalClassFeature3Module1MyFinalClassFromLocation</db_table>
|
||||
<naming>
|
||||
<attributes>
|
||||
<attribute id="name2"/>
|
||||
<attribute id="finalclass"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name2"/>
|
||||
<attribute id="finalclass"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
<order>
|
||||
<columns>
|
||||
<column id="name2" ascending="false"/>
|
||||
</columns>
|
||||
</order>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name2" xsi:type="AttributeString">
|
||||
<sql>name2</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation/>
|
||||
<parent>Location</parent>
|
||||
</class>
|
||||
<class id="FinalClassFeature3Module1MyClass" _delta="define">
|
||||
<properties>
|
||||
<category>bizmodel,searchable</category>
|
||||
<abstract>false</abstract>
|
||||
<db_table>FinalClassFeature3Module1MyClass</db_table>
|
||||
<naming>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
<order>
|
||||
<columns>
|
||||
<column id="name" ascending="false"/>
|
||||
</columns>
|
||||
</order>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<validation_pattern/>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<presentation/>
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
</class>
|
||||
</classes>
|
||||
<menus/>
|
||||
<user_rights/>
|
||||
<module_parameters/>
|
||||
</itop_design>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
//// PHP Data Model definition file
|
||||
//
|
||||
//// WARNING - WARNING - WARNING
|
||||
//// DO NOT EDIT THIS FILE (unless you know what you are doing)
|
||||
////
|
||||
//// If you use supply a datamodel.xxxx.xml file with your module
|
||||
//// the this file WILL BE overwritten by the compilation of the
|
||||
//// module (during the setup) if the datamodel.xxxx.xml file
|
||||
//// contains the definition of new classes or menus.
|
||||
////
|
||||
//// The recommended way to define new classes (for iTop 2.0) is via the XML definition.
|
||||
//// This file remains in the module's template only for the cases where there is:
|
||||
//// - either no new class or menu defined in the XML file
|
||||
//// - or no XML file at all supplied by the module
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
//
|
||||
// iTop module definition file
|
||||
//
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'finalclass_ext3_module1/6.6.6',
|
||||
[
|
||||
// Identification
|
||||
//
|
||||
'label' => 'Ext For Test',
|
||||
'category' => 'business',
|
||||
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => [
|
||||
'itop-structure/3.2.0',
|
||||
],
|
||||
'mandatory' => false,
|
||||
'visible' => true,
|
||||
'installer' => '',
|
||||
|
||||
// Components
|
||||
//
|
||||
'datamodel' => [
|
||||
'model.finalclass_ext3_module1.php',
|
||||
],
|
||||
'webservice' => [],
|
||||
'data.struct' => [// add your 'structure' definition XML files here,
|
||||
],
|
||||
'data.sample' => [// add your sample data XML files here,
|
||||
],
|
||||
|
||||
// Documentation
|
||||
//
|
||||
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
|
||||
'doc.more_information' => '', // hyperlink to more information, if any
|
||||
|
||||
// Default settings
|
||||
//
|
||||
'settings' => [// Module specific settings go here, if any
|
||||
],
|
||||
]
|
||||
);
|
||||
@@ -1,243 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<steps type="array">
|
||||
<step>
|
||||
<title>Configuration Management options</title>
|
||||
<description><![CDATA[<h2>The options below allow you to configure the type of elements that are to be managed inside iTop.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-apps-tab.svg</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-core</extension_code>
|
||||
<title>Configuration Management Core</title>
|
||||
<description>All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-structure</module>
|
||||
<module>itop-config-mgmt</module>
|
||||
<module>itop-attachments</module>
|
||||
<module>itop-profiles-itil</module>
|
||||
<module>itop-welcome-itil</module>
|
||||
<module>itop-tickets</module>
|
||||
<module>itop-files-information</module>
|
||||
<module>combodo-db-tools</module>
|
||||
<module>itop-core-update</module>
|
||||
<module>itop-hub-connector</module>
|
||||
<module>itop-oauth-client</module>
|
||||
<module>combodo-backoffice-darkmoon-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-high-contrast-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-protanopia-deuteranopia-theme</module>
|
||||
<module>combodo-backoffice-fullmoon-tritanopia-theme</module>
|
||||
<module>itop-themes-compat</module>
|
||||
<module>combodo-my-account</module>
|
||||
<module>combodo-my-account-user-info</module>
|
||||
<module>combodo-oauth2-client</module>
|
||||
<module>itop-attribute-class-set</module>
|
||||
<module>itop-attribute-encrypted-password</module>
|
||||
<module>itop-ui-copypaste</module>
|
||||
</modules>
|
||||
<mandatory>true</mandatory>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-datacenter</extension_code>
|
||||
<title>Data Center Devices</title>
|
||||
<description>Manage Data Center devices such as Racks, Enclosures, PDUs, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-datacenter-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-end-user</extension_code>
|
||||
<title>End-User Devices</title>
|
||||
<description>Manage devices related to end-users: PCs, Phones, Tablets, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-endusers-devices</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-storage</extension_code>
|
||||
<title>Storage Devices</title>
|
||||
<description>Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-storage-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-virtualization</extension_code>
|
||||
<title>Virtualization</title>
|
||||
<description>Manage Hypervisors, Virtual Machines and Farms.</description>
|
||||
<modules type="array">
|
||||
<module>itop-virtualization-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-container-mgmt</extension_code>
|
||||
<title>Containerization</title>
|
||||
<description><![CDATA[Manage Container Images, Applications and Hosts]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-container-mgmt</module>
|
||||
</modules>
|
||||
<default>false</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
<step>
|
||||
<title>Service Management options</title>
|
||||
<description><![CDATA[<h2>Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-services.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-enterprise</extension_code>
|
||||
<title>Service Management for Enterprises</title>
|
||||
<description>Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-service-provider</extension_code>
|
||||
<title>Service Management for Service Providers</title>
|
||||
<description>Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt-provider</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Tickets Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to respond to user requests and incidents.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-discussion-forum.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket</extension_code>
|
||||
<title>Simple Ticket Management</title>
|
||||
<description>Select this option to use one single type of tickets for all kind of requests.</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil</extension_code>
|
||||
<title>ITIL Compliant Tickets Management</title>
|
||||
<description>Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields</description>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-user-request</extension_code>
|
||||
<title>User Request Management</title>
|
||||
<description>Manage User Request tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-incident</extension_code>
|
||||
<title>Incident Management</title>
|
||||
<description>Manage Incidents tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-incident-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-none</extension_code>
|
||||
<title>No Tickets Management</title>
|
||||
<description>Don't manage incidents or user requests in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Change Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to manage changes to the IT infrastructure.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-change.svg</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-simple</extension_code>
|
||||
<title>Simple Change Management</title>
|
||||
<description>Select this option to use one type of ticket for all kind of changes.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-itil</extension_code>
|
||||
<title>ITIL Change Management</title>
|
||||
<description>Select this option to use Normal/Routine/Emergency change tickets.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-none</extension_code>
|
||||
<title>No Change Management</title>
|
||||
<description>Don't manage changes in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Additional ITIL tickets</title>
|
||||
<description><![CDATA[<h2>Pick from the list below the additional ITIL processes that are to be implemented in iTop.</h2>]]></description>
|
||||
<banner>/images/icons/icons8-important-book.svg</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
|
||||
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||
<title>Known Errors Management and FAQ</title>
|
||||
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-faq-light</module>
|
||||
<module>itop-knownerror-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-problem-mgmt</extension_code>
|
||||
<title>Problem Management</title>
|
||||
<description>Select this option to track "Problems" in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-problem-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
</steps>
|
||||
</installation>
|
||||
Reference in New Issue
Block a user