diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 534d3031a..05443b292 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1838,7 +1838,7 @@ class MenuBlock extends DisplayBlock if ($bIsModifyAllowed) { $aRegularActions['UI:Menu:Modify'] = array( 'label' => Dict::S('UI:Menu:Modify'), - 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#", + 'url' => "{$sRootUrl}pages/$sUIPage?operation=object.modify&class=$sClass&id=$id{$sContext}#", ) + $aActionParams; } if ($bIsCreationAllowed) { diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 89b2773d6..e43f2704e 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -349,6 +349,7 @@ return array( 'Combodo\\iTop\\Controller\\AbstractController' => $baseDir . '/sources/Controller/AbstractController.php', 'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php', 'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php', + 'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => $baseDir . '/sources/Controller/Base/Layout/ObjectController.php', 'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => $baseDir . '/sources/Controller/OAuth/OAuthLandingController.php', 'Combodo\\iTop\\Controller\\PreferencesController' => $baseDir . '/sources/Controller/PreferencesController.php', 'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => $baseDir . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index fd07da1e5..cca9b11d4 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -714,6 +714,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Controller\\AbstractController' => __DIR__ . '/../..' . '/sources/Controller/AbstractController.php', 'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php', 'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php', + 'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ObjectController.php', 'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthLandingController.php', 'Combodo\\iTop\\Controller\\PreferencesController' => __DIR__ . '/../..' . '/sources/Controller/PreferencesController.php', 'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php', diff --git a/pages/UI.php b/pages/UI.php index 7a79afe18..c2ff97cfa 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -19,6 +19,7 @@ use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; +use Combodo\iTop\Controller\Base\Layout\ObjectController; /** * Displays a popup welcome message, once per session at maximum @@ -301,7 +302,7 @@ require_once(APPROOT.'/application/startup.inc.php'); try { - $operation = utils::ReadParam('operation', ''); + $operation = utils::ReadParam('operation', '', false, utils::ENUM_SANITIZATION_FILTER_OPERATION); $bPrintable = (utils::ReadParam('printable', 0) == '1'); $oKPI = new ExecutionKPI(); @@ -321,22 +322,16 @@ try switch($operation) { case 'new': // Form to create a new object - case 'modify': // Form to modify an object case 'apply_new': // Creation of a new object case 'apply_modify': // Applying the modifications to an existing object case 'form_for_modify_all': // Form to modify multiple objects (bulk modify) case 'bulk_stimulus': // For to apply a stimulus to multiple objects case 'stimulus': // Form displayed when applying a stimulus (state change) case 'apply_stimulus': // Form displayed when applying a stimulus (state change) - $oP->add_linked_script("../js/json.js"); - $oP->add_linked_script("../js/forms-json-utils.js"); - $oP->add_linked_script("../js/wizardhelper.js"); - $oP->add_linked_script("../js/wizard.utils.js"); - $oP->add_linked_script("../js/linkswidget.js"); - $oP->add_linked_script("../js/linksdirectwidget.js"); - $oP->add_linked_script("../js/extkeywidget.js"); - $oP->add_linked_script("../js/jquery.blockUI.js"); - break; + foreach (ObjectController::EnumRequiredForModificationJsFilesRelPaths() as $sJsFileRelPath) { + $oP->add_linked_script("../$sJsFileRelPath"); + } + break; } switch($operation) @@ -659,29 +654,10 @@ EOF /////////////////////////////////////////////////////////////////////////////////////////// - case 'modify': // Form to modify an object - $oP->DisableBreadCrumb(); - $sClass = utils::ReadParam('class', '', false, 'class'); - $id = utils::ReadParam('id', ''); - if (empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! - { - throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); - } - // Check if the user can modify this object - $oObj = MetaModel::GetObject($sClass, $id, false /* MustBeFound */); - if (is_null($oObj)) { - $oP->set_title(Dict::S('UI:ErrorPageTitle')); - $oP->P(Dict::S('UI:ObjectDoesNotExist')); - } else { - // The object could be read - check if it is allowed to modify it - $oSet = CMDBObjectSet::FromObject($oObj); - if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_NO) { - throw new SecurityException('User not allowed to modify this object', array('class' => $sClass, 'id' => $id)); - } - $oP->SetContentLayout(PageContentFactory::MakeForObjectDetails($oObj, cmdbAbstractObject::ENUM_DISPLAY_MODE_EDIT)); - // Note: code duplicated to the case 'apply_modify' when a data integrity issue has been found - $oObj->DisplayModifyForm($oP, array('wizard_container' => 1)); // wizard_container: Display the title above the form - } + case 'modify': // Legacy operation + case 'object.modify': // New operation + $oController = new ObjectController(); + $oP = $oController->Modify(); break; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 9b44ac332..2e2cfe21b 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -11,6 +11,7 @@ use Combodo\iTop\Application\UI\Base\Component\Html\Html; use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Controller\AjaxRenderController; use Combodo\iTop\Controller\Base\Layout\ActivityPanelController; +use Combodo\iTop\Controller\Base\Layout\ObjectController; use Combodo\iTop\Controller\PreferencesController; use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer; use Combodo\iTop\Renderer\Console\ConsoleFormRenderer; @@ -2668,6 +2669,14 @@ EOF $oAjaxRenderController->GetMenusCount($oPage); break; + //-------------------------------- + // Object + //-------------------------------- + case 'object.modify': + $oController = new ObjectController(); + $oPage = $oController->Modify(); + break; + default: $oPage->p("Invalid query."); } diff --git a/sources/Application/WebPage/iTopWebPage.php b/sources/Application/WebPage/iTopWebPage.php index 16ec6c91e..13e8cda8f 100644 --- a/sources/Application/WebPage/iTopWebPage.php +++ b/sources/Application/WebPage/iTopWebPage.php @@ -529,9 +529,9 @@ JS */ public function SetContentLayout(PageContent $oLayout) { - $oPrevContentLayout=$this->oContentLayout; + $oPrevContentLayout = $this->oContentLayout; $this->oContentLayout = $oLayout; - foreach ($oPrevContentLayout->GetSubBlocks() as $oBlock){ + foreach ($oPrevContentLayout->GetSubBlocks() as $oBlock) { $this->AddUiBlock($oBlock); } diff --git a/sources/Controller/AbstractController.php b/sources/Controller/AbstractController.php index 37d020203..e2f4b6939 100644 --- a/sources/Controller/AbstractController.php +++ b/sources/Controller/AbstractController.php @@ -17,5 +17,16 @@ namespace Combodo\iTop\Controller; */ class AbstractController { - // Empty stub for now, factorized needs might come later + /** + * It works if your JavaScript library sets an X-Requested-With HTTP header. + * It is known to work with common JavaScript frameworks: {@link https://wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript} + * + * @see \Symfony\Component\HttpFoundation\Request::isXmlHttpRequest() Inspired by + * + * @return bool True if the current request is an XmlHttpRequest (eg. an AJAX request) + */ + public function IsHandlingXmlHttpRequest(): bool + { + return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'); + } } \ No newline at end of file diff --git a/sources/Controller/Base/Layout/ObjectController.php b/sources/Controller/Base/Layout/ObjectController.php new file mode 100644 index 000000000..b52f855cf --- /dev/null +++ b/sources/Controller/Base/Layout/ObjectController.php @@ -0,0 +1,107 @@ + + * @since 3.1.0 + * @package Combodo\iTop\Controller\Base\Layout + */ +class ObjectController extends \Combodo\iTop\Controller\AbstractController +{ + public function View() + { + + } + + /** + * @return \iTopWebPage|\AjaxPage Object edit form in its webpage + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \SecurityException + */ + public function Modify() + { + $bPrintable = utils::ReadParam('printable', '0') === '1'; + $sClass = utils::ReadParam('class', '', false, 'class'); + $sId = utils::ReadParam('id', ''); + + $sClass = 'Person'; + $sId = 6; + + // Check parameters + if (utils::IsNullOrEmptyString($sClass) || utils::IsNullOrEmptyString($sId)) + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); + } + + $oObj = MetaModel::GetObject($sClass, $sId, false); + // Check user permissions + // - Is allowed to view it? + if (is_null($oObj)) { + throw new ApplicationException(Dict::S('UI:ObjectDoesNotExist')); + } + + // - Is allowed to edit it? + $oSet = CMDBObjectSet::FromObject($oObj); + if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_NO) { + throw new SecurityException('User not allowed to modify this object', array('class' => $sClass, 'id' => $sId)); + } + + // Prepare web page (should more likely be some kind of response object like for Symfony) + if ($this->IsHandlingXmlHttpRequest()) { + $oPage = new AjaxPage(''); + } else { + $oPage = new iTopWebPage('', $bPrintable); + $oPage->DisableBreadCrumb(); + $oPage->SetContentLayout(PageContentFactory::MakeForObjectDetails($oObj, cmdbAbstractObject::ENUM_DISPLAY_MODE_EDIT)); + } + // - JS files + foreach (static::EnumRequiredForModificationJsFilesRelPaths() as $sJsFileRelPath) { + $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().$sJsFileRelPath); + } + + // Note: Code duplicated to the case 'apply_modify' in UI.php when a data integrity issue has been found + $oObj->DisplayModifyForm($oPage, array('wizard_container' => 1)); // wizard_container: Display the title above the form + + return $oPage; + } + + /** + * @return string[] Rel. paths (to iTop root folder) of required JS files for object modification (create, edit, stimulus, ...) + */ + public static function EnumRequiredForModificationJsFilesRelPaths(): array + { + return [ + 'js/json.js', + 'js/forms-json-utils.js', + 'js/wizardhelper.js', + 'js/wizard.utils.js', + 'js/linkswidget.js', + 'js/linksdirectwidget.js', + 'js/extkeywidget.js', + 'js/jquery.blockUI.js', + ]; + } +} \ No newline at end of file