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 000000000..dd1df1081 Binary files /dev/null and b/datamodels/2.x/itop-hub-connector/images/itophub-logo.png differ 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 000000000..94de452c7 Binary files /dev/null and b/datamodels/2.x/itop-hub-connector/images/landing-extension.png differ diff --git a/datamodels/2.x/itop-hub-connector/images/orange-progress.gif b/datamodels/2.x/itop-hub-connector/images/orange-progress.gif new file mode 100644 index 000000000..eec03c09e Binary files /dev/null and b/datamodels/2.x/itop-hub-connector/images/orange-progress.gif differ diff --git a/datamodels/2.x/itop-hub-connector/images/rocket.svg b/datamodels/2.x/itop-hub-connector/images/rocket.svg new file mode 100644 index 000000000..4433ecd9e --- /dev/null +++ b/datamodels/2.x/itop-hub-connector/images/rocket.svg @@ -0,0 +1,134 @@ + + + + + + + + + + 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();