Merge remote-tracking branch 'origin/support/2.7' into develop

# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php
#	datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
This commit is contained in:
Pierre Goiffon
2021-11-26 17:22:19 +01:00
7 changed files with 84 additions and 47 deletions

View File

@@ -1128,7 +1128,7 @@ class Config
'svg_sanitizer' => [
'type' => 'string',
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
'default' => 'SvgDOMSanitizer',
'default' => 'SVGDOMSanitizer',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,

View File

@@ -419,7 +419,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
/**
* @since 2.6.5 2.7.6 3.0.0 N°4360
*/
class SvgDOMSanitizer extends DOMSanitizer
class SVGDOMSanitizer extends DOMSanitizer
{
public function GetTagsWhiteList()
{

View File

@@ -178,6 +178,12 @@ SQL;
SetupLog::Info("Initializing attachment/item_org_id - zero to the container");
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_org_id = 0");
$oSet = new DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad([
'Attachment' => [
'item_class',
'item_id',
]
]);
$iUpdated = 0;
while ($oAttachment = $oSet->Fetch())
{

View File

@@ -22,7 +22,7 @@ namespace Combodo\iTop\Portal\Form;
use AttachmentPlugIn;
use AttributeDateTime;
use AttributeTagSet;
use AttributeSet;
use CMDBChangeOpAttachmentAdded;
use CMDBChangeOpAttachmentRemoved;
use Combodo\iTop\Form\Field\Field;
@@ -42,7 +42,6 @@ use Exception;
use InlineImage;
use IssueLog;
use MetaModel;
use ormTagSet;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -91,26 +90,34 @@ class ObjectFormManager extends FormManager
* - formmode : view|edit|create
* - values for parent
*
* @param bool $bTrustContent if false then won't allow TWIG content
*
* @inheritDoc
* @throws \Exception
* @throws \SecurityException if twig content is present and $bTrustContent is false
*/
static function FromJSON($sJson)
public static function FromJSON($sJson, $bTrustContent = false)
{
if (is_array($sJson))
{
if (is_array($sJson)) {
$aJson = $sJson;
}
else
{
} else {
$aJson = json_decode($sJson, true);
}
if (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);
// Retrieving object to edit
if (!isset($aJson['formobject_class']))
{
if (!isset($aJson['formobject_class'])) {
throw new Exception('Object class must be defined in order to generate the form');
}
$sObjectClass = $aJson['formobject_class'];
@@ -151,14 +158,44 @@ class ObjectFormManager extends FormManager
}
// Retrieving callback urls
if (!isset($aJson['formcallbacks']))
{
if (!isset($aJson['formcallbacks'])) {
// TODO
}
return $oFormManager;
}
/**
* @param string $sPostedFormManagerData received data from the browser
* @param array $aOriginalFormProperties data generated server side
*
* @return bool true if the data are identical
*
* @since 2.7.6 3.0.0 N°4384 check formmanager_data
*/
public static function CanTrustFormLayoutContent($sPostedFormManagerData, $aOriginalFormProperties)
{
$aPostedFormManagerData = json_decode($sPostedFormManagerData, true);
$sPostedFormLayoutType = (isset($aPostedFormManagerData['formproperties']['layout']['type'])) ? $aPostedFormManagerData['formproperties']['layout']['type'] : '';
if ($sPostedFormLayoutType === 'xhtml') {
return true;
}
// we need to parse the content so that autoclose tags are returned correctly (`<div />` => `<div></div>`)
$oHtmlDocument = new \DOMDocument();
$sPostedFormLayoutContent = (isset($aPostedFormManagerData['formproperties']['layout']['content'])) ? $aPostedFormManagerData['formproperties']['layout']['content'] : '';
$oHtmlDocument->loadXML('<root>'.$sPostedFormLayoutContent.'</root>');
$sPostedFormLayoutRendered = $oHtmlDocument->saveHTML();
$sOriginalFormLayoutContent = (isset($aOriginalFormProperties['layout']['content'])) ? $aOriginalFormProperties['layout']['content'] : '';
$oHtmlDocument->loadXML('<root>'.$sOriginalFormLayoutContent.'</root>');
$sOriginalFormLayoutContentRendered = $oHtmlDocument->saveHTML();
return ($sPostedFormLayoutRendered === $sOriginalFormLayoutContentRendered);
}
/**
*
* @return \Symfony\Component\DependencyInjection\ContainerInterface
@@ -1299,25 +1336,19 @@ class ObjectFormManager extends FormManager
// Setting value in the object
$this->oObject->Set($sAttCode, $oLinkSet);
}
elseif ($oAttDef instanceof AttributeTagSet)
{
/** @var \ormTagSet $oTagSet */
$oTagSet = $this->oObject->Get($sAttCode);
if (is_null($oTagSet))
{
$oTagSet = new ormTagSet(get_class($this->oObject), $sAttCode, $oAttDef->GetMaxItems());
} elseif ($oAttDef instanceof AttributeSet) {
/** @var \ormSet $oTagSet */
$oOrmSet = $this->oObject->Get($sAttCode);
if (is_null($oOrmSet)) {
$oOrmSet = new \ormSet(get_class($this->oObject), $sAttCode, $oAttDef->GetMaxItems());
}
$oTagSet->ApplyDelta(json_decode($value, true));
$this->oObject->Set($sAttCode, $oTagSet);
}
elseif ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
$oOrmSet->ApplyDelta(json_decode($value, true));
$this->oObject->Set($sAttCode, $oOrmSet);
} elseif ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($value != null)
{
if ($value != null) {
$value = $oAttDef->GetFormat()->Parse($value);
if (is_object($value))
{
if (is_object($value)) {
$value = $value->format($oAttDef->GetInternalFormat());
}
}

View File

@@ -108,10 +108,10 @@ class ObjectFormHandlerHelper
/**
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param string $sMode
* @param string $sObjectClass
* @param string $sObjectId
* @param string $aFormProperties
* @param string $sMode
* @param string $sObjectClass
* @param string $sObjectId
* @param array $aFormProperties
*
* @return array
*
@@ -127,9 +127,10 @@ class ObjectFormHandlerHelper
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
// - Retrieve form properties
$aOriginalFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
if ($aFormProperties === null)
{
$aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
$aFormProperties = $aOriginalFormProperties;
}
// - Create and
@@ -281,12 +282,11 @@ class ObjectFormHandlerHelper
$oFormManager->Build();
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
// Check the number of editable fields
// Check the number of editable fields
$aFormData['editable_fields_count'] = $oFormManager->GetForm()->GetEditableFieldCount();
}
else
{
} else {
// Update / Submit / Cancel
/** @var \Combodo\iTop\Portal\Form\ObjectFormManager $sFormManagerClass */
$sFormManagerClass = $this->oRequestManipulator->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW);
$sFormManagerData = $this->oRequestManipulator->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW);
if (empty($sFormManagerClass) || empty($sFormManagerData))
@@ -295,13 +295,12 @@ class ObjectFormHandlerHelper
throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameters formmanager_class and formmanager_data must be defined.');
}
/** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData);
$bTrustContent = $sFormManagerClass::CanTrustFormLayoutContent($sFormManagerData, $aOriginalFormProperties);
$oFormManager = $sFormManagerClass::FromJSON($sFormManagerData, $bTrustContent);
$oFormManager->SetContainer($this->oContainer);
// Applying action rules if present
if (($oFormManager->GetActionRulesToken() !== null) && ($oFormManager->GetActionRulesToken() !== ''))
{
if (($oFormManager->GetActionRulesToken() !== null) && ($oFormManager->GetActionRulesToken() !== '')) {
$aActionRules = ContextManipulatorHelper::DecodeRulesToken($oFormManager->GetActionRulesToken());
$oObj = $oFormManager->GetObject();
$this->oContextManipulator->PrepareObject($aActionRules, $oObj);

View File

@@ -40,7 +40,8 @@ abstract class FormManager
* - formrenderer_class : The class of the FormRenderer to use in the FormManager
* - formrenderer_endpoint : The endpoint of the renderer
*
* @param string $sJson
* @param string|string[] $sJson
*
* @return $this
*/
static function FromJSON($sJson)

View File

@@ -2,7 +2,7 @@
namespace Combodo\iTop\Test\UnitTest\Core\Sanitizer;
use SvgDOMSanitizer;
use SVGDOMSanitizer;
require_once __DIR__.'/AbstractDOMSanitizerTest.php';
@@ -13,7 +13,7 @@ require_once __DIR__.'/AbstractDOMSanitizerTest.php';
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class SvgDOMSanitizerTest extends AbstractDOMSanitizerTest
class SVGDOMSanitizerTest extends AbstractDOMSanitizerTest
{
/**
* @dataProvider DoSanitizeProvider
@@ -26,7 +26,7 @@ class SvgDOMSanitizerTest extends AbstractDOMSanitizerTest
$sOutputHtml = $this->ReadTestFile($sFileToTest, self::OUTPUT_DIRECTORY);
$sOutputHtml = $this->RemoveNewLines($sOutputHtml);
$oSanitizer = new SvgDOMSanitizer();
$oSanitizer = new SVGDOMSanitizer();
$sRes = $oSanitizer->DoSanitize($sInputHtml);
// Removing newlines as the parser gives different results depending on the PHP version