Merge branch 'develop' into feature/faf_event_service

This commit is contained in:
Eric Espie
2022-05-25 10:04:54 +02:00
929 changed files with 65776 additions and 34526 deletions

View File

@@ -359,9 +359,7 @@ function DisplayLostAttachments(iTopWebPage &$oP, ApplicationContext &$oAppConte
$sHistoryEntry = Dict::Format('DBTools:LostAttachments:History', $oOrmDocument->GetFileName());
CMDBObject::SetTrackInfo(UserRights::GetUserFriendlyName());
$oChangeOp = MetaModel::NewObject('CMDBChangeOpPlugin');
/** @var \Change $oChange */
$oChange = CMDBObject::GetCurrentChange();
$oChangeOp->Set('change', $oChange->GetKey());
// CMDBChangeOp.change will be automatically filled
$oChangeOp->Set('objclass', $sTargetClass);
$oChangeOp->Set('objkey', $sTargetId);
$oChangeOp->Set('description', $sHistoryEntry);

View File

@@ -30,7 +30,6 @@ if (!defined('APPROOT'))
}
}
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');

View File

@@ -30,7 +30,6 @@ if (!defined('APPROOT'))
}
}
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');

View File

@@ -30,7 +30,6 @@ if (!defined('APPROOT'))
}
}
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');

View File

@@ -5828,30 +5828,69 @@
<type>Overload-cmdbAbstractObject</type>
<code><![CDATA[ protected function UpdateConnectedNetworkDevice()
{
$oDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'));
if (is_object($oDevice) && (get_class($oDevice) == 'NetworkDevice'))
{
// Note: in case a port has been changed, search with the original values
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$aFields = array('networkdevice_id','connectableci_id','network_port','device_port','connection_type');
$aChanges = $this->ListPreviousValuesForUpdatedAttributes();
$aPrev = array(); // Previous values of the current link object before it was modified
foreach ($aFields as $sFieldCode) {
$aPrev[$sFieldCode] = array_key_exists($sFieldCode, $aChanges) ? $aChanges[$sFieldCode] : $this->Get($sFieldCode);
}
$sPrevLink = ($aPrev['connection_type'] == 'uplink') ? 'downlink' : 'uplink';
$sConnLink = ($this->Get('connection_type') == 'uplink') ? 'downlink' : 'uplink';
$oNewDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'), false);
$oPrevDevice = MetaModel::GetObject('ConnectableCI', $aPrev['connectableci_id'], false);
$bNew = (is_object($oNewDevice) && (get_class($oNewDevice) == 'NetworkDevice'));
$bPrev = (is_object($oPrevDevice) && (get_class($oPrevDevice) == 'NetworkDevice'));
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport AND connection_type = :link";
if ($bPrev) { // There was a twin
// Retrieve twin link using previous values of the current link
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
array(),
array(
'network' => $aPrev['connectableci_id'],
'device' => $aPrev['networkdevice_id'],
'devport' => $aPrev['network_port'],
'nwport' => $aPrev['device_port'],
'link' => $sPrevLink,
)
);
if ($bNew) { // and a twin must still exist, so update the existing
while ($oConnection = $oConnectionSet->Fetch()) {
$oConnection->Set('networkdevice_id', $this->Get('connectableci_id'));
$oConnection->Set('connectableci_id', $this->Get('networkdevice_id'));
$oConnection->Set('network_port', $this->Get('device_port'));
$oConnection->Set('device_port', $this->Get('network_port'));
$oConnection->Set('connection_type',$sConnLink);
$oConnection->DBUpdate();
}
}
else { // and no twin is needed anymore, so delete the existing
while ($oConnection = $oConnectionSet->Fetch()) {
$oConnection->DBDelete();
}
}
}
elseif ($bNew) { // There was no twin but a twin must exist now
// Search for a twin link using current values inverted
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
array(),
array(
'network' => $this->Get('connectableci_id'),
'device' => $this->Get('networkdevice_id'),
'devport' => $this->GetOriginal('network_port'),
'nwport' => $this->GetOriginal('device_port'),
'devport' => $this->Get('device_port'),
'nwport' => $this->Get('network_port'),
'link' => $sConnLink,
)
);
$sLink = $this->Get('connection_type');
$sConnLink = ($sLink == 'uplink') ? 'downlink' : 'uplink';
// There should be one link - do it in a safe manner anyway
while ($oConnection = $oConnectionSet->Fetch())
{
$oConnection->Set('connection_type', $sConnLink);
$oConnection->Set('network_port', $this->Get('device_port'));
$oConnection->Set('device_port', $this->Get('network_port'));
$oConnection->DBUpdate();
if ($oConnectionSet->Count() == 0) {
$oNewLink = new lnkConnectableCIToNetworkDevice();
$oNewLink->Set('networkdevice_id', $this->Get('connectableci_id'));
$oNewLink->Set('connectableci_id', $this->Get('networkdevice_id'));
$oNewLink->Set('network_port', $this->Get('device_port'));
$oNewLink->Set('device_port', $this->Get('network_port'));
$oNewLink->Set('connection_type', $sConnLink);
$oNewLink->DBInsert();
}
}
}]]></code>

