From ae597802979b8ca583ab80deae1be47c9a856d57 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 17 Jan 2018 10:04:33 +0000 Subject: [PATCH] The Hub Connects !! Adding iTop Hub Connector. SVN:trunk[5270] --- datamodels/2.x/installation.xml | 1 + datamodels/2.x/itop-hub-connector/ajax.php | 362 +++++++++++++++ datamodels/2.x/itop-hub-connector/css/hub.css | 252 ++++++++++ .../datamodel.itop-hub-connector.xml | 24 + .../en.dict.itop-hub-connector.php | 70 +++ .../2.x/itop-hub-connector/extension.xml | 9 + .../fr.dict.itop-hub-connector.php | 68 +++ .../hubconnectorpage.class.inc.php | 119 +++++ .../hubruntimeenvironment.class.inc.php | 59 +++ .../itop-hub-connector/images/black-close.svg | 2 + .../images/itophub-logo.png | Bin 0 -> 17632 bytes .../images/itophub-logo.svg | 105 +++++ .../images/landing-extension.png | Bin 0 -> 3736 bytes .../images/orange-progress.gif | Bin 0 -> 40302 bytes .../2.x/itop-hub-connector/images/rocket.svg | 134 ++++++ .../images/white-arrow-right.svg | 2 + .../2.x/itop-hub-connector/installation.xml | 212 +++++++++ datamodels/2.x/itop-hub-connector/js/hub.js | 187 ++++++++ datamodels/2.x/itop-hub-connector/land.php | 334 ++++++++++++++ datamodels/2.x/itop-hub-connector/launch.php | 435 ++++++++++++++++++ datamodels/2.x/itop-hub-connector/menus.php | 19 + .../model.itop-hub-connector.php | 17 + .../module.itop-hub-connector.php | 47 ++ .../2.x/itop-hub-connector/myextensions.php | 129 ++++++ 24 files changed, 2587 insertions(+) create mode 100644 datamodels/2.x/itop-hub-connector/ajax.php create mode 100644 datamodels/2.x/itop-hub-connector/css/hub.css create mode 100644 datamodels/2.x/itop-hub-connector/datamodel.itop-hub-connector.xml create mode 100644 datamodels/2.x/itop-hub-connector/en.dict.itop-hub-connector.php create mode 100644 datamodels/2.x/itop-hub-connector/extension.xml create mode 100644 datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php create mode 100644 datamodels/2.x/itop-hub-connector/hubconnectorpage.class.inc.php create mode 100644 datamodels/2.x/itop-hub-connector/hubruntimeenvironment.class.inc.php create mode 100644 datamodels/2.x/itop-hub-connector/images/black-close.svg create mode 100644 datamodels/2.x/itop-hub-connector/images/itophub-logo.png create mode 100644 datamodels/2.x/itop-hub-connector/images/itophub-logo.svg create mode 100644 datamodels/2.x/itop-hub-connector/images/landing-extension.png create mode 100644 datamodels/2.x/itop-hub-connector/images/orange-progress.gif create mode 100644 datamodels/2.x/itop-hub-connector/images/rocket.svg create mode 100644 datamodels/2.x/itop-hub-connector/images/white-arrow-right.svg create mode 100644 datamodels/2.x/itop-hub-connector/installation.xml create mode 100644 datamodels/2.x/itop-hub-connector/js/hub.js create mode 100644 datamodels/2.x/itop-hub-connector/land.php create mode 100644 datamodels/2.x/itop-hub-connector/launch.php create mode 100644 datamodels/2.x/itop-hub-connector/menus.php create mode 100644 datamodels/2.x/itop-hub-connector/model.itop-hub-connector.php create mode 100644 datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php create mode 100644 datamodels/2.x/itop-hub-connector/myextensions.php diff --git a/datamodels/2.x/installation.xml b/datamodels/2.x/installation.xml index aa7d9d75b..f4c013b53 100755 --- a/datamodels/2.x/installation.xml +++ b/datamodels/2.x/installation.xml @@ -16,6 +16,7 @@ itop-profiles-itil itop-welcome-itil itop-tickets + itop-hub-connector true diff --git a/datamodels/2.x/itop-hub-connector/ajax.php b/datamodels/2.x/itop-hub-connector/ajax.php new file mode 100644 index 000000000..5112a6a0c --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/ajax.php @@ -0,0 +1,362 @@ + + +/** + * Handles various ajax requests - called through pages/exec.php + * + * @copyright Copyright (C) 2010-2017 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ +if (!defined('__DIR__')) + define('__DIR__', dirname(__FILE__)); +require_once (APPROOT.'application/webpage.class.inc.php'); +require_once (APPROOT.'application/ajaxwebpage.class.inc.php'); +require_once (APPROOT.'application/utils.inc.php'); +require_once (APPROOT.'core/log.class.inc.php'); +IssueLog::Enable(APPROOT.'log/error.log'); + +require_once (APPROOT.'setup/runtimeenv.class.inc.php'); +require_once (APPROOT.'setup/backup.class.inc.php'); +require_once (APPROOT.'core/mutex.class.inc.php'); +require_once (APPROOT.'core/dict.class.inc.php'); +require_once (__DIR__.'/hubruntimeenvironment.class.inc.php'); + +/** + * Overload of DBBackup to handle logging + */ +class DBBackupWithErrorReporting extends DBBackup +{ + + protected $aInfos = array(); + + protected $aErrors = array(); + + protected function LogInfo($sMsg) + { + $aInfos[] = $sMsg; + } + + protected function LogError($sMsg) + { + IssueLog::Error($sMsg); + $aErrors[] = $sMsg; + } + + public function GetInfos() + { + return $this->aInfos; + } + + public function GetErrors() + { + return $this->aErrors; + } +} + +/** + * + * @param string $sTargetFile + * @throws Exception + * @return DBBackupWithErrorReporting + */ +function DoBackup($sTargetFile) +{ + // Make sure the target directory exists + $sBackupDir = dirname($sTargetFile); + SetupUtils::builddir($sBackupDir); + + $oBackup = new DBBackupWithErrorReporting(); + $oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '')); + $sSourceConfigFile = APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE; + + $oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment()); + $oMutex->Lock(); + try + { + $oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile); + } + catch (Exception $e) + { + $oMutex->Unlock(); + throw $e; + } + $oMutex->Unlock(); + return $oBackup; +} + +/** + * Outputs the status of the current ajax execution (as a JSON structure) + * + * @param string $sMessage + * @param bool $bSuccess + * @param number $iErrorCode + * @param array $aMoreFields + * Extra fields to pass to the caller, if needed + */ +function ReportStatus($sMessage, $bSuccess, $iErrorCode = 0, $aMoreFields = array()) +{ + $oPage = new ajax_page(""); + $oPage->no_cache(); + $oPage->SetContentType('application/json'); + $aResult = array( + 'code' => $iErrorCode, + 'message' => $sMessage, + 'fields' => $aMoreFields + ); + $oPage->add(json_encode($aResult)); + $oPage->output(); +} + +/** + * Helper to output the status of a successful execution + * + * @param string $sMessage + * @param array $aMoreFields + * Extra fields to pass to the caller, if needed + */ +function ReportSuccess($sMessage, $aMoreFields = array()) +{ + ReportStatus($sMessage, true, 0, $aMoreFields); +} + +/** + * Helper to output the status of a failed execution + * + * @param string $sMessage + * @param number $iErrorCode + * @param array $aMoreFields + * Extra fields to pass to the caller, if needed + */ +function ReportError($sMessage, $iErrorCode, $aMoreFields = array()) +{ + if ($iErrorCode==0) + { + // 0 means no error, so change it if no meaningful error code is supplied + $iErrorCode = -1; + } + ReportStatus($sMessage, false, $iErrorCode, $aMoreFields); +} + +try +{ + utils::PushArchiveMode(false); + + ini_set('max_execution_time', max(3600, ini_get('max_execution_time'))); // Under Windows SQL/backup operations are part of the PHP timeout and require extra time + ini_set('display_errors', 1); // Make sure that fatal errors remain visible from the end-user + + // Most of the ajax calls are done without the MetaModel being loaded + // Therefore, the language must be passed as an argument, + // and the dictionnaries be loaded here + $sLanguage = utils::ReadParam('language', ''); + if ($sLanguage!='') + { + foreach (glob(APPROOT.'env-production/dictionaries/*.dict.php') as $sFilePath) + { + require_once ($sFilePath); + } + + $aLanguages = Dict::GetLanguages(); + if (array_key_exists($sLanguage, $aLanguages)) + { + Dict::SetUserLanguage($sLanguage); + } + } + $sOperation = utils::ReadParam('operation', ''); + switch ($sOperation) + { + case 'check_before_backup': + require_once (APPROOT.'/application/startup.inc.php'); + require_once (APPROOT.'/application/loginwebpage.class.inc.php'); + LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin) + + $sDBBackupPath = APPROOT.'data/backups/manual'; + $aChecks = SetupUtils::CheckBackupPrerequisites($sDBBackupPath); + $bFailed = false; + foreach ($aChecks as $oCheckResult) + { + if ($oCheckResult->iSeverity==CheckResult::ERROR) + { + $bFailed = true; + ReportError($oCheckResult->sLabel, -2); + } + } + if (!$bFailed) + { + // Continue the checks + $fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath); + if ($fFreeSpace!==false) + { + $sMessage = Dict::Format('iTopHub:BackupFreeDiskSpaceIn', SetupUtils::HumanReadableSize($fFreeSpace), dirname($sDBBackupPath)); + ReportSuccess($sMessage); + } + else + { + ReportError(Dict::S('iTopHub:FailedToCheckFreeDiskSpace'), -1); + } + } + break; + + case 'do_backup': + require_once (APPROOT.'/application/startup.inc.php'); + require_once (APPROOT.'/application/loginwebpage.class.inc.php'); + LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin) + + try + { + set_time_limit(0); + $sBackupPath = APPROOT.'/data/backups/manual/backup-'; + $iSuffix = 1; + $sSuffix = ''; + // Generate a unique name... + do + { + $sBackupFile = $sBackupPath.date('Y-m-d-His').$sSuffix; + $sSuffix = '-'.$iSuffix; + $iSuffix++ ; + } + while (file_exists($sBackupFile)); + + $oBackup = DoBackup($sBackupFile); + $aErrors = $oBackup->GetErrors(); + if (count($aErrors)>0) + { + ReportError(Dict::S('iTopHub:BackupFailed'), -1, $aErrors); + } + else + { + ReportSuccess(Dict::S('iTopHub:BackupOk')); + } + } + catch (Exception $e) + { + ReportError($e->getMessage(), $e->getCode()); + } + break; + + case 'compile': + // First step: prepare the datamodel, if it fails, roll-back + $aSelectedExtensionCodes = utils::ReadParam('extension_codes', array()); + $aSelectedExtensionDirs = utils::ReadParam('extension_dirs', array()); + + $oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build + $oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs); + + $oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE); + + $aSelectModules = $oRuntimeEnv->CompileFrom('production', false); // WARNING symlinks does not seem to be compatible with manual Commit + + $oRuntimeEnv->UpdateIncludes($oConfig); + + $oRuntimeEnv->InitDataModel($oConfig, true /* model only */); + + $oRuntimeEnv->CheckMetaModel(); // Will throw an exception if a problem is detected + + // Everything seems Ok so far, commit in env-production! + $oRuntimeEnv->WriteConfigFileSafe($oConfig); + $oRuntimeEnv->Commit(); + + // Report the success in a way that will be detected by the ajax caller + ReportSuccess('Ok'); // No access to Dict::S here + break; + + case 'move_to_production': + // Second step: update the schema and the data + // Everything happening below is based on env-production + $oRuntimeEnv = new RunTimeEnvironment('production', true); + + try + { + // Load the "production" config file to clone & update it + $oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE); + + $oRuntimeEnv->InitDataModel($oConfig, true /* model only */); + + $aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true); + + $aSelectedModules = array(); + foreach ($aAvailableModules as $sModuleId => $aModule) + { + if (($sModuleId==ROOT_MODULE)||($sModuleId==DATAMODEL_MODULE)) + { + continue; + } + else + { + $aSelectedModules[] = $sModuleId; + } + } + + $oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation'); + + // Now I assume that there is a DB for this environment... + $oRuntimeEnv->CreateDatabaseStructure($oConfig, 'upgrade'); + + $oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation'); + + $oRuntimeEnv->UpdatePredefinedObjects(); + + $oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup'); + + // Record the installation so that the "about box" knows about the installed modules + $sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion(); + + $oExtensionsMap = new iTopExtensionsMap(); + + // Default choices = as before + $oExtensionsMap->LoadChoicesFromDatabase($oConfig); + foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) + { + // Plus all "remote" extensions + if ($oExtension->sSource==iTopExtension::SOURCE_REMOTE) + { + $oExtensionsMap->MarkAsChosen($oExtension->sCode); + } + } + $aSelectedExtensionCodes = array(); + foreach ($oExtensionsMap->GetChoices() as $oExtension) + { + $aSelectedExtensionCodes[] = $oExtension->sCode; + } + $aSelectedExtensions = $oExtensionsMap->GetChoices(); + $oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules, $aSelectedExtensionCodes, 'Done by the iTop Hub Connector'); + + // Report the success in a way that will be detected by the ajax caller + ReportSuccess(Dict::S('iTopHub:CompiledOK')); + } + catch (Exception $e) + { + // Note: at this point, the dictionnary is not necessarily loaded + IssueLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage()); + IssueLog::Error('Debug trace: '.$e->getTraceAsString()); + ReportError($e->getMessage(), $e->getCode()); + } + break; + + default: + ReportError("Invalid operation: '$sOperation'", -1); + } +} +catch (Exception $e) +{ + IssueLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage()); + IssueLog::Error('Debug trace: '.$e->getTraceAsString()); + + utils::PopArchiveMode(); + + ReportError($e->getMessage(), $e->getCode()); +} diff --git a/datamodels/2.x/itop-hub-connector/css/hub.css b/datamodels/2.x/itop-hub-connector/css/hub.css new file mode 100644 index 000000000..f2defdb4a --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/css/hub.css @@ -0,0 +1,252 @@ +#hub_popup_menu li span { + display: block; + padding: 5px 12px; + text-decoration: none; + nowidth: 70px; + white-space: nowrap; +} + +#hub_popup_menu li li p { + text-align: left; + margin: 2px; + margin-left: 5px; + margin-right: 5px; + cursor: pointer; + background-color: transparent; + color: #000; +} +#hub_popup_menu > ul > li > ul { + margin-top: 8px; +} +#hub_popup_menu > ul > li > ul > li { + display: block; + border-bottom: 1px #ddd solid; + padding-top: 10px; + padding-bottom: 10px; +} +#hub_popup_menu > ul > li > ul > li:hover { + color: #fff; + background-color: #E87C1E; +} +#hub_popup_menu li li:last-of-type { + border-bottom: 0; +} +#hub_popup_menu li { + list-style: none; + cursor: pointer; +} +#hub_popup_menu li ul { + width: 340px; +} +#top-left-hub-cell { + padding-right: 8px !important; +} +#hub_launch_container { + padding:10px; + overflow: visible; + width: 800px; + margin-left: auto; + margin-right: auto; +} +#hub_launch_container h1 { + font-size: 14pt; + line-height: 24pt; +} +#hub_launch_container h1 img { + vertical-align: middle; + margin-right: 0.25em; + height: 24pt; +} +#hub_launch_image { + width: 120pt; + height: 250pt; + float: left; + padding-top: 10pt; + position: relative; + top: -23px; +} + +.animate #rocket, .animate #flame { + animation: animationFrames ease-in 2s; + animation-iteration-count: 1; + transform-origin: 0% 0%; + animation-fill-mode:forwards; /*when the spec is finished*/ +} +#flame { + opacity: 0; +} +.animate #flame { + animation: flameAnimationFrames ease-in 2s; + animation-iteration-count: 1; + transform-origin: 0% 0%; + animation-fill-mode:forwards; /*when the spec is finished*/ +} +#chameleon { + opacity: 0; + animation: chameleonAnimationFrames linear 1s; + animation-delay: 1s; + animation-iteration-count: 1; + transform-origin: 0% 0%; + animation-fill-mode:forwards; /*when the spec is finished*/ +} + +@keyframes animationFrames { + 0% { + transform: translate(0px,0px) ; + } + 15% { + transform: translate(0px,0px) ; + } + 100% { + transform: translate(0px,-950px) ; + } +} +@keyframes flameAnimationFrames { + 0% { + opacity: 0; + transform: translate(0px,0px) ; + } + 15% { + opacity: 1; + transform: translate(0px,0px) ; + } + 100% { + opacity: 1; + transform: translate(0px,-950px) ; + } +} +@keyframes chameleonAnimationFrames { + 0% { + opacity: 0; + transform: translate(-45px,0px) ; + } + 69% { + opacity: 0; + transform: translate(-45px,0px) ; + } + 70% { + opacity: 1; + transform: translate(-45px,0px) ; + } + 100% { + opacity: 1; + transform: translate(0px,0px) ; + } +} + +#backup_form { + display: none; +} +.landing-extension { + padding: 0.25em; + border: 1px solid; + margin-top: 2px; + margin-bottom: 2px; + border-radius: 5px; +} +.landing-upgrade { + background-color: #eeeeff; + border-color: #bbbbdd; +} +.landing-downgrade { + background-color: #ffcccc; + border-color: #dd9999; +} +.landing-installation { + background-color: #eeffee; + border-color: #bbddbb; +} +.landing-no-change, .landing-no-change h2 { + background-color: #eeeeee; + border-color: #bbbbbb; + color: #555555; +} +#hub-landing-page-title h1 { + font-size: 14pt; + line-height: 24pt; +} +#hub-landing-page-title h1 img { + vertical-align: middle; + margin-right: 0.25em; + height: 24pt; +} +#hub-installation-feedback { + display: none; + -moz-box-shadow: 5px 5px 10px 0px #656565; + -webkit-box-shadow: 5px 5px 10px 0px #656565; + -o-box-shadow: 5px 5px 10px 0px #656565; + box-shadow: 5px 5px 10px 0px #656565; + filter:progid:DXImageTransform.Microsoft.Shadow(color=#656565, Direction=134, Strength=10); + padding-bottom: 1em; + background-color: #FFF; + padding-top: 1em; + position: absolute; + top: 50px; + left: 100px; + width: 400px; +} +#hub-installation-progress-text { + text-align: center; + margin-top: 1em; +} +#hub-installation-progress { + height: 20px; + background-image: url(../images/orange-progress.gif); + background-color: #ff9000; + text-align: center; + margin-top: 1em; + margin-bottom: 1em; + margin-left: auto; + margin-right: auto; + width: 300px; + border: 1px solid #000; + +} +#hub_top_banner { + height: 77px; + background-color: rgb(27, 28, 29); +} +#hub_launch_content { + width: 100%; +} +button { + cursor: pointer; + font-weight: bold; + box-shadow: 0px 0px 0px 1px transparent inset, 0px 0em 0px 0px rgba(34, 36, 38, 0.15) inset; + border: 0; + background-color: #E0E1E2; + height: 40px; + vertical-align: middle; + padding: 0; +} +button.positive { + background-color: rgb(234, 125, 30); + color: #FFFFFF; +} +button:disabled, button[disabled] { + background-color: #828487!important; + cursor: progress !important; +} +button > span { + display: inline-block; + padding: 0.8em; + padding-left: 1.3em; + padding-right: 1.3em; +} +button:hover { + background-color: #D0D0D0; +} +button.positive:hover { + background-color: rgb(221, 113, 27); +} +button > img { + width: 16px; + height: 16px; + vertical-align: middle; + padding: 12px; + background-color: rgba(0, 0, 0, 0.1); +} +.horiz-spacer { + display: inline-block; + width: 1.5em; +} \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/datamodel.itop-hub-connector.xml b/datamodels/2.x/itop-hub-connector/datamodel.itop-hub-connector.xml new file mode 100644 index 000000000..f26fc7562 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/datamodel.itop-hub-connector.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + https://www.proto2.itophub.io + /my-instances/landing-from-remote + /stateless-remote-itop/landing-from-remote-stateless + /api/notifications + ../pages/exec.php?exec_module=itop-hub-connector&exec_page=launch.php&target=inform_after_setup + + + diff --git a/datamodels/2.x/itop-hub-connector/en.dict.itop-hub-connector.php b/datamodels/2.x/itop-hub-connector/en.dict.itop-hub-connector.php new file mode 100644 index 000000000..ed15372bd --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/en.dict.itop-hub-connector.php @@ -0,0 +1,70 @@ + 'iTop Hub', + 'Menu:iTopHub:Register' => 'Connect to iTop Hub', + 'Menu:iTopHub:Register+' => 'Go to iTop Hub to update your iTop instance', + 'Menu:iTopHub:Register:Description' => '

Get access to your community platform iTop Hub!
Find all the content and information you need, manage your instances through personalized tools & install more extensions.

By connecting to the Hub from this page, you will push information about this iTop instance into the Hub.

', + 'Menu:iTopHub:MyExtensions' => 'Deployed extensions', + 'Menu:iTopHub:MyExtensions+' => 'See the list of extensions deployed on this instance of iTop', + 'Menu:iTopHub:BrowseExtensions' => 'Get extensions from iTop Hub', + 'Menu:iTopHub:BrowseExtensions+' => 'Browse for more extensions on iTop Hub', + 'Menu:iTopHub:BrowseExtensions:Description' => '

Look into iTop Hub’s store, your one stop place to find wonderful iTop extensions !
Find the ones that will help you customize and adapt iTop to your processes.

By connecting to the Hub from this page, you will push information about this iTop instance into the Hub.

', + 'iTopHub:GoBtn' => 'Go To iTop Hub', + 'iTopHub:CloseBtn' => 'Close', + 'iTopHub:GoBtn:Tooltip' => 'Jump to www.itophub.io', + 'iTopHub:OpenInNewWindow' => 'Open iTop Hub in a new window', + 'iTopHub:AutoSubmit' => 'Don\'t ask me again. Next time, go to iTop Hub automatically.', + 'UI:About:RemoteExtensionSource' => 'iTop Hub', + 'iTopHub:Explanation' => 'By clicking this button you will be redirected to iTop Hub.', + + 'iTopHub:BackupFreeDiskSpaceIn' => '%1$s free disk space in %2$s.', + 'iTopHub:FailedToCheckFreeDiskSpace' => 'Failed to check free disk space.', + 'iTopHub:BackupOk' => 'Backup Ok.', + 'iTopHub:BackupFailed' => 'Backup failed!', + 'iTopHub:Landing:Status' => 'Deployment status', + 'iTopHub:Landing:Install' => 'Deploying extensions...', + 'iTopHub:CompiledOK' => 'Compilation successful.', + 'iTopHub:ConfigurationSafelyReverted' => 'Error detected during deployment!
iTop configuration has NOT been modified.', + + 'iTopHub:InstalledExtensions' => 'Extensions deployed on this instance', + 'iTopHub:ExtensionCategory:Manual' => 'Extensions deployed manually', + 'iTopHub:ExtensionCategory:Manual+' => 'The following extensions have been deployed by copying them manually in the %1$s directory of iTop:', + 'iTopHub:ExtensionCategory:Remote' => 'Extensions deployed from iTop Hub', + 'iTopHub:ExtensionCategory:Remote+' => 'The following extensions have been deployed from iTop Hub:', + 'iTopHub:NoExtensionInThisCategory' => 'There is no extension in this category.

Browse iTop Hub to find the extensions that will help you customize and adapt iTop to your processes.', + 'iTopHub:ExtensionNotInstalled' => 'Not installed', + 'iTopHub:GetMoreExtensions' => 'Get extensions from iTop Hub...', + + 'iTopHub:LandingWelcome' => 'Congratulations! The following extensions were downloaded from iTop Hub and deployed into your iTop.', + 'iTopHub:GoBackToITopBtn' => 'Go Back to iTop', + 'iTopHub:Uncompressing' => 'Uncompressing extensions...', + 'iTopHub:InstallationWelcome' => 'Installation of the extensions downloaded from iTop Hub', + 'iTopHub:DBBackupLabel' => 'Instance backup', + 'iTopHub:DBBackupSentence' => 'Do a backup of the database and iTop configuration before updating', + 'iTopHub:DeployBtn' => 'Deploy !', + 'iTopHub:DatabaseBackupProgress' => 'Instance backup...', + + 'iTopHub:InstallationEffect:Install' => 'Version: %1$s will be installed.', + 'iTopHub:InstallationEffect:NoChange' => 'Version: %1$s already installed. Nothing will change.', + 'iTopHub:InstallationEffect:Upgrade' => 'Will be upgraded from version %1$s to version %2$s.', + 'iTopHub:InstallationEffect:Downgrade' => 'Will be DOWNGRADED from version %1$s to version %2$s.', + 'iTopHub:InstallationProgress:DatabaseBackup' => 'iTop Instance backup...', + 'iTopHub:InstallationProgress:ExtensionsInstallation' => 'Installation of the extensions', + 'iTopHub:InstallationEffect:MissingDependencies' => 'This extension cannot be installed because of unmet dependencies.', + 'iTopHub:InstallationEffect:MissingDependencies_Details' => 'The extension requires the module(s): %1$s', + 'iTopHub:InstallationProgress:InstallationSuccessful' => 'Installation successful!', + + 'iTopHub:InstallationStatus:Installed_Version' => '%1$s version: %2$s.', + 'iTopHub:InstallationStatus:Installed' => 'Installed', + 'iTopHub:InstallationStatus:Version_NotInstalled' => 'Version %1$s NOT installed.', +)); + + diff --git a/datamodels/2.x/itop-hub-connector/extension.xml b/datamodels/2.x/itop-hub-connector/extension.xml new file mode 100644 index 000000000..93961d79b --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/extension.xml @@ -0,0 +1,9 @@ + + + itop-hub-connector + 0.0.4 + + Extension to connect iTop with the central iTopHub platform + false + + \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php b/datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php new file mode 100644 index 000000000..f841162fc --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php @@ -0,0 +1,68 @@ + 'iTop Hub', + 'Menu:iTopHub:Register' => 'Se connecter à iTop Hub', + 'Menu:iTopHub:Register+' => 'Connectez-vous à iTop Hub pour enregistrer cette instance d\'iTop', + 'Menu:iTopHub:Register:Description' => '

Connectez-vous à la communauté iTop Hub!
Trouvez tout le contenu dont vous avez besoin, gérer vos instances d\'iTop depuis un tableau de bord centralisé et déployez de nouvelles extensions.

En vous connectant au Hub depuis cette page, vous transmettez au Hub des informations relatives à cette instance d\'iTop.

', + 'Menu:iTopHub:MyExtensions' => 'Extensions déployées', + 'Menu:iTopHub:MyExtensions+' => 'Voir la liste des extensions déployes sur cette instance', + 'Menu:iTopHub:BrowseExtensions' => 'Obtenir des extensions depuis iTop Hub', + 'Menu:iTopHub:BrowseExtensions+' => 'Parcourir la listes des extensions disponibles sur iTop Hub', + 'Menu:iTopHub:BrowseExtensions:Description' => '

Découvrez le magasin d\'extensions iTop Hub !
Trouvez en quelques clics celles qui vous permettront de construire un iTop sur mesure qui se conforme à vos processus.

En vous connectant au Hub depuis cette page, vous transmettez au Hub des informations relatives à cette instance d\'iTop.

', + 'iTopHub:GoBtn' => 'Aller sur iTop Hub', + 'iTopHub:CloseBtn' => 'Fermer', + 'iTopHub:GoBtn:Tooltip' => 'Naviguer vers www.itophub.io', + 'iTopHub:OpenInNewWindow' => 'Ouvrir iTop Hub dans une nouvelle fenêtre', + 'iTopHub:AutoSubmit' => 'Ne plus me demander. La prochaine fois, aller sur iTop Hub automatiquement.', + 'UI:About:RemoteExtensionSource' => 'iTop Hub', + 'iTopHub:Explanation' => 'En cliquant sur ce bouton, vous serez redirigé vers iTop Hub.', + + 'iTopHub:BackupFreeDiskSpaceIn' => '%1$s d\'espace disque disponible sur %2$s.', + 'iTopHub:FailedToCheckFreeDiskSpace' => 'Echec de la vérification de l\'espace disque.', + 'iTopHub:BackupOk' => 'Sauvegarde Ok.', + 'iTopHub:BackupFailed' => 'Echec de la sauvegarde !', + 'iTopHub:Landing:Status' => 'Etat du déploiement', + 'iTopHub:Landing:Install' => 'Déploiement des extensions...', + 'iTopHub:CompiledOK' => 'Compilation réussie.', + 'iTopHub:ConfigurationSafelyReverted' => 'Une erreur a été détectée durant le déploiement!
La configuration d\'iTop n\'a PAS été modifiée.', + + 'iTopHub:InstalledExtensions' => 'Extensions déployées sur cette instance', + 'iTopHub:ExtensionCategory:Manual' => 'Extensions déployées manuellement', + 'iTopHub:ExtensionCategory:Manual+' => 'Les extensions ci-dessous ont été déployées en les copiant manuellement dans le répertoire %1$s d\'iTop:', + 'iTopHub:ExtensionCategory:Remote' => 'Extensions déployées depuis iTop Hub', + 'iTopHub:ExtensionCategory:Remote+' => 'Les extensions ci-dessous ont été déployées depuis iTop Hub:', + 'iTopHub:NoExtensionInThisCategory' => 'Il n\'y a pas d\'extension dans cette catégorie

Avec iTop Hub trouvez en quelques clics les extensions qui vous permettront de construire un iTop sur mesure qui se conforme à vos processus.', + 'iTopHub:ExtensionNotInstalled' => 'Non installée', + 'iTopHub:GetMoreExtensions' => 'Obtenir des extensions depuis iTop Hub...', + + 'iTopHub:LandingWelcome' => 'Félicitations! Les extensions ci-dessous ont été téléchargées depuis iTop Hub et installées sur cette instance d\'iTop.', + 'iTopHub:GoBackToITopBtn' => 'Retourner dans iTop', + 'iTopHub:Uncompressing' => 'Décompression des extensions...', + 'iTopHub:InstallationWelcome' => 'Installation des extensions téléchargées depuis iTop Hub', + 'iTopHub:DBBackupLabel' => 'Sauvegarde de l\'instance iTop', + 'iTopHub:DBBackupSentence' => 'Faire une sauvegarde de la base de données et des paramétrages d\'iTop', + 'iTopHub:DeployBtn' => 'Déployer !', + 'iTopHub:DatabaseBackupProgress' => 'Sauvegarde de l\'instance...', + + 'iTopHub:InstallationEffect:Install' => 'Version: %1$s sera installée.', + 'iTopHub:InstallationEffect:NoChange' => 'Version: %1$s déjà installée. Rien ne changera.', + 'iTopHub:InstallationEffect:Upgrade' => 'Sera mise à jour de version %1$s en version %2$s.', + 'iTopHub:InstallationEffect:Downgrade' => 'Sera DEGRADEE de version %1$s en version %2$s.', + 'iTopHub:InstallationEffect:MissingDependencies' => 'Cette extension ne peut pas être installée à cause de ses dépendences.', + 'iTopHub:InstallationEffect:MissingDependencies_Details' => 'Cette extension nécessite le(s) module(s): %1$s', + 'iTopHub:InstallationProgress:DatabaseBackup' => 'Sauvegarde de l\'instance iTop...', + 'iTopHub:InstallationProgress:ExtensionsInstallation' => 'Installation des extensions', + 'iTopHub:InstallationProgress:InstallationSuccessful' => 'Installation réussie !', + + 'iTopHub:InstallationStatus:Installed_Version' => '%1$s version: %2$s.', + 'iTopHub:InstallationStatus:Installed' => 'Installée', + 'iTopHub:InstallationStatus:Version_NotInstalled' => 'Version %1$s NON installée.' +)); \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/hubconnectorpage.class.inc.php b/datamodels/2.x/itop-hub-connector/hubconnectorpage.class.inc.php new file mode 100644 index 000000000..f1782e9e6 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/hubconnectorpage.class.inc.php @@ -0,0 +1,119 @@ +add_header("Cache-control: no-cache"); + + $sImagesDir = utils::GetAbsoluteUrlAppRoot().'images'; + $sModuleImagesDir = utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/images'; + + $sUserPrefs = appUserPreferences::GetAsJSON(); + $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js'); + $this->add_script( +<<add_style( +<< button, #wiz_buttons > form{ + margin: 20px; +} +#launcher { + display: inline-block; +} +.header_message { + color: black; + margin-top: 10px; +} +.checkup_info { + background: url("$sImagesDir/info-mini.png") no-repeat left; + padding-left: 2em; +} +.checkup_error { + background: url("$sImagesDir/validation_error.png") no-repeat left; + padding-left: 2em; +} +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: url("$sModuleImagesDir/ui-bg_flat_35_555555_40x100.png") repeat-x scroll 50% 50% #555555; + border: 1px solid #555555; + color: #EEEEEE; + font-weight: bold; +} +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, + .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-focus .ui-state-focus { + background: url("$sModuleImagesDir/ui-bg_flat_33_F58400_40x100.png") repeat-x scroll 50% 50% #F58400; + border: 1px solid #F58400; + color: #EEEEEE; + font-weight: bold; +} +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { + border-bottom-right-radius: 0; +} +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { + border-bottom-left-radius: 0; +} +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { + border-top-right-radius: 0; +} +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { + border-top-left-radius: 0; +} +.ui-widget { + font-family: Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-button, .ui-button:link, .ui-button:visited, .ui-button:hover, .ui-button:active { + text-decoration: none; +} +.ui-button { + cursor: pointer; + display: inline-block; + line-height: normal; + margin-right: 0.1em; + overflow: visible; + padding: 0; + position: relative; + text-align: center; + vertical-align: middle; +} +#db_backup_path { + width: 99%; +} +div#integrity_issues { + background-color: #FFFFFF; +} +div#integrity_issues .query { + font-size: smaller; +} + +EOF + ); + } +} diff --git a/datamodels/2.x/itop-hub-connector/hubruntimeenvironment.class.inc.php b/datamodels/2.x/itop-hub-connector/hubruntimeenvironment.class.inc.php new file mode 100644 index 000000000..597d2230c --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/hubruntimeenvironment.class.inc.php @@ -0,0 +1,59 @@ +sTargetEnv) + { + SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sTargetEnv.'-modules'); + } + } + + /** + * Update the includes for the target environment + * @param Config $oConfig + */ + public function UpdateIncludes(Config $oConfig) + { + $oConfig->UpdateIncludes('env-'.$this->sTargetEnv); // TargetEnv != FinalEnv + } + + /** + * Move an extension (path to folder of this extension) to the target environment + * @param string $sExtensionDirectory The folder of the extension + * @throws Exception + */ + public function MoveExtension($sExtensionDirectory) + { + if (!is_dir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) + { + if (!mkdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) throw new Exception("ERROR: failed to create directory:'".(APPROOT.'/data/'.$this->sTargetEnv.'-modules')."'"); + } + $sDestinationPath = APPROOT.'/data/'.$this->sTargetEnv.'-modules/'; + if (!rename($sExtensionDirectory, $sDestinationPath.basename($sExtensionDirectory))) throw new Exception("ERROR: failed move directory:'$sExtensionDirectory' to '".$sDestinationPath.basename($sExtensionDirectory)."'"); + } + + /** + * Move the selected extensions located in the given directory in data/-modules + * @param string $sDownloadedExtensionsDir The directory to scan + * @param string[] $aSelectedExtensionDirs The list of folders to move + * @throws Exception + */ + public function MoveSelectedExtensions($sDownloadedExtensionsDir, $aSelectedExtensionDirs) + { + foreach(glob($sDownloadedExtensionsDir.'*', GLOB_ONLYDIR) as $sExtensionDir) + { + if (in_array(basename($sExtensionDir), $aSelectedExtensionDirs)) + { + $this->MoveExtension($sExtensionDir); + } + } + } +} diff --git a/datamodels/2.x/itop-hub-connector/images/black-close.svg b/datamodels/2.x/itop-hub-connector/images/black-close.svg new file mode 100644 index 000000000..3e9c0655b --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/images/black-close.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/images/itophub-logo.png b/datamodels/2.x/itop-hub-connector/images/itophub-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..dd1df1081917048014c28706692edc4f574ce4d3 GIT binary patch literal 17632 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 G?Ee6b2PL5Z literal 0 HcmV?d00001 diff --git a/datamodels/2.x/itop-hub-connector/images/itophub-logo.svg b/datamodels/2.x/itop-hub-connector/images/itophub-logo.svg new file mode 100644 index 000000000..3571f8fd6 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/images/itophub-logo.svg @@ -0,0 +1,105 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/datamodels/2.x/itop-hub-connector/images/landing-extension.png b/datamodels/2.x/itop-hub-connector/images/landing-extension.png new file mode 100644 index 0000000000000000000000000000000000000000..94de452c7d8b5e57a60cafaa97e2eb2504b838cb GIT binary patch literal 3736 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 + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/datamodels/2.x/itop-hub-connector/images/white-arrow-right.svg b/datamodels/2.x/itop-hub-connector/images/white-arrow-right.svg new file mode 100644 index 000000000..f0b8455f5 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/images/white-arrow-right.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/installation.xml b/datamodels/2.x/itop-hub-connector/installation.xml new file mode 100644 index 000000000..5854a936c --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/installation.xml @@ -0,0 +1,212 @@ + + + + + Configuration Management options + The options below allow you to configure the type of elements that are to be managed inside iTop.]]> + /images/modules.png + + + itop-config-mgmt-core + Configuration Management Core + All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc. + + itop-config-mgmt + itop-attachments + itop-profiles-itil + itop-welcome-itil + itop-tickets + itop-hub-connector + + true + + + itop-config-mgmt-datacenter + Data Center Devices + Manage Data Center devices such as Racks, Enclosures, PDUs, etc. + + itop-datacenter-mgmt + + true + + + itop-config-mgmt-end-user + End-User Devices + Manage devices related to end-users: PCs, Phones, Tablets, etc. + + itop-endusers-devices + + true + + + itop-config-mgmt-storage + Storage Devices + Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc. + + itop-storage-mgmt + + true + + + itop-config-mgmt-virtualization + Virtualization + Manage Hypervisors, Virtual Machines and Farms. + + itop-virtualization-mgmt + + true + + + + + Service Management options + Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.]]> + ./wizard-icons/service.png + + + itop-service-mgmt-enterprise + Service Management for Enterprises + Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend. + + itop-service-mgmt + + true + + + itop-service-mgmt-service-provider + Service Management for Service Providers + Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices. + + itop-service-mgmt-provider + + + + + + Tickets Management options + Select the type of tickets you want to use in order to respond to user requests and incidents.]]> + ./itop-incident-mgmt-itil/images/incident-escalated.png + + + itop-ticket-mgmt-simple-ticket + Simple Ticket Management + Select this option to use one single type of tickets for all kind of requests. + + itop-request-mgmt + + true + + + + itop-ticket-mgmt-simple-ticket-enhanced-portal + Enhanced Customer Portal + Replace the built-in customer portal with a more modern version, working better with hand-held devices and bringing new features + + itop-portal + itop-portal-base + + true + + + + + + itop-ticket-mgmt-itil + ITIL Compliant Tickets Management + Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields + + + + itop-ticket-mgmt-itil-user-request + User Request Management + Manage User Request tickets in iTop + + itop-request-mgmt-itil + + + + itop-ticket-mgmt-itil-incident + Incident Management + Manage Incidents tickets in iTop + + itop-incident-mgmt-itil + + + + itop-ticket-mgmt-itil-enhanced-portal + Enhanced Customer Portal + Replace the built-in customer portal with a more modern version, working better with hand-held devices and bringing new features + + itop-portal + itop-portal-base + + true + + + + + + itop-ticket-mgmt-none + No Tickets Management + Don't manage incidents or user requests in iTop + + + + + + + Change Management options + Select the type of tickets you want to use in order to manage changes to the IT infrastructure.]]> + ./itop-change-mgmt/images/change.png + + + itop-change-mgmt-simple + Simple Change Management + Select this option to use one type of ticket for all kind of changes. + + itop-change-mgmt + + true + + + itop-change-mgmt-itil + ITIL Change Management + Select this option to use Normal/Routine/Emergency change tickets. + + itop-change-mgmt-itil + + + + itop-change-mgmt-none + No Change Management + Don't manage changes in iTop + + + + + + + Additional ITIL tickets + Pick from the list below the additional ITIL processes that are to be implemented in iTop.]]> + ./itop-knownerror-mgmt/images/known-error.png + + + itop-kown-error-mgmt + Known Errors Management + Select this option to track "Known Errors" and FAQs in iTop. + + itop-knownerror-mgmt + + + + itop-problem-mgmt + Problem Management + Select this option track "Problems" in iTop. + + itop-problem-mgmt + + + + + + diff --git a/datamodels/2.x/itop-hub-connector/js/hub.js b/datamodels/2.x/itop-hub-connector/js/hub.js new file mode 100644 index 000000000..952b2e36f --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/js/hub.js @@ -0,0 +1,187 @@ +//jQuery UI style "widget" for running the installation +$(function() +{ + // the widget definition, where "itop" is the namespace, + // "fieldsorter" the widget name + $.widget( "itop.hub_installation", + { + // default options + options: + { + self_url: '', + redirect_after_completion_url: '', + iframe_url: '', + mysql_bindir: '', + main_page_url: './UI.php', + labels: { + database_backup: 'Database backup...', + extensions_installation: 'Installation of the extensions...', + installation_successful: 'Installation successful!', + rollback: 'iTop configuration has NOT been modified.' + } + }, + + // the constructor + _create: function() + { + var me = this; + + this.element + .addClass('itop-hub_installation'); + + $( document ).ajaxError( function(event, jqxhr, settings, thrownError) { me._on_ajax_error(event, jqxhr, settings, thrownError);} ); + }, + // events bound via _bind are removed automatically + // revert other modifications here + _destroy: function() + { + this.element + .removeClass('itop-hub_installation'); + }, + // _setOptions is called with a hash of all options that are changing + _setOptions: function() + { + this._superApply(arguments); + }, + // _setOption is called for each individual option that is changing + _setOption: function(key, value) + { + if (this.options[key] != value) + { + // If any option changes, clear the cache + this._clearCache(); + } + + this._superApply(arguments); + }, + check_before_backup: function() + { + var me = this; + $.post(this.options.self_url, {operation: 'check_before_backup', mysql_bindir: this.options.mysql_bindir}, function(data) { me._on_check_before_backup(data) }, 'json'); + }, + _on_check_before_backup: function(data) + { + if (data.code == 0) + { + $('#backup_status').html(data.message); + $('#backup_form').show(); + } + else + { + $('#backup_status').html(data.message); + } + if ($('#hub_start_installation').hasClass('ui-button')) + { + $('#hub_start_installation').button('enable'); + } + else + { + $('#hub_start_installation').prop('disabled', false); + } + }, + backup: function() + { + var me = this; + $('#hub-installation-progress-text').html(' '+this.options.labels.database_backup); + $.post(this.options.self_url, {operation: 'do_backup'}, function(data) { me._on_backup(data) }, 'json'); + }, + _on_backup: function(data) + { + if (data.code == 0) + { + this._reportSuccess(data.message); + // continue to the compilation + this.compile(); + } + else + { + this._reportError(data.message); + } + }, + compile: function() + { + $('#hub-installation-progress-text').html(' '+this.options.labels.extensions_installation); + var me = this; + var aExtensionCodes = []; + var aExtensionDirs = []; + $('.choice :input:checked').each(function() { aExtensionCodes.push($(this).attr('data-extension-code')); aExtensionDirs.push($(this).attr('data-extension-dir')); }); + $.post(this.options.self_url, {operation: 'compile', extension_codes: aExtensionCodes, extension_dirs: aExtensionDirs}, function(data) { me._on_compile(data) }, 'json'); + }, + _on_compile: function(data) + { + if (data.code == 0) + { + this._reportSuccess(data.message); + // continue to the move to prod + this.move_to_prod(); + } + else + { + this._reportError(data.message); + } + }, + move_to_prod: function() + { + $('#hub-installation-progress-text').html(' '+this.options.labels.extensions_installation); + var me = this; + $.post(this.options.self_url, {operation: 'move_to_production'}, function(data) { me._on_move_to_prod(data) }, 'json'); + }, + _on_move_to_prod: function(data) + { + if (data.code == 0) + { + this._reportSuccess(data.message); + $('#hub-installation-progress-text').html(' '+this.options.labels.installation_successful); + // Report the installation status to iTop Hub + $('#at_the_end').append(''); + if (this.options.redirect_after_completion_url != '') + { + var sUrl = this.options.redirect_after_completion_url; + window.setTimeout(function() { window.location.href = sUrl; }, 500); + } + } + else + { + this._reportError(data.message); + } + }, + start_installation: function() + { + $('#installation-summary :input').prop('disabled', true); // Prevent changing the settings + $('#database-backup-fieldset').hide(); + $('#hub-installation-feedback').show(); + if ($('#hub_start_installation').hasClass('ui-button')) + { + $('#hub_start_installation').button('disable'); + } + else + { + $('#hub_start_installation').prop('disabled', true); + } + if ($('#backup_checkbox').prop('checked')) + { + this.backup(); + } + else + { + this.compile(); + } + }, + _reportError: function(sMessage) + { + $('#hub-installation-progress-text').html(' '+this.options.labels.rollback+'
'+sMessage+''); + $('#hub_start_installation').val('Go Back to iTop'); + $('#hub_start_installation').prop('disabled', false); + $('#hub-installation-progress').hide(); + var me = this; + $('#hub_start_installation').off('click').on('click', function() { window.location.href = me.options.main_page_url; }) + }, + _reportSuccess: function(sMessage) + { + }, + _on_ajax_error: function(event, jqxhr, settings, thrownError) + { + this._reportError(jqxhr.responseText); + } + }); +}); \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/land.php b/datamodels/2.x/itop-hub-connector/land.php new file mode 100644 index 000000000..deeda7908 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/land.php @@ -0,0 +1,334 @@ +set_title(Dict::S('iTopHub:Landing:Status')); + + $oPage->add(''); + $sBannerUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/landing-extension.png'; + $oPage->add('
'); + $oPage->add('

'.Dict::S('iTopHub:LandingWelcome').'

'); + $oPage->add('
'); + + $oPage->add('
'); + // Now scan the extensions and display a report of the extensions brought by the hub + $oExtensionsMap = new iTopExtensionsMap(); + $sPath = APPROOT.'data/downloaded-extensions/'; + if (is_dir($sPath)) + { + $oExtensionsMap->ReadDir($sPath, iTopExtension::SOURCE_REMOTE); // Also read the extra downloaded-modules directory + } + $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); + + foreach($oExtensionsMap->GetAllExtensions() as $oExtension) + { + if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE) + { + $aCSSClasses = array('landing-extension'); + if ($oExtension->sInstalledVersion === '') + { + $aCSSClasses[] = 'landing-installation'; + $sInstallation = Dict::Format('iTopHub:InstallationStatus:Version_NotInstalled', $oExtension->sVersion); + + } + else + { + $aCSSClasses[] = 'landing-no-change'; + $sBadge = ''.Dict::S('iTopHub:InstallationStatus:Installed').''; + $sInstallation = Dict::Format('iTopHub:InstallationStatus:Installed_Version', $sBadge, $oExtension->sInstalledVersion); + } + + $oPage->add('
'); + $sCode = $oExtension->sCode; + $sDir = basename($oExtension->sSourceDir); + $oPage->add(' '); + $oPage->add(''); + $oPage->add('
'); + $oPage->add('

'); + if ($oExtension->sDescription != '') + { + $oPage->add(htmlentities($oExtension->sDescription, ENT_QUOTES, 'UTF-8').'
'); + } + $oPage->add('

'); + $oPage->add('
'); + $oPage->add('
'); + } + } + $oPage->add('
'); + $oPage->add('
'); +} + +function DoLanding(WebPage $oPage) +{ + $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/css/hub.css'); + $oPage->add(''); + $sBannerUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/landing-extension.png'; + $oPage->add('
'); + $oPage->add('

'.Dict::S('iTopHub:InstallationWelcome').'

'); + $oPage->add('
'); + + $oPage->set_title(Dict::S('iTopHub:Landing:Status')); + + $oPage->add('

'.Dict::S('iTopHub:Uncompressing').'

'); + + + $sProduct = utils::ReadParam('applicationName', '', false, 'raw_data'); + $sVersion = utils::ReadParam('applicationVersion', '', false, 'raw_data'); + $sInstanceUUID = utils::ReadParam('uuidFile', '', false, 'raw_data'); + $sDatabaseUUID = utils::ReadParam('uuidBdd', '', false, 'raw_data'); + $aExtensions = utils::ReadParam('extensions', array(), false, 'raw_data'); + + // Basic consistency validation + if ($sProduct != ITOP_APPLICATION) + { + throw new Exception("Inconsistent product '$sProduct', expecting '".ITOP_APPLICATION."'"); + } + + if ($sVersion != ITOP_VERSION) + { + throw new Exception("Inconsistent version '$sVersion', expecting ".ITOP_VERSION."'"); + } + + $sFileUUID = (string) trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n"); + if ($sInstanceUUID != $sFileUUID) + { + throw new Exception("Inconsistent file UUID '$sInstanceUUID', expecting ".$sFileUUID."'"); + } + + $sDBUUID = (string) trim(DBProperty::GetProperty('database_uuid', ''), '{}'); + if ($sDatabaseUUID != $sDBUUID) + { + throw new Exception("Inconsistent database UUID '$sDatabaseUUID', expecting ".$sDBUUID."'"); + } + + // Uncompression of extensions in data/downloaded-extensions + // only newly downloaded extensions reside in this folder + $i = 0; + $sPath = APPROOT.'data/downloaded-extensions/'; + if (!is_dir($sPath)) + { + if (!mkdir($sPath)) throw new Exception("ERROR: Unable to create the directory '$sPath'. Cannot download any extension. Check the access rights on '".dirname($sPath)."'"); + } + else + { + // Make sure that the directory is empty + SetupUtils::tidydir($sPath); + } + + foreach($aExtensions as $sBase64Archive) + { + $sArchive = base64_decode($sBase64Archive); + + $sZipArchiveFile = $sPath."/extension-{$i}.zip"; + file_put_contents($sZipArchiveFile, $sArchive); + // Expand the content of extension-x.zip into APPROOT.'data/downloaded-extensions/' + // where the installation will load the extension automatically + $oZip = new ZipArchive(); + if (!$oZip->open($sZipArchiveFile)) + { + throw new Exception('Unable to open "'.$sZipArchiveFile.'" for extraction. Make sure that the directory "'.$sPath.'" is writable for the web server.'); + } + for($idx = 0; $idx < $oZip->numFiles; $idx++) + { + $sCompressedFile = $oZip->getNameIndex($idx); + $oZip->extractTo($sPath, $sCompressedFile); + } + @$oZip->close(); + @unlink($sZipArchiveFile); // Get rid of the temporary file + $i++; + } + + // Now scan the extensions and display a report of the extensions brought by the hub + $sNextPage = utils::GetAbsoluteUrlModulePage('itop-hub-connector', 'land.php', array('operation' => 'install')); + $oPage->add_ready_script("window.location.href='$sNextPage'"); + +} + +function DoInstall(WebPage $oPage) +{ + $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/css/hub.css'); + $oPage->add(''); + $sBannerUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/landing-extension.png'; + $oPage->add('
'); + $oPage->add('

'.Dict::S('iTopHub:InstallationWelcome').'

'); + $oPage->add('
'); + + $oPage->set_title(Dict::S('iTopHub:Landing:Install')); + $oPage->add('
'); + + + // Now scan the extensions and display a report of the extensions brought by the hub + $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap->NormalizeOldExtensions(iTopExtension::SOURCE_REMOTE); + $sPath = APPROOT.'data/downloaded-extensions/'; + $oExtensionsMap->ReadDir($sPath, iTopExtension::SOURCE_REMOTE); // Also read the extra downloaded-extensions directory + $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); + + foreach($oExtensionsMap->GetAllExtensions() as $oExtension) + { + if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE) + { + if (count($oExtension->aMissingDependencies) > 0) + { + $oPage->add('
'); + $oPage->add(' '); + $sTitle = Dict::Format('iTopHub:InstallationEffect:MissingDependencies_Details', implode(', ', $oExtension->aMissingDependencies)); + $oPage->add(''); + $oPage->add('
'); + $oPage->add('

'); + if ($oExtension->sDescription != '') + { + $oPage->add(htmlentities($oExtension->sDescription, ENT_QUOTES, 'UTF-8').'
'); + } + $oPage->add('

'); + $oPage->add('
'); + $oPage->add('
'); + } + else + { + $aCSSClasses = array('landing-extension'); + if ($oExtension->sInstalledVersion === '') + { + $aCSSClasses[] = 'landing-installation'; + $sInstallation = Dict::Format('iTopHub:InstallationEffect:Install', $oExtension->sVersion); + } + else if ($oExtension->sInstalledVersion == $oExtension->sVersion) + { + $aCSSClasses[] = 'landing-no-change'; + $sInstallation = Dict::Format('iTopHub:InstallationEffect:NoChange', $oExtension->sVersion); + } + else if (version_compare($oExtension->sInstalledVersion, $oExtension->sVersion, '<')) + { + $aCSSClasses[] = 'landing-upgrade'; + $sInstallation = Dict::Format('iTopHub:InstallationEffect:Upgrade', $oExtension->sInstalledVersion, $oExtension->sVersion); + } + else + { + $aCSSClasses[] = 'landing-downgrade'; + $sInstallation = Dict::Format('iTopHub:InstallationEffect:Downgrade', $oExtension->sInstalledVersion, $oExtension->sVersion); + } + $oPage->add('
'); + $sCode = $oExtension->sCode; + $sDir = basename($oExtension->sSourceDir); + $oPage->add(' '); + $oPage->add(''); + $oPage->add('
'); + $oPage->add('

