diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 6648ad60a4..39791fa86e 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -1552,6 +1552,14 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ], + 'setup.launch_button.enabled' => [ + 'type' => 'bool', + 'description' => 'If true displays in the Application Upgrade screen a button allowing to launch the setup in a single click (no more manual config file permission change needed)', + 'default' => null, + 'value' => false, + 'source_of_value' => '', + 'show_in_conf_sample' => false, + ], ]; public function IsProperty($sPropCode) diff --git a/datamodels/2.x/itop-config/config.php b/datamodels/2.x/itop-config/config.php index 23a10ec6b5..d1bf9378e3 100644 --- a/datamodels/2.x/itop-config/config.php +++ b/datamodels/2.x/itop-config/config.php @@ -189,14 +189,16 @@ function CheckAsyncTasksRetryConfig(Config $oTempConfig, iTopWebPage $oP) $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'save')); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); - // - Cancel button + //--- Cancel button $oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('config-cancel'), 'cancel_button', null, true, 'cancel_button'); $oCancelButton->SetOnClickJsCode("return ResetConfig();"); $oForm->AddSubBlock($oCancelButton); - // - Submit button + //--- Submit button $oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('config-apply'), null, Dict::S('config-apply'), true, 'submit_button'); $oForm->AddSubBlock($oSubmitButton); + + //--- Config editor $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('prev_config', $sOriginalConfigEscaped, 'prev_config')); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('new_config', $sConfigEscaped)); $oForm->AddHtml("
"); diff --git a/datamodels/2.x/itop-core-update/dictionaries/en.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/dictionaries/en.dict.itop-core-update.php index d3afe6a064..a0bd40fdc3 100644 --- a/datamodels/2.x/itop-core-update/dictionaries/en.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/dictionaries/en.dict.itop-core-update.php @@ -54,6 +54,7 @@ 'iTopUpdate:UI:Status' => 'Status', 'iTopUpdate:UI:Action' => 'Update', + 'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.' Setup', 'iTopUpdate:UI:History' => 'Versions History', 'iTopUpdate:UI:Progress' => 'Progress of the upgrade', @@ -81,6 +82,9 @@ + 'iTopUpdate:UI:SetupLaunch' => 'Launch '.ITOP_APPLICATION_SHORT.' Setup', + 'iTopUpdate:UI:SetupLaunchConfirm' => 'This will launch '.ITOP_APPLICATION_SHORT.' setup, are you sure?', + // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start', 'iTopUpdate:UI:SetupMessage:EnterMaintenance' => 'Entering maintenance mode', diff --git a/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php b/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php index 11a1f7dbbc..9cda7be83f 100644 --- a/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php +++ b/datamodels/2.x/itop-core-update/src/Controller/AjaxController.php @@ -17,6 +17,7 @@ use Exception; use IssueLog; use MetaModel; +use SecurityException; use SetupUtils; use utils; @@ -211,8 +212,7 @@ public function OperationUpdateDatabase() CoreUpdater::UpdateDatabase(); $iResponseCode = 200; } - catch (Exception $e) - { + catch (Exception $e) { IssueLog::Error("Compile: ".$e->getMessage()); $aParams['sError'] = $e->getMessage(); $iResponseCode = 500; @@ -220,4 +220,20 @@ public function OperationUpdateDatabase() $this->DisplayJSONPage($aParams, $iResponseCode); } + + /** + * @throws \SecurityException if CSRF token invalid + */ + public function OperationLaunchSetup() + { + $sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id'); + if (false === utils::IsTransactionValid($sTransactionId)) { + throw new SecurityException('Access forbidden'); + } + + $sConfigFile = APPCONF.'production/config-itop.php'; + @chmod($sConfigFile, 0770); // Allow overwriting the file + + header('Location: ../setup/'); + } } diff --git a/datamodels/2.x/itop-core-update/src/Controller/UpdateController.php b/datamodels/2.x/itop-core-update/src/Controller/UpdateController.php index 0c68726107..ba9eb36203 100644 --- a/datamodels/2.x/itop-core-update/src/Controller/UpdateController.php +++ b/datamodels/2.x/itop-core-update/src/Controller/UpdateController.php @@ -35,6 +35,25 @@ public function OperationSelectUpdateFile() $oSet = new DBObjectSet($oFilter, ['installed' => false]); // Most recent first $aParams['oSet'] = $oSet; + $oConfig = utils::GetConfig(); + $bConfigParamSetupLaunchButtonEnabled = $oConfig->Get('setup.launch_button.enabled'); + if (is_null($bConfigParamSetupLaunchButtonEnabled)) { + $bIsSetupLaunchButtonEnabled = utils::IsDevelopmentEnvironment(); + } else if (false === $bConfigParamSetupLaunchButtonEnabled) { + $bIsSetupLaunchButtonEnabled = false; + } else { + $bIsSetupLaunchButtonEnabled = $bConfigParamSetupLaunchButtonEnabled || utils::IsDevelopmentEnvironment(); + } + $aParams['bIsSetupLaunchButtonEnabled'] = $bIsSetupLaunchButtonEnabled; + if ($bIsSetupLaunchButtonEnabled) { + $sLaunchSetupUrl = utils::GetAbsoluteUrlModulePage('itop-core-update', 'ajax.php', + [ + 'operation' => 'LaunchSetup', + 'transaction_id' => $sTransactionId, + ]);; + $aParams['sLaunchSetupUrl'] = $sLaunchSetupUrl; + } + $this->DisplayPage($aParams); } diff --git a/datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig b/datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig index 6ffaea7349..393f074dad 100644 --- a/datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig +++ b/datamodels/2.x/itop-core-update/view/SelectUpdateFile.html.twig @@ -87,6 +87,14 @@ {% EndUIFieldSet %} + {% if bIsSetupLaunchButtonEnabled %} + {% UIFieldSet Standard {'sLegend':'iTopUpdate:UI:Setup'|dict_s} %} + {% UIForm Standard {'sId':'launch-setup-form', Action:sLaunchSetupUrl} %} + {% UIButton ForDestructiveAction {'sLabel':'iTopUpdate:UI:SetupLaunch'|dict_s, 'sName':'launch-setup', 'sValue':'launch-setup', 'bIsSubmit':true, 'sId':'launch-setup'} %} + {% EndUIForm %} + {% EndUIFieldSet %} + {% endif %} + {% UIFieldSet Standard {'sLegend':'iTopUpdate:UI:History'|dict_s} %} {% UIDataTable ForRendering {'sListId':'iboupdatehistory', 'oSet':oSet} %}{% EndUIDataTable %} {% EndUIFieldSet %} diff --git a/datamodels/2.x/itop-core-update/view/SelectUpdateFile.ready.js.twig b/datamodels/2.x/itop-core-update/view/SelectUpdateFile.ready.js.twig index a7ed94e228..956c92eaa9 100644 --- a/datamodels/2.x/itop-core-update/view/SelectUpdateFile.ready.js.twig +++ b/datamodels/2.x/itop-core-update/view/SelectUpdateFile.ready.js.twig @@ -101,3 +101,7 @@ $("#check-update").on("click", function(e) { e.stopPropagation(); return false; }); + +$("#launch-setup-form").on("submit", function () { + return window.confirm("{{ 'iTopUpdate:UI:SetupLaunchConfirm'|dict_s }}"); +}); \ No newline at end of file diff --git a/index.php b/index.php index 61a6734258..c50fd8cbb6 100644 --- a/index.php +++ b/index.php @@ -25,9 +25,11 @@ } else { - echo "

Security Warning: the configuration file '$sConfigFile' should be read-only.

"; - echo "

Please modify the access rights to this file.

"; - echo "

Click here to ignore this warning and continue to run iTop.

"; + echo <<Security Warning: the configuration file '{$sConfigFile}' should be read-only.

+

Please modify the access rights to this file.

+

Click here to ignore this warning and continue to run iTop.

+HTML; } } else diff --git a/setup/wizardcontroller.class.inc.php b/setup/wizardcontroller.class.inc.php index 93ed2f95ef..7275a115f9 100644 --- a/setup/wizardcontroller.class.inc.php +++ b/setup/wizardcontroller.class.inc.php @@ -182,7 +182,12 @@ protected function DisplayStep(WizardStep $oStep) $oP->add("

Fatal error

\n"); $oP->error("Error: the configuration file '".$sRelativePath."' already exists and cannot be overwritten."); $oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '".$sRelativePath."' can be modified by the web server."); - $oP->p(''); + + $sButtonsHtml = <<Reload +HTML; + $oP->p($sButtonsHtml); + $oP->output(); // Prevent token creation exit;