mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-04 16:44:11 +01:00
Merge remote-tracking branch 'origin/feature/8772_form_dependencies_manager' into feature/8772_form_dependencies_manager
This commit is contained in:
@@ -56,11 +56,14 @@ use ZipArchive;
|
||||
|
||||
abstract class Controller extends AbstractController
|
||||
{
|
||||
const ENUM_PAGE_TYPE_HTML = 'html';
|
||||
const ENUM_PAGE_TYPE_BASIC_HTML = 'basic_html';
|
||||
const ENUM_PAGE_TYPE_AJAX = 'ajax';
|
||||
const ENUM_PAGE_TYPE_HTML = 'html';
|
||||
const ENUM_PAGE_TYPE_BASIC_HTML = 'basic_html';
|
||||
const ENUM_PAGE_TYPE_AJAX = 'ajax';
|
||||
const ENUM_PAGE_TYPE_TURBO_FORM_AJAX = 'turbo_ajax';
|
||||
const ENUM_PAGE_TYPE_SETUP = 'setup';
|
||||
const ENUM_PAGE_TYPE_SETUP = 'setup';
|
||||
|
||||
const TWIG_ERROR = 'error';
|
||||
const TWIG_WARNING = 'warning';
|
||||
|
||||
/** @var \Twig\Environment */
|
||||
private $oTwig;
|
||||
@@ -105,12 +108,15 @@ abstract class Controller extends AbstractController
|
||||
/** @var CsrfTokenManager Csrf manager (from Symfony form component @link https://symfony.com/doc/current/security/csrf.html) */
|
||||
private CsrfTokenManager $oCsrfTokenManager;
|
||||
private ?string $sContentType = null;
|
||||
private ?string $sPageType = null;
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
*
|
||||
* @param string $sViewPath Path of the twig files
|
||||
* @param string $sModuleName name of the module (or 'core' if not a module)
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct($sViewPath = '', $sModuleName = 'core', $aAdditionalPaths = [])
|
||||
{
|
||||
@@ -190,12 +196,10 @@ abstract class Controller extends AbstractController
|
||||
$sModulePath = dirname(dirname($this->GetDir()));
|
||||
$this->SetModuleName(basename($sModulePath));
|
||||
$this->SetViewPath($sModulePath.'/view');
|
||||
try
|
||||
{
|
||||
try {
|
||||
$this->aDefaultParams = array('sIndexURL' => utils::GetAbsoluteUrlModulePage($this->m_sModule, 'index.php'));
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -245,8 +249,7 @@ abstract class Controller extends AbstractController
|
||||
*/
|
||||
public function HandleOperation()
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
$this->CheckAccess();
|
||||
$this->m_sOperation = utils::ReadParam('operation', $this->sDefaultOperation);
|
||||
|
||||
@@ -261,8 +264,7 @@ abstract class Controller extends AbstractController
|
||||
|
||||
$this->DisplayBadRequest();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
|
||||
@@ -280,8 +282,7 @@ abstract class Controller extends AbstractController
|
||||
*/
|
||||
public function HandleAjaxOperation()
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
$this->CheckAccess();
|
||||
$this->m_sOperation = utils::ReadParam('operation', $this->sDefaultOperation);
|
||||
|
||||
@@ -296,8 +297,7 @@ abstract class Controller extends AbstractController
|
||||
|
||||
$this->DisplayPageNotFound();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$aResponse = array('sError' => $e->getMessage());
|
||||
echo json_encode($aResponse);
|
||||
@@ -312,6 +312,7 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
|
||||
$this->$sMethodName();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -334,13 +335,12 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°3606 - Adapt TwigBase Controller for combodo-monitoring extension
|
||||
* @throws \Exception
|
||||
* @since 3.0.0 N°3606 - Adapt TwigBase Controller for combodo-monitoring extension
|
||||
*/
|
||||
protected function CheckAccess()
|
||||
{
|
||||
if ($this->bCheckDemoMode && MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
if ($this->bCheckDemoMode && MetaModel::GetConfig()->Get('demo_mode')) {
|
||||
throw new Exception("Sorry, iTop is in <b>demonstration mode</b>: this feature is disabled.");
|
||||
}
|
||||
|
||||
@@ -348,31 +348,30 @@ abstract class Controller extends AbstractController
|
||||
|
||||
$sConfiguredAccessTokenValue = empty($this->sAccessTokenConfigParamId) ? "" : trim(MetaModel::GetConfig()->GetModuleSetting($sExecModule, $this->sAccessTokenConfigParamId));
|
||||
|
||||
if (empty($sExecModule) || empty($sConfiguredAccessTokenValue)){
|
||||
if (empty($sExecModule) || empty($sConfiguredAccessTokenValue)) {
|
||||
LoginWebPage::DoLogin($this->bMustBeAdmin);
|
||||
} else {
|
||||
//token mode without login required
|
||||
//N°7147 - Error HTTP 500 due to access_token not URL decoded
|
||||
$sPassedToken = utils::ReadPostedParam($this->sAccessTokenConfigParamId, null, false, 'raw_data');
|
||||
if (is_null($sPassedToken)){
|
||||
if (is_null($sPassedToken)) {
|
||||
$sPassedToken = utils::ReadParam($this->sAccessTokenConfigParamId, null, false, 'raw_data');
|
||||
}
|
||||
|
||||
$sDecodedPassedToken = urldecode($sPassedToken);
|
||||
if ($sDecodedPassedToken !== $sConfiguredAccessTokenValue){
|
||||
if ($sDecodedPassedToken !== $sConfiguredAccessTokenValue) {
|
||||
$sMsg = "Invalid token passed under '$this->sAccessTokenConfigParamId' http param to reach '$sExecModule' page.";
|
||||
IssueLog::Error($sMsg, null,
|
||||
[
|
||||
'sHtmlDecodedToken' => $sDecodedPassedToken,
|
||||
'conf param ID' => $this->sAccessTokenConfigParamId,
|
||||
'conf param ID' => $this->sAccessTokenConfigParamId,
|
||||
]
|
||||
);
|
||||
throw new Exception("Invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->sMenuId))
|
||||
{
|
||||
if (!empty($this->sMenuId)) {
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->sMenuId);
|
||||
}
|
||||
}
|
||||
@@ -482,9 +481,9 @@ abstract class Controller extends AbstractController
|
||||
$this->DisplayPage($aParams, $sTemplateName, 'setup');
|
||||
}
|
||||
|
||||
public function DisplayTurboAjaxPage ($aParams = array(), $sTemplateName = 'turbo-ajax-update.html.twig')
|
||||
public function DisplayTurboAjaxPage($aParams = array())
|
||||
{
|
||||
$this->DisplayPage($aParams, $sTemplateName, self::ENUM_PAGE_TYPE_TURBO_FORM_AJAX);
|
||||
$this->DisplayPage($aParams, 'application/forms/turbo-ajax-update', self::ENUM_PAGE_TYPE_TURBO_FORM_AJAX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -498,37 +497,42 @@ abstract class Controller extends AbstractController
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function DisplayPage($aParams = array(), $sTemplateName = null, $sPageType = 'html')
|
||||
public function DisplayPage($aParams = array(), $sTemplateName = null, $sPageType = self::ENUM_PAGE_TYPE_HTML)
|
||||
{
|
||||
if (empty($sTemplateName)) {
|
||||
$sTemplateName = $this->m_sOperation;
|
||||
}
|
||||
|
||||
$this->sPageType = $sPageType;
|
||||
|
||||
$aParams = array_merge($this->GetDefaultParameters(), $aParams);
|
||||
$this->CreatePage($sPageType);
|
||||
$sHTMLContent = $this->RenderTemplate($aParams, $sTemplateName, 'html', $sErrorMsg);
|
||||
$sHTMLContent = $this->RenderTemplate($aParams, $sTemplateName, 'html', $aErrors);
|
||||
if ($sHTMLContent !== false) {
|
||||
$this->AddToPage($sHTMLContent);
|
||||
}
|
||||
$sJSScript = $this->RenderTemplate($aParams, $sTemplateName, 'js', $sErrorMsg);
|
||||
$sJSScript = $this->RenderTemplate($aParams, $sTemplateName, 'js', $aErrors);
|
||||
if ($sJSScript !== false) {
|
||||
$this->AddScriptToPage($sJSScript);
|
||||
}
|
||||
$sReadyScript = $this->RenderTemplate($aParams, $sTemplateName, 'ready.js', $sErrorMsg);
|
||||
$sReadyScript = $this->RenderTemplate($aParams, $sTemplateName, 'ready.js', $aErrors);
|
||||
if ($sReadyScript !== false) {
|
||||
$this->AddReadyScriptToPage($sReadyScript);
|
||||
}
|
||||
$sStyle = $this->RenderTemplate($aParams, $sTemplateName, 'css', $sErrorMsg);
|
||||
$sStyle = $this->RenderTemplate($aParams, $sTemplateName, 'css', $aErrors);
|
||||
if ($sStyle !== false) {
|
||||
$this->AddStyleToPage($sStyle);
|
||||
}
|
||||
if ($sHTMLContent === false && $sJSScript === false && $sReadyScript === false && $sStyle === false) {
|
||||
if (utils::IsNullOrEmptyString($sErrorMsg)) {
|
||||
$sErrorMsg = "Missing TWIG template for $sTemplateName";
|
||||
if (is_null($aErrors) || count($aErrors) === 0) {
|
||||
$aErrors[self::TWIG_ERROR] = "Missing TWIG template for $sTemplateName";
|
||||
}
|
||||
IssueLog::Error($sErrorMsg);
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_error.html.twig', ['sControllerError' => $sErrorMsg]));
|
||||
IssueLog::Error(implode("\n",$aErrors[self::TWIG_ERROR] ?? [])."\n".implode("\n",$aErrors[self::TWIG_WARNING] ?? []));
|
||||
} else {
|
||||
// Ignore warnings
|
||||
$aErrors[self::TWIG_WARNING] = [];
|
||||
}
|
||||
$this->RenderErrors($aErrors);
|
||||
|
||||
$this->ManageDebugExtensions($aParams, $sPageType);
|
||||
|
||||
@@ -553,6 +557,7 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
$this->SetContentTypeToPage();
|
||||
$this->OutputPage();
|
||||
$this->sPageType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,8 +574,7 @@ abstract class Controller extends AbstractController
|
||||
$oKpi = new ExecutionKPI();
|
||||
http_response_code($iResponseCode);
|
||||
header('Content-Type: application/json');
|
||||
foreach ($aHeaders as $sHeader)
|
||||
{
|
||||
foreach ($aHeaders as $sHeader) {
|
||||
header($sHeader);
|
||||
}
|
||||
$sJSON = json_encode($aParams);
|
||||
@@ -623,16 +627,13 @@ abstract class Controller extends AbstractController
|
||||
$sArchiveFileFullPath = tempnam(SetupUtils::GetTmpDir(), 'itop_download-').'.zip';
|
||||
$oArchive = new ZipArchive();
|
||||
$oArchive->open($sArchiveFileFullPath, ZipArchive::CREATE);
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
foreach ($aFiles as $sFile) {
|
||||
$oArchive->addFile($sFile, basename($sFile));
|
||||
}
|
||||
$oArchive->close();
|
||||
|
||||
if ($bUnlinkFiles)
|
||||
{
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
if ($bUnlinkFiles) {
|
||||
foreach ($aFiles as $sFile) {
|
||||
unlink($sFile);
|
||||
}
|
||||
}
|
||||
@@ -645,8 +646,7 @@ abstract class Controller extends AbstractController
|
||||
$sFileMimeType = utils::GetFileMimeType($sFilePath);
|
||||
header('Content-Type: '.$sFileMimeType);
|
||||
|
||||
if ($bFileTransfer)
|
||||
{
|
||||
if ($bFileTransfer) {
|
||||
header('Content-Disposition: attachment; filename="'.$sDownloadArchiveName.'"');
|
||||
}
|
||||
|
||||
@@ -662,8 +662,7 @@ abstract class Controller extends AbstractController
|
||||
|
||||
readfile($sFilePath);
|
||||
|
||||
if ($bRemoveFile)
|
||||
{
|
||||
if ($bRemoveFile) {
|
||||
unlink($sFilePath);
|
||||
}
|
||||
exit(0);
|
||||
@@ -675,6 +674,7 @@ abstract class Controller extends AbstractController
|
||||
* @api
|
||||
*
|
||||
* @param string $sScript Script path to link
|
||||
*
|
||||
* @since 3.2.0 $sScript must be absolute URI
|
||||
*/
|
||||
public function AddLinkedScript($sScript)
|
||||
@@ -688,6 +688,7 @@ abstract class Controller extends AbstractController
|
||||
* @api
|
||||
*
|
||||
* @param string $sStylesheet Stylesheet path to link
|
||||
*
|
||||
* @since 3.2.0 $sScript must be absolute URI
|
||||
*/
|
||||
public function AddLinkedStylesheet($sStylesheet)
|
||||
@@ -710,13 +711,13 @@ abstract class Controller extends AbstractController
|
||||
/**
|
||||
* Add an AJAX tab to the current page
|
||||
*
|
||||
* @param string $sCode Code of the tab
|
||||
* @api
|
||||
*
|
||||
* @param string $sURL URL to call when the tab is activated
|
||||
* @param bool $bCache If true, cache the result for the current web page
|
||||
* @param string $sLabel Label of the tab (if null the code is translated)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sCode Code of the tab
|
||||
*/
|
||||
public function AddAjaxTab($sCode, $sURL, $bCache = true, $sLabel = null)
|
||||
{
|
||||
@@ -728,6 +729,7 @@ abstract class Controller extends AbstractController
|
||||
|
||||
/**
|
||||
* @param array $aBlockParams
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetBlockParams(array $aBlockParams)
|
||||
@@ -747,18 +749,20 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4760 method creation
|
||||
* @see Controller::SetBreadCrumbEntry() to set breadcrumb content (by default will be title)
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4760 method creation
|
||||
*/
|
||||
public function DisableBreadCrumb() {
|
||||
public function DisableBreadCrumb()
|
||||
{
|
||||
$this->bIsBreadCrumbEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4760 method creation
|
||||
* @see iTopWebPage::SetBreadCrumbEntry()
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4760 method creation
|
||||
*/
|
||||
public function SetBreadCrumbEntry($sId, $sLabel, $sDescription, $sUrl = '', $sIcon = '') {
|
||||
public function SetBreadCrumbEntry($sId, $sLabel, $sDescription, $sUrl = '', $sIcon = '')
|
||||
{
|
||||
$this->aBreadCrumbEntry = [$sId, $sLabel, $sDescription, $sUrl, $sIcon];
|
||||
}
|
||||
|
||||
@@ -779,7 +783,7 @@ abstract class Controller extends AbstractController
|
||||
*/
|
||||
public function GetFormBuilder(AbstractFormBlock $oFormBlock, mixed $data = null): FormBuilderInterface
|
||||
{
|
||||
return $this->oFormFactoryBuilder->getFormFactory()->createNamedBuilder($oFormBlock->GetName(), $oFormBlock->GetFormType(), $data,$oFormBlock->GetOptions());
|
||||
return $this->oFormFactoryBuilder->getFormFactory()->createNamedBuilder($oFormBlock->GetName(), $oFormBlock->GetFormType(), $data, $oFormBlock->GetOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -797,7 +801,8 @@ abstract class Controller extends AbstractController
|
||||
if (is_null($data)) {
|
||||
$data = $type::GetDefaultData();
|
||||
}
|
||||
return $this->GetFormBuilder($type, $data,$options)->getForm();
|
||||
|
||||
return $this->GetFormBuilder($type, $data, $options)->getForm();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -808,35 +813,34 @@ abstract class Controller extends AbstractController
|
||||
* @return string|false
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function RenderTemplate(array $aParams, string $sName, string $sTemplateFileExtension, string &$sErrorMsg = null): string|false
|
||||
private function RenderTemplate(array $aParams, string $sName, string $sTemplateFileExtension, ?array &$aErrors): string|false
|
||||
{
|
||||
if (is_null($aErrors)) {
|
||||
$aErrors = [];
|
||||
}
|
||||
$sTemplateFile = $sName.'.'.$sTemplateFileExtension.'.twig';
|
||||
if (empty($this->oTwig))
|
||||
{
|
||||
if (empty($this->oTwig)) {
|
||||
throw new Exception('Not initialized. Call Controller::InitFromModule() or Controller::SetViewPath() before any display');
|
||||
}
|
||||
try
|
||||
{
|
||||
try {
|
||||
return $this->oTwig->render($sTemplateFile, $aParams);
|
||||
}
|
||||
catch (SyntaxError $e) {
|
||||
IssueLog::Error($e->getMessage().' - file: '.$e->getFile().'('.$e->getLine().')');
|
||||
return $this->oTwig->render('application/forms/itop_error.html.twig', ['sControllerError' => $e->getMessage()]);
|
||||
$aErrors[self::TWIG_ERROR][] = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sExceptionMessage = $e->getMessage();
|
||||
if (str_contains($sExceptionMessage, 'at line')) {
|
||||
IssueLog::Error($sExceptionMessage);
|
||||
return $this->oTwig->render('application/forms/itop_error.html.twig', ['sControllerError' => $sExceptionMessage]);
|
||||
$aErrors[self::TWIG_ERROR][] = $sExceptionMessage;
|
||||
return false;
|
||||
}
|
||||
if (!str_contains($sExceptionMessage, 'Unable to find template'))
|
||||
{
|
||||
if (!str_contains($sExceptionMessage, 'Unable to find template')) {
|
||||
IssueLog::Error($sExceptionMessage);
|
||||
}
|
||||
if (is_null($sErrorMsg)) {
|
||||
$sErrorMsg = '';
|
||||
}
|
||||
$sErrorMsg .= $sExceptionMessage."\n";
|
||||
$aErrors[self::TWIG_WARNING][] = $sExceptionMessage;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -849,8 +853,7 @@ abstract class Controller extends AbstractController
|
||||
*/
|
||||
private function CreatePage(string $sPageType): void
|
||||
{
|
||||
switch ($sPageType)
|
||||
{
|
||||
switch ($sPageType) {
|
||||
case self::ENUM_PAGE_TYPE_HTML:
|
||||
$this->oPage = new iTopWebPage($this->GetOperationTitle(), false);
|
||||
$this->oPage->add_http_headers();
|
||||
@@ -885,7 +888,7 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
$this->oTwig->addGlobal('UIBlockParent', [$this->oPage]);
|
||||
$this->oTwig->addGlobal('oPage', $this->oPage);
|
||||
$this->oTwig->addGlobal('debug', utils::IsDevelopmentEnvironment());
|
||||
//$this->oTwig->addGlobal('debug', utils::IsDevelopmentEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -959,11 +962,10 @@ abstract class Controller extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sKey
|
||||
* @param $value
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private function SetBlockParamToPage(string $sKey, $value)
|
||||
@@ -1002,7 +1004,7 @@ abstract class Controller extends AbstractController
|
||||
if (!in_array($sPageType, [self::ENUM_PAGE_TYPE_HTML, self::ENUM_PAGE_TYPE_AJAX, self::ENUM_PAGE_TYPE_TURBO_FORM_AJAX])) {
|
||||
return;
|
||||
}
|
||||
$sContent = '';
|
||||
$aProfilesInfo = [];
|
||||
foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProfilerExtension::class) as $sExtension) {
|
||||
/** @var \Combodo\iTop\Application\TwigBase\Controller\iProfilerExtension $oExtensionInstance */
|
||||
$oExtensionInstance = $sExtension::GetInstance();
|
||||
@@ -1021,17 +1023,48 @@ abstract class Controller extends AbstractController
|
||||
if (is_array($aSaas)) {
|
||||
$this->aSaas = array_merge($this->aSaas, $aSaas);
|
||||
}
|
||||
$sContent .= $this->oTwig->render($sDebugTemplate, $aDebugParams);
|
||||
$aProfilesInfo[] = ['sTemplate' => $sDebugTemplate, 'aProfileData' => $aDebugParams];
|
||||
}
|
||||
}
|
||||
if ($sContent === '') {
|
||||
if (count($aProfilesInfo) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sPageType === self::ENUM_PAGE_TYPE_HTML || $sPageType === self::ENUM_PAGE_TYPE_AJAX) {
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_debug.html.twig', ['sProfilerContent' => $sContent]));
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_debug.html.twig', ['aProfilesInfo' => $aProfilesInfo]));
|
||||
} elseif ($sPageType === self::ENUM_PAGE_TYPE_TURBO_FORM_AJAX) {
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_debug_update.html.twig', ['sProfilerContent' => $sContent]));
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_debug_update.html.twig', ['aProfilesInfo' => $aProfilesInfo]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* render error message
|
||||
*
|
||||
* @param array $aErrors
|
||||
*
|
||||
* @return string
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function RenderErrors(array $aErrors): void
|
||||
{
|
||||
if (is_null($this->sPageType)) {
|
||||
return;
|
||||
}
|
||||
$sErrorMsg = '';
|
||||
if (count($aErrors[self::TWIG_ERROR] ?? []) > 0) {
|
||||
$sErrorMsg .= implode("\n", $aErrors[self::TWIG_ERROR]);
|
||||
$sErrorMsg .= "\n";
|
||||
}
|
||||
if (count($aErrors[self::TWIG_WARNING] ?? []) > 0) {
|
||||
$sErrorMsg .= implode("\n", $aErrors[self::TWIG_WARNING]);
|
||||
}
|
||||
|
||||
if ($this->sPageType === self::ENUM_PAGE_TYPE_TURBO_FORM_AJAX) {
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_error_update.html.twig', ['sControllerError' => $sErrorMsg]));
|
||||
}
|
||||
|
||||
$this->AddToPage($this->oTwig->render('application/forms/itop_error.html.twig', ['sControllerError' => $sErrorMsg]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,9 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use ContextTag;
|
||||
use DateTimeFormat;
|
||||
use DBSearch;
|
||||
use DeprecatedCallsLog;
|
||||
use Dict;
|
||||
use ExecutionKPI;
|
||||
use InlineImage;
|
||||
use iPageUIBlockExtension;
|
||||
use MetaModel;
|
||||
use UserRights;
|
||||
use utils;
|
||||
@@ -756,7 +754,7 @@ HTML;
|
||||
}
|
||||
}
|
||||
|
||||
// Render HTKL content
|
||||
// Render HTML content
|
||||
$sHtml = $this->RenderContent();
|
||||
|
||||
// Echo global HTML
|
||||
|
||||
@@ -134,8 +134,7 @@ abstract class AbstractFormBlock
|
||||
*/
|
||||
public function AddInput(string $sName, string $sType): void
|
||||
{
|
||||
$oFormInput = new FormInput($sName, $sType);
|
||||
$oFormInput->SetOwnerBlock($this);
|
||||
$oFormInput = new FormInput($sName, $sType, $this);
|
||||
$this->aFormInputs[$oFormInput->GetName()] = $oFormInput;
|
||||
}
|
||||
|
||||
@@ -167,8 +166,7 @@ abstract class AbstractFormBlock
|
||||
*/
|
||||
public function AddOutput(string $sName, string $sType, AbstractConverter $oConverter = null): void
|
||||
{
|
||||
$oFormOutput = new FormOutput($sName, $sType, $oConverter);
|
||||
$oFormOutput->SetOwnerBlock($this);
|
||||
$oFormOutput = new FormOutput($sName, $sType, $this, $oConverter);
|
||||
$this->aFormOutputs[$oFormOutput->GetName()] = $oFormOutput;
|
||||
}
|
||||
|
||||
@@ -296,6 +294,7 @@ abstract class AbstractFormBlock
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -333,6 +332,7 @@ abstract class AbstractFormBlock
|
||||
$aBindings[$oFormOutput->GetName()] = $oFormOutput->GetBinding();
|
||||
}
|
||||
}
|
||||
|
||||
return $aBindings;
|
||||
}
|
||||
|
||||
@@ -389,11 +389,11 @@ abstract class AbstractFormBlock
|
||||
/** Iterate throw output @var FormOutput $oFormOutput */
|
||||
foreach ($this->aFormOutputs as $oFormOutput) {
|
||||
|
||||
try{
|
||||
try {
|
||||
// Compute the output value
|
||||
$oFormOutput->ComputeValue($sEventType, $oData);
|
||||
}
|
||||
catch(IOException $oException){
|
||||
catch (IOException $oException) {
|
||||
IssueLog::Exception(sprintf('Unable to compute values for output %s of block %s', $oFormOutput->GetName(), $this->GetName()), $oException);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ use Combodo\iTop\Forms\Block\FormType\ChoiceFormType;
|
||||
*/
|
||||
class ChoiceFormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return ChoiceFormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -22,13 +22,13 @@ class CollectionBlock extends AbstractFormBlock
|
||||
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return CollectionType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
$sBlockEntryType = $this->GetOptions()['block_entry_type'];
|
||||
@@ -39,7 +39,7 @@ class CollectionBlock extends AbstractFormBlock
|
||||
$oBlock = new ($sBlockEntryType)('prototype', $sBlockEntryOptions);
|
||||
|
||||
return [
|
||||
'entry_type' => $oBlock->GetFormType()
|
||||
'entry_type' => $oBlock->GetFormType(),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\FormsException;
|
||||
use Exception;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
|
||||
/**
|
||||
@@ -40,17 +41,17 @@ class FormBlock extends AbstractFormBlock
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return FormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [
|
||||
'compound' => true
|
||||
'compound' => true,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -68,6 +69,7 @@ class FormBlock extends AbstractFormBlock
|
||||
$oSubFormBlock = new ($sType)($sName, $aOptions);
|
||||
$this->aChildrenBlocks[$sName] = $oSubFormBlock;
|
||||
$oSubFormBlock->SetParent($this);
|
||||
|
||||
return $oSubFormBlock;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
*/
|
||||
class TextAreaFormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return TextareaType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [];
|
||||
|
||||
@@ -8,10 +8,9 @@ namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormBlockException;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\StringToAttributeConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\StringToAttributeConverter;
|
||||
use Combodo\iTop\Forms\FormType\AttributeFormType;
|
||||
use CoreException;
|
||||
use MetaModel;
|
||||
|
||||
@@ -28,22 +27,23 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
// outputs
|
||||
public const OUTPUT_ATTRIBUTE = 'attribute';
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
$aOptions = parent::InitOptions();
|
||||
|
||||
// $aOptions['placeholder'] = 'Select an attribute...';
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitInputs(): void
|
||||
{
|
||||
parent::InitInputs();
|
||||
$this->AddInput(self::INPUT_CLASS_NAME, ClassIOFormat::class);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
@@ -67,11 +67,12 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
$aOptions = parent::GetOptions();
|
||||
|
||||
$oValue = $this->GetInput(self::INPUT_CLASS_NAME)->Value();
|
||||
if($oValue == '')
|
||||
if ($oValue == '') {
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
$aAttributeCodes = MetaModel::GetAttributesList($oValue);
|
||||
$aAttributeCodes = array_combine($aAttributeCodes, $aAttributeCodes) ;
|
||||
$aAttributeCodes = array_combine($aAttributeCodes, $aAttributeCodes);
|
||||
$aOptions['choices'] = $aAttributeCodes;
|
||||
|
||||
return $aOptions;
|
||||
|
||||
@@ -11,7 +11,6 @@ use Combodo\iTop\Forms\Block\FormBlockException;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\FormType\AttributeValueFormType;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
@@ -27,21 +26,21 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
public const INPUT_ATTRIBUTE = 'attribute';
|
||||
|
||||
// Outputs
|
||||
public const OUTPUT_VALUE = 'value';
|
||||
public const OUTPUT_VALUE = 'value';
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(array &$aOptions = []): array
|
||||
{
|
||||
$aOptions['multiple'] = true;
|
||||
$aOptions['attr'] = [
|
||||
'size' => 5,
|
||||
'style' => 'height: auto;'
|
||||
'size' => 5,
|
||||
'style' => 'height: auto;',
|
||||
];
|
||||
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitInputs(): void
|
||||
{
|
||||
parent::InitInputs();
|
||||
@@ -49,7 +48,7 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
$this->AddInput(self::INPUT_ATTRIBUTE, AttributeIOFormat::class);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
@@ -64,12 +63,12 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
{
|
||||
$bDependentOk = $this->GetInput(self::INPUT_CLASS_NAME)->Value() != '' && $this->GetInput(self::INPUT_ATTRIBUTE)->Value() != '';
|
||||
|
||||
if($bDependentOk) {
|
||||
if ($bDependentOk) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetInput(self::INPUT_CLASS_NAME)->Value()->__toString(), $this->GetInput(self::INPUT_ATTRIBUTE)->Value()->__toString());
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
|
||||
return $aValues != null;
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -82,18 +81,21 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
$aOptions = parent::GetOptions();
|
||||
|
||||
$oClassName = $this->GetInput(self::INPUT_CLASS_NAME)->Value();
|
||||
if($oClassName == '')
|
||||
if ($oClassName == '') {
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
$oAttribute = $this->GetInput(self::INPUT_ATTRIBUTE)->Value();
|
||||
if($oAttribute == '')
|
||||
if ($oAttribute == '') {
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(strval($oClassName), strval($oAttribute));
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
|
||||
if($aValues === null)
|
||||
if ($aValues === null) {
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
$aOptions['choices'] = array_flip($aValues ?? []);
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\TextAreaFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\OqlToClassConverter;
|
||||
use Combodo\iTop\Forms\Block\FormType\OqlFormType;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\OqlToClassConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
|
||||
/**
|
||||
* Form block for oql expression.
|
||||
@@ -21,27 +21,27 @@ class OqlFormBlock extends TextAreaFormBlock
|
||||
// outputs
|
||||
public const OUTPUT_SELECTED_CLASS = 'selected_class';
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return OqlFormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(self::OUTPUT_SELECTED_CLASS, ClassIOFormat::class, new OqlToClassConverter());
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
$aOptions = parent::InitOptions();
|
||||
$aOptions['with_ai_button'] = true;
|
||||
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\FormType;
|
||||
|
||||
use MetaModel;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
class AttributeValueFormType extends AbstractType
|
||||
@@ -19,14 +20,14 @@ class AttributeValueFormType extends AbstractType
|
||||
{
|
||||
$aValues = [];
|
||||
|
||||
if(!empty($inputs['attribute'])){
|
||||
$oAttDef = \MetaModel::GetAttributeDef($inputs['object_class'], $inputs['attribute']);
|
||||
if (!empty($inputs['attribute'])) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($inputs['object_class'], $inputs['attribute']);
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
$aValues = $aValues !== null ? array_combine($aValues, $aValues) : [];
|
||||
}
|
||||
|
||||
return [
|
||||
'choices' => $aValues
|
||||
'choices' => $aValues,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,22 @@ use Symfony\Component\Form\FormEvents;
|
||||
*/
|
||||
class ChoiceFormType extends AbstractType
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
// on preset data
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) use ($options){
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) use ($options) {
|
||||
|
||||
if($options['multiple'] === false && $options['required'] === true) {
|
||||
if ($options['multiple'] === false && $options['required'] === true) {
|
||||
if ($event->getData() === null) {
|
||||
$FirstElement = array_shift($options['choices']);
|
||||
if($FirstElement !== null){
|
||||
if ($FirstElement !== null) {
|
||||
$event->setData($FirstElement);
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,10 @@ class ChoiceFormType extends AbstractType
|
||||
});
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options){
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options) {
|
||||
|
||||
// reset value if not in available choices
|
||||
if(!empty($event->getData()) && !$this->CheckValue($event->getData(), $options)){
|
||||
if (!empty($event->getData()) && !$this->CheckValue($event->getData(), $options)) {
|
||||
$event->getForm()->addError(new FormError("The value has been reset because it is not part of the available choices anymore."));
|
||||
$event->setData(null);
|
||||
}
|
||||
@@ -63,16 +63,15 @@ class ChoiceFormType extends AbstractType
|
||||
private function CheckValue($oValue, $options): bool
|
||||
{
|
||||
// Check multi selection values
|
||||
if($options['multiple'] === true){
|
||||
foreach ($oValue as $v){
|
||||
if(!in_array($v, $options['choices'])){
|
||||
if ($options['multiple'] === true) {
|
||||
foreach ($oValue as $v) {
|
||||
if (!in_array($v, $options['choices'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check single selection values
|
||||
else{
|
||||
if(!in_array($oValue, $options['choices'])){
|
||||
} // Check single selection values
|
||||
else {
|
||||
if (!in_array($oValue, $options['choices'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -81,5 +80,4 @@ class ChoiceFormType extends AbstractType
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,13 +7,7 @@
|
||||
namespace Combodo\iTop\Forms\Block\FormType;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\Event\PreSubmitEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
@@ -21,7 +15,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
*/
|
||||
class CollectionFormType extends AbstractType
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function getParent(): string
|
||||
{
|
||||
return CollectionType::class;
|
||||
|
||||
@@ -34,38 +34,40 @@ class OqlFormType extends AbstractType
|
||||
]);
|
||||
|
||||
$resolver->setDefault('outputs', array(
|
||||
'selected_class' => function($oData) {
|
||||
if($oData === null)
|
||||
'selected_class' => function ($oData) {
|
||||
if ($oData === null) {
|
||||
return null;
|
||||
}
|
||||
// extract selected class
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
|
||||
return $aMatches[1] ?? null;
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
$resolver->setDefined('with_ai_button');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
parent::buildForm($builder, $options);
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) use ($options){
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) use ($options) {
|
||||
|
||||
try{
|
||||
try {
|
||||
$oClassConverter = new OqlToClassConverter();
|
||||
$oClassConverter->Convert($event->getData());
|
||||
}
|
||||
catch(Exception $e){
|
||||
catch (Exception $e) {
|
||||
$event->getForm()->addError(new FormError($e->getMessage()));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
parent::buildView($view, $form, $options);
|
||||
|
||||
@@ -38,10 +38,11 @@ class AbstractFormIO
|
||||
* @param string $sName name of the IO
|
||||
* @param string $sType type of the IO
|
||||
*/
|
||||
public function __construct(string $sName, string $sType)
|
||||
public function __construct(string $sName, string $sType, AbstractFormBlock $oOwnerBlock)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->sType = $sType;
|
||||
$this->oOwnerBlock = $oOwnerBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,19 +55,6 @@ class AbstractFormIO
|
||||
return $this->oOwnerBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the owner block.
|
||||
*
|
||||
* @param AbstractFormBlock $oOwnerBlock
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetOwnerBlock(AbstractFormBlock $oOwnerBlock): self
|
||||
{
|
||||
$this->oOwnerBlock = $oOwnerBlock;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IO name.
|
||||
*
|
||||
@@ -87,6 +75,7 @@ class AbstractFormIO
|
||||
public function SetName(string $sName): self
|
||||
{
|
||||
$this->sName = $sName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -136,6 +125,7 @@ class AbstractFormIO
|
||||
{
|
||||
$PostSetDataExist = array_key_exists(FormEvents::POST_SET_DATA, $this->aValues) && $this->aValues[FormEvents::POST_SET_DATA] !== null;
|
||||
$PostSubmitExist = array_key_exists(FormEvents::POST_SUBMIT, $this->aValues) && $this->aValues[FormEvents::POST_SUBMIT] !== null;
|
||||
|
||||
return $PostSetDataExist || $PostSubmitExist;
|
||||
}
|
||||
|
||||
@@ -170,12 +160,13 @@ class AbstractFormIO
|
||||
*/
|
||||
public function Value(): mixed
|
||||
{
|
||||
if(array_key_exists(FormEvents::POST_SUBMIT, $this->aValues) ){
|
||||
if (array_key_exists(FormEvents::POST_SUBMIT, $this->aValues)) {
|
||||
return $this->aValues[FormEvents::POST_SUBMIT];
|
||||
}
|
||||
if(array_key_exists(FormEvents::POST_SET_DATA, $this->aValues) ){
|
||||
if (array_key_exists(FormEvents::POST_SET_DATA, $this->aValues)) {
|
||||
return $this->aValues[FormEvents::POST_SET_DATA];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,24 +15,25 @@ use Symfony\Component\Filesystem\Exception\IOException;
|
||||
*/
|
||||
class OqlToClassConverter extends AbstractConverter
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?ClassIOFormat
|
||||
{
|
||||
if($oData === null)
|
||||
if ($oData === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract OQL information
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
|
||||
// Selected class
|
||||
if(isset($aMatches[1])){
|
||||
if (isset($aMatches[1])) {
|
||||
$sSelectedClass = $aMatches[1];
|
||||
if(!MetaModel::IsValidClass($sSelectedClass)){
|
||||
throw new IOException('Incorrect OQL select class name ' . $sSelectedClass);
|
||||
if (!MetaModel::IsValidClass($sSelectedClass)) {
|
||||
throw new IOException('Incorrect OQL select class name '.$sSelectedClass);
|
||||
}
|
||||
|
||||
return new ClassIOFormat($aMatches[1]);
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
throw new IOException('Incorrect OQL sentence');
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,13 @@ use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
*/
|
||||
class StringToAttributeConverter extends AbstractConverter
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?AttributeIOFormat
|
||||
{
|
||||
if($oData === null)
|
||||
if ($oData === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AttributeIOFormat($oData);
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,17 @@ namespace Combodo\iTop\Forms\Block\IO;
|
||||
*/
|
||||
class FormBinding
|
||||
{
|
||||
public readonly AbstractFormIO $oSourceIO;
|
||||
public readonly AbstractFormIO $oDestinationIO;
|
||||
|
||||
/**
|
||||
* @param AbstractFormIO $oSourceIO
|
||||
* @param AbstractFormIO $oDestinationIO
|
||||
*/
|
||||
public function __construct(public readonly AbstractFormIO $oSourceIO, public readonly AbstractFormIO $oDestinationIO)
|
||||
public function __construct(AbstractFormIO $oSourceIO, AbstractFormIO $oDestinationIO)
|
||||
{
|
||||
$this->oDestinationIO = $oDestinationIO;
|
||||
$this->oSourceIO = $oSourceIO;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\AbstractConverter;
|
||||
|
||||
/**
|
||||
@@ -13,11 +14,11 @@ use Combodo\iTop\Forms\Block\IO\Converter\AbstractConverter;
|
||||
*/
|
||||
class FormOutput extends AbstractFormIO
|
||||
{
|
||||
/** @var AbstractConverter|null */
|
||||
/** @var AbstractConverter|null */
|
||||
private null|AbstractConverter $oConverter;
|
||||
|
||||
|
||||
/** @var array */
|
||||
/** @var array */
|
||||
private array $aBindingsToOutputs = [];
|
||||
|
||||
/**
|
||||
@@ -27,9 +28,9 @@ class FormOutput extends AbstractFormIO
|
||||
* @param string $sType
|
||||
* @param AbstractConverter|null $oConverter
|
||||
*/
|
||||
public function __construct(string $sName, string $sType, AbstractConverter $oConverter = null)
|
||||
public function __construct(string $sName, string $sType, AbstractFormBlock $oOwnerBlock, AbstractConverter $oConverter = null)
|
||||
{
|
||||
parent::__construct($sName, $sType);
|
||||
parent::__construct($sName, $sType, $oOwnerBlock);
|
||||
$this->oConverter = $oConverter;
|
||||
}
|
||||
|
||||
@@ -45,6 +46,7 @@ class FormOutput extends AbstractFormIO
|
||||
if (is_null($this->oConverter)) {
|
||||
return $oData;
|
||||
}
|
||||
|
||||
return $this->oConverter->Convert($oData);
|
||||
}
|
||||
|
||||
@@ -83,7 +85,6 @@ class FormOutput extends AbstractFormIO
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Bind to output.
|
||||
*
|
||||
@@ -103,7 +104,6 @@ class FormOutput extends AbstractFormIO
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the bindings.
|
||||
*
|
||||
|
||||
@@ -6,8 +6,11 @@ use JsonSerializable;
|
||||
|
||||
class AttributeIOFormat implements JsonSerializable
|
||||
{
|
||||
public function __construct(public string $sAttributeName)
|
||||
public string $sAttributeName;
|
||||
|
||||
public function __construct(string $sAttributeName)
|
||||
{
|
||||
$this->sAttributeName = $sAttributeName;
|
||||
// validation du format sinon exception
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ use JsonSerializable;
|
||||
|
||||
class ClassIOFormat implements JsonSerializable
|
||||
{
|
||||
public function __construct(public string $sClassName)
|
||||
public string $sClassName;
|
||||
|
||||
public function __construct(string $sClassName)
|
||||
{
|
||||
$this->sClassName = $sClassName;
|
||||
// validation du format sinon exception
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@ namespace Combodo\iTop\Forms\Block\IO\Format;
|
||||
|
||||
class RawFormat
|
||||
{
|
||||
public function __construct(public string $oValue)
|
||||
public string $oValue;
|
||||
|
||||
public function __construct(string $oValue)
|
||||
{
|
||||
$this->oValue = $oValue;
|
||||
// validation du format sinon exception
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,11 @@ class DependencyHandler
|
||||
|
||||
/** @var array Debug data */
|
||||
private array $aDebugData = [];
|
||||
private readonly string $sName;
|
||||
private readonly AbstractFormBlock $oFormBlock;
|
||||
private readonly FormBuilder $oFormBuilder;
|
||||
private readonly array $aSubBlocks;
|
||||
private readonly array $aDependentBlocks;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -37,8 +42,13 @@ class DependencyHandler
|
||||
* @param array $aSubBlocks Sub blocks
|
||||
* @param array $aDependentBlocks Dependants blocks
|
||||
*/
|
||||
public function __construct(private readonly string $sName, private readonly AbstractFormBlock $oFormBlock, private readonly FormBuilder $oFormBuilder, private readonly array $aSubBlocks, private readonly array $aDependentBlocks)
|
||||
public function __construct(string $sName, AbstractFormBlock $oFormBlock, FormBuilder $oFormBuilder, array $aSubBlocks, array $aDependentBlocks)
|
||||
{
|
||||
$this->aDependentBlocks = $aDependentBlocks;
|
||||
$this->aSubBlocks = $aSubBlocks;
|
||||
$this->oFormBuilder = $oFormBuilder;
|
||||
$this->oFormBlock = $oFormBlock;
|
||||
$this->sName = $sName;
|
||||
// dependencies map
|
||||
$this->oDependenciesMap = new DependencyMap($aDependentBlocks);
|
||||
|
||||
@@ -79,15 +89,15 @@ class DependencyHandler
|
||||
foreach ($this->oDependenciesMap->GetListenedOutputBlockNames() as $sOutputBlockName) {
|
||||
|
||||
// inner binding
|
||||
if($sOutputBlockName === $this->oFormBlock->getName()) {
|
||||
if ($sOutputBlockName === $this->oFormBlock->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen',
|
||||
'form' => $sOutputBlockName,
|
||||
'value' => 'NA'
|
||||
'event' => 'form.listen',
|
||||
'form' => $sOutputBlockName,
|
||||
'value' => 'NA',
|
||||
];
|
||||
|
||||
// Listen the output block POST_SET_DATA & POST_SUBMIT
|
||||
@@ -111,9 +121,9 @@ class DependencyHandler
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => $sEventType,
|
||||
'form' => $oEvent->getForm()->getName(),
|
||||
'value' => $oEvent->getData()
|
||||
'event' => $sEventType,
|
||||
'form' => $oEvent->getForm()->getName(),
|
||||
'value' => $oEvent->getData(),
|
||||
];
|
||||
|
||||
// Get the form
|
||||
@@ -123,7 +133,7 @@ class DependencyHandler
|
||||
$oFormBlock = $this->aSubBlocks[$oForm->getName()];
|
||||
|
||||
// Compute the block outputs with the data
|
||||
if(!$oFormBlock instanceof FormBlock) {
|
||||
if (!$oFormBlock instanceof FormBlock) {
|
||||
$oFormBlock->ComputeOutputs($sEventType, $oEvent->getData());
|
||||
}
|
||||
|
||||
@@ -142,16 +152,15 @@ class DependencyHandler
|
||||
private function CheckDependencies(FormInterface|FormBuilderInterface $oForm): void
|
||||
{
|
||||
/** Iterate throw dependencies... @var AbstractFormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $qBlockName => $oDependentBlock)
|
||||
{
|
||||
foreach ($this->aDependentBlocks as $qBlockName => $oDependentBlock) {
|
||||
// When dependencies met, add the dependent field if not already done
|
||||
if(!$oDependentBlock->IsAdded() && $oDependentBlock->IsInputsDataReady()) {
|
||||
if (!$oDependentBlock->IsAdded() && $oDependentBlock->IsInputsDataReady()) {
|
||||
|
||||
// Get the dependent field options
|
||||
$aOptions = $oDependentBlock->UpdateOptions();
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
if ($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
|
||||
// Pass the listener call back to be registered by the dependency form builder
|
||||
$aOptions = array_merge($aOptions, [
|
||||
@@ -159,22 +168,23 @@ class DependencyHandler
|
||||
]);
|
||||
}
|
||||
|
||||
if($oDependentBlock->AllowAdd()) {
|
||||
if ($oDependentBlock->AllowAdd()) {
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.add',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA'
|
||||
'event' => 'form.add',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA',
|
||||
];
|
||||
|
||||
if(array_key_exists('builder_listener', $aOptions))
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA'
|
||||
];
|
||||
if (array_key_exists('builder_listener', $aOptions)) {
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => 'NA',
|
||||
];
|
||||
}
|
||||
|
||||
// Mark the dependency as added
|
||||
$oDependentBlock->SetAdded(true);
|
||||
@@ -186,11 +196,11 @@ class DependencyHandler
|
||||
|
||||
}
|
||||
|
||||
if($oDependentBlock->IsAdded() && !$oDependentBlock->IsInputsDataReady()) {
|
||||
if ($oDependentBlock->IsAdded() && !$oDependentBlock->IsInputsDataReady()) {
|
||||
$oForm->add($oDependentBlock->GetName(), HiddenType::class, [
|
||||
'form_block' => $oDependentBlock,
|
||||
'form_block' => $oDependentBlock,
|
||||
'prevent_form_build' => true,
|
||||
]);
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\FormBinding;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
@@ -25,14 +24,16 @@ class DependencyMap
|
||||
|
||||
/** @var array output to outputs */
|
||||
private array $aOutputToOutputsMap = [];
|
||||
private readonly array $aDependentBlocks;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $aDependentBlocks
|
||||
*/
|
||||
public function __construct(private readonly array $aDependentBlocks)
|
||||
public function __construct(array $aDependentBlocks)
|
||||
{
|
||||
$this->aDependentBlocks = $aDependentBlocks;
|
||||
// Initialization
|
||||
$this->Init();
|
||||
}
|
||||
@@ -44,31 +45,31 @@ class DependencyMap
|
||||
*/
|
||||
private function Init(): void
|
||||
{
|
||||
/** Iterate throw blocks with dependencies... @var FormBlock $oDependentBlock */
|
||||
/** Iterate throw blocks with dependencies... @var \Combodo\iTop\Forms\Block\Base\FormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $sBlockName => $oDependentBlock) {
|
||||
|
||||
/** Iterate throw the block inputs bindings... @var FormBinding $oBinding**/
|
||||
/** Iterate throw the block inputs bindings... @var FormBinding $oBinding * */
|
||||
foreach ($oDependentBlock->GetInputsBindings() as $oBinding) {
|
||||
|
||||
// Output to inputs map
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput){
|
||||
if ($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput) {
|
||||
$this->AddBindingToMap($this->aOutputToInputsMap, $oBinding);
|
||||
}
|
||||
// Input to inputs map
|
||||
if($oBinding->oSourceIO instanceof FormInput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput){
|
||||
if ($oBinding->oSourceIO instanceof FormInput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput) {
|
||||
$this->AddBindingToMap($this->aInputToInputsMap, $oBinding);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Iterate throw the block inputs connections... @var FormBinding $oBinding**/
|
||||
/** Iterate throw the block inputs connections... @var FormBinding $oBinding * */
|
||||
foreach ($oDependentBlock->GetOutputBindings() as $oBinding) {
|
||||
|
||||
// Output to outputs map
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormOutput){
|
||||
if ($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormOutput) {
|
||||
$this->AddBindingToMap($this->aOutputToOutputsMap, $oBinding);
|
||||
}
|
||||
|
||||
@@ -111,8 +112,8 @@ class DependencyMap
|
||||
{
|
||||
$aResult = [];
|
||||
|
||||
foreach(array_keys($this->aOutputToInputsMap) as $sOutputBlockName) {
|
||||
if(!array_key_exists($sOutputBlockName, $this->aDependentBlocks)){
|
||||
foreach (array_keys($this->aOutputToInputsMap) as $sOutputBlockName) {
|
||||
if (!array_key_exists($sOutputBlockName, $this->aDependentBlocks)) {
|
||||
$aResult[] = $sOutputBlockName;
|
||||
}
|
||||
}
|
||||
@@ -120,6 +121,19 @@ class DependencyMap
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function GetImpacted(string $sBlockName): array
|
||||
{
|
||||
$aImpacted = [];
|
||||
foreach ($this->aOutputToInputsMap[$sBlockName] as $aBindings) {
|
||||
foreach ($aBindings as $oBinding) {
|
||||
$oDestBlock = $oBinding->oDestinationIO->GetOwnerBlock();
|
||||
$aImpacted[] = $oDestBlock;
|
||||
}
|
||||
}
|
||||
|
||||
return $aImpacted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sBlockName
|
||||
*
|
||||
@@ -147,9 +161,8 @@ class DependencyMap
|
||||
|
||||
public function IsTheBlockInDependencies(string $sBlockName): bool
|
||||
{
|
||||
foreach ($this->aDependentBlocks as $oDependentBlock)
|
||||
{
|
||||
if($oDependentBlock->getName() === $sBlockName) {
|
||||
foreach ($this->aDependentBlocks as $oDependentBlock) {
|
||||
if ($oDependentBlock->getName() === $sBlockName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use IteratorAggregate;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
@@ -22,9 +23,9 @@ use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||
use Traversable;
|
||||
|
||||
class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
{
|
||||
/** @var DependencyHandler|null */
|
||||
/** @var DependencyHandler|null */
|
||||
private ?DependencyHandler $oDependencyHandler = null;
|
||||
|
||||
/** @var AbstractFormBlock */
|
||||
@@ -32,6 +33,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
|
||||
/** @var array sub blocks */
|
||||
private array $aSubFormBlocks = [];
|
||||
private readonly FormBuilderInterface $builder;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -39,13 +41,14 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
* @param FormBuilderInterface $builder
|
||||
*
|
||||
*/
|
||||
public function __construct(private readonly FormBuilderInterface $builder)
|
||||
public function __construct(FormBuilderInterface $builder)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
/** Get the corresponding form block @var AbstractFormBlock $oFormBlock */
|
||||
$oFormBlock = $this->builder->getOption('form_block');
|
||||
|
||||
// Build the form
|
||||
if($oFormBlock instanceof FormBlock) {
|
||||
if ($oFormBlock instanceof FormBlock) {
|
||||
$this->BuildForm($oFormBlock);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +64,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
{
|
||||
// Hidden (ignore)
|
||||
$aOptions = $this->builder->getOptions();
|
||||
if(array_key_exists('prevent_form_build', $aOptions) && $aOptions['prevent_form_build']) {
|
||||
if (array_key_exists('prevent_form_build', $aOptions) && $aOptions['prevent_form_build']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
$bHasDependency = $this->HandleSubBlock($oSubFormBlock);
|
||||
|
||||
// Add to the dependencies array
|
||||
if($bHasDependency){
|
||||
if ($bHasDependency) {
|
||||
$aDependentBlocks[$oSubFormBlock->GetName()] = $oSubFormBlock;
|
||||
}
|
||||
|
||||
@@ -103,17 +106,15 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
|
||||
// Insert a hidden type to save the place
|
||||
$this->builder->add($oSubFormBlock->GetName(), HiddenType::class, [
|
||||
'form_block' => $oSubFormBlock,
|
||||
'form_block' => $oSubFormBlock,
|
||||
'prevent_form_build' => true,
|
||||
// 'mapped' => false,
|
||||
// 'disabled' => true,
|
||||
// 'mapped' => false,
|
||||
// 'disabled' => true,
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
// Directly insert the block corresponding form type
|
||||
$this->add($oSubFormBlock->GetName(), $oSubFormBlock->GetFormType(), $oSubFormBlock->UpdateOptions());
|
||||
$oSubFormBlock->SetAdded(true);
|
||||
@@ -145,11 +146,17 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
return $this->oDependencyHandler;
|
||||
}
|
||||
|
||||
public function GetDependencyMap(): ?DependencyMap
|
||||
{
|
||||
return $this->oDependencyHandler?->GetMap();
|
||||
}
|
||||
|
||||
// pure decoration of FormBuilderInterface
|
||||
|
||||
public function add(string|FormBuilderInterface $child, ?string $type = null, array $options = []): static
|
||||
{
|
||||
$this->builder->add($child, $type, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -162,6 +169,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
{
|
||||
return $this->builder->count();
|
||||
}
|
||||
|
||||
public function create(string $name, ?string $type = null, array $options = []): FormBuilderInterface
|
||||
{
|
||||
return $this->builder->create($name, $type, $options);
|
||||
@@ -175,6 +183,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
public function remove(string $name): static
|
||||
{
|
||||
$this->builder->remove($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -196,126 +205,147 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
public function addEventListener(string $eventName, callable $listener, int $priority = 0): static
|
||||
{
|
||||
$this->builder->addEventListener($eventName, $listener, $priority);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addEventSubscriber(EventSubscriberInterface $subscriber): static
|
||||
{
|
||||
$this->builder->addEventSubscriber($subscriber);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addViewTransformer(DataTransformerInterface $viewTransformer, bool $forcePrepend = false): static
|
||||
{
|
||||
$this->builder->addViewTransformer($viewTransformer, $forcePrepend);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetViewTransformers(): static
|
||||
{
|
||||
$this->builder->resetViewTransformers();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addModelTransformer(DataTransformerInterface $modelTransformer, bool $forceAppend = false): static
|
||||
{
|
||||
$this->builder->addModelTransformer($modelTransformer, $forceAppend);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetModelTransformers(): static
|
||||
{
|
||||
$this->builder->resetModelTransformers();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAttribute(string $name, mixed $value): static
|
||||
{
|
||||
$this->builder->setAttribute($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAttributes(array $attributes): static
|
||||
{
|
||||
$this->builder->setAttributes($attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataMapper(?DataMapperInterface $dataMapper): static
|
||||
{
|
||||
$this->builder->setDataMapper($dataMapper);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDisabled(bool $disabled): static
|
||||
{
|
||||
$this->builder->setDisabled($disabled);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEmptyData(mixed $emptyData): static
|
||||
{
|
||||
$this->builder->setEmptyData($emptyData);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setErrorBubbling(bool $errorBubbling): static
|
||||
{
|
||||
$this->builder->setErrorBubbling($errorBubbling);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequired(bool $required): static
|
||||
{
|
||||
$this->builder->setRequired($required);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPropertyPath(PropertyPathInterface|string|null $propertyPath): static
|
||||
{
|
||||
$this->builder->setPropertyPath($propertyPath);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMapped(bool $mapped): static
|
||||
{
|
||||
$this->builder->setMapped($mapped);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setByReference(bool $byReference): static
|
||||
{
|
||||
$this->builder->setByReference($byReference);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInheritData(bool $inheritData): static
|
||||
{
|
||||
$this->builder->setInheritData($inheritData);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCompound(bool $compound): static
|
||||
{
|
||||
$this->builder->setCompound($compound);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(ResolvedFormTypeInterface $type): static
|
||||
{
|
||||
$this->builder->setType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData(mixed $data): static
|
||||
{
|
||||
$this->builder->setData($data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataLocked(bool $locked): static
|
||||
{
|
||||
$this->builder->setDataLocked($locked);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -327,24 +357,28 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
public function setAction(string $action): static
|
||||
{
|
||||
$this->builder->setAction($action);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMethod(string $method): static
|
||||
{
|
||||
$this->builder->setMethod($method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequestHandler(RequestHandlerInterface $requestHandler): static
|
||||
{
|
||||
$this->builder->setRequestHandler($requestHandler);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAutoInitialize(bool $initialize): static
|
||||
{
|
||||
$this->builder->setAutoInitialize($initialize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -356,6 +390,7 @@ class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
public function setIsEmptyCallback(?callable $isEmptyCallback): static
|
||||
{
|
||||
$this->builder->setIsEmptyCallback($isEmptyCallback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@ class FormHelper
|
||||
return FormEvents::POST_SET_DATA;
|
||||
} else if ($event instanceof PostSubmitEvent) {
|
||||
return FormEvents::POST_SUBMIT;
|
||||
}
|
||||
else if ($event instanceof PreSubmitEvent) {
|
||||
} else if ($event instanceof PreSubmitEvent) {
|
||||
return FormEvents::PRE_SUBMIT;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -22,38 +21,38 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
class FormTypeExtension extends AbstractTypeExtension
|
||||
{
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [
|
||||
FormType::class
|
||||
FormType::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefined([
|
||||
'form_block',
|
||||
'builder_listener',
|
||||
'prevent_form_build'
|
||||
'prevent_form_build',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
if(array_key_exists('builder_listener', $options)) {
|
||||
if (array_key_exists('builder_listener', $options)) {
|
||||
$builder->addEventListener(FormEvents::POST_SET_DATA, $options['builder_listener']);
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, $options['builder_listener']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
if(array_key_exists('form_block', $options)) {
|
||||
if (array_key_exists('form_block', $options)) {
|
||||
$view->vars['form_block'] = $options['form_block'];
|
||||
|
||||
$oFormBlock = $options['form_block'];
|
||||
|
||||
@@ -16,6 +16,7 @@ class ResolvedFormType extends SymfonyResolvedFormType implements ResolvedFormTy
|
||||
protected function newBuilder(string $name, ?string $dataClass, FormFactoryInterface $factory, array $options): FormBuilderInterface
|
||||
{
|
||||
$builder = parent::newBuilder($name, $dataClass, $factory, $options);
|
||||
|
||||
return new FormBuilder($builder);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ class FormCompatibilityExtension extends AbstractExtension
|
||||
// Alias of dict_s, to be compatible with Symfony/Twig standard
|
||||
new TwigFilter('trans', function ($sStringCode, $aData = null, $sTransDomain = false) {
|
||||
return Dict::S($sStringCode);
|
||||
})
|
||||
}),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div id="itop_profiler">
|
||||
{{ sProfilerContent|raw }}
|
||||
<div id="turbo_itop_profiler">
|
||||
{% for aProfileInfo in aProfilesInfo %}
|
||||
{% set aProfileData = aProfileInfo.aProfileData %}
|
||||
{{ include(aProfileInfo.sTemplate) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -1,8 +1,11 @@
|
||||
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<turbo-stream action="update" target="itop_profiler">
|
||||
<turbo-stream action="update" target="turbo_itop_profiler">
|
||||
<template>
|
||||
{{ sProfilerContent|raw }}
|
||||
{% for aProfileInfo in aProfilesInfo %}
|
||||
{% set aProfileData = aProfileInfo.aProfileData %}
|
||||
{{ include(aProfileInfo.sTemplate) }}
|
||||
{% endfor %}
|
||||
</template>
|
||||
</turbo-stream>
|
||||
@@ -1,6 +1,14 @@
|
||||
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% if sControllerError %}
|
||||
{% UIAlert ForDanger { sTitle:'UI:Error:TwigController'|dict_s, sContent:sControllerError } %}{% EndUIAlert %}
|
||||
{% endif %}
|
||||
<div id="turbo_itop_error">
|
||||
{% if sControllerError %}
|
||||
<div class="ibo-alert ibo-content-block ibo-block ibo-is-danger ibo-is-opened" data-role="ibo-alert">
|
||||
<div class="ibo-alert--action-button ibo-alert--maximize-button" data-role="ibo-alert--collapse-toggler"><i class="fas fa-caret-down"></i></div>
|
||||
<div class="ibo-alert--action-button ibo-alert--minimize-button" data-role="ibo-alert--collapse-toggler"><i class="fas fa-caret-up"></i></div>
|
||||
<div class="ibo-alert--action-button ibo-alert--close-button" data-role="ibo-alert--close-button"><i class="fas fa-times"></i></div>
|
||||
<div class="ibo-alert--title" data-role="ibo-alert--collapse-toggler">{{ 'UI:Error:TwigController'|dict_s }}</div>
|
||||
<div class="ibo-alert--body">{{ sControllerError }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
16
templates/application/forms/itop_error_update.html.twig
Normal file
16
templates/application/forms/itop_error_update.html.twig
Normal file
@@ -0,0 +1,16 @@
|
||||
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<turbo-stream action="update" target="turbo_itop_error">
|
||||
<template>
|
||||
{% if sControllerError %}
|
||||
<div class="ibo-alert ibo-content-block ibo-block ibo-is-danger ibo-is-opened" data-role="ibo-alert">
|
||||
<div class="ibo-alert--action-button ibo-alert--maximize-button" data-role="ibo-alert--collapse-toggler"><i class="fas fa-caret-down"></i></div>
|
||||
<div class="ibo-alert--action-button ibo-alert--minimize-button" data-role="ibo-alert--collapse-toggler"><i class="fas fa-caret-up"></i></div>
|
||||
<div class="ibo-alert--action-button ibo-alert--close-button" data-role="ibo-alert--close-button"><i class="fas fa-times"></i></div>
|
||||
<div class="ibo-alert--title" data-role="ibo-alert--collapse-toggler">{{ 'UI:Error:TwigController'|dict_s }}</div>
|
||||
<div class="ibo-alert--body">{{ sControllerError }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</template>
|
||||
</turbo-stream>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\sources\Forms\Block\IO;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class AbstractFormIOTest extends ItopDataTestCase
|
||||
{
|
||||
|
||||
public function testRawBlockHasNoIO(): void
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock('test');
|
||||
|
||||
self::assertEquals([], $oBlock->GetInputs(), 'Row form block must not have input by default');
|
||||
self::assertEquals([], $oBlock->GetOutputs(), 'Row form block must not have output by default');
|
||||
}
|
||||
|
||||
public function testAddingOneInputToABlock_StoresIt(): void
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock('test', [], [
|
||||
['io_type' => FormInput::class, 'name' => 'input', 'data_type' => RawFormat::class,],
|
||||
]);
|
||||
|
||||
self::assertCount(1, $oBlock->GetInputs(), 'Inputs must be saved in block forms');
|
||||
$aInputs = $oBlock->GetInputs();
|
||||
self::assertEquals('input', array_shift($aInputs)->getName(), 'Inputs must be saved in block forms');
|
||||
self::assertEquals(RawFormat::class, $oBlock->GetInput('input')->GetDataType(), 'Format must be kept in inputs saved in block forms');
|
||||
}
|
||||
|
||||
public function testAddingOneOutputToABlock_StoresIt(): void
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock('test', [], [
|
||||
['io_type' => FormOutput::class, 'name' => 'output', 'data_type' => RawFormat::class],
|
||||
]);
|
||||
|
||||
self::assertCount(1, $oBlock->GetOutputs(), 'Outputs must be saved in block forms');
|
||||
$aInputs = $oBlock->GetOutputs();
|
||||
self::assertEquals('output', array_shift($aInputs)->getName(), 'Outputs must be saved in block forms');
|
||||
self::assertEquals(RawFormat::class, $oBlock->GetOutput('output')->GetDataType(), 'Format must be kept in outputs saved in block forms');
|
||||
}
|
||||
|
||||
public function testAddingMultipleInputsAndOutputsToABlock_StoresThem(): void
|
||||
{
|
||||
$oBlock = $this->GivenFormBlock('test', [], [
|
||||
['io_type' => FormInput::class, 'name' => 'input1', 'data_type' => RawFormat::class,],
|
||||
['io_type' => FormInput::class, 'name' => 'input2', 'data_type' => RawFormat::class,],
|
||||
['io_type' => FormInput::class, 'name' => 'input3', 'data_type' => RawFormat::class,],
|
||||
['io_type' => FormOutput::class, 'name' => 'output1', 'data_type' => RawFormat::class],
|
||||
['io_type' => FormOutput::class, 'name' => 'output2', 'data_type' => RawFormat::class],
|
||||
]);
|
||||
|
||||
self::assertCount(3, $oBlock->GetInputs(), 'Inputs must be saved in block forms');
|
||||
self::assertCount(2, $oBlock->GetOutputs(), 'Outputs must be saved in block forms');
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
/// GIVEN methods
|
||||
///
|
||||
private function GivenFormBlock(string $sName, array $aOptions = [], array $aIOs = []): AbstractFormBlock
|
||||
{
|
||||
$oBlock = new FormBlock($sName, $aOptions);
|
||||
|
||||
foreach ($aIOs as $aIO) {
|
||||
if ($aIO['io_type'] === FormInput::class) {
|
||||
$oBlock->AddInput($aIO['name'], $aIO['data_type']);
|
||||
} else {
|
||||
if (isset($aIO['converter_class'])) {
|
||||
$oBlock->AddOutput($aIO['name'], $aIO['data_type'], new $aIO['converter_class']);
|
||||
} else {
|
||||
$oBlock->AddOutput($aIO['name'], $aIO['data_type']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user