'); + if ($oExtension->sDescription != '') + { + $oPage->add(htmlentities($oExtension->sDescription, ENT_QUOTES, 'UTF-8').'
'); + } + $oPage->add('

'); + $oPage->add('
'); + $oPage->add('
'); + } + } + } + + $oPage->add('
'); + $oPage->add('
'.Dict::S('iTopHub:DatabaseBackupProgress').'
'); + $oPage->add('
'); + $oPage->add('
'); + + $oPage->add('
'); // module-selection-body + + + $oPage->add_linked_stylesheet('../css/font-awesome/css/font-awesome.min.css'); + $oPage->add('
'); + $oPage->add('
'.Dict::S('iTopHub:DBBackupLabel').''); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('

'); + + $sIframeUrl = utils::GetAbsoluteUrlModulePage('itop-hub-connector', 'launch.php', array('target' => 'inform_after_setup')); + $sStatusPageUrl = utils::GetAbsoluteUrlModulePage('itop-hub-connector', 'land.php', array('operation' => 'done')); + + $aWidgetParams = array( + 'self_url' => utils::GetAbsoluteUrlModulePage('itop-hub-connector', 'ajax.php'), + 'iframe_url' => $sIframeUrl, + 'redirect_after_completion_url' => $sStatusPageUrl, + 'mysql_bindir' => MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''), + 'labels' => array( + 'database_backup' => Dict::S('iTopHub:InstallationProgress:DatabaseBackup'), + 'extensions_installation' => Dict::S('iTopHub:InstallationProgress:ExtensionsInstallation'), + 'installation_successful' => Dict::S('iTopHub:InstallationProgress:InstallationSuccessful'), + 'rollback' => Dict::S('iTopHub:ConfigurationSafelyReverted'), + ), + ); + + $sWidgetParams = json_encode($aWidgetParams); + + $oPage->add_ready_script("$('#hub_installation_widget').hub_installation($sWidgetParams);"); + $oPage->add_ready_script("$('#hub_start_installation').click(function() { $('#hub_installation_widget').hub_installation('start_installation');} );"); + $oPage->add_ready_script("$('#hub_installation_widget').hub_installation('check_before_backup');"); + $oPage->add('
'); +} + + +try +{ + require_once(APPROOT.'/application/application.inc.php'); + require_once(APPROOT.'/setup/setuppage.class.inc.php'); + require_once(APPROOT.'/setup/extensionsmap.class.inc.php'); + require_once(APPROOT.'/application/startup.inc.php'); + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); + + LoginWebPage::DoLoginEx(null, true /* $bMustBeAdmin */); // Check user rights and prompt if needed + + $oPage = new SetupPage(''); // Title will be set later, depending on $sOperation + $oPage->add_linked_script(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/js/hub.js'); + $oPage->add_linked_stylesheet('../css/font-combodo/font-combodo.css'); + + $oPage->add_style("div.choice { margin: 0.5em;}"); + $oPage->add_style("div.choice a { text-decoration:none; font-weight: bold; color: #1C94C4 }"); + $oPage->add_style("div.description { margin-left: 2em; }"); + $oPage->add_style("div.description p { margin-top: 0.25em; margin-bottom: 0.5em; }"); + $oPage->add_style(".choice-disabled { color: #999; }"); + + + $sOperation = utils::ReadParam('operation', 'land'); + + switch($sOperation) + { + case 'done': + DisplayStatus($oPage); + break; + + case 'install': + DoInstall($oPage); + break; + + case 'land': + default: + DoLanding($oPage); + } + + $oPage->output(); + +} +catch(Exception $e) +{ + require_once(APPROOT.'/setup/setuppage.class.inc.php'); + $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); + $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); + $oP->output(); + + if (MetaModel::IsLogEnabledIssue()) + { + if (MetaModel::IsValidClass('EventIssue')) + { + $oLog = new EventIssue(); + + $oLog->Set('message', $e->getMessage()); + $oLog->Set('userinfo', ''); + $oLog->Set('issue', 'PHP Exception'); + $oLog->Set('impact', 'Page could not be displayed'); + $oLog->Set('callstack', $e->getTrace()); + $oLog->Set('data', array()); + $oLog->DBInsertNoReload(); + } + + IssueLog::Error($e->getMessage()); + } +} diff --git a/datamodels/2.x/itop-hub-connector/launch.php b/datamodels/2.x/itop-hub-connector/launch.php new file mode 100644 index 000000000..afe9a2783 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/launch.php @@ -0,0 +1,435 @@ + + +/** + * iTop Hub Launch Page + * Collect the information to be posted to iTopHub + * + * @copyright Copyright (c) 2017 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +/** + * Collect the configuration information to be posted to the hub + * + * @return string[][] + */ + +/* + * + * json schema available on itop hub, under this url : /bundles/combodoremoteitop/json_schema/itop-configuration.schema.json + * + * syntactically valid fictional file sample, : + * + * + * { + * "itop_hub_target_route": "view_dashboard|browse_extensions|deploy_extensions|read_documentation" + * , + * "itop_stack":{ + * "uuidBdd" : "11a90082-b8a6-11e6-90f5-56524dec1a20", + * "uuidFile" : "11a90082-b8a6-11e6-90f5-56352dec71a2", + * "instance_friendly_name" : "example friendly name", + * "instance_host" : "http://example.com", + * "application_name" : "iTop", + * "application_version" : "2.4.0", + * "itop_user_id" : "42", + * "itop_user_lang" : "fr", + * "itop_modules" : { + * "foo-bar": "1.0.1", + * "barBaz": "1.3-dev" + * }, + * "itop_extensions" : { + * "itop-extra-extension": { + * "label": "Super Nice Addon for iTop", + * "value": "1.2.0" + * }, + * "itop-hyper-extension": { + * "label": "Hyper Fabulous Extension", + * "value": "2.5.1" + * }, + * }, + * "itop_installation_options" : { + * "itop-service-mgmt": { + * "label": "Service Management for Enterprises", + * "value": "2.4.0" + * }, + * "itop-simple-tickets": { + * "label": "Simple Ticket Managment", + * "value": "2.4.0" + * }, + * } + * }, + * + * "server_stack":{ + * "os_name": "Linux", + * "web_server_name": "apache", + * "web_server_version": "2.4.12", + * "database_name": "MySQL", + * "database_version": "5.7-ubuntu", + * "database_settings":{ + * "max_allowed_packet": "314116" + * }, + * "php_version": "7.1", + * "php_settings":{ + * "timezone": "Europe/Paris", + * "memory_limit": "128M" + * }, + * "php_extensions":{ + * "php-mysql": "1.2", + * "php-mcrypt": "3.1.6" + * } + * } + * } + * + * + */ + +/** + * Return a cleaned (i.e. + * properly truncated) versin number from + * a very long version number like "7.0.18-0unbuntu0-16.04.1" + * + * @param string $sString + * @return string + */ +function CleanVersionNumber($sString) +{ + $aMatches = array(); + if (preg_match("|^([0-9\\.]+)-|", $sString, $aMatches)) + { + return $aMatches[1]; + } + return $sString; +} + +function collect_configuration() +{ + $aConfiguration = array( + 'php' => array(), + 'mysql' => array(), + 'apache' => array() + ); + + // Database information + $class = new ReflectionClass('CMDBSource'); + $m_oMysqli_property = $class->getProperty('m_oMysqli'); + $m_oMysqli_property->setAccessible(true); + $m_oMysqli = $m_oMysqli_property->getValue(); + + $aConfiguration['database_settings']['server'] = (string) $m_oMysqli->server_version; + $aConfiguration['database_settings']['client'] = (string) $m_oMysqli->client_version; + + /** @var mysqli_result $resultSet */ + $result = CMDBSource::Query('SHOW VARIABLES LIKE "%max_allowed_packet%"'); + if ($result) + { + $row = $result->fetch_object(); + $aConfiguration['database_settings']['max_allowed_packet'] = (string) $row->Value; + } + + /** @var mysqli_result $resultSet */ + $result = CMDBSource::Query('SHOW VARIABLES LIKE "%version_comment%"'); + if ($result) + { + $row = $result->fetch_object(); + if (preg_match('/mariadb/i', $row->Value)) + { + $aConfiguration['database_name'] = 'MariaDB'; + } + } + + // Web server information + if (function_exists('apache_get_version')) + { + $aConfiguration['web_server_name'] = 'apache'; + $aConfiguration['web_server_version'] = apache_get_version(); + } + else + { + $aConfiguration['web_server_name'] = substr($_SERVER["SERVER_SOFTWARE"], 0, strpos($_SERVER["SERVER_SOFTWARE"], '/')); + $aConfiguration['web_server_version'] = substr($_SERVER["SERVER_SOFTWARE"], strpos($_SERVER["SERVER_SOFTWARE"], '/'), strpos($_SERVER["SERVER_SOFTWARE"], 'PHP')); + } + + // PHP extensions + if (!MetaModel::GetConfig()->GetModuleSetting('itop-hub-connector', 'php_extensions_enable', true)) + { + $aConfiguration['php_extensions'] = array(); + } + else + { + foreach (get_loaded_extensions() as $extension) + { + $aConfiguration['php_extensions'][$extension] = $extension; + } + } + + // Collect some PHP settings having a known impact on iTop + $aIniGet = MetaModel::GetConfig()->GetModuleSetting('itop-hub-connector', 'php_settings_array', array()); // by default, on the time of the writting, it values are : array('post_max_size', 'upload_max_filesize', 'apc.enabled', 'timezone', 'memory_limit', 'max_execution_time'); + $aConfiguration['php_settings'] = array(); + foreach ($aIniGet as $iniGet) + { + $aConfiguration['php_settings'][$iniGet] = (string) ini_get($iniGet); + } + + // iTop modules + $oConfig = MetaModel::GetConfig(); + $sLatestInstallationDate = CMDBSource::QueryToScalar("SELECT max(installed) FROM ".$oConfig->GetDBSubname()."priv_module_install"); + // Get the latest installed modules, without the "root" ones (iTop version and datamodel version) + $aInstalledModules = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->GetDBSubname()."priv_module_install WHERE installed = '".$sLatestInstallationDate."' AND parent_id != 0"); + + foreach ($aInstalledModules as $aDBInfo) + { + $aConfiguration['itop_modules'][$aDBInfo['name']] = $aDBInfo['version']; + } + + // iTop Installation Options, i.e. "Extensions" + $oExtensionMap = new iTopExtensionsMap(); + $oExtensionMap->LoadChoicesFromDatabase($oConfig); + $aConfiguration['itop_extensions'] = array(); + foreach ($oExtensionMap->GetChoices() as $oExtension) + { + switch ($oExtension->sSource) + { + case iTopExtension::SOURCE_MANUAL: + case iTopExtension::SOURCE_REMOTE: + $aConfiguration['itop_extensions'][$oExtension->sCode] = array( + 'label' => $oExtension->sLabel, + 'value' => $oExtension->sInstalledVersion + ); + break; + + default: + $aConfiguration['itop_installation_options'][$oExtension->sCode] = array( + 'label' => $oExtension->sLabel, + 'value' => true + ); + } + } + return $aConfiguration; +} + +function MakeDataToPost($sTargetRoute) +{ + $aConfiguration = collect_configuration(); + + $aDataToPost = array( + 'itop_hub_target_route' => $sTargetRoute, + 'itop_stack' => array( + "uuidBdd" => (string) trim(DBProperty::GetProperty('database_uuid', ''), '{}'), // TODO check if empty and... regenerate a new UUID ?? + "uuidFile" => (string) trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n"), // TODO check if empty and... regenerate a new UUID ?? + 'instance_friendly_name' => (string) $_SERVER['SERVER_NAME'], + 'instance_host' => (string) utils::GetAbsoluteUrlAppRoot(), + 'application_name' => (string) ITOP_APPLICATION, + 'application_version' => (string) ITOP_VERSION, + 'application_version_full' => (string) Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE), + 'itop_user_id' => (string) (UserRights::GetUserId()===null) ? "1" : UserRights::GetUserId(), + 'itop_user_lang' => (string) UserRights::GetUserLanguage(), + 'itop_modules' => (object) $aConfiguration['itop_modules'], + 'itop_extensions' => (object) $aConfiguration['itop_extensions'], + 'itop_installation_options' => (object) $aConfiguration['itop_installation_options'] + ), + 'server_stack' => array( + 'os_name' => (string) PHP_OS, + 'web_server_name' => (string) $aConfiguration['web_server_name'], + 'web_server_version' => (string) $aConfiguration['web_server_version'], + 'database_name' => (string) isset($aConfiguration['database_name']) ? $aConfiguration['database_name'] : 'MySQL', // if we do not detect MariaDB, we assume this is mysql + 'database_version' => (string) CMDBSource::GetDBVersion(), + 'database_settings' => (object) $aConfiguration['database_settings'], + 'php_version' => (string) CleanVersionNumber(phpversion()), + 'php_settings' => (object) $aConfiguration['php_settings'], + 'php_extensions' => (object) $aConfiguration['php_extensions'] + ) + ); + return $aDataToPost; +} + +try +{ + require_once (APPROOT.'/application/application.inc.php'); + require_once (APPROOT.'/application/itopwebpage.class.inc.php'); + require_once (APPROOT.'/setup/extensionsmap.class.inc.php'); + require_once ('hubconnectorpage.class.inc.php'); + + require_once (APPROOT.'/application/startup.inc.php'); + + $sTargetRoute = utils::ReadParam('target', ''); // ||browse_extensions|deploy_extensions| + + if ($sTargetRoute != 'inform_after_setup') + { + require_once (APPROOT.'/application/loginwebpage.class.inc.php'); + LoginWebPage::DoLoginEx(null, true /* $bMustBeAdmin */); // Check user rights and prompt if needed + } + + $sHubUrlStateless = MetaModel::GetModuleSetting('itop-hub-connector', 'url').MetaModel::GetModuleSetting('itop-hub-connector', 'route_landing_stateless'); + $sHubUrl = MetaModel::GetModuleSetting('itop-hub-connector', 'url').MetaModel::GetModuleSetting('itop-hub-connector', 'route_landing'); + + // Display... or not... the page + + switch ($sTargetRoute) + { + case 'inform_after_setup': + // Hidden IFRAME at the end of the setup + require_once (APPROOT.'/application/ajaxwebpage.class.inc.php'); + $oPage = new NiceWebPage(''); + $aDataToPost = MakeDataToPost($sTargetRoute); + $oPage->add('
'); + $oPage->add(''); + $oPage->add_ready_script('$("#hub_launch_form").submit();'); + break; + + default: + // All other cases, special "Hub like" web page + if ($sTargetRoute == 'view_dashboard') + { + $sTitle = Dict::S('Menu:iTopHub:Register'); + $sLabel = Dict::S('Menu:iTopHub:Register+'); + $sText = Dict::S('Menu:iTopHub:Register:Description'); + } + else + { + $sTitle = Dict::S('Menu:iTopHub:BrowseExtensions'); + $sLabel = Dict::S('Menu:iTopHub:BrowseExtensions+'); + $sText = Dict::S('Menu:iTopHub:BrowseExtensions:Description'); + } + $sLogoUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/itophub-logo.svg'; + $sArrowUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/white-arrow-right.svg'; + $sCloseUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-hub-connector/images/black-close.svg'; + + $oPage = new HubConnectorPage(Dict::S('iTopHub:Connect')); + $oPage->add_linked_script(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/js/hub.js'); + $oPage->add_linked_stylesheet('../css/font-combodo/font-combodo.css'); + $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/css/hub.css'); + + $aDataToPost = MakeDataToPost($sTargetRoute); + + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add(file_get_contents(__DIR__.'/images/rocket.svg')); + $oPage->add('
'); + $oPage->add('

'.$sTitle.'

'); + $oPage->add($sText); + $oPage->add('

'); + $sFormTarget = appUserPreferences::GetPref('itophub_open_in_new_window', 1) ? 'target="_blank"' : ''; + $oPage->add(''); + $oPage->add(''); + + // $sNewWindowChecked = appUserPreferences::GetPref('itophub_open_in_new_window', 1) == 1 ? 'checked' : ''; + // $oPage->add('


'); + + // Beware the combination auto-submit and open in new window (cf above) is blocked by (some) browsers (namely Chrome) + $sAutoSubmitChecked = appUserPreferences::GetPref('itophub_auto_submit', 0)==1 ? 'checked' : ''; + $oPage->add('

'); + $oPage->add(''); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('
'); + + $oPage->add_ready_script('$(".userpref").on("click", function() { var value = $(this).prop("checked") ? 1 : 0; var code = $(this).attr("id"); SetUserPreference(code, value, true); });'); + $oPage->add_ready_script( +<<add_ready_script('$("#GoToHubBtn").trigger("click");'); + } + + if (utils::ReadParam('show-json', false)) + { + $oPage->add('

DEBUG : json

'); + $oPage->add('
'.json_encode($aDataToPost, JSON_PRETTY_PRINT).'
'); + } + } + + $oPage->output(); +} +catch (CoreException $e) +{ + require_once (APPROOT.'/setup/setuppage.class.inc.php'); + $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); + $oP->error(Dict::Format('UI:Error_Details', $e->getHtmlDesc())); + $oP->output(); + + if (MetaModel::IsLogEnabledIssue()) + { + if (MetaModel::IsValidClass('EventIssue')) + { + $oLog = new EventIssue(); + + $oLog->Set('message', $e->getMessage()); + $oLog->Set('userinfo', ''); + $oLog->Set('issue', $e->GetIssue()); + $oLog->Set('impact', 'Page could not be displayed'); + $oLog->Set('callstack', $e->getTrace()); + $oLog->Set('data', $e->getContextData()); + $oLog->DBInsertNoReload(); + } + + IssueLog::Error($e->getMessage()); + } + + // For debugging only + // throw $e; +} +catch (Exception $e) +{ + require_once (APPROOT.'/setup/setuppage.class.inc.php'); + $oP = new SetupPage(Dict::S('UI:PageTitle:FatalError')); + $oP->add("

".Dict::S('UI:FatalErrorMessage')."

\n"); + $oP->error(Dict::Format('UI:Error_Details', $e->getMessage())); + $oP->output(); + + if (MetaModel::IsLogEnabledIssue()) + { + if (MetaModel::IsValidClass('EventIssue')) + { + $oLog = new EventIssue(); + + $oLog->Set('message', $e->getMessage()); + $oLog->Set('userinfo', ''); + $oLog->Set('issue', 'PHP Exception'); + $oLog->Set('impact', 'Page could not be displayed'); + $oLog->Set('callstack', $e->getTrace()); + $oLog->Set('data', array()); + $oLog->DBInsertNoReload(); + } + + IssueLog::Error($e->getMessage()); + } +} + \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/menus.php b/datamodels/2.x/itop-hub-connector/menus.php new file mode 100644 index 000000000..0d4b8753a --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/menus.php @@ -0,0 +1,19 @@ +GetIndex(), $fRank++); + new WebPageMenuNode('iTopHub:MyExtensions', $sMyExtensionsUrl, $oHubMenu->GetIndex(), $fRank++); + new WebPageMenuNode('iTopHub:BrowseExtensions', $sRootUrl.'&target=browse_extensions', $oHubMenu->GetIndex(), $fRank++); + } + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-hub-connector/model.itop-hub-connector.php b/datamodels/2.x/itop-hub-connector/model.itop-hub-connector.php new file mode 100644 index 000000000..ccca9daf7 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/model.itop-hub-connector.php @@ -0,0 +1,17 @@ + 'iTop Hub Connector', + 'category' => 'business', + + // Setup + // + 'dependencies' => array( + 'itop-config-mgmt/2.4.0', // Actually this module requires iTop 2.4.1 minimum + ), + 'mandatory' => false, + 'visible' => true, + + // Components + // + 'datamodel' => array( + 'menus.php', + 'model.itop-hub-connector.php' + ), + 'webservice' => array( + + ), + 'data.struct' => array( + // add your 'structure' definition XML files here, + ), + 'data.sample' => array( + // add your sample data XML files here, + ), + + // Documentation + // + 'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any + 'doc.more_information' => '', // hyperlink to more information, if any + ) +); + + +?> diff --git a/datamodels/2.x/itop-hub-connector/myextensions.php b/datamodels/2.x/itop-hub-connector/myextensions.php new file mode 100644 index 000000000..6a8ac8d10 --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/myextensions.php @@ -0,0 +1,129 @@ + + +/** + * Tools to design OQL queries and test them + * + * @copyright Copyright (C) 2010-2016 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ +require_once ('../approot.inc.php'); +require_once (APPROOT.'/application/application.inc.php'); +require_once (APPROOT.'/application/itopwebpage.class.inc.php'); +require_once (APPROOT.'setup/extensionsmap.class.inc.php'); + +require_once (APPROOT.'/application/startup.inc.php'); + +require_once (APPROOT.'/application/loginwebpage.class.inc.php'); +LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin) + +$oAppContext = new ApplicationContext(); + +$oPage = new iTopWebPage(Dict::S('iTopHub:InstalledExtensions')); +$oPage->SetBreadCrumbEntry('ui-hub-myextensions', Dict::S('Menu:iTopHub:MyExtensions'), Dict::S('Menu:iTopHub:MyExtensions+'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png'); + +function DisplayExtensionInfo(Webpage $oPage, iTopExtension $oExtension) +{ + $oPage->add('
  • '); + if ($oExtension->sInstalledVersion == '') + { + $oPage->add(''.$oExtension->sLabel.' '.Dict::Format('UI:About:Extension_Version', $oExtension->sVersion).' '.Dict::S('iTopHub:ExtensionNotInstalled').''); + } + else + { + $oPage->add(''.$oExtension->sLabel.' '.Dict::Format('UI:About:Extension_Version', $oExtension->sInstalledVersion)); + } + $oPage->add('

    '.$oExtension->sDescription.'

    '); + $oPage->add('
  • '); +} + +// Main program +try +{ + $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); + + $oPage->add('

    '.Dict::S('iTopHub:InstalledExtensions').'

    '); + + $oPage->add('
    '); + $oPage->add(''.Dict::S('iTopHub:ExtensionCategory:Remote').''); + $oPage->p(Dict::S('iTopHub:ExtensionCategory:Remote+')); + $oPage->add('
      '); + $iCount = 0; + foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) + { + if ($oExtension->sSource == iTopExtension::SOURCE_REMOTE) + { + $iCount++ ; + DisplayExtensionInfo($oPage, $oExtension); + } + } + $oPage->add('
    '); + if ($iCount == 0) + { + $oPage->p(Dict::S('iTopHub:NoExtensionInThisCategory')); + } + $oPage->add('
    '); + $sUrl = utils::GetAbsoluteUrlModulePage('itop-hub-connector', 'launch.php', array('target' => 'browse_extensions')); + $oPage->add('

    '); + + // Display the section about "manually deployed" extensions, only if there are some already + $iCount = 0; + foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) + { + if ($oExtension->sSource == iTopExtension::SOURCE_MANUAL) + { + $iCount++ ; + } + } + + if ($iCount > 0) + { + $oPage->add('
    '); + $oPage->add(''.Dict::S('iTopHub:ExtensionCategory:Manual').''); + $oPage->p(Dict::Format('iTopHub:ExtensionCategory:Manual+', '"extensions"')); + $oPage->add('
      '); + $iCount = 0; + foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) + { + if ($oExtension->sSource == iTopExtension::SOURCE_MANUAL) + { + DisplayExtensionInfo($oPage, $oExtension); + } + } + $oPage->add('
    '); + } + + $oPage->add('
    '); + $sExtensionsDirTooltip = json_encode(APPROOT.'extensions'); + $oPage->add_style( +<<p(''.Dict::Format('UI:Error_Details', $e->getMessage()).''); +} + +$oPage->output();