View File

@@ -189,14 +189,16 @@ try {
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'save'));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId()));
// - Cancel button
//--- Cancel button
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('config-cancel'), 'cancel_button', null, true, 'cancel_button');
$oCancelButton->SetOnClickJsCode("return ResetConfig();");
$oForm->AddSubBlock($oCancelButton);
// - Submit button
//--- Submit button
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('config-apply'), null, Dict::S('config-apply'), true, 'submit_button');
$oForm->AddSubBlock($oSubmitButton);
//--- Config editor
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('prev_config', $sOriginalConfigEscaped, 'prev_config'));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('new_config', $sConfigEscaped));
$oForm->AddHtml("<div id =\"new_config\" style=\"position: absolute; top: ".$iEditorTopMargin."em; bottom: 0; left: 5px; right: 5px;\"></div>");

View File

@@ -54,6 +54,7 @@ Dict::Add('EN US', 'English', 'English', array(
'iTopUpdate:UI:Status' => 'Status',
'iTopUpdate:UI:Action' => 'Update',
'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup',
'iTopUpdate:UI:History' => 'Versions History',
'iTopUpdate:UI:Progress' => 'Progress of the upgrade',
@@ -81,6 +82,9 @@ Dict::Add('EN US', 'English', 'English', array(
'iTopUpdate:UI:SetupLaunch' => 'Launch '.ITOP_APPLICATION_SHORT.' Setup',
'iTopUpdate:UI:SetupLaunchConfirm' => 'This will launch '.ITOP_APPLICATION_SHORT.' setup, are you sure?',
// Setup Messages
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start',
'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Entering maintenance mode',

View File

@@ -17,6 +17,7 @@ use Dict;
use Exception;
use IssueLog;
use MetaModel;
use SecurityException;
use SetupUtils;
use utils;
@@ -211,8 +212,7 @@ class AjaxController extends Controller
CoreUpdater::UpdateDatabase();
$iResponseCode = 200;
}
catch (Exception $e)
{
catch (Exception $e) {
IssueLog::Error("Compile: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
@@ -220,4 +220,22 @@ class AjaxController extends Controller
$this->DisplayJSONPage($aParams, $iResponseCode);
}
/**
* @throws \SecurityException if CSRF token invalid
*
* @since 3.1.0 N°4919
*/
public function OperationLaunchSetup()
{
$sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
if (false === utils::IsTransactionValid($sTransactionId)) {
throw new SecurityException('Access forbidden');
}
$sConfigFile = APPCONF.'production/config-itop.php';
@chmod($sConfigFile, 0770); // Allow overwriting the file
header('Location: ../setup/');
}
}

View File

@@ -35,6 +35,25 @@ class UpdateController extends Controller
$oSet = new DBObjectSet($oFilter, ['installed' => false]); // Most recent first
$aParams['oSet'] = $oSet;
$oConfig = utils::GetConfig();
$bConfigParamSetupLaunchButtonEnabled = $oConfig->Get('setup.launch_button.enabled');
if (is_null($bConfigParamSetupLaunchButtonEnabled)) {
$bIsSetupLaunchButtonEnabled = utils::IsDevelopmentEnvironment();
} else if (false === $bConfigParamSetupLaunchButtonEnabled) {
$bIsSetupLaunchButtonEnabled = false;
} else {
$bIsSetupLaunchButtonEnabled = $bConfigParamSetupLaunchButtonEnabled || utils::IsDevelopmentEnvironment();
}
$aParams['bIsSetupLaunchButtonEnabled'] = $bIsSetupLaunchButtonEnabled;
if ($bIsSetupLaunchButtonEnabled) {
$sLaunchSetupUrl = utils::GetAbsoluteUrlModulePage('itop-core-update', 'ajax.php',
[
'operation' => 'LaunchSetup',
'transaction_id' => $sTransactionId,
]);;
$aParams['sLaunchSetupUrl'] = $sLaunchSetupUrl;
}
$this->DisplayPage($aParams);
}

View File

@@ -87,6 +87,14 @@
{% EndUIFieldSet %}
{% if bIsSetupLaunchButtonEnabled %}
{% UIFieldSet Standard {'sLegend':'iTopUpdate:UI:Setup'|dict_s} %}
{% UIForm Standard {'sId':'launch-setup-form', Action:sLaunchSetupUrl} %}
{% UIButton ForDestructiveAction {'sLabel':'iTopUpdate:UI:SetupLaunch'|dict_s, 'sName':'launch-setup', 'sValue':'launch-setup', 'bIsSubmit':true, 'sId':'launch-setup'} %}
{% EndUIForm %}
{% EndUIFieldSet %}
{% endif %}
{% UIFieldSet Standard {'sLegend':'iTopUpdate:UI:History'|dict_s} %}
{% UIDataTable ForRendering {'sListId':'iboupdatehistory', 'oSet':oSet} %}{% EndUIDataTable %}
{% EndUIFieldSet %}

View File

@@ -101,3 +101,7 @@ $("#check-update").on("click", function(e) {
e.stopPropagation();
return false;
});
$("#launch-setup-form").on("submit", function () {
return window.confirm("{{ 'iTopUpdate:UI:SetupLaunchConfirm'|dict_s }}");
});

View File

@@ -22,18 +22,20 @@
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once (APPROOT.'application/webpage.class.inc.php');
require_once (APPROOT.'application/utils.inc.php');
require_once (APPROOT.'core/log.class.inc.php');
if (!defined('__DIR__')) {
define('__DIR__', dirname(__FILE__));
}
require_once(APPROOT.'application/utils.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
IssueLog::Enable(APPROOT.'log/error.log');
require_once (APPROOT.'setup/runtimeenv.class.inc.php');
require_once (APPROOT.'setup/backup.class.inc.php');
require_once (APPROOT.'core/mutex.class.inc.php');
require_once (APPROOT.'core/dict.class.inc.php');
require_once (APPROOT.'setup/xmldataloader.class.inc.php');
require_once (__DIR__.'/hubruntimeenvironment.class.inc.php');
require_once(APPROOT.'setup/runtimeenv.class.inc.php');
require_once(APPROOT.'setup/backup.class.inc.php');
require_once(APPROOT.'core/mutex.class.inc.php');
require_once(APPROOT.'core/dict.class.inc.php');
require_once(APPROOT.'setup/xmldataloader.class.inc.php');
require_once(__DIR__.'/hubruntimeenvironment.class.inc.php');
/**
* Overload of DBBackup to handle logging

View File

@@ -280,7 +280,6 @@ try {
switch ($sTargetRoute) {
case 'inform_after_setup':
// Hidden IFRAME at the end of the setup
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
$oPage = new NiceWebPage('');
$aDataToPost = MakeDataToPost($sTargetRoute);
$oPage->add('<form id="hub_launch_form" action="'.$sHubUrlStateless.'" method="post">');

View File

@@ -1142,39 +1142,40 @@
<static>false</static>
<access>public</access>
<type>LifecycleAction</type>
<code><![CDATA[ public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// single person
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a departement!
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$iPriority = 1;
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
{
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
<code><![CDATA[public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// single person
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a departement!
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'priority');
$iPriority = $oAttDef->IsNullAllowed() ? null : $oAttDef->GetDefaultValue();
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')])) {
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
</method>
<method id="ComputeValues">
<static>false</static>

View File

@@ -162,12 +162,20 @@ class BrickCollection
// - Home
$this->aHomeOrdering = $this->aAllowedBricks;
usort($this->aHomeOrdering, function (PortalBrick $a, PortalBrick $b) {
return $a->GetRankHome() > $b->GetRankHome();
if ($a->GetRankHome() === $b->GetRankHome()) {
return 0;
}
return $a->GetRankHome() > $b->GetRankHome() ? 1 : -1;
});
// - Navigation menu
$this->aNavigationMenuOrdering = $this->aAllowedBricks;
usort($this->aNavigationMenuOrdering, function (PortalBrick $a, PortalBrick $b) {
return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu();
if ($a->GetRankNavigationMenu() === $b->GetRankNavigationMenu()) {
return 0;
}
return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu() ? 1 : -1;
});
}

View File

@@ -485,7 +485,11 @@ class ManageBrick extends PortalBrick
if (!$this->IsGroupingByDistinctValues($sName))
{
usort($this->aGrouping[$sName]['groups'], function ($a, $b) {
return $a['rank'] > $b['rank'];
if ($a['rank'] === $b['rank']) {
return 0;
}
return $a['rank'] > $b['rank'] ? 1 : -1;
});
}

View File

@@ -1246,7 +1246,7 @@ class ObjectController extends BrickController
}
$aData['att_id'] = $iAttId;
$aData['preview'] = $oDocument->IsPreviewAvailable() ;
$aData['preview'] = $oDocument->IsPreviewAvailable();
$aData['file_size'] = $oDocument->GetFormattedSize();
$aData['creation_date'] = $oAttachment->Get('creation_date');
$aData['user_id_friendlyname'] = $oAttachment->Get('user_id_friendlyname');

View File

@@ -91,7 +91,10 @@ class Lists extends AbstractConfiguration
}
// - Sorting list items by rank
usort($aListItems, function ($a, $b) {
return $a['rank'] > $b['rank'];
if ($a['rank'] == $b['rank']) {
return 0;
}
return $a['rank'] > $b['rank'] ? 1 : -1;
});
$aClassLists[$sListId] = $aListItems;
}

View File

@@ -120,17 +120,6 @@ class ObjectFormManager extends FormManager
{
$aJson = static::DecodeFormManagerData($sJson);
$oConfig = utils::GetConfig();
$bIsContentCheckEnabled = $oConfig->GetModuleSetting(PORTAL_ID, 'enable_formmanager_content_check', true);
if ($bIsContentCheckEnabled && (false === $bTrustContent)) {
/** @noinspection NestedPositiveIfStatementsInspection */
if (isset($aJson['formproperties']['layout']['type']) && ($aJson['formproperties']['layout']['type'] === 'twig')) {
// There will be an IssueLog above in the hierarchy due to the exception, but we are logging here so that we can output the JSON data !
IssueLog::Error('Portal received a query with forbidden twig content!', \LogChannels::PORTAL, ['formmanager_data' => $aJson]);
throw new \SecurityException('Twig content not allowed in this context!');
}
}
/** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */
$oFormManager = parent::FromJSON($sJson);
@@ -703,7 +692,7 @@ class ObjectFormManager extends FormManager
/** @var Field $oField */
$oField = null;
if (is_callable(get_class($oAttDef).'::MakeFormField'))
if (is_callable([$oAttDef, 'MakeFormField']))
{
$oField = $oAttDef->MakeFormField($this->oObject);
}
@@ -1185,16 +1174,18 @@ class ObjectFormManager extends FormManager
$sObjectClass = get_class($this->oObject);
try {
// modification flags
$bIsNew = $this->oObject->IsNew();
$bWasModified = $this->oObject->IsModified();
$bActivateTriggers = (!$bIsNew && $bWasModified);
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
$bAllowWrite = ($sObjectClass === 'Person' && $this->oObject->GetKey() == UserRights::GetContactId());
$bAllowWrite = $this->oContainer->get('security_helper')->IsActionAllowed($bIsNew ? UR_ACTION_CREATE : UR_ACTION_MODIFY, $sObjectClass, $this->oObject->GetKey());
if ($bAllowWrite) {
$this->oObject->AllowWrite(true);
}
// Writing object to DB
$bIsNew = $this->oObject->IsNew();
$bWasModified = $this->oObject->IsModified();
$bActivateTriggers = (!$bIsNew && $bWasModified);
try
{
$this->oObject->DBWrite();

View File

@@ -103,6 +103,12 @@ class SecurityHelper
return false;
}
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
$bObjectIsCurrentUser = ($sObjectClass === 'Person' && $sObjectId == UserRights::GetContactId());
if(in_array($sAction , array(UR_ACTION_MODIFY, UR_ACTION_READ)) && $bObjectIsCurrentUser){
return true;
}
// Checking the scopes layer
// - Transforming scope action as there is only 2 values
$sScopeAction = ($sAction === UR_ACTION_READ) ? UR_ACTION_READ : UR_ACTION_MODIFY;

View File

@@ -20,6 +20,7 @@
namespace Combodo\iTop\Portal\Twig;
use AttributeDate;
use Combodo\iTop\Application\TwigBase\Twig\Extension;
use Twig\Extension\AbstractExtension;
use AttributeDateTime;
@@ -33,194 +34,29 @@ use MetaModel;
/**
* Class AppExtension
*
* Automatically loaded by portal's Symfony configuration to register TWIG extensions.
* The class must be kept by it is using the factorized filters/functions of the iTop core.
*
* @package Combodo\iTop\Portal\Twig
* @since 2.7.0
* @author Bruno Da Silva <bruno.dasilva@combodo.com>
* @deprected 3.1.0 N°4287
*/
class AppExtension extends AbstractExtension
{
/**
* @return array|\Twig\TwigFilter[]|\Twig_SimpleFilter[]
* @inheritDoc
*/
public function getFilters()
{
$filters = array();
// Filter to translate a string via the Dict::S function
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
$filters[] = new Twig_SimpleFilter('dict_s',
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
}
);
// Filter to format a string via the Dict::Format function
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
$filters[] = new Twig_SimpleFilter('dict_format',
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
}
);
/**
* Filter to format output
* example a DateTime is converted to user format
* Usage in twig: {{ 'String:ToFormat'|output_format }}
*
* @since 3.0.0
*/
$filters[] = new Twig_SimpleFilter('date_format',
function ($sDate) {
try
{
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
{
return AttributeDateTime::GetFormat()->Format($sDate);
}
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate)))
{
return AttributeDate::GetFormat()->Format($sDate);
}
}
catch (Exception $e)
{
}
return $sDate;
}
);
/**
* Filter to format output
* example a DateTime is converted to user format
* Usage in twig: {{ 'String:ToFormat'|output_format }}
*
* @since 3.0.0
*/
$filters[] = new Twig_SimpleFilter('size_format',
function ($sSize) {
return utils::BytesToFriendlyFormat($sSize);
}
);
// Filter to enable base64 encode/decode
// Usage in twig: {{ 'String to encode'|base64_encode }}
$filters[] = new Twig_SimpleFilter('base64_encode', 'base64_encode');
$filters[] = new Twig_SimpleFilter('base64_decode', 'base64_decode');
// Filter to enable json decode (encode already exists)
// Usage in twig: {{ aSomeArray|json_decode }}
$filters[] = new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
return json_decode($sJsonString, $bAssoc);
}
);
/**
* Filter to sanitize a text
* Usage in twig: {{ 'variable_name:to-sanitize'|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }}
*
* @uses \utils::Sanitize()
* @since 3.0.0
*/
$filters[] = new Twig_SimpleFilter('sanitize', function (string $sString, string $sFilter) {
return utils::Sanitize($sString, '', $sFilter);
}
);
/**
* Filter to transform the wiki syntax ONLY into HTML.
*
* @uses \AttributeText::RenderWikiHtml()
* @since 3.0.0
*/
$filters[] = new Twig_SimpleFilter('render_wiki_to_html', function ($sString) {
return AttributeText::RenderWikiHtml($sString, true /* Important, otherwise hyperlinks will be tranformed as well */);
}
);
// Filter to add itopversion to an url
$filters[] = new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
$sUrl = utils::AddParameterToUrl($sUrl, 'itopversion', ITOP_VERSION);
return $sUrl;
});
// Filter to add a module's version to an url
$filters[] = new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
$sUrl = utils::AddParameterToUrl($sUrl, 'moduleversion', $sModuleVersion);
return $sUrl;
});
/**
* var_export can be used for example to transform a PHP boolean to 'true' or 'false' strings
* @see https://www.php.net/manual/fr/function.var-export.php
*
* @since 3.0.0
*/
$filters[] = new Twig_SimpleFilter('var_export', 'var_export');
return $filters;
return Extension::GetFilters();
}
/**
* @return array|\Twig\TwigFunction[]|\Twig_SimpleFunction[]
* @inheritDoc
*/
public function getFunctions()
{
$functions = array();
// Function to check our current environment
// Usage in twig: {% if is_development_environment() %}
$functions[] = new Twig_SimpleFunction('is_development_environment', function () {
return utils::IsDevelopmentEnvironment();
});
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$functions[] = new Twig_SimpleFunction('get_config_parameter', function ($sParamName) {
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
});
/**
* Function to get a module setting
* Usage in twig: {{ get_module_setting(<MODULE_CODE>, <PROPERTY_CODE> [, <DEFAULT_VALUE>]) }}
*
* @uses Config::GetModuleSetting()
* @since 3.0.0
*/
$functions[] = new Twig_SimpleFunction('get_module_setting',
function (string $sModuleCode, string $sPropertyCode, $defaultValue = null) {
$oConfig = MetaModel::GetConfig();
return $oConfig->GetModuleSetting($sModuleCode, $sPropertyCode, $defaultValue);
});
/**
* Function to get iTop's app root absolute URL (eg. https://aaa.bbb.ccc/xxx/yyy/)
* Usage in twig: {{ get_absolute_url_app_root() }}
*
* @since 3.0.0
*/
$functions[] = new Twig_SimpleFunction('get_absolute_url_app_root', function () {
return utils::GetAbsoluteUrlAppRoot();
});
/**
* Function to get iTop's modules root absolute URL (eg. https://aaa.bbb.ccc/xxx/yyy/env-zzz/)
* Usage in twig: {{ get_absolute_url_modules_root() }}
*
* @since 3.0.0
*/
$functions[] = new Twig_SimpleFunction('get_absolute_url_modules_root', function () {
return utils::GetAbsoluteUrlModulesRoot();
});
return $functions;
return Extension::GetFunctions();
}

View File

@@ -172,9 +172,13 @@
</value>
</values>
<sql>priority</sql>
<default_value>1</default_value>
<default_value>4</default_value>
<is_null_allowed>false</is_null_allowed>
<display_style>list</display_style>
<dependencies>
<attribute id="impact"/>
<attribute id="urgency"/>
</dependencies>
</field>
<field id="related_change_id" xsi:type="AttributeExternalKey">
<filter><![CDATA[SELECT Change WHERE status != "closed"]]></filter>
@@ -481,39 +485,40 @@
<static>false</static>
<access>public</access>
<type>LifecycleAction</type>
<code><![CDATA[ public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// single person
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a departement!
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$iPriority = 1;
if (isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
{
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
<code><![CDATA[public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// single person
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a departement!
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'priority');
$iPriority = $oAttDef->IsNullAllowed() ? null : $oAttDef->GetDefaultValue();
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')])) {
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
</method>
<method id="ComputeValues">
<static>false</static>
@@ -564,10 +569,10 @@
<rank>50</rank>
</item>
<item id="related_incident_list">
<rank>50</rank>
<rank>60</rank>
</item>
<item id="col:col1">
<rank>60</rank>
<rank>70</rank>
<items>
<item id="fieldset:Ticket:baseinfo">
<rank>10</rank>
@@ -575,31 +580,44 @@
<item id="ref">
<rank>10</rank>
</item>
<item id="title">
<item id="org_id">
<rank>20</rank>
</item>
<item id="org_id">
<item id="caller_id">
<rank>30</rank>
</item>
<item id="status">
<rank>40</rank>
</item>
<item id="priority">
<item id="product">
<rank>50</rank>
</item>
<item id="service_id">
<item id="title">
<rank>60</rank>
</item>
<item id="servicesubcategory_id">
<item id="description">
<rank>70</rank>
</item>
<item id="product">
<rank>80</rank>
</item>
</items>
</item>
<item id="fieldset:Ticket:moreinfo">
<rank>20</rank>
<items>
<item id="service_id">
<rank>10</rank>
</item>
<item id="servicesubcategory_id">
<rank>20</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col2">
<rank>80</rank>
<items>
<item id="fieldset:Ticket:Type">
<rank>10</rank>
<items>
<item id="impact">
<rank>10</rank>
@@ -607,18 +625,13 @@
<item id="urgency">
<rank>20</rank>
</item>
<item id="description">
<item id="priority">
<rank>30</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col2">
<rank>70</rank>
<items>
<item id="fieldset:Ticket:date">
<rank>10</rank>
<rank>20</rank>
<items>
<item id="start_date">
<rank>10</rank>
@@ -637,22 +650,25 @@
</item>
</items>
</item>
</items>
</item>
<item id="col:col3">
<rank>90</rank>
<items>
<item id="fieldset:Ticket:contact">
<rank>20</rank>
<rank>10</rank>
<items>
<item id="caller_id">
<item id="team_id">
<rank>10</rank>
</item>
<item id="team_id">
<rank>20</rank>
</item>
<item id="agent_id">
<rank>30</rank>
<rank>20</rank>
</item>
</items>
</item>
<item id="fieldset:Ticket:relation">
<rank>30</rank>
<rank>20</rank>
<items>
<item id="related_change_id">
<rank>10</rank>
@@ -743,9 +759,9 @@
<menus>
<menu id="ProblemManagement" xsi:type="MenuGroup" _delta="define_if_not_exists">
<rank>42</rank>
<style>
<decoration_classes>fas fa-question</decoration_classes>
</style>
<style>
<decoration_classes>fas fa-question</decoration_classes>
</style>
</menu>
<menu id="Problem:Shortcuts" xsi:type="TemplateMenuNode" _delta="define_if_not_exists">
<rank>5</rank>

View File

@@ -1302,39 +1302,40 @@
<static>false</static>
<access>public</access>
<type>LifecycleAction</type>
<code><![CDATA[ public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// a department
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a person
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$iPriority = 1;
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
{
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
<code><![CDATA[public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// a department
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a person
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'priority');
$iPriority = $oAttDef->IsNullAllowed() ? null : $oAttDef->GetDefaultValue();
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')])) {
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
</method>
<method id="ComputeValues">
<static>false</static>

View File

@@ -1348,39 +1348,40 @@
<static>false</static>
<access>public</access>
<type>LifecycleAction</type>
<code><![CDATA[ public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// a department
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a person
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$iPriority = 1;
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
{
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
<code><![CDATA[public function ComputePriority()
{
// priority[impact][urgency]
$aPriorities = array(
// a department
1 => array(
1 => 1,
2 => 1,
3 => 2,
4 => 4,
),
// a group
2 => array(
1 => 1,
2 => 2,
3 => 3,
4 => 4,
),
// a person
3 => array(
1 => 2,
2 => 3,
3 => 3,
4 => 4,
),
);
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'priority');
$iPriority = $oAttDef->IsNullAllowed() ? null : $oAttDef->GetDefaultValue();
if (isset($aPriorities[(int)$this->Get('impact')]) && isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')])) {
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
}
return $iPriority;
}]]></code>
</method>
<method id="ComputeValues">
<static>false</static>

View File

@@ -1544,120 +1544,100 @@ public function PrefillSearchForm(&$aContextParam)
</reconciliation>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
<sql>name</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="description" xsi:type="AttributeText">
<sql>description</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="org_id" xsi:type="AttributeExternalKey">
<sql>org_id</sql>
<target_class>Organization</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
</field>
<field id="organization_name" xsi:type="AttributeExternalField">
<extkey_attcode>org_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="slts_list" xsi:type="AttributeLinkedSetIndirect">
<linked_class>lnkSLAToSLT</linked_class>
<ext_key_to_me>sla_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
<ext_key_to_remote>slt_id</ext_key_to_remote>
<duplicates/>
</field>
<field id="customercontracts_list" xsi:type="AttributeLinkedSetIndirect">
<linked_class>lnkCustomerContractToService</linked_class>
<ext_key_to_me>sla_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
<ext_key_to_remote>customercontract_id</ext_key_to_remote>
<duplicates>true</duplicates>
</field>
</fields>
<methods>
<method id="DoCheckToWrite">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$aCustomerContracts = $this->Get("customercontracts_list");
foreach ($aCustomerContracts as $sAttCode => $oCustomerContracts)
{
// Recurse inside the subdirectories
$sOql = "SELECT lnkCustomerContractToService AS ccs WHERE ccs.customercontract_id=:customercontract_id AND ccs.service_id=:service_id";
$aQueryParams['customercontract_id'] = $oCustomerContracts->Get("customercontract_id");
$aQueryParams['service_id'] = $oCustomerContracts->Get("service_id");
if ($this->Get("id") != null)
{
$sOql = $sOql." AND ccs.sla_id!=:sla_id";
$aQueryParams['sla_id'] = $this->Get("id");
}
$oQuery = DBSearch::FromOQL($sOql, $aQueryParams);
$oResultSql = new DBObjectSet($oQuery);
$oResultSql->OptimizeColumnLoad(['ccs' => ['customercontract_name','service_name']]);
if ($aCurrentRow = $oResultSql->Fetch())
{
$this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name'));
}
}
}
]]></code>
</method>
</methods>
<field id="name" xsi:type="AttributeString">
<sql>name</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="description" xsi:type="AttributeText">
<sql>description</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="org_id" xsi:type="AttributeExternalKey">
<sql>org_id</sql>
<target_class>Organization</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
</field>
<field id="organization_name" xsi:type="AttributeExternalField">
<extkey_attcode>org_id</extkey_attcode>
<target_attcode>name</target_attcode>
</field>
<field id="slts_list" xsi:type="AttributeLinkedSetIndirect">
<linked_class>lnkSLAToSLT</linked_class>
<ext_key_to_me>sla_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
<ext_key_to_remote>slt_id</ext_key_to_remote>
<duplicates/>
</field>
<field id="customercontracts_list" xsi:type="AttributeLinkedSet">
<linked_class>lnkCustomerContractToService</linked_class>
<ext_key_to_me>sla_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
<edit_mode>none</edit_mode>
</field>
</fields>
<methods/>
<presentation>
<details>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="slts_list">
<rank>40</rank>
</item>
<item id="customercontracts_list">
<rank>50</rank>
</item>
</items>
</details>
<search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
</items>
</search>
<list>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
</items>
</list>
</presentation>
<details>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="slts_list">
<rank>40</rank>
</item>
<item id="customercontracts_list">
<rank>50</rank>
</item>
</items>
</details>
<search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
</items>
</search>
<default_search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
</items>
</default_search>
<list>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="org_id">
<rank>20</rank>
</item>
</items>
</list>
</presentation>
</class>
<class id="SLT" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -1748,83 +1728,110 @@ public function PrefillSearchForm(&$aContextParam)
<values>
<value id="hours">
<code>hours</code>
</value>
<value id="minutes">
<code>minutes</code>
</value>
</values>
<sql>unit</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<display_style>radio_horizontal</display_style>
</field>
</fields>
</value>
<value id="minutes">
<code>minutes</code>
</value>
</values>
<sql>unit</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<display_style>radio_horizontal</display_style>
</field>
<field id="slas_list" xsi:type="AttributeLinkedSetIndirect">
<linked_class>lnkSLAToSLT</linked_class>
<ext_key_to_me>slt_id</ext_key_to_me>
<count_min>0</count_min>
<count_max>0</count_max>
<ext_key_to_remote>sla_id</ext_key_to_remote>
<duplicates/>
</field>
</fields>
<methods/>
<presentation>
<details>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="priority">
<rank>20</rank>
</item>
<item id="request_type">
<rank>30</rank>
</item>
<item id="metric">
<rank>40</rank>
</item>
<item id="value">
<rank>50</rank>
</item>
<item id="unit">
<rank>60</rank>
</item>
</items>
</details>
<search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="priority">
<rank>20</rank>
</item>
<item id="request_type">
<rank>30</rank>
</item>
<item id="metric">
<rank>40</rank>
</item>
<item id="value">
<rank>50</rank>
</item>
<item id="unit">
<rank>60</rank>
</item>
</items>
</search>
<list>
<items>
<item id="priority">
<rank>10</rank>
</item>
<item id="request_type">
<rank>20</rank>
</item>
<item id="metric">
<rank>30</rank>
</item>
<item id="value">
<rank>40</rank>
</item>
<item id="unit">
<rank>50</rank>
</item>
</items>
</list>
</presentation>
</item>
<item id="priority">
<rank>20</rank>
</item>
<item id="request_type">
<rank>30</rank>
</item>
<item id="metric">
<rank>40</rank>
</item>
<item id="value">
<rank>50</rank>
</item>
<item id="unit">
<rank>60</rank>
</item>
<item id="slas_list">
<rank>100</rank>
</item>
</items>
</details>
<search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="priority">
<rank>20</rank>
</item>
<item id="request_type">
<rank>30</rank>
</item>
<item id="metric">
<rank>40</rank>
</item>
<item id="value">
<rank>50</rank>
</item>
<item id="unit">
<rank>60</rank>
</item>
</items>
</search>
<default_search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="priority">
<rank>20</rank>
</item>
<item id="request_type">
<rank>30</rank>
</item>
<item id="metric">
<rank>40</rank>
</item>
</items>
</default_search>
<list>
<items>
<item id="priority">
<rank>10</rank>
</item>
<item id="request_type">
<rank>20</rank>
</item>
<item id="metric">
<rank>30</rank>
</item>
<item id="value">
<rank>40</rank>
</item>
<item id="unit">
<rank>50</rank>
</item>
</items>
</list>
</presentation>
</class>
<class id="lnkSLAToSLT" _delta="define">
<parent>cmdbAbstractObject</parent>

View File

@@ -341,7 +341,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SLA/Attribute:slts_list' => 'SLTs',
'Class:SLA/Attribute:slts_list+' => 'All the service level targets for this SLA',
'Class:SLA/Attribute:customercontracts_list' => 'Customer contracts',
'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracts using this SLA',
'Class:SLA/Attribute:customercontracts_list+' => 'All the customer contracted services using this SLA',
'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Could not save link with Customer contract %1$s and service %2$s : SLA already exists',
));
@@ -384,6 +384,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SLT/Attribute:unit/Value:hours+' => 'hours',
'Class:SLT/Attribute:unit/Value:minutes' => 'minutes',
'Class:SLT/Attribute:unit/Value:minutes+' => 'minutes',
'Class:SLT/Attribute:slas_list' => 'SLAs',
'Class:SLT/Attribute:slas_list+' => 'All the service level agreements using this SLT',
));
//

View File

@@ -331,7 +331,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SLA/Attribute:slts_list' => 'SLTs',
'Class:SLA/Attribute:slts_list+' => '',
'Class:SLA/Attribute:customercontracts_list' => 'Contrats clients',
'Class:SLA/Attribute:customercontracts_list+' => '',
'Class:SLA/Attribute:customercontracts_list+' => 'Services contractés par des clients avec ce niveau de service',
'Class:SLA/Error:UniqueLnkCustomerContractToService' => 'Impossible de sauvegarder le lien avec le contrat client %1$s et le service %2$s : un SLA existe déjà.',
));
@@ -374,6 +374,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SLT/Attribute:unit/Value:hours+' => 'heures',
'Class:SLT/Attribute:unit/Value:minutes' => 'minutes',
'Class:SLT/Attribute:unit/Value:minutes+' => 'minutes',
'Class:SLT/Attribute:slas_list' => 'SLAs',
'Class:SLT/Attribute:slas_list+' => 'Tous les niveaux de service utilisant cet objectif',
));
//

View File

@@ -1873,6 +1873,14 @@
<precompiled_stylesheet>itop-structure/precompiled-themes/test-red/main.css</precompiled_stylesheet>
</theme>
</themes>
<themes_common>
<variables>
</variables>
<imports>
</imports>
<stylesheets>
</stylesheets>
</themes_common>
</branding>
<user_rights>
<groups>