mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-19 02:14:10 +01:00
Compare commits
54 Commits
2.7.10
...
issue/7426
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ea5ce0e9 | ||
|
|
ffb0d4c68e | ||
|
|
163a3afc0f | ||
|
|
f8b54be896 | ||
|
|
53dc452d61 | ||
|
|
ccaf2dc5b7 | ||
|
|
5d5df5ad1a | ||
|
|
32140b360f | ||
|
|
d85767a838 | ||
|
|
eeec57536b | ||
|
|
16ff6341d0 | ||
|
|
9dab8679d6 | ||
|
|
4c78488644 | ||
|
|
b65e931c4c | ||
|
|
6cb3519308 | ||
|
|
cfb9fae648 | ||
|
|
f4e791734f | ||
|
|
6653ab0668 | ||
|
|
7ab258ba03 | ||
|
|
b5af30a93f | ||
|
|
bbfa601ab1 | ||
|
|
172b1cb1ff | ||
|
|
ca356859a3 | ||
|
|
5efe294895 | ||
|
|
e0170ccc7e | ||
|
|
3b78885f38 | ||
|
|
ed562c9f73 | ||
|
|
85c576a986 | ||
|
|
5a34c76cc4 | ||
|
|
da99a250bf | ||
|
|
dbd5ba0377 | ||
|
|
5d6f293956 | ||
|
|
986c24d777 | ||
|
|
763112c179 | ||
|
|
a5efd981d8 | ||
|
|
2922b22478 | ||
|
|
2af05a437e | ||
|
|
a9f8dcc5e8 | ||
|
|
c325294e17 | ||
|
|
da490739be | ||
|
|
b867faa355 | ||
|
|
7453cc184f | ||
|
|
473cf004b6 | ||
|
|
f6fec506b1 | ||
|
|
5c12151c26 | ||
|
|
5d6c4939f6 | ||
|
|
1b3a2c8470 | ||
|
|
618d8e6468 | ||
|
|
01a955a16f | ||
|
|
87582a021b | ||
|
|
9830178a47 | ||
|
|
c140ebcb6b | ||
|
|
7a0a4e377b | ||
|
|
7fffbb60e9 |
83
.github/pull_request_template.md
vendored
Normal file
83
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<!--
|
||||
|
||||
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||
|
||||
Any PRs not following the guidelines or with missing information will not be considered.
|
||||
|
||||
-->
|
||||
|
||||
## Base information
|
||||
| Question | Answer
|
||||
|---------------------------------------------------------------|--------
|
||||
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
|
||||
| Type of change? | Bug fix / Enhancement / Translations
|
||||
|
||||
|
||||
## Symptom (bug) / Objective (enhancement)
|
||||
<!--
|
||||
If it's a bug
|
||||
- Explain the symptom in details
|
||||
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
|
||||
|
||||
If it's an enhancement
|
||||
- Describe what is blocking you, what is the objective with as much details as possible.
|
||||
- Add screenshots if it's related to UI.
|
||||
-->
|
||||
|
||||
|
||||
## Reproduction procedure (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
|
||||
|
||||
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
|
||||
-->
|
||||
|
||||
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
|
||||
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
|
||||
2. First go there
|
||||
2. Then do that
|
||||
3. ...
|
||||
4. Finally, see that...
|
||||
|
||||
|
||||
## Cause (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain what is the cause of the issue (where in the code and why)
|
||||
-->
|
||||
|
||||
|
||||
## Proposed solution (bug and enhancement)
|
||||
<!--
|
||||
Explain in details how you are proposing to solve this:
|
||||
- What did you do in the code and why
|
||||
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
|
||||
-->
|
||||
|
||||
|
||||
## Checklist before requesting a review
|
||||
<!--
|
||||
Don't remove these lines, check them once done.
|
||||
-->
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have tested all changes I made on an iTop instance
|
||||
- [ ] I have added a unit test, otherwise I have explained why I couldn't
|
||||
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
|
||||
|
||||
## Checklist of things to do before PR is ready to merge
|
||||
<!--
|
||||
Things that needs to be done in the PR before it can be considered as ready to be merged
|
||||
|
||||
Examples:
|
||||
- Changes requested in the review
|
||||
- Unit test to add
|
||||
- Dictionary entries to translate
|
||||
- ...
|
||||
-->
|
||||
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
@@ -27,7 +27,7 @@ $iTopFolder = __DIR__."/../../../";
|
||||
require_once("$iTopFolder/approot.inc.php");
|
||||
require_once(APPROOT."/application/utils.inc.php");
|
||||
|
||||
if (php_sapi_name() !== 'cli')
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -48,4 +48,4 @@ if (!file_exists($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
|
||||
require_once ("$iTopFolder/approot.inc.php");
|
||||
require_once (APPROOT."/setup/setuputils.class.inc.php");
|
||||
|
||||
if (php_sapi_name() !== 'cli')
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -70,4 +70,4 @@ if (false === empty($aMissing)) {
|
||||
echo "Some new tests dirs exists !\n"
|
||||
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
|
||||
.' List of dirs:'."\n".var_export($aMissing, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1709,8 +1709,16 @@ class RestUtils
|
||||
elseif (is_string($key))
|
||||
{
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
}
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreOqlException('Query failed to execute', [
|
||||
'query' => $key,
|
||||
'exception_class' => get_class($e),
|
||||
'exception_message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
|
||||
@@ -3218,13 +3218,13 @@ EOF
|
||||
if ($oAttDef->GetEditClass() == 'Document')
|
||||
{
|
||||
$oDocument = $this->Get($sAttCode);
|
||||
if (!$oDocument->IsEmpty())
|
||||
if (is_object($oDocument) && !$oDocument->IsEmpty())
|
||||
{
|
||||
$sDisplayValue = $this->GetAsHTML($sAttCode);
|
||||
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
|
||||
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
|
||||
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -117,6 +117,11 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
unset($_SESSION['login_temp_auth_user']);
|
||||
if (is_null(UserRights::GetUserObject())){
|
||||
//N°7085 avoid infinite loop
|
||||
IssueLog::Error("No user logged in. exit");
|
||||
exit(-1);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -132,4 +137,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,16 +199,14 @@ class utils
|
||||
|
||||
public static function IsModeCLI()
|
||||
{
|
||||
$sSAPIName = php_sapi_name();
|
||||
$sCleanName = strtolower(trim($sSAPIName));
|
||||
if ($sCleanName == 'cli')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
$bRes = $sCleanName === 'cli';
|
||||
IssueLog::Error("IsModeCLI", null, [
|
||||
'sCleanName' => $sCleanName,
|
||||
'bRes' => $bRes,
|
||||
]);
|
||||
|
||||
return $bRes;
|
||||
}
|
||||
|
||||
protected static $bPageMode = null;
|
||||
@@ -316,13 +314,13 @@ class utils
|
||||
}
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
|
||||
{
|
||||
if ($value === $defaultValue)
|
||||
@@ -338,7 +336,7 @@ class utils
|
||||
$retValue = $defaultValue;
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,11 +479,11 @@ class utils
|
||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
@@ -494,7 +492,7 @@ class utils
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
@@ -507,7 +505,7 @@ class utils
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||
break;
|
||||
@@ -615,17 +613,17 @@ class utils
|
||||
|
||||
return $aSelectedObj;
|
||||
}
|
||||
|
||||
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
return privUITransaction::GetNewTransactionId();
|
||||
}
|
||||
|
||||
|
||||
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
||||
{
|
||||
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
|
||||
public static function RemoveTransaction($sId)
|
||||
{
|
||||
return privUITransaction::RemoveTransaction($sId);
|
||||
@@ -810,9 +808,9 @@ class utils
|
||||
$aDateTokens = array_keys($aSpec);
|
||||
$aDateRegexps = array_values($aSpec);
|
||||
}
|
||||
|
||||
|
||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||
|
||||
|
||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||
{
|
||||
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
||||
@@ -829,7 +827,7 @@ class utils
|
||||
}
|
||||
// http://www.spaweditor.com/scripts/regex/index.php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert an old date/time format specification (using % placeholders)
|
||||
* to a format compatible with DateTime::createFromFormat
|
||||
@@ -1248,7 +1246,7 @@ class utils
|
||||
{
|
||||
$aArguments['param_file'] = $sParamFile;
|
||||
}
|
||||
|
||||
|
||||
$aArgs = array();
|
||||
foreach($aArguments as $sName => $value)
|
||||
{
|
||||
@@ -1257,7 +1255,7 @@ class utils
|
||||
$aArgs[] = "--$sName=".escapeshellarg($value);
|
||||
}
|
||||
$sArgs = implode(' ', $aArgs);
|
||||
|
||||
|
||||
$sScript = realpath(APPROOT.$sScriptName);
|
||||
if (!file_exists($sScript))
|
||||
{
|
||||
@@ -1348,7 +1346,7 @@ class utils
|
||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||
{
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
//
|
||||
switch($iMenuId)
|
||||
{
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
@@ -1373,7 +1371,7 @@ class utils
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
|
||||
{
|
||||
// Bulk export actions
|
||||
@@ -1387,7 +1385,7 @@ class utils
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
@@ -1401,7 +1399,7 @@ class utils
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
||||
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page & CSV Export
|
||||
@@ -1465,7 +1463,7 @@ class utils
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
$oPage->add_linked_script($sLinkedScript);
|
||||
@@ -1602,7 +1600,7 @@ class utils
|
||||
return $sProposed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
||||
* @param string $sId The ID to sanitize
|
||||
@@ -1612,13 +1610,13 @@ class utils
|
||||
{
|
||||
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to execute an HTTP POST request
|
||||
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||
* originaly named after do_post_request
|
||||
* Does not require cUrl but requires openssl for performing https POSTs.
|
||||
*
|
||||
*
|
||||
* @param string $sUrl The URL to POST the data to
|
||||
* @param array $aData The data to POST as an array('param_name' => value)
|
||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||
@@ -1629,11 +1627,11 @@ class utils
|
||||
*
|
||||
* @return string The result of the POST request
|
||||
* @throws Exception with a specific error message depending on the cause
|
||||
*/
|
||||
*/
|
||||
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
|
||||
if (function_exists('curl_init'))
|
||||
{
|
||||
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
|
||||
@@ -1667,7 +1665,7 @@ class utils
|
||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||
);
|
||||
|
||||
|
||||
$aAllOptions = $aCurlOptions + $aOptions;
|
||||
$ch = curl_init($sUrl);
|
||||
curl_setopt_array($ch, $aAllOptions);
|
||||
@@ -1693,7 +1691,7 @@ class utils
|
||||
else
|
||||
{
|
||||
// cURL is not available let's try with streams and fopen...
|
||||
|
||||
|
||||
$sData = http_build_query($aData);
|
||||
$aParams = array('http' => array(
|
||||
'method' => 'POST',
|
||||
@@ -1705,7 +1703,7 @@ class utils
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
@@ -1746,7 +1744,7 @@ class utils
|
||||
|
||||
/**
|
||||
* Get a standard list of character sets
|
||||
*
|
||||
*
|
||||
* @param array $aAdditionalEncodings Additional values
|
||||
* @return array of iconv code => english label, sorted by label
|
||||
*/
|
||||
@@ -1776,8 +1774,8 @@ class utils
|
||||
public static function HtmlEntities($sValue)
|
||||
{
|
||||
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to encapsulation iTop's html_entity_decode
|
||||
* @param string $sValue
|
||||
@@ -1806,7 +1804,7 @@ class utils
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
|
||||
* and escaping HTML entities
|
||||
@@ -1819,7 +1817,7 @@ class utils
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
|
||||
*
|
||||
@@ -1882,7 +1880,7 @@ class utils
|
||||
|
||||
return $sCss;
|
||||
}
|
||||
|
||||
|
||||
public static function GetImageSize($sImageData)
|
||||
{
|
||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||
@@ -1939,7 +1937,7 @@ class utils
|
||||
case 'image/png':
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
@@ -1953,14 +1951,14 @@ class utils
|
||||
else
|
||||
{
|
||||
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
||||
|
||||
|
||||
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
|
||||
|
||||
$iNewWidth = $iWidth * $fScale;
|
||||
$iNewHeight = $iHeight * $fScale;
|
||||
|
||||
|
||||
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||
|
||||
|
||||
// Preserve transparency
|
||||
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
||||
{
|
||||
@@ -1968,38 +1966,38 @@ class utils
|
||||
imagealphablending($new, false);
|
||||
imagesavealpha($new, true);
|
||||
}
|
||||
|
||||
|
||||
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
||||
|
||||
|
||||
ob_start();
|
||||
switch ($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
imagegif($new); // send image to output buffer
|
||||
break;
|
||||
|
||||
|
||||
case 'image/jpeg':
|
||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
|
||||
|
||||
case 'image/png':
|
||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
}
|
||||
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||
@ob_end_clean();
|
||||
|
||||
|
||||
imagedestroy($img);
|
||||
imagedestroy($new);
|
||||
|
||||
|
||||
return $oResampledImage;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
||||
*
|
||||
*
|
||||
* Note: this method can be run from the command line as well as from the web server.
|
||||
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
||||
* consider using open_ssl or PHP 7 methods.
|
||||
@@ -2037,7 +2035,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
||||
*
|
||||
@@ -2068,7 +2066,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sProperty The name of the property to retrieve
|
||||
* @param mixed $defaultvalue
|
||||
@@ -2078,7 +2076,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sModuleName
|
||||
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
||||
@@ -2087,7 +2085,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given path/url is an http(s) URL
|
||||
* @param string $sPath
|
||||
@@ -2102,7 +2100,7 @@ class utils
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||
@@ -2151,7 +2149,7 @@ class utils
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the content of a file (and retrieve its MIME type) from either:
|
||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||
@@ -2195,7 +2193,7 @@ class utils
|
||||
'html' => 'text/html',
|
||||
'exe' => 'application/octet-stream',
|
||||
);
|
||||
|
||||
|
||||
$sData = null;
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
@@ -2251,7 +2249,7 @@ class utils
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
@@ -2265,7 +2263,7 @@ class utils
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
{
|
||||
$aCleanHeaders = array();
|
||||
@@ -2290,7 +2288,7 @@ class utils
|
||||
}
|
||||
return $aCleanHeaders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
||||
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
||||
@@ -2445,7 +2443,7 @@ class utils
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
|
||||
http_response_code(503);
|
||||
// Display message depending on the request
|
||||
include(APPROOT.'application/maintenancemsg.php');
|
||||
$sSAPIName = strtoupper(trim(php_sapi_name()));
|
||||
$sSAPIName = strtoupper(trim(PHP_SAPI));
|
||||
|
||||
switch (true)
|
||||
{
|
||||
|
||||
@@ -2695,6 +2695,11 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
return ($proposedValue == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -2707,7 +2712,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue))
|
||||
{
|
||||
/** @var \DBObject $proposedValue */
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
@@ -5940,6 +5944,11 @@ class AttributeDateTime extends AttributeDBField
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -7655,9 +7664,9 @@ class AttributeBlob extends AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Users can provide the document from an URL (including an URL on iTop itself)
|
||||
* for CSV import. Administrators can even provide the path to a local file
|
||||
* {@inheritDoc}
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
|
||||
*
|
||||
* @see AttributeDefinition::MakeRealValue()
|
||||
*/
|
||||
|
||||
@@ -674,10 +674,9 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sSQLQuery
|
||||
*
|
||||
* @return \mysqli_result|null
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \CoreException
|
||||
* @return mysqli_result|null
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 2.7.0 N°679 handles nested transactions
|
||||
*/
|
||||
|
||||
@@ -1634,7 +1634,7 @@ class Config
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
throw new ConfigException('Syntax error in configuration file',
|
||||
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
|
||||
}
|
||||
@@ -1909,6 +1909,24 @@ class Config
|
||||
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.11 N°7085
|
||||
* Add login mode if not configured already
|
||||
* @param string $sLoginMode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddAllowedLoginTypes($sLoginMode)
|
||||
{
|
||||
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
|
||||
if (in_array($sLoginMode, $aAllowedLoginTypes)){
|
||||
return;
|
||||
}
|
||||
|
||||
$aAllowedLoginTypes[] = $sLoginMode;
|
||||
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||
{
|
||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||
@@ -2428,7 +2446,7 @@ class ConfigPlaceholdersResolver
|
||||
}
|
||||
|
||||
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
|
||||
|
||||
|
||||
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
|
||||
{
|
||||
return $rawValue;
|
||||
@@ -2481,4 +2499,4 @@ class ConfigPlaceholdersResolver
|
||||
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
|
||||
throw new ConfigException($sErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,11 +532,10 @@ abstract class DBObject implements iDisplay
|
||||
* Attributes setter
|
||||
*
|
||||
* Set $sAttCode to $value.
|
||||
* The value must be valid according to the type of attribute.
|
||||
* The value must be valid according to the type of attribute : see the different {@see AttributeDefinition::MakeRealValue()} implementations
|
||||
* The value will not be recorded into the DB until DBObject::DBWrite() is called.
|
||||
*
|
||||
* @api
|
||||
* @see DBWrite()
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param mixed $value
|
||||
@@ -544,6 +543,8 @@ abstract class DBObject implements iDisplay
|
||||
* @return bool
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*
|
||||
* @see DBWrite()
|
||||
*/
|
||||
public function Set($sAttCode, $value)
|
||||
{
|
||||
@@ -2084,16 +2085,17 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
||||
*
|
||||
* @param string $sUniquenessRuleId uniqueness rule ID
|
||||
* @return \DBSearch
|
||||
* @throws \OQLException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param string $sUniquenessRuleId uniqueness rule ID
|
||||
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
||||
*
|
||||
* @return \DBSearch
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
* @since 2.6.0 N°659 uniqueness constraint
|
||||
* @api
|
||||
* @since 2.6.0 N°659 uniqueness constraint
|
||||
* @since 2.7.11 3.1.2 3.2.0 N°4314 Fix Uniqueness rules not working with Silo
|
||||
*/
|
||||
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||
{
|
||||
@@ -2123,8 +2125,10 @@ abstract class DBObject implements iDisplay
|
||||
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||
}
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
$oUniquenessQuery->AllowAllData();
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check integrity rules (before inserting or updating the object)
|
||||
|
||||
@@ -866,11 +866,11 @@ abstract class DBSearch
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
}
|
||||
|
||||
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
|
||||
@@ -900,6 +900,55 @@ abstract class DBSearch
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
|
||||
* @param string $sAttCode
|
||||
* @param string|null $sClassAlias
|
||||
*
|
||||
* @return array
|
||||
* @throws ConfigException
|
||||
* @throws CoreException
|
||||
* @throws MissingQueryArgument
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*/
|
||||
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
|
||||
{
|
||||
if(is_null($sClassAlias)) {
|
||||
$sClassAlias = $this->GetClassAlias();
|
||||
}
|
||||
|
||||
$sClass = $this->GetClass();
|
||||
if($sAttCode === 'id'){
|
||||
$aAttToLoad[$sClassAlias]=[];
|
||||
} else {
|
||||
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$sColName = $sClassAlias.$sAttCode;
|
||||
|
||||
$aRes = [];
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$aMappedRow = array();
|
||||
if($sAttCode === 'id') {
|
||||
$aMappedRow[$sAttCode] = $aRow[$sColName];
|
||||
} else {
|
||||
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
|
||||
}
|
||||
$aRes[] = $aMappedRow;
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Construction of the SQL queries
|
||||
|
||||
@@ -110,7 +110,7 @@ abstract class UserRightsAddOnAPI
|
||||
$oSearchSharers->AllowAllData();
|
||||
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
|
||||
$aSharers = array();
|
||||
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
|
||||
foreach($oSearchSharers->SelectAttributeToArray('id') as $aRow)
|
||||
{
|
||||
$aSharers[] = $aRow['id'];
|
||||
}
|
||||
@@ -135,7 +135,7 @@ abstract class UserRightsAddOnAPI
|
||||
$oOrgField = new FieldExpression('org_id', $sShareClass);
|
||||
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
||||
$aShared = array();
|
||||
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
|
||||
foreach($oSearchShares->SelectAttributeToArray($sShareAttCode) as $aRow)
|
||||
{
|
||||
$aShared[] = $aRow[$sShareAttCode];
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
/* integrityCheck: begin (do not remove/edit) */
|
||||
/* Helpers classes */
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
/* Animations */
|
||||
@keyframes progress_bar_color_ongoing {
|
||||
from {
|
||||
@@ -265,4 +269,4 @@ fieldset > legend {
|
||||
font-weight: bold;
|
||||
color: #e60000b8;
|
||||
}
|
||||
|
||||
/* integrityCheck: end (do not remove/edit) */
|
||||
|
||||
@@ -56,6 +56,9 @@ $progress-bar-error-bg-color: #F56565 !default;
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes progress_bar_color_ongoing {
|
||||
|
||||
19
datamodels/2.x/itop-hub-connector/TokenValidation.php
Normal file
19
datamodels/2.x/itop-hub-connector/TokenValidation.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
class TokenValidation
|
||||
{
|
||||
// construct function
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function isSetupTokenValid($sParamToken) : bool
|
||||
{
|
||||
if (!file_exists(APPROOT.'data/.setup')) {
|
||||
return false;
|
||||
}
|
||||
$sSetupToken = trim(file_get_contents(APPROOT.'data/.setup'));
|
||||
unlink(APPROOT.'data/.setup');
|
||||
return $sParamToken === $sSetupToken;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -280,6 +280,7 @@ try
|
||||
require_once ('hubconnectorpage.class.inc.php');
|
||||
|
||||
require_once (APPROOT.'/application/startup.inc.php');
|
||||
require_once('TokenValidation.php');
|
||||
|
||||
$sTargetRoute = utils::ReadParam('target', ''); // ||browse_extensions|deploy_extensions|
|
||||
|
||||
@@ -299,11 +300,20 @@ try
|
||||
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('<form id="hub_launch_form" action="'.$sHubUrlStateless.'" method="post">');
|
||||
$oPage->add('<input type="hidden" name="json" value="'.htmlentities(json_encode($aDataToPost), ENT_QUOTES, 'UTF-8').'">');
|
||||
$oPage->add_ready_script('$("#hub_launch_form").submit();');
|
||||
|
||||
$sParamToken = utils::ReadParam('setup_token');
|
||||
$oTokenValidation = new TokenValidation();
|
||||
$bIsTokenValid = $oTokenValidation->isSetupTokenValid($sParamToken);
|
||||
if (UserRights::IsAdministrator() || $bIsTokenValid) {
|
||||
$oPage = new NiceWebPage('');
|
||||
$aDataToPost = MakeDataToPost($sTargetRoute);
|
||||
$oPage->add('<form id="hub_launch_form" action="' . $sHubUrlStateless . '" method="post">');
|
||||
$oPage->add('<input type="hidden" name="json" value="' . htmlentities(json_encode($aDataToPost), ENT_QUOTES, 'UTF-8') . '">');
|
||||
$oPage->add_ready_script('$("#hub_launch_form").submit();');
|
||||
} else {
|
||||
IssueLog::Error('TokenValidation failed on inform_after_setup page');
|
||||
throw new Exception("Not allowed");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -92,6 +92,8 @@ const OAuthConnect = function(sClass, sId, sAjaxUri) {
|
||||
function (oData) {
|
||||
if (oData.status === 'success') {
|
||||
oOpenSignInWindow(oData.data.authorization_url, 'OAuth authorization')
|
||||
} else {
|
||||
alert(oData.error_description);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -339,6 +339,11 @@
|
||||
<default_value>no</default_value>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
</field>
|
||||
<field id="tenant" xsi:type="AttributeString">
|
||||
<sql>tenant</sql>
|
||||
<default_value>common</default_value>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
</field>
|
||||
</fields>
|
||||
<presentation>
|
||||
<details>
|
||||
@@ -364,15 +369,18 @@
|
||||
<item id="redirect_url">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
<item id="client_id">
|
||||
<item id="tenant">
|
||||
<rank>60</rank>
|
||||
</item>
|
||||
<item id="client_secret">
|
||||
<item id="client_id">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
<item id="mailbox_list">
|
||||
<item id="client_secret">
|
||||
<rank>80</rank>
|
||||
</item>
|
||||
<item id="mailbox_list">
|
||||
<rank>90</rank>
|
||||
</item>
|
||||
</items>
|
||||
</item>
|
||||
</items>
|
||||
|
||||
@@ -93,6 +93,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:OAuthClientAzure/Attribute:used_for_smtp+' => 'At least one OAuth client must have this flag to “Yes”, if you want iTop to use it for sending mails',
|
||||
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:yes' => 'Yes',
|
||||
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:no' => 'No',
|
||||
'Class:OAuthClientAzure/Attribute:tenant' => 'Tenant',
|
||||
'Class:OAuthClientAzure/Attribute:tenant+' => 'Tenant ID of the configured application. For multi-tenant application, use common.',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -10,6 +10,7 @@ use cmdbAbstractObject;
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use MetaModel;
|
||||
@@ -31,8 +32,13 @@ class AjaxOauthClientController extends Controller
|
||||
|
||||
$aResult = ['status' => 'success', 'data' => []];
|
||||
|
||||
$sAuthorizationUrl = OAuthClientProviderFactory::GetAuthorizationUrl($oOAuthClient);
|
||||
$aResult['data']['authorization_url'] = $sAuthorizationUrl;
|
||||
try {
|
||||
$sAuthorizationUrl = OAuthClientProviderFactory::GetAuthorizationUrl($oOAuthClient);
|
||||
$aResult['data']['authorization_url'] = $sAuthorizationUrl;
|
||||
} catch (Exception $oException) {
|
||||
$aResult['status'] = 'error';
|
||||
$aResult['error_description'] = $oException->getMessage();
|
||||
}
|
||||
|
||||
$this->DisplayJSONPage($aResult);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class UserProvider
|
||||
@@ -91,6 +92,9 @@ class UserProvider implements ContainerAwareInterface
|
||||
}
|
||||
$this->oContainer->set('combodo.current_user', $oUser);
|
||||
|
||||
// User allowed to log off or not
|
||||
$this->oContainer->set('combodo.current_user.can_logoff', utils::CanLogOff());
|
||||
|
||||
// Allowed portals
|
||||
$aAllowedPortals = UserRights::GetAllowedPortals();
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
{% if app['combodo.current_user'] is defined and app['combodo.current_user'] is not null %}
|
||||
{% set bUserConnected = true %}
|
||||
{% set bUserCanLogOff = app['combodo.current_user.can_logoff'] %}
|
||||
{% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
|
||||
{% set sUserEmail = app['combodo.current_user'].Get('email') %}
|
||||
{% set sUserPhotoUrl = app['combodo.current_contact.photo_url'] %}
|
||||
{% else %}
|
||||
{% set bUserConnected = false %}
|
||||
{% set bUserCanLogOff = false %}
|
||||
{% set sUserFullname = '' %}
|
||||
{% set sUserEmail = '' %}
|
||||
{% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
|
||||
@@ -62,12 +64,12 @@
|
||||
{% endif %}
|
||||
{# Custom CSS that is supposed to do adjustments to the portal #}
|
||||
{% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
|
||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
|
||||
<link href="{{ app['combodo.absolute_url'] ~ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
|
||||
{% endif %}
|
||||
{# Others CSS that will come after the theme/portal/custom, in an undefined order #}
|
||||
{% if app['combodo.portal.instance.conf'].properties.themes.others is defined %}
|
||||
{% for theme in app['combodo.portal.instance.conf'].properties.themes.others %}
|
||||
<link href="{{ theme|add_itop_version }}" rel="stylesheet">
|
||||
<link href="{{ app['combodo.absolute_url'] ~ theme|add_itop_version }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -230,11 +232,13 @@
|
||||
<li><a href="{{ aPortal.url }}" target="_blank"><span class="brick_icon {{ sIconClass }} fa-2x fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% if bUserCanLogOff %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -269,11 +273,13 @@
|
||||
<li><a href="{{ aPortal.url }}" {% if app['combodo.portal.instance.conf'].properties.allowed_portals.opening_mode == 'tab' %}target="_blank"{% endif %} title="{{ aPortal.label|dict_s }}"><span class="brick_icon {{ sGlyphiconClass }} fa-lg fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% if bUserCanLogOff %}
|
||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||
<li role="separator" class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
4
js/moment-with-locales.min.js
vendored
4
js/moment-with-locales.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,20 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2019 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -113,7 +100,7 @@ function GetRuleResultFilter($iRuleId, $oDefinitionFilter, $oAppContext)
|
||||
{
|
||||
// The query returns only the valid elements, all the others are invalid
|
||||
// Warning : we're generating a `WHERE ID IN`... query, and this could be very slow if there are lots of id !
|
||||
$aValidRows = $oRuleFilter->ToDataArray(array('id'));
|
||||
$aValidRows = $oRuleFilter->ToDataArray(array('id'));
|
||||
$aValidIds = array();
|
||||
foreach($aValidRows as $aRow)
|
||||
{
|
||||
@@ -163,7 +150,7 @@ try
|
||||
require_once(APPROOT.'/application/itopwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/csvpage.class.inc.php');
|
||||
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
$operation = utils::ReadParam('operation', '');
|
||||
$oAppContext = new ApplicationContext();
|
||||
@@ -232,7 +219,7 @@ try
|
||||
$oP->p("<div id=\"$sBlockId\" style=\"clear:both\">\n");
|
||||
$oBlock = DisplayBlock::FromObjectSet($oErrorObjectSet, 'csv', array('show_obsolete_data' => true));
|
||||
$oBlock->Display($oP, 1);
|
||||
$oP->p("</div>\n");
|
||||
$oP->p("</div>\n");
|
||||
// Adjust the size of the Textarea containing the CSV to fit almost all the remaining space
|
||||
$oP->add_ready_script(" $('#1>textarea').height(400);"); // adjust the size of the block
|
||||
$sExportUrl = utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey();
|
||||
@@ -246,7 +233,7 @@ try
|
||||
$oP->SetBreadCrumbEntry('ui-tool-auditerrors', $sTitle, '', '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png');
|
||||
$iCategory = utils::ReadParam('category', '');
|
||||
$iRuleIndex = utils::ReadParam('rule', 0);
|
||||
|
||||
|
||||
$oAuditCategory = MetaModel::GetObject('AuditCategory', $iCategory);
|
||||
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
|
||||
$oDefinitionFilter->UpdateContextFromUser();
|
||||
@@ -265,7 +252,7 @@ try
|
||||
$sExportUrl = utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey();
|
||||
$oP->add_ready_script("$('a[href*=\"pages/UI.php?operation=search\"]').attr('href', '".$sExportUrl."')");
|
||||
break;
|
||||
|
||||
|
||||
case 'audit':
|
||||
default:
|
||||
$oP->SetBreadCrumbEntry('ui-tool-audit', Dict::S('Menu:Audit'), Dict::S('UI:Audit:InteractiveAudit'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png');
|
||||
@@ -286,7 +273,7 @@ try
|
||||
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
|
||||
$oDefinitionFilter->UpdateContextFromUser();
|
||||
FilterByContext($oDefinitionFilter, $oAppContext);
|
||||
|
||||
|
||||
$aObjectsWithErrors = array();
|
||||
if (!empty($currentOrganization))
|
||||
{
|
||||
@@ -308,7 +295,7 @@ try
|
||||
if ($iCount == 0)
|
||||
{
|
||||
// nothing to check, really !
|
||||
$aRow['nb_errors'] = "<a href=\"audit.php?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>";
|
||||
$aRow['nb_errors'] = "<a href=\"audit.php?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>";
|
||||
$aRow['percent_ok'] = '100.00';
|
||||
$aRow['class'] = GetReportColor($iCount, 0);
|
||||
}
|
||||
@@ -317,19 +304,19 @@ try
|
||||
try
|
||||
{
|
||||
$oFilter = GetRuleResultFilter($oAuditRule->GetKey(), $oDefinitionFilter, $oAppContext);
|
||||
$aErrors = $oFilter->ToDataArray(array('id'));
|
||||
$aErrors = $oFilter->SelectAttributeToArray('id');
|
||||
$iErrorsCount = count($aErrors);
|
||||
foreach($aErrors as $aErrorRow)
|
||||
{
|
||||
$aObjectsWithErrors[$aErrorRow['id']] = true;
|
||||
}
|
||||
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">(CSV)</a>";
|
||||
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">(CSV)</a>";
|
||||
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
|
||||
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
|
||||
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$aRow['nb_errors'] = "OQL Error";
|
||||
$aRow['nb_errors'] = "OQL Error";
|
||||
$aRow['percent_ok'] = 'n/a';
|
||||
$aRow['class'] = 'red';
|
||||
$sMessage = Dict::Format('UI:Audit:ErrorIn_Rule_Reason', $oAuditRule->GetHyperlink(), $e->getMessage());
|
||||
@@ -346,12 +333,12 @@ try
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['description'] = "OQL error";
|
||||
$aRow['nb_errors'] = "n/a";
|
||||
$aRow['nb_errors'] = "n/a";
|
||||
$aRow['percent_ok'] = '';
|
||||
$aRow['class'] = 'red';
|
||||
$aRow['class'] = 'red';
|
||||
$sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), utils::HtmlEntities($e->getMessage()));
|
||||
$oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/> ".$sMessage);
|
||||
$aResults[] = $aRow;
|
||||
$aResults[] = $aRow;
|
||||
|
||||
$sClass = 'red';
|
||||
$iTotalErrors = 'n/a';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -25,7 +25,7 @@ require_once(APPROOT.'setup/backup.class.inc.php');
|
||||
* The installation process is split into a sequence of unitary steps
|
||||
* for performance reasons (i.e; timeout, memory usage) and also in order
|
||||
* to provide some feedback about the progress of the installation.
|
||||
*
|
||||
*
|
||||
* This class can be used for a step by step interactive installation
|
||||
* while displaying a progress bar, or in an unattended manner
|
||||
* (for example from the command line), to run all the steps
|
||||
@@ -157,7 +157,7 @@ class ApplicationInstaller
|
||||
}
|
||||
}
|
||||
while(($aRes['status'] != self::ERROR) && ($aRes['next-step'] != ''));
|
||||
|
||||
|
||||
return ($iOverallStatus == self::OK);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ class ApplicationInstaller
|
||||
|
||||
case 'copy':
|
||||
$aPreinstall = $this->oParams->Get('preinstall');
|
||||
$aCopies = $aPreinstall['copies'];
|
||||
$aCopies = $aPreinstall['copies'] ?? [];
|
||||
|
||||
self::DoCopy($aCopies);
|
||||
$sReport = "Copying...";
|
||||
@@ -472,7 +472,7 @@ class ApplicationInstaller
|
||||
{
|
||||
$sSource = $aCopy['source'];
|
||||
$sDestination = APPROOT.$aCopy['destination'];
|
||||
|
||||
|
||||
SetupUtils::builddir($sDestination);
|
||||
SetupUtils::tidydir($sDestination);
|
||||
SetupUtils::copydir($sSource, $sDestination);
|
||||
@@ -513,7 +513,7 @@ class ApplicationInstaller
|
||||
$oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static function DoCompile($aSelectedModules, $sSourceDir, $sExtensionDir, $sTargetDir, $sEnvironment, $bUseSymbolicLinks = false)
|
||||
{
|
||||
SetupPage::log_info("Compiling data model.");
|
||||
@@ -525,7 +525,7 @@ class ApplicationInstaller
|
||||
if (empty($sSourceDir) || empty($sTargetDir))
|
||||
{
|
||||
throw new Exception("missing parameter source_dir and/or target_dir");
|
||||
}
|
||||
}
|
||||
|
||||
$sSourcePath = APPROOT.$sSourceDir;
|
||||
$aDirsToScan = array($sSourcePath);
|
||||
@@ -582,10 +582,10 @@ class ApplicationInstaller
|
||||
}
|
||||
|
||||
$oFactory = new ModelFactory($aDirsToScan);
|
||||
|
||||
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
$oFactory->LoadModule($oDictModule);
|
||||
|
||||
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
@@ -598,7 +598,7 @@ class ApplicationInstaller
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oApplicationModule);
|
||||
}
|
||||
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
|
||||
foreach($aModules as $oModule)
|
||||
@@ -611,7 +611,7 @@ class ApplicationInstaller
|
||||
}
|
||||
// Dump the "reference" model, just before loading any actual delta
|
||||
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$sEnvironment.'.xml');
|
||||
|
||||
|
||||
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
{
|
||||
@@ -635,12 +635,12 @@ class ApplicationInstaller
|
||||
if (file_exists($sFileToPatch))
|
||||
{
|
||||
$sContent = file_get_contents($sFileToPatch);
|
||||
|
||||
|
||||
$sContent = str_replace("require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", "//\n// The line below is no longer needed in iTop 2.0 -- patched by the setup program\n// require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", $sContent);
|
||||
|
||||
|
||||
file_put_contents($sFileToPatch, $sContent);
|
||||
}
|
||||
|
||||
|
||||
// Set an "Instance UUID" identifying this machine based on a file located in the data directory
|
||||
$sInstanceUUIDFile = APPROOT.'data/instance.txt';
|
||||
SetupUtils::builddir(APPROOT.'data');
|
||||
@@ -699,7 +699,7 @@ class ApplicationInstaller
|
||||
// Starting 2.0, all table names must be lowercase
|
||||
if ($sMode != 'install')
|
||||
{
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' into '{$sDBPrefix}priv_internaluser' (lowercase)");
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' into '{$sDBPrefix}priv_internaluser' (lowercase)");
|
||||
// This command will have no effect under Windows...
|
||||
// and it has been written in two steps so as to make it work under windows!
|
||||
CMDBSource::SelectDB($sDBName);
|
||||
@@ -710,18 +710,18 @@ class ApplicationInstaller
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' failed (already done in a previous upgrade?)");
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' failed (already done in a previous upgrade?)");
|
||||
}
|
||||
|
||||
|
||||
// let's remove the records in priv_change which have no counterpart in priv_changeop
|
||||
SetupPage::log_info("Cleanup of '{$sDBPrefix}priv_change' to remove orphan records");
|
||||
SetupPage::log_info("Cleanup of '{$sDBPrefix}priv_change' to remove orphan records");
|
||||
CMDBSource::SelectDB($sDBName);
|
||||
try
|
||||
{
|
||||
$sTotalCount = "SELECT COUNT(*) FROM `{$sDBPrefix}priv_change`";
|
||||
$iTotalCount = (int)CMDBSource::QueryToScalar($sTotalCount);
|
||||
SetupPage::log_info("There is a total of $iTotalCount records in {$sDBPrefix}priv_change.");
|
||||
|
||||
|
||||
$sOrphanCount = "SELECT COUNT(c.id) FROM `{$sDBPrefix}priv_change` AS c left join `{$sDBPrefix}priv_changeop` AS o ON c.id = o.changeid WHERE o.id IS NULL";
|
||||
$iOrphanCount = (int)CMDBSource::QueryToScalar($sOrphanCount);
|
||||
SetupPage::log_info("There are $iOrphanCount useless records in {$sDBPrefix}priv_change (".sprintf('%.2f', ((100.0*$iOrphanCount)/$iTotalCount))."%)");
|
||||
@@ -745,11 +745,11 @@ class ApplicationInstaller
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
SetupPage::log_info("Cleanup of orphan records in `{$sDBPrefix}priv_change` failed: ".$e->getMessage());
|
||||
SetupPage::log_info("Cleanup of orphan records in `{$sDBPrefix}priv_change` failed: ".$e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Module specific actions (migrate the data)
|
||||
//
|
||||
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT.$sModulesDir);
|
||||
@@ -757,9 +757,9 @@ class ApplicationInstaller
|
||||
|
||||
if(!$oProductionEnv->CreateDatabaseStructure(MetaModel::GetConfig(), $sMode))
|
||||
{
|
||||
throw new Exception("Failed to create/upgrade the database structure for environment '$sTargetEnvironment'");
|
||||
throw new Exception("Failed to create/upgrade the database structure for environment '$sTargetEnvironment'");
|
||||
}
|
||||
|
||||
|
||||
// Set a DBProperty with a unique ID to identify this instance of iTop
|
||||
$sUUID = DBProperty::GetProperty('database_uuid', '');
|
||||
if ($sUUID === '')
|
||||
@@ -767,10 +767,10 @@ class ApplicationInstaller
|
||||
$sUUID = utils::CreateUUID('database');
|
||||
DBProperty::SetProperty('database_uuid', $sUUID, 'Installation/upgrade of '.ITOP_APPLICATION, 'Unique ID of this '.ITOP_APPLICATION.' Database');
|
||||
}
|
||||
|
||||
|
||||
// priv_change now has an 'origin' field to distinguish between the various input sources
|
||||
// Let's initialize the field with 'interactive' for all records were it's null
|
||||
// Then check if some records should hold a different value, based on a pattern matching in the userinfo field
|
||||
// Then check if some records should hold a different value, based on a pattern matching in the userinfo field
|
||||
CMDBSource::SelectDB($sDBName);
|
||||
try
|
||||
{
|
||||
@@ -778,21 +778,21 @@ class ApplicationInstaller
|
||||
$iCount = (int)CMDBSource::QueryToScalar($sCount);
|
||||
if ($iCount > 0)
|
||||
{
|
||||
SetupPage::log_info("Initializing '{$sDBPrefix}priv_change.origin' ($iCount records to update)");
|
||||
|
||||
SetupPage::log_info("Initializing '{$sDBPrefix}priv_change.origin' ($iCount records to update)");
|
||||
|
||||
// By default all uninitialized values are considered as 'interactive'
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'interactive' WHERE `origin` IS NULL";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
|
||||
// CSV Import was identified by the comment at the end
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-import.php' WHERE `userinfo` LIKE '%Web Service (CSV)'";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
|
||||
// CSV Import was identified by the comment at the end
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'csv-interactive' WHERE `userinfo` LIKE '%(CSV)' AND origin = 'interactive'";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
|
||||
|
||||
|
||||
// Syncho data sources were identified by the comment at the end
|
||||
// Unfortunately the comment is localized, so we have to search for all possible patterns
|
||||
$sCurrentLanguage = Dict::GetUserLanguage();
|
||||
@@ -806,19 +806,19 @@ class ApplicationInstaller
|
||||
Dict::SetUserLanguage($sCurrentLanguage);
|
||||
$sCondition = "`userinfo` LIKE ".implode(" OR `userinfo` LIKE ", array_keys($aSuffixes));
|
||||
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'synchro-data-source' WHERE ($sCondition)";
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_change` SET `origin` = 'synchro-data-source' WHERE ($sCondition)";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
SetupPage::log_info("Initialization of '{$sDBPrefix}priv_change.origin' completed.");
|
||||
|
||||
SetupPage::log_info("Initialization of '{$sDBPrefix}priv_change.origin' completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupPage::log_info("'{$sDBPrefix}priv_change.origin' already initialized, nothing to do.");
|
||||
SetupPage::log_info("'{$sDBPrefix}priv_change.origin' already initialized, nothing to do.");
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
SetupPage::log_error("Initializing '{$sDBPrefix}priv_change.origin' failed: ".$e->getMessage());
|
||||
SetupPage::log_error("Initializing '{$sDBPrefix}priv_change.origin' failed: ".$e->getMessage());
|
||||
}
|
||||
|
||||
// priv_async_task now has a 'status' field to distinguish between the various statuses rather than just relying on the date columns
|
||||
@@ -830,24 +830,24 @@ class ApplicationInstaller
|
||||
$iCount = (int)CMDBSource::QueryToScalar($sCount);
|
||||
if ($iCount > 0)
|
||||
{
|
||||
SetupPage::log_info("Initializing '{$sDBPrefix}priv_async_task.status' ($iCount records to update)");
|
||||
|
||||
SetupPage::log_info("Initializing '{$sDBPrefix}priv_async_task.status' ($iCount records to update)");
|
||||
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'planned' WHERE (`status` IS NULL) AND (`started` IS NULL)";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
$sInit = "UPDATE `{$sDBPrefix}priv_async_task` SET `status` = 'error' WHERE (`status` IS NULL) AND (`started` IS NOT NULL)";
|
||||
CMDBSource::Query($sInit);
|
||||
|
||||
SetupPage::log_info("Initialization of '{$sDBPrefix}priv_async_task.status' completed.");
|
||||
|
||||
SetupPage::log_info("Initialization of '{$sDBPrefix}priv_async_task.status' completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupPage::log_info("'{$sDBPrefix}priv_async_task.status' already initialized, nothing to do.");
|
||||
SetupPage::log_info("'{$sDBPrefix}priv_async_task.status' already initialized, nothing to do.");
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
SetupPage::log_error("Initializing '{$sDBPrefix}priv_async_task.status' failed: ".$e->getMessage());
|
||||
SetupPage::log_error("Initializing '{$sDBPrefix}priv_async_task.status' failed: ".$e->getMessage());
|
||||
}
|
||||
|
||||
SetupPage::log_info("Database Schema Successfully Updated for environment '$sTargetEnvironment'.");
|
||||
@@ -887,15 +887,15 @@ class ApplicationInstaller
|
||||
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
|
||||
$oProductionEnv->InitDataModel($oConfig, true); // load data model and connect to the database
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously
|
||||
|
||||
self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously
|
||||
|
||||
// Perform here additional DB setup... profiles, etc...
|
||||
//
|
||||
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT.$sModulesDir);
|
||||
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
|
||||
|
||||
$oProductionEnv->UpdatePredefinedObjects();
|
||||
|
||||
|
||||
if($sMode == 'install')
|
||||
{
|
||||
if (!self::CreateAdminAccount(MetaModel::GetConfig(), $sAdminUser, $sAdminPwd, $sAdminLanguage))
|
||||
@@ -907,20 +907,20 @@ class ApplicationInstaller
|
||||
SetupPage::log_info("Administrator account '$sAdminUser' created.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Perform final setup tasks here
|
||||
//
|
||||
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to create and administrator account for iTop
|
||||
* @return boolean true on success, false otherwise
|
||||
* @return boolean true on success, false otherwise
|
||||
*/
|
||||
protected static function CreateAdminAccount(Config $oConfig, $sAdminUser, $sAdminPwd, $sLanguage)
|
||||
{
|
||||
SetupPage::log_info('CreateAdminAccount');
|
||||
|
||||
|
||||
if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage))
|
||||
{
|
||||
return true;
|
||||
@@ -946,9 +946,9 @@ class ApplicationInstaller
|
||||
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
|
||||
|
||||
|
||||
//Load the MetaModel if needed (asynchronous mode)
|
||||
if (!self::$bMetaModelStarted)
|
||||
{
|
||||
@@ -956,8 +956,8 @@ class ApplicationInstaller
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
|
||||
self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, APPROOT.$sModulesDir);
|
||||
$oProductionEnv->LoadData($aAvailableModules, $aSelectedModules, $bSampleData);
|
||||
|
||||
@@ -992,7 +992,7 @@ class ApplicationInstaller
|
||||
$bPreserveModuleSettings = false;
|
||||
if ($sMode == 'upgrade')
|
||||
{
|
||||
try
|
||||
try
|
||||
{
|
||||
$oOldConfig = new Config($sPreviousConfigFile);
|
||||
$oConfig = clone($oOldConfig);
|
||||
@@ -1038,7 +1038,7 @@ class ApplicationInstaller
|
||||
{
|
||||
mkdir(APPCONF);
|
||||
chmod(APPCONF, 0770); // RWX for owner and group, nothing for others
|
||||
SetupPage::log_info("Created configuration directory: ".APPCONF);
|
||||
SetupPage::log_info("Created configuration directory: ".APPCONF);
|
||||
}
|
||||
|
||||
// Write the final configuration file
|
||||
@@ -1048,7 +1048,7 @@ class ApplicationInstaller
|
||||
@chmod($sConfigDir, 0770); // RWX for owner and group, nothing for others
|
||||
|
||||
$oConfig->WriteToFile($sConfigFile);
|
||||
|
||||
|
||||
// try to make the final config file read-only
|
||||
@chmod($sConfigFile, 0440); // Read-only for owner and group, nothing for others
|
||||
|
||||
|
||||
121
setup/csp-detection.js
Normal file
121
setup/csp-detection.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Detects the current server's Content-Security-Policy to stop the setup if any directive doesn't meet the application's requirements
|
||||
*
|
||||
* @type {{_FindItopVersionInURI: (function(): string|string), aFlags: {bUnsafeInlineScriptOk: boolean, bUnsafeEvalScriptOk: boolean, bUnsafeInlineStyleOk: boolean}, _TestUnsafeEvalScript: SetupCSPDetection._TestUnsafeEvalScript, _HideContinueButtonIfPolicyNotOk: SetupCSPDetection._HideContinueButtonIfPolicyNotOk, _TestUnsafeInlineStyle: SetupCSPDetection._TestUnsafeInlineStyle, Run: SetupCSPDetection.Run, _TestUnSafeInlineScript: SetupCSPDetection._TestUnSafeInlineScript, _AddErrorAlert: SetupCSPDetection._AddErrorAlert}}
|
||||
* @since 2.7.11 3.0.5 3.1.2 3.2.0 N°7075
|
||||
*/
|
||||
SetupCSPDetection = {
|
||||
aFlags: {
|
||||
bUnsafeInlineScriptOk: false,
|
||||
bUnsafeEvalScriptOk: false,
|
||||
bUnsafeInlineStyleOk: false,
|
||||
},
|
||||
Run: function () {
|
||||
this._TestUnSafeInlineScript();
|
||||
this._TestUnsafeEvalScript();
|
||||
this._TestUnsafeInlineStyle();
|
||||
this._HideContinueButtonIfPolicyNotOk();
|
||||
},
|
||||
/**
|
||||
* Test if the CSP "unsafe-inline" directive for script-src if enabled, otherwise it forbids the setup to go further
|
||||
* @private
|
||||
*/
|
||||
_TestUnSafeInlineScript: function() {
|
||||
var sBaitElemID = "csp-detection--unsafe-inline-script-bait";
|
||||
|
||||
// Add inline script that should add an element in the DOM
|
||||
var sAddedScript = '<script>$("body").append(\'<div id="' + sBaitElemID + '" class="hidden">If this is present in the DOM, then unsafe-inline for scripts policy is allowed</div>\')</script>';
|
||||
$("body").append(sAddedScript);
|
||||
|
||||
// Check if element has been added to the DOM
|
||||
if ($("#" + sBaitElemID).length === 1) {
|
||||
this.aFlags.bUnsafeInlineScriptOk = true;
|
||||
} else {
|
||||
this._AddErrorAlert("unsafe-inline", "script");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Test if the CSP "unsafe-eval" directive for script-src if enabled, otherwise it forbids the setup to go further
|
||||
* @private
|
||||
*/
|
||||
_TestUnsafeEvalScript: function() {
|
||||
var sBaitElemID = "csp-detection--unsafe-eval-script-bait";
|
||||
|
||||
// Add inline eval script that should add an element in the DOM
|
||||
var sAddedScript = '<script>eval(\'$("body").append(\\\'<div id="' + sBaitElemID + '" class="hidden">If this is present in the DOM, then unsafe-eval for scripts policy is allowed</div>\\\')\')</script>';
|
||||
$("body").append(sAddedScript);
|
||||
|
||||
// Check if element has been added to the DOM
|
||||
if ($("#" + sBaitElemID).length === 1) {
|
||||
this.aFlags.bUnsafeEvalScriptOk = true;
|
||||
} else {
|
||||
this._AddErrorAlert("unsafe-eval", "script");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Test if the CSP "unsafe-inline" directive for style-src if enabled, otherwise it forbids the setup to go further
|
||||
* @private
|
||||
*/
|
||||
_TestUnsafeInlineStyle: function() {
|
||||
var sBaitElemID = "csp-detection--unsafe-inline-style-bait";
|
||||
|
||||
// Add inline eval script that should add an element in the DOM
|
||||
$("body").append("<div id=\"" + sBaitElemID + "\">If this is present in the DOM and visible, then unsafe-inline for styles policy must be allowed</div>");
|
||||
$("body").append("<style>#" + sBaitElemID + " { display: none; }</style>");
|
||||
|
||||
// Check if style has been applied
|
||||
if ($("#" + sBaitElemID).is(":visible") === false) {
|
||||
this.aFlags.bUnsafeInlineStyleOk = true;
|
||||
} else {
|
||||
// Remove bait div to avoid polluting the screen
|
||||
$("#" + sBaitElemID).remove();
|
||||
this._AddErrorAlert("unsafe-inline", "style");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Hide continue button to prevent setup from going further if any policy is not OK
|
||||
* @private
|
||||
*/
|
||||
_HideContinueButtonIfPolicyNotOk: function() {
|
||||
if (false === this.aFlags.bUnsafeInlineScriptOk || false === this.aFlags.bUnsafeEvalScriptOk || false === this.aFlags.bUnsafeInlineStyleOk) {
|
||||
// Hide next button to prevent user from going forward.
|
||||
// Note that we don't remove it completely to be able to by-pass it.
|
||||
$("#btn_next").addClass("hidden");
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Internal helper to add an error alert in case of failure
|
||||
* @param {String} sPolicyOption e.g. "unsafe-inline", "unsafe-eval", ...
|
||||
* @param {String} sResourceType script|style
|
||||
* @private
|
||||
*/
|
||||
_AddErrorAlert: function(sPolicyOption, sResourceType) {
|
||||
var sFilesType = sResourceType === "script" ? "scripts" : "styles";
|
||||
|
||||
// Add alert in the DOM
|
||||
$("<div class=\"message message-error\"><span class=\"message-title\">Error:</span>Your server Content-Security-Policy header doesn't allow <b>" + sPolicyOption + " " + sFilesType + "</b>. Therefore, the application cannot be installed (<a" +
|
||||
" href=\"https://www.itophub.io/wiki/page?id=" + this._FindItopVersionInURI() + ":install:security#content-security-policy\" target=\"_blank\">see documentation</a>).</div>")
|
||||
.insertAfter(
|
||||
$("#wiz_form h1:first")
|
||||
);
|
||||
|
||||
},
|
||||
/**
|
||||
* Internal helper to find the iTop version as wiki syntax from the script URI
|
||||
* @returns {string|string}
|
||||
* @private
|
||||
*/
|
||||
_FindItopVersionInURI: function() {
|
||||
// First find script tag for the current file
|
||||
var sScriptURI = $('script[src*="setup/csp-detection.js"]').attr("src");
|
||||
|
||||
// Extract parameter value from URI
|
||||
var regex = new RegExp('[\\?&]' + 'itop_version_wiki_syntax' + '=([^&#]*)');
|
||||
var results = regex.exec(sScriptURI);
|
||||
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
SetupCSPDetection.Run();
|
||||
});
|
||||
@@ -932,10 +932,12 @@ class ModelFactory
|
||||
$this->aDictKeys[$sLanguageCode][$sCode] = $oXmlEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
} catch (Exception|Error $e) // Error can occurs on eval() calls
|
||||
{
|
||||
throw new Exception('Failed to load dictionary file "'.$sPHPFile.'", reason: '.$e->getMessage());
|
||||
throw new DictException('Failed to load dictionary file "' . $sPHPFile . '"', [
|
||||
'exception_class' => get_class($e),
|
||||
'exception_msg' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class ModuleDiscovery
|
||||
'doc.manual_setup' => 'url',
|
||||
'doc.more_information' => 'url',
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Cache the results and the source directories
|
||||
protected static $m_aSearchDirs = null;
|
||||
@@ -148,7 +148,7 @@ class ModuleDiscovery
|
||||
self::$m_aModuleVersionByName[$sModuleName]['version'] = $sModuleVersion;
|
||||
self::$m_aModuleVersionByName[$sModuleName]['id'] = $sId;
|
||||
}
|
||||
|
||||
|
||||
self::$m_aModules[$sId] = $aArgs;
|
||||
|
||||
// Now keep the relative paths, as provided
|
||||
@@ -250,7 +250,7 @@ class ModuleDiscovery
|
||||
if ($bAbortOnMissingDependency && count($aDependencies) > 0)
|
||||
{
|
||||
$aModulesInfo = array();
|
||||
$aModuleDeps = array();
|
||||
$aModuleDeps = array();
|
||||
foreach($aDependencies as $sId => $aDeps)
|
||||
{
|
||||
$aModule = $aModules[$sId];
|
||||
@@ -282,7 +282,7 @@ class ModuleDiscovery
|
||||
// The de-duplication is now done directly by the AddModule method
|
||||
return $aModules;
|
||||
}
|
||||
|
||||
|
||||
protected static function DependencyIsResolved($sDepString, $aOrderedModules, $aSelectedModules)
|
||||
{
|
||||
$bResult = false;
|
||||
@@ -329,12 +329,12 @@ class ModuleDiscovery
|
||||
if (version_compare($sCurrentVersion, $sExpectedVersion, $sOperator))
|
||||
{
|
||||
$aReplacements[$sModuleId] = '(true)'; // Add parentheses to protect against invalid condition causing
|
||||
// a function call that results in a runtime fatal error
|
||||
// a function call that results in a runtime fatal error
|
||||
}
|
||||
else
|
||||
{
|
||||
$aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing
|
||||
// a function call that results in a runtime fatal error
|
||||
// a function call that results in a runtime fatal error
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -393,20 +393,20 @@ class ModuleDiscovery
|
||||
{
|
||||
self::ResetCache();
|
||||
}
|
||||
|
||||
|
||||
if (is_null(self::$m_aSearchDirs))
|
||||
{
|
||||
self::$m_aSearchDirs = $aSearchDirs;
|
||||
|
||||
|
||||
// Not in cache, let's scan the disk
|
||||
foreach($aSearchDirs as $sSearchDir)
|
||||
{
|
||||
$sLookupDir = realpath($sSearchDir);
|
||||
$sLookupDir = realpath($sSearchDir);
|
||||
if ($sLookupDir == '')
|
||||
{
|
||||
throw new Exception("Invalid directory '$sSearchDir'");
|
||||
}
|
||||
|
||||
|
||||
clearstatcache();
|
||||
self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir));
|
||||
}
|
||||
@@ -418,7 +418,7 @@ class ModuleDiscovery
|
||||
return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function ResetCache()
|
||||
{
|
||||
self::$m_aSearchDirs = null;
|
||||
@@ -430,7 +430,7 @@ class ModuleDiscovery
|
||||
* Helper function to interpret the name of a module
|
||||
* @param $sModuleId string Identifier of the module, in the form 'name/version'
|
||||
* @return array(name, version)
|
||||
*/
|
||||
*/
|
||||
public static function GetModuleName($sModuleId)
|
||||
{
|
||||
$aMatches = array();
|
||||
@@ -459,7 +459,7 @@ class ModuleDiscovery
|
||||
{
|
||||
static $iDummyClassIndex = 0;
|
||||
$sDirectory = $sRootDir.'/'.$sRelDir;
|
||||
|
||||
|
||||
if ($hDir = opendir($sDirectory))
|
||||
{
|
||||
// This is the correct way to loop over the directory. (according to the documentation)
|
||||
@@ -495,12 +495,12 @@ class ModuleDiscovery
|
||||
$idx++;
|
||||
}
|
||||
$bRet = eval($sModuleFileContents);
|
||||
|
||||
|
||||
if ($bRet === false)
|
||||
{
|
||||
SetupPage::log_warning("Eval of $sRelDir/$sFile returned false");
|
||||
}
|
||||
|
||||
|
||||
//echo "<p>Done.</p>\n";
|
||||
}
|
||||
catch(ParseError $e)
|
||||
@@ -528,7 +528,7 @@ class ModuleDiscovery
|
||||
/** Alias for backward compatibility with old module files in which
|
||||
* the declaration of a module invokes SetupWebPage::AddModule()
|
||||
* whereas the new form is ModuleDiscovery::AddModule()
|
||||
*/
|
||||
*/
|
||||
class SetupWebPage extends ModuleDiscovery
|
||||
{
|
||||
// For backward compatibility with old modules...
|
||||
@@ -555,9 +555,9 @@ class SetupWebPage extends ModuleDiscovery
|
||||
public static function log($sText)
|
||||
{
|
||||
SetupPage::log($sText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Ugly patch !!!
|
||||
* In order to be able to analyse / load several times
|
||||
* the same module file, we rename the class (to avoid duplicate class definitions)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -100,14 +100,14 @@ class RunTimeEnvironment
|
||||
$this->log_info(sprintf('%.3fs - query: %s ', $fDuration, $sQuery));
|
||||
$this->log_db_query($sQuery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to initialize the ORM and load the data model
|
||||
* from the given file
|
||||
* @param $oConfig object The configuration (volatile, not necessarily already on disk)
|
||||
* @param $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
|
||||
* @param $bModelOnly boolean Whether or not to allow loading a data model with no corresponding DB
|
||||
* @return none
|
||||
*/
|
||||
*/
|
||||
public function InitDataModel($oConfig, $bModelOnly = true, $bUseCache = false)
|
||||
{
|
||||
require_once APPROOT.'/setup/moduleinstallation.class.inc.php';
|
||||
@@ -121,15 +121,15 @@ class RunTimeEnvironment
|
||||
{
|
||||
$this->log_info("MetaModel::Startup (ModelOnly = $bModelOnly)");
|
||||
}
|
||||
|
||||
|
||||
if (!$bUseCache)
|
||||
{
|
||||
// Reset the cache for the first use !
|
||||
MetaModel::ResetCache(md5(APPROOT).'-'.$this->sTargetEnv);
|
||||
}
|
||||
|
||||
|
||||
MetaModel::Startup($oConfig, $bModelOnly, $bUseCache, false /* $bTraceSourceFiles */, $this->sTargetEnv);
|
||||
|
||||
|
||||
if ($this->oExtensionsMap === null)
|
||||
{
|
||||
$this->oExtensionsMap = new iTopExtensionsMap($this->sTargetEnv);
|
||||
@@ -139,7 +139,7 @@ class RunTimeEnvironment
|
||||
/**
|
||||
* Analyzes the current installation and the possibilities
|
||||
*
|
||||
* @param Config $oConfig Defines the target environment (DB)
|
||||
* @param null|Config $oConfig Defines the target environment (DB)
|
||||
* @param mixed $modulesPath Either a single string or an array of absolute paths
|
||||
* @param bool $bAbortOnMissingDependency ...
|
||||
* @param array $aModulesToLoad List of modules to search for, defaults to all if omitted
|
||||
@@ -178,7 +178,7 @@ class RunTimeEnvironment
|
||||
'name_code' => ITOP_APPLICATION,
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$aDirs = is_array($modulesPath) ? $modulesPath : array($modulesPath);
|
||||
$aModules = ModuleDiscovery::GetAvailableModules($aDirs, $bAbortOnMissingDependency, $aModulesToLoad);
|
||||
foreach($aModules as $sModuleId => $aModuleInfo)
|
||||
@@ -194,11 +194,11 @@ class RunTimeEnvironment
|
||||
//throw new Exception("Missing version for the module: '$sModuleId'");
|
||||
$sModuleVersion = '1.0.0';
|
||||
}
|
||||
|
||||
|
||||
$sModuleAppVersion = $aModuleInfo['itop_version'];
|
||||
$aModuleInfo['version_db'] = '';
|
||||
$aModuleInfo['version_code'] = $sModuleVersion;
|
||||
|
||||
|
||||
if (!in_array($sModuleAppVersion, array('1.0.0', '1.0.1', '1.0.2')))
|
||||
{
|
||||
// This module is NOT compatible with the current version
|
||||
@@ -223,18 +223,20 @@ class RunTimeEnvironment
|
||||
}
|
||||
$aRes[$sModuleName] = $aModuleInfo;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
$aSelectInstall = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install");
|
||||
$aSelectInstall = array();
|
||||
if (! is_null($oConfig)) {
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
$aSelectInstall = CMDBSource::QueryToArray("SELECT * FROM ".$oConfig->Get('db_subname')."priv_module_install");
|
||||
}
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
// No database or erroneous information
|
||||
$aSelectInstall = array();
|
||||
}
|
||||
|
||||
|
||||
// Build the list of installed module (get the latest installation)
|
||||
//
|
||||
$aInstallByModule = array(); // array of <module> => array ('installed' => timestamp, 'version' => <version>)
|
||||
@@ -251,7 +253,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($aSelectInstall as $aInstall)
|
||||
{
|
||||
//$aInstall['comment']; // unsused
|
||||
@@ -265,7 +267,7 @@ class RunTimeEnvironment
|
||||
// as being installed
|
||||
$sModuleVersion = '0.0.0';
|
||||
}
|
||||
|
||||
|
||||
if ($aInstall['parent_id'] == 0)
|
||||
{
|
||||
$sModuleName = ROOT_MODULE;
|
||||
@@ -275,7 +277,7 @@ class RunTimeEnvironment
|
||||
// Skip all modules belonging to previous installations
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists($sModuleName, $aInstallByModule))
|
||||
{
|
||||
if ($iInstalled < $aInstallByModule[$sModuleName]['installed'])
|
||||
@@ -283,30 +285,30 @@ class RunTimeEnvironment
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($aInstall['parent_id'] == 0)
|
||||
{
|
||||
$aRes[$sModuleName]['version_db'] = $sModuleVersion;
|
||||
$aRes[$sModuleName]['name_db'] = $aInstall['name'];
|
||||
}
|
||||
|
||||
|
||||
$aInstallByModule[$sModuleName]['installed'] = $iInstalled;
|
||||
$aInstallByModule[$sModuleName]['version'] = $sModuleVersion;
|
||||
}
|
||||
|
||||
|
||||
// Adjust the list of proposed modules
|
||||
//
|
||||
foreach ($aInstallByModule as $sModuleName => $aModuleDB)
|
||||
{
|
||||
if ($sModuleName == ROOT_MODULE) continue; // Skip the main module
|
||||
|
||||
|
||||
if (!array_key_exists($sModuleName, $aRes))
|
||||
{
|
||||
// A module was installed, it is not proposed in the new build... skip
|
||||
// A module was installed, it is not proposed in the new build... skip
|
||||
continue;
|
||||
}
|
||||
$aRes[$sModuleName]['version_db'] = $aModuleDB['version'];
|
||||
|
||||
|
||||
if ($aRes[$sModuleName]['install']['flag'] == MODULE_ACTION_MANDATORY)
|
||||
{
|
||||
$aRes[$sModuleName]['uninstall'] = array(
|
||||
@@ -322,7 +324,7 @@ class RunTimeEnvironment
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
@@ -336,9 +338,9 @@ class RunTimeEnvironment
|
||||
{
|
||||
self::MakeDirSafe(APPCONF);
|
||||
self::MakeDirSafe(APPCONF.$this->sTargetEnv);
|
||||
|
||||
|
||||
$sTargetConfigFile = APPCONF.$this->sTargetEnv.'/'.ITOP_CONFIG_FILE;
|
||||
|
||||
|
||||
// Write the config file
|
||||
@chmod($sTargetConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others
|
||||
$oConfig->WriteToFile($sTargetConfigFile);
|
||||
@@ -354,7 +356,7 @@ class RunTimeEnvironment
|
||||
// Do nothing, overload this method if needed
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decide whether or not the given extension is selected for installation
|
||||
* @param iTopExtension $oExtension
|
||||
@@ -364,10 +366,10 @@ class RunTimeEnvironment
|
||||
{
|
||||
return ($oExtension->sSource == iTopExtension::SOURCE_REMOTE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the installed modules (only the installed ones)
|
||||
*/
|
||||
* Get the installed modules (only the installed ones)
|
||||
*/
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
|
||||
{
|
||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
||||
@@ -388,7 +390,7 @@ class RunTimeEnvironment
|
||||
|
||||
$aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile);
|
||||
$aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs);
|
||||
|
||||
|
||||
$aRet = array();
|
||||
|
||||
// Determine the installed modules and extensions
|
||||
@@ -396,7 +398,7 @@ class RunTimeEnvironment
|
||||
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
|
||||
$oSourceEnv = new RunTimeEnvironment($sSourceEnv);
|
||||
$aAvailableModules = $oSourceEnv->AnalyzeInstallation($oSourceConfig, $aDirsToCompile);
|
||||
|
||||
|
||||
// Actually read the modules available for the target environment,
|
||||
// but get the selection from the source environment and finally
|
||||
// mark as (automatically) chosen alll the "remote" modules present in the
|
||||
@@ -416,7 +418,7 @@ class RunTimeEnvironment
|
||||
//
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
$aRet[$oDictModule->GetName()] = $oDictModule;
|
||||
|
||||
|
||||
$oFactory = new ModelFactory($aDirsToCompile);
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile))
|
||||
@@ -430,14 +432,14 @@ class RunTimeEnvironment
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$aRet[$oApplicationModule->GetName()] = $oApplicationModule;
|
||||
}
|
||||
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
foreach($aModules as $oModule)
|
||||
{
|
||||
$sModule = $oModule->GetName();
|
||||
$sModuleRootDir = $oModule->GetRootDir();
|
||||
$bIsExtra = $this->oExtensionsMap->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE);
|
||||
if (array_key_exists($sModule, $aAvailableModules))
|
||||
if (array_key_exists($sModule, $aAvailableModules))
|
||||
{
|
||||
if (($aAvailableModules[$sModule]['version_db'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) //Extra modules are always unless they are 'AutoSelect'
|
||||
{
|
||||
@@ -445,7 +447,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now process the 'AutoSelect' modules
|
||||
do
|
||||
{
|
||||
@@ -514,7 +516,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($oModule instanceof MFDeltaModule)
|
||||
{
|
||||
@@ -565,7 +567,7 @@ class RunTimeEnvironment
|
||||
{
|
||||
$this->log_info("Creating the structure in '".$oConfig->Get('db_name')."'.");
|
||||
}
|
||||
|
||||
|
||||
//MetaModel::CheckDefinitions();
|
||||
if ($sMode == 'install')
|
||||
{
|
||||
@@ -597,7 +599,7 @@ class RunTimeEnvironment
|
||||
|
||||
MetaModel::DBCreate(array($this, 'LogQueryCallback'));
|
||||
$this->log_ok("Database structure successfully updated.");
|
||||
|
||||
|
||||
// Check (and update only if it seems needed) the hierarchical keys
|
||||
if (MFCompiler::SkipRebuildHKeys()) {
|
||||
$this->log_ok("Hierchical keys are NOT rebuilt due to the presence of the \"data/.setup-rebuild-hkeys-never\" file");
|
||||
@@ -657,7 +659,7 @@ class RunTimeEnvironment
|
||||
if ($aPredefinedObjects != null)
|
||||
{
|
||||
$this->log_info("$sClass::GetPredefinedObjects() returned " . count($aPredefinedObjects) . " elements.");
|
||||
|
||||
|
||||
// Create/Delete/Update objects of this class,
|
||||
// according to the given constant values
|
||||
//
|
||||
@@ -699,7 +701,7 @@ class RunTimeEnvironment
|
||||
// Restore the previous access mode
|
||||
$oConfig->Set('access_mode', $iPrevAccessMode);
|
||||
}
|
||||
|
||||
|
||||
public function RecordInstallation(Config $oConfig, $sDataModelVersion, $aSelectedModuleCodes, $aSelectedExtensionCodes, $sShortComment = null)
|
||||
{
|
||||
// Have it work fine even if the DB has been set in read-only mode for the users
|
||||
@@ -708,7 +710,7 @@ class RunTimeEnvironment
|
||||
//$oConfig->Set('access_mode', ACCESS_FULL);
|
||||
|
||||
if (CMDBSource::DBName() == '')
|
||||
{
|
||||
{
|
||||
// In case this has not yet been done
|
||||
CMDBSource::InitFromConfig($oConfig);
|
||||
}
|
||||
@@ -718,7 +720,7 @@ class RunTimeEnvironment
|
||||
$sShortComment = 'Done by the setup program';
|
||||
}
|
||||
$sMainComment = $sShortComment."\nBuilt on ".ITOP_BUILD_DATE;
|
||||
|
||||
|
||||
// Record datamodel version
|
||||
$aData = array(
|
||||
'source_dir' => $oConfig->Get('source_dir'),
|
||||
@@ -731,7 +733,7 @@ class RunTimeEnvironment
|
||||
$oInstallRec->Set('parent_id', 0); // root module
|
||||
$oInstallRec->Set('installed', $iInstallationTime);
|
||||
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
|
||||
|
||||
|
||||
// Record main installation
|
||||
$oInstallRec = new ModuleInstallation();
|
||||
$oInstallRec->Set('name', ITOP_APPLICATION);
|
||||
@@ -740,8 +742,8 @@ class RunTimeEnvironment
|
||||
$oInstallRec->Set('parent_id', 0); // root module
|
||||
$oInstallRec->Set('installed', $iInstallationTime);
|
||||
$iMainItopRecord = $oInstallRec->DBInsertNoReload();
|
||||
|
||||
|
||||
|
||||
|
||||
// Record installed modules and extensions
|
||||
//
|
||||
$aAvailableExtensions = array();
|
||||
@@ -775,7 +777,7 @@ class RunTimeEnvironment
|
||||
$aComments[] = "Depends on module: $sDependOn";
|
||||
}
|
||||
$sComment = implode("\n", $aComments);
|
||||
|
||||
|
||||
$oInstallRec = new ModuleInstallation();
|
||||
$oInstallRec->Set('name', $sName);
|
||||
$oInstallRec->Set('version', $sVersion);
|
||||
@@ -784,7 +786,7 @@ class RunTimeEnvironment
|
||||
$oInstallRec->Set('installed', $iInstallationTime);
|
||||
$oInstallRec->DBInsertNoReload();
|
||||
}
|
||||
|
||||
|
||||
if ($this->oExtensionsMap)
|
||||
{
|
||||
// Mark as chosen the selected extensions code passed to us
|
||||
@@ -796,7 +798,7 @@ class RunTimeEnvironment
|
||||
$this->oExtensionsMap->MarkAsChosen($oExtension->sCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($this->oExtensionsMap->GetChoices() as $oExtension)
|
||||
{
|
||||
$oInstallRec = new ExtensionInstallation();
|
||||
@@ -813,9 +815,9 @@ class RunTimeEnvironment
|
||||
MetaModel::GetConfig()->Set('access_mode', $iPrevAccessMode);
|
||||
|
||||
// Database is created, installation has been tracked into it
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function GetApplicationVersion(Config $oConfig)
|
||||
{
|
||||
$aResult = false;
|
||||
@@ -832,7 +834,7 @@ class RunTimeEnvironment
|
||||
$this->log_error('Exception '.$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Scan the list of installed modules to get the version of the 'ROOT' module which holds the main application version
|
||||
foreach ($aSelectInstall as $aInstall)
|
||||
{
|
||||
@@ -844,7 +846,7 @@ class RunTimeEnvironment
|
||||
// as being installed
|
||||
$sModuleVersion = '0.0.0';
|
||||
}
|
||||
|
||||
|
||||
if ($aInstall['parent_id'] == 0)
|
||||
{
|
||||
if ($aInstall['name'] == DATAMODEL_MODULE)
|
||||
@@ -870,7 +872,7 @@ class RunTimeEnvironment
|
||||
$aResult['datamodel_version'] = $aResult['product_version'];
|
||||
}
|
||||
$this->log_info("GetApplicationVersion returns: product_name: ".$aResult['product_name'].', product_version: '.$aResult['product_version']);
|
||||
return $aResult;
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public static function MakeDirSafe($sDir)
|
||||
@@ -886,8 +888,8 @@ class RunTimeEnvironment
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrappers for logging into the setup log files
|
||||
*/
|
||||
* Wrappers for logging into the setup log files
|
||||
*/
|
||||
protected function log_error($sText)
|
||||
{
|
||||
SetupPage::log_error($sText);
|
||||
@@ -923,7 +925,7 @@ class RunTimeEnvironment
|
||||
fclose($hSetupQueriesFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetCurrentDataModelVersion()
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT ModuleInstallation WHERE name='".DATAMODEL_MODULE."'");
|
||||
@@ -1079,13 +1081,14 @@ class RunTimeEnvironment
|
||||
SetupUtils::tidydir(APPROOT.'env-'.$this->sTargetEnv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the given handler method for all selected modules having an installation handler
|
||||
* @param array[] $aAvailableModules
|
||||
* @param string[] $aSelectedModules
|
||||
* @param string $sHandlerName
|
||||
*/
|
||||
|
||||
/**
|
||||
* Call the given handler method for all selected modules having an installation handler
|
||||
* @param array[] $aAvailableModules
|
||||
* @param string[] $aSelectedModules
|
||||
* @param string $sHandlerName
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function CallInstallerHandlers($aAvailableModules, $aSelectedModules, $sHandlerName)
|
||||
{
|
||||
foreach($aAvailableModules as $sModuleId => $aModule)
|
||||
@@ -1098,12 +1101,24 @@ class RunTimeEnvironment
|
||||
$aCallSpec = array($sModuleInstallerClass, $sHandlerName);
|
||||
if (is_callable($aCallSpec))
|
||||
{
|
||||
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
|
||||
}
|
||||
try {
|
||||
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
|
||||
} catch (Exception $e) {
|
||||
$sErrorMessage = "Module $sModuleId : error when calling module installer class $sModuleInstallerClass for $sHandlerName handler";
|
||||
$aExceptionContextData = [
|
||||
'ModulelId' => $sModuleId,
|
||||
'ModuleInstallerClass' => $sModuleInstallerClass,
|
||||
'ModuleInstallerHandler' => $sHandlerName,
|
||||
'ExceptionClass' => get_class($e),
|
||||
'ExceptionMessage' => $e->getMessage(),
|
||||
];
|
||||
throw new CoreException($sErrorMessage, $aExceptionContextData, '', $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load data from XML files for the selected modules (structural data and/or sample data)
|
||||
* @param array[] $aAvailableModules All available modules and their definition
|
||||
@@ -1113,13 +1128,13 @@ class RunTimeEnvironment
|
||||
public function LoadData($aAvailableModules, $aSelectedModules, $bSampleData)
|
||||
{
|
||||
$oDataLoader = new XMLDataLoader();
|
||||
|
||||
|
||||
CMDBObject::SetTrackInfo("Initialization");
|
||||
$oMyChange = CMDBObject::GetCurrentChange();
|
||||
|
||||
|
||||
SetupPage::log_info("starting data load session");
|
||||
$oDataLoader->StartSession($oMyChange);
|
||||
|
||||
|
||||
$aFiles = array();
|
||||
$aPreviouslyLoadedFiles = array();
|
||||
foreach($aAvailableModules as $sModuleId => $aModule)
|
||||
@@ -1160,7 +1175,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Simulate the load of the previously loaded files, in order to initialize
|
||||
// the mapping between the identifiers in the XML and the actual identifiers
|
||||
// in the current database
|
||||
@@ -1172,12 +1187,12 @@ class RunTimeEnvironment
|
||||
{
|
||||
throw(new Exception("File $sFileName does not exist"));
|
||||
}
|
||||
|
||||
|
||||
$oDataLoader->LoadFile($sFileName, true);
|
||||
$sResult = sprintf("loading of %s done.", basename($sFileName));
|
||||
SetupPage::log_info($sResult);
|
||||
}
|
||||
|
||||
|
||||
foreach($aFiles as $sFileRelativePath)
|
||||
{
|
||||
$sFileName = APPROOT.$sFileRelativePath;
|
||||
@@ -1186,16 +1201,16 @@ class RunTimeEnvironment
|
||||
{
|
||||
throw(new Exception("File $sFileName does not exist"));
|
||||
}
|
||||
|
||||
|
||||
$oDataLoader->LoadFile($sFileName);
|
||||
$sResult = sprintf("loading of %s done.", basename($sFileName));
|
||||
SetupPage::log_info($sResult);
|
||||
}
|
||||
|
||||
|
||||
$oDataLoader->EndSession();
|
||||
SetupPage::log_info("ending data load session");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge two arrays of file names, adding the relative path to the files provided in the array to merge
|
||||
* @param string[] $aSourceArray
|
||||
@@ -1212,7 +1227,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
return array_merge($aSourceArray, $aToMerge);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the MetaModel for some common pitfall (class name too long, classes requiring too many joins...)
|
||||
* The check takes about 900 ms for 200 classes
|
||||
@@ -1252,7 +1267,7 @@ class RunTimeEnvironment
|
||||
$iCount++;
|
||||
}
|
||||
$fDuration = microtime(true) - $fStart;
|
||||
|
||||
|
||||
return sprintf("Checked %d classes in %.1f ms. No error found.\n", $iCount, $fDuration*1000.0);
|
||||
}
|
||||
} // End of class
|
||||
|
||||
@@ -35,6 +35,7 @@ class SetupPage extends NiceWebPage
|
||||
parent::__construct($sTitle);
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../setup/setup.js");
|
||||
$this->add_linked_script("../setup/csp-detection.js?itop_version_wiki_syntax=" . utils::GetItopVersionWikiSyntax());
|
||||
$this->add_saas("css/setup.scss");
|
||||
}
|
||||
|
||||
|
||||
@@ -897,16 +897,29 @@ class SetupUtils
|
||||
return $f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $fBytes size in raw bytes, for example 162594750464.0
|
||||
* @return string formatted string, for example "161.62 GB"
|
||||
*
|
||||
* @link https://en.wiktionary.org/wiki/byte byte and not Byte
|
||||
* @link https://en.wikipedia.org/wiki/Kilobyte kB and not KB (IEC 80000-13)
|
||||
* @link https://en.wiktionary.org/wiki/petabyte petabyte PB
|
||||
* @link https://en.wiktionary.org/wiki/exabyte exabyte EB
|
||||
*/
|
||||
public static function HumanReadableSize($fBytes)
|
||||
{
|
||||
$aSizes = array('bytes', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Hb');
|
||||
$aSizes = array('bytes', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB');
|
||||
$index = 0;
|
||||
while (($fBytes > 1000) && ($index < count($aSizes)))
|
||||
{
|
||||
while (($fBytes > 1000) && ($index < count($aSizes))) {
|
||||
$index++;
|
||||
$fBytes = $fBytes / 1000;
|
||||
}
|
||||
|
||||
if ($index == 0) {
|
||||
// display int for bytes
|
||||
return sprintf('%d %s', $fBytes, $aSizes[$index]);
|
||||
}
|
||||
|
||||
return sprintf('%.2f %s', $fBytes, $aSizes[$index]);
|
||||
}
|
||||
|
||||
|
||||
13
setup/unattended-install/.htaccess
Normal file
13
setup/unattended-install/.htaccess
Normal file
@@ -0,0 +1,13 @@
|
||||
# Apache 2.4
|
||||
<ifModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</ifModule>
|
||||
|
||||
# Apache 2.2
|
||||
<ifModule !mod_authz_core.c>
|
||||
deny from all
|
||||
Satisfy All
|
||||
</ifModule>
|
||||
|
||||
# Apache 2.2 and 2.4
|
||||
IndexIgnore *
|
||||
365
setup/unattended-install/InstallationFileService.php
Normal file
365
setup/unattended-install/InstallationFileService.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
|
||||
|
||||
if (version_compare(ITOP_DESIGN_LATEST_VERSION, '2.7', '<=')) {
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
require_once(APPROOT.'/core/log.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/application/clipage.class.inc.php');
|
||||
}
|
||||
|
||||
class InstallationFileService {
|
||||
/** @var \RunTimeEnvironment $oProductionEnv */
|
||||
private $oProductionEnv;
|
||||
|
||||
/** @var \ItopExtensionsMap $oProductionEnv */
|
||||
private $oItopExtensionsMap;
|
||||
|
||||
private $sTargetEnvironment;
|
||||
private $sInstallationPath;
|
||||
private $aSelectedModules;
|
||||
private $aSelectedExtensions;
|
||||
private $aAfterComputationSelectedExtensions;
|
||||
private $aUnSelectedModules;
|
||||
private $aAutoSelectModules;
|
||||
private $bInstallationOptionalChoicesChecked;
|
||||
|
||||
/**
|
||||
* @param string $sInstallationPath
|
||||
* @param string $sTargetEnvironment
|
||||
* @param array $aSelectedExtensions
|
||||
* @param bool $bInstallationOptionalChoicesChecked : this option is used only when no extensions are selected (ie empty
|
||||
* $aSelectedExtensions)
|
||||
*/
|
||||
public function __construct(string $sInstallationPath, string $sTargetEnvironment='production', array $aSelectedExtensions = [], bool $bInstallationOptionalChoicesChecked=true) {
|
||||
$this->sInstallationPath = $sInstallationPath;
|
||||
$this->aSelectedModules = [];
|
||||
$this->aUnSelectedModules = [];
|
||||
$this->sTargetEnvironment = $sTargetEnvironment;
|
||||
$this->aSelectedExtensions = $aSelectedExtensions;
|
||||
$this->aAfterComputationSelectedExtensions = (count($aSelectedExtensions)==0) ? [] : $aSelectedExtensions;
|
||||
$this->bInstallationOptionalChoicesChecked = $bInstallationOptionalChoicesChecked;
|
||||
}
|
||||
|
||||
public function Init(): void {
|
||||
clearstatcache();
|
||||
|
||||
$this->ProcessDefaultModules();
|
||||
$this->ProcessInstallationChoices();
|
||||
$this->ProcessExtensionModulesNotSpecifiedInChoices();
|
||||
$this->ProcessAutoSelectModules();
|
||||
}
|
||||
|
||||
public function GetProductionEnv(): RunTimeEnvironment {
|
||||
if (is_null($this->oProductionEnv)){
|
||||
$this->oProductionEnv = new RunTimeEnvironment();
|
||||
}
|
||||
return $this->oProductionEnv;
|
||||
}
|
||||
|
||||
public function SetProductionEnv(RunTimeEnvironment $oProductionEnv): void {
|
||||
$this->oProductionEnv = $oProductionEnv;
|
||||
}
|
||||
|
||||
public function GetAfterComputationSelectedExtensions(): array {
|
||||
return $this->aAfterComputationSelectedExtensions;
|
||||
}
|
||||
|
||||
public function SetItopExtensionsMap(ItopExtensionsMap $oItopExtensionsMap): void {
|
||||
$this->oItopExtensionsMap = $oItopExtensionsMap;
|
||||
}
|
||||
|
||||
public function GetItopExtensionsMap(): ItopExtensionsMap {
|
||||
if (is_null($this->oItopExtensionsMap)){
|
||||
$this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment, true);
|
||||
}
|
||||
return $this->oItopExtensionsMap;
|
||||
}
|
||||
|
||||
public function GetAutoSelectModules(): array {
|
||||
return $this->aAutoSelectModules;
|
||||
}
|
||||
|
||||
public function GetSelectedModules(): array {
|
||||
return $this->aSelectedModules;
|
||||
}
|
||||
|
||||
public function GetUnSelectedModules(): array {
|
||||
return $this->aUnSelectedModules;
|
||||
}
|
||||
|
||||
public function ProcessInstallationChoices(): void {
|
||||
$oXMLParameters = new XMLParameters($this->sInstallationPath);
|
||||
$aSteps = $oXMLParameters->Get('steps', []);
|
||||
if (! is_array($aSteps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($aSteps as $aStepInfo) {
|
||||
$aOptions = $aStepInfo["options"] ?? null;
|
||||
if (! is_null($aOptions) && is_array($aOptions)) {
|
||||
foreach ($aOptions as $aChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aChoiceInfo, $this->bInstallationOptionalChoicesChecked);
|
||||
}
|
||||
}
|
||||
$aOptions = $aStepInfo["alternatives"] ?? null;
|
||||
if (! is_null($aOptions) && is_array($aOptions)) {
|
||||
foreach ($aOptions as $aChoiceInfo) {
|
||||
$this->ProcessSelectedChoice($aChoiceInfo, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->aSelectedModules as $sModuleId => $sVal){
|
||||
if (array_key_exists($sModuleId, $this->aUnSelectedModules)){
|
||||
unset($this->aUnSelectedModules[$sModuleId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ProcessUnSelectedChoice($aChoiceInfo) {
|
||||
if (!is_array($aChoiceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aCurrentModules = $aChoiceInfo["modules"] ?? [];
|
||||
foreach ($aCurrentModules as $sModuleId){
|
||||
$this->aUnSelectedModules[$sModuleId] = true;
|
||||
}
|
||||
|
||||
$aAlternatives = $aChoiceInfo["alternatives"] ?? null;
|
||||
if (!is_null($aAlternatives) && is_array($aAlternatives)) {
|
||||
foreach ($aAlternatives as $aSubChoiceInfo) {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('sub_options', $aChoiceInfo)) {
|
||||
if (array_key_exists('options', $aChoiceInfo['sub_options'])) {
|
||||
$aSubOptions = $aChoiceInfo['sub_options']['options'];
|
||||
if (!is_null($aSubOptions) && is_array($aSubOptions)) {
|
||||
foreach ($aSubOptions as $aSubChoiceInfo) {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (array_key_exists('alternatives', $aChoiceInfo['sub_options'])) {
|
||||
$aSubAlternatives = $aChoiceInfo['sub_options']['alternatives'];
|
||||
if (!is_null($aSubAlternatives) && is_array($aSubAlternatives)) {
|
||||
foreach ($aSubAlternatives as $aSubChoiceInfo) {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ProcessSelectedChoice($aChoiceInfo, bool $bAllChecked) {
|
||||
if (!is_array($aChoiceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sDefault = $aChoiceInfo["default"] ?? "false";
|
||||
$sMandatory = $aChoiceInfo["mandatory"] ?? "false";
|
||||
|
||||
$aCurrentModules = $aChoiceInfo["modules"] ?? [];
|
||||
$sExtensionCode = $aChoiceInfo["extension_code"] ?? null;
|
||||
if (0 === count($this->aSelectedExtensions)){
|
||||
$bSelected = $bAllChecked || $sDefault === "true" || $sMandatory === "true";
|
||||
if ($bSelected){
|
||||
$this->aAfterComputationSelectedExtensions[]= $sExtensionCode;
|
||||
}
|
||||
} else {
|
||||
$bSelected = $sMandatory === "true" ||
|
||||
(null !== $sExtensionCode && in_array($sExtensionCode, $this->aSelectedExtensions));
|
||||
}
|
||||
|
||||
foreach ($aCurrentModules as $sModuleId){
|
||||
if ($bSelected) {
|
||||
$this->aSelectedModules[$sModuleId] = true;
|
||||
} else {
|
||||
$this->aUnSelectedModules[$sModuleId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$aAlternatives = $aChoiceInfo["alternatives"] ?? null;
|
||||
if (!is_null($aAlternatives) && is_array($aAlternatives)) {
|
||||
foreach ($aAlternatives as $aSubChoiceInfo) {
|
||||
if ($bSelected) {
|
||||
$this->ProcessSelectedChoice($aSubChoiceInfo, $bAllChecked);
|
||||
} else {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('sub_options', $aChoiceInfo)) {
|
||||
if (array_key_exists('options', $aChoiceInfo['sub_options'])) {
|
||||
$aSubOptions = $aChoiceInfo['sub_options']['options'];
|
||||
if (!is_null($aSubOptions) && is_array($aSubOptions)) {
|
||||
foreach ($aSubOptions as $aSubChoiceInfo) {
|
||||
if ($bSelected) {
|
||||
$this->ProcessSelectedChoice($aSubChoiceInfo, $bAllChecked);
|
||||
} else {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (array_key_exists('alternatives', $aChoiceInfo['sub_options'])) {
|
||||
$aSubAlternatives = $aChoiceInfo['sub_options']['alternatives'];
|
||||
if (!is_null($aSubAlternatives) && is_array($aSubAlternatives)) {
|
||||
foreach ($aSubAlternatives as $aSubChoiceInfo) {
|
||||
if ($bSelected) {
|
||||
$this->ProcessSelectedChoice($aSubChoiceInfo, false);
|
||||
} else {
|
||||
$this->ProcessUnSelectedChoice($aSubChoiceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function GetExtraDirs() : array {
|
||||
$aSearchDirs = [];
|
||||
|
||||
$aDirs = [
|
||||
'/datamodels/1.x',
|
||||
'/datamodels/2.x',
|
||||
'data/' . $this->sTargetEnvironment . '-modules',
|
||||
'extensions',
|
||||
];
|
||||
foreach ($aDirs as $sRelativeDir){
|
||||
$sDirPath = APPROOT.$sRelativeDir;
|
||||
if (is_dir($sDirPath))
|
||||
{
|
||||
$aSearchDirs[] = $sDirPath;
|
||||
}
|
||||
}
|
||||
|
||||
return $aSearchDirs;
|
||||
}
|
||||
|
||||
public function ProcessDefaultModules() : void {
|
||||
$sProductionModuleDir = APPROOT.'data/' . $this->sTargetEnvironment . '-modules/';
|
||||
|
||||
$aAvailableModules = $this->GetProductionEnv()->AnalyzeInstallation(MetaModel::GetConfig(), $this->GetExtraDirs(), false, null);
|
||||
|
||||
$this->aAutoSelectModules = [];
|
||||
foreach ($aAvailableModules as $sModuleId => $aModule) {
|
||||
if (($sModuleId != ROOT_MODULE)) {
|
||||
if (isset($aModule['auto_select'])) {
|
||||
$this->aAutoSelectModules[$sModuleId] = $aModule;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($aModule['category'] == 'authentication') || (!$aModule['visible'])) {
|
||||
$this->aSelectedModules[$sModuleId] = true;
|
||||
continue;
|
||||
}
|
||||
$bIsExtra = (array_key_exists('root_dir', $aModule) && (strpos($aModule['root_dir'],
|
||||
$sProductionModuleDir) !== false)); // Some modules (root, datamodel) have no 'root_dir'
|
||||
if ($bIsExtra) {
|
||||
// Modules in data/production-modules/ are considered as mandatory and always installed
|
||||
$this->aSelectedModules[$sModuleId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function ProcessAutoSelectModules() : void {
|
||||
foreach($this->GetAutoSelectModules() as $sModuleId => $aModule)
|
||||
{
|
||||
try {
|
||||
$bSelected = false;
|
||||
SetupInfo::SetSelectedModules($this->aSelectedModules);
|
||||
eval('$bSelected = ('.$aModule['auto_select'].');');
|
||||
if ($bSelected)
|
||||
{
|
||||
// Modules in data/production-modules/ are considered as mandatory and always installed
|
||||
$this->aSelectedModules[$sModuleId] = true;
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function CanChooseUnpackageExtension(iTopExtension $oExtension) : bool {
|
||||
if ($oExtension->sSource === iTopExtension::SOURCE_REMOTE){
|
||||
SetupLog::Info("Data Extension can be selected", null, ['extension' => $oExtension->sCode]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$bSelectable = $this->bInstallationOptionalChoicesChecked && ($oExtension->sSource === iTopExtension::SOURCE_MANUAL);
|
||||
if ($bSelectable){
|
||||
SetupLog::Info("Manual Extension can be selected", null, ['extension' => $oExtension->sCode]);
|
||||
} else {
|
||||
SetupLog::Debug("Manual Extension can NOT be selected", null, ['extension' => $oExtension->sCode]);
|
||||
}
|
||||
|
||||
return $bSelectable;
|
||||
}
|
||||
|
||||
public function ProcessExtensionModulesNotSpecifiedInChoices() {
|
||||
/** @var \iTopExtension $oExtension */
|
||||
foreach($this->GetItopExtensionsMap()->GetAllExtensions() as $oExtension) {
|
||||
if (in_array($oExtension->sCode, $this->aAfterComputationSelectedExtensions)){
|
||||
//extension already processed in installation.xml
|
||||
SetupLog::Info("Extension already processed via installation choices", null,
|
||||
[
|
||||
'extension' => $oExtension->sCode,
|
||||
]) ;
|
||||
continue;
|
||||
}
|
||||
if ($this->CanChooseUnpackageExtension($oExtension)){
|
||||
if (($oExtension->bVisible) && (count($oExtension->aMissingDependencies) === 0)) {
|
||||
$aCurrentModules = [];
|
||||
$aUnselectableModules = [];
|
||||
$bIsExtensionSelectable = true;
|
||||
foreach ($oExtension->aModules as $sModuleId) {
|
||||
if (array_key_exists($sModuleId, $this->aSelectedModules)) {
|
||||
//already selected
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($sModuleId, $this->aUnSelectedModules)) {
|
||||
$aUnselectableModules[] = $sModuleId;
|
||||
|
||||
//already unselected
|
||||
$bIsExtensionSelectable = false;
|
||||
} else {
|
||||
$aCurrentModules[$sModuleId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bIsExtensionSelectable) {
|
||||
SetupLog::Debug("Add modules from unpackaged extension", null,
|
||||
[
|
||||
'extension' => $oExtension->sCode,
|
||||
'source' => $oExtension->sSource,
|
||||
'modules to add' => array_keys($aCurrentModules),
|
||||
]);
|
||||
$this->aSelectedModules = array_merge($this->aSelectedModules, $aCurrentModules);
|
||||
$this->aAfterComputationSelectedExtensions[] = $oExtension->sCode;
|
||||
} else {
|
||||
SetupLog::Warning("Unpackaged extension can not be selected due to modules incompatible with installation choices",
|
||||
null,
|
||||
[
|
||||
'extension' => $oExtension->sCode,
|
||||
'source' => $oExtension->sSource,
|
||||
'modules' => array_keys($aCurrentModules),
|
||||
'unselectable modules' => $aUnselectableModules,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
setup/unattended-install/README.md
Normal file
25
setup/unattended-install/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Unattended-install
|
||||
|
||||
This script allows to install and update iTop via CLI.
|
||||
|
||||
For more information, see the official Wiki : [Automated installation [iTop Documentation]](https://www.itophub.io/wiki/page?id=latest:advancedtopics:automatic_install)
|
||||
|
||||
|
||||
#install-itop.sh
|
||||
You can install your iTop by only using config-itop.php settings and run either
|
||||
|
||||
- a non-ITIL iTop fresh installation (use itil-fresh-install.xml to have ITIL modules instead)
|
||||
```
|
||||
./install-itop.sh ./xml_setup/fresh-install.xml
|
||||
```
|
||||
|
||||
- a non-ITIL iTop upgrade (use itil-upgrade.xml to have ITIL modules instead)
|
||||
```
|
||||
./install-itop.sh ./xml_setup/upgrade.xml
|
||||
```
|
||||
|
||||
- a specific iTop installation by providing both xml setup file
|
||||
in below example file provided is the one generated by iTop during last setup.
|
||||
```
|
||||
./install-itop.sh ../../log/install-2024-04-03.xml
|
||||
```
|
||||
50
setup/unattended-install/install-itop.sh
Normal file
50
setup/unattended-install/install-itop.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#! /bin/bash
|
||||
|
||||
CLI_NAME=$(basename $0)
|
||||
DIR=$(dirname $0)
|
||||
ITOP_DIR="$DIR/../.."
|
||||
|
||||
HELP="Syntax: $CLI_NAME XML_SETUP [INSTALLATION_XML]"
|
||||
|
||||
function HELP {
|
||||
echo $HELP
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -lt 1 ]
|
||||
then
|
||||
echo "Missing parameters passed."
|
||||
HELP
|
||||
fi
|
||||
|
||||
if [ $# -gt 2 ]
|
||||
then
|
||||
echo "Too much parameters passed ($#) : $*."
|
||||
HELP
|
||||
fi
|
||||
|
||||
XML_SETUP=$1
|
||||
if [ ! -f $XML_SETUP ]
|
||||
then
|
||||
echo "XML_SETUP file ($XML_SETUP) not found."
|
||||
HELP
|
||||
fi
|
||||
|
||||
if [ $# -eq 2 ]
|
||||
then
|
||||
INSTALLATION_XML=$2
|
||||
if [ ! -f $INSTALLATION_XML ]
|
||||
then
|
||||
echo "INSTALLATION_XML file ($INSTALLATION_XML) not found."
|
||||
HELP
|
||||
fi
|
||||
else
|
||||
INSTALLATION_XML="$ITOP_DIR/datamodels/2.x/installation.xml"
|
||||
fi
|
||||
|
||||
echo "$CLI_NAME: Using XML_SETUP ($XML_SETUP) and INSTALLATION_XML ($INSTALLATION_XML) files during unattended itop installation."
|
||||
|
||||
rm -rf $ITOP_DIR/data/.maintenance;
|
||||
echo php $DIR/unattended-install.php --use_itop_config --installation_xml="$INSTALLATION_XML" --param-file="$XML_SETUP"
|
||||
|
||||
php $DIR/unattended-install.php --use_itop_config --installation_xml="$INSTALLATION_XML" --param-file="$XML_SETUP"
|
||||
406
setup/unattended-install/unattended-install.php
Normal file
406
setup/unattended-install/unattended-install.php
Normal file
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__, 3) . '/approot.inc.php');
|
||||
require_once(__DIR__ . '/InstallationFileService.php');
|
||||
|
||||
function PrintUsageAndExit()
|
||||
{
|
||||
echo <<<EOF
|
||||
Usage: php unattended-install.php --param-file=<path_to_response_file> [--installation_xml=<path_to_installation_xml>] [--use_itop_config]
|
||||
|
||||
Options:
|
||||
--param-file=<path_to_response_file> Path to the file (XML) to use for the unattended installation. That file (generated by the setup into log directory) must contain the following sections:
|
||||
- target_env: the target environment (production, test, dev)
|
||||
- database: the database settings (server, user, pwd, name, prefix)
|
||||
- selected_modules: the list of modules to install
|
||||
--response_file DEPRECATED: use `--param-file` instead
|
||||
--installation_xml=<path_to_installation_xml> Use an installation.xml file to compute the modules to install depending on the selected extensions listed in the param file
|
||||
--use_itop_config Use the iTop configuration file to get the database settings, otherwise use the database settings from the parameters file
|
||||
|
||||
Advanced options:
|
||||
--check-consistency=1 Check the data model consistency after the installation (default: 0)
|
||||
--clean=1 In case of a first installation, cleanup the environment before proceeding: delete the configuration file, the cache directory, the target directory, the database (default: 0)
|
||||
--install=0 Set to 0 to perform a dry-run (default: 1)
|
||||
EOF;
|
||||
exit(-1);
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
if ($sCleanName !== 'cli')
|
||||
{
|
||||
echo "Mode CLI only";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (in_array('--help', $argv)) {
|
||||
PrintUsageAndExit();
|
||||
}
|
||||
|
||||
$sParamFile = utils::ReadParam('param-file', null, true /* CLI allowed */, 'raw_data') ?? utils::ReadParam('response_file', null, true /* CLI allowed */, 'raw_data');
|
||||
if (is_null($sParamFile)) {
|
||||
echo "Missing mandatory argument `--param-file`.\n";
|
||||
PrintUsageAndExit();
|
||||
}
|
||||
$bCheckConsistency = (utils::ReadParam('check-consistency', '0', true /* CLI allowed */) == '1');
|
||||
|
||||
if (false === file_exists($sParamFile)) {
|
||||
echo "Param file `$sParamFile` doesn't exist! Exiting...\n";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
$oParams = new XMLParameters($sParamFile);
|
||||
|
||||
$sMode = $oParams->Get('mode');
|
||||
|
||||
$sTargetEnvironment = $oParams->Get('target_env', '');
|
||||
if ($sTargetEnvironment == '')
|
||||
{
|
||||
$sTargetEnvironment = 'production';
|
||||
}
|
||||
|
||||
$sXmlSetupBaseName = basename($sParamFile);
|
||||
$sInstallationXmlPath = utils::ReadParam('installation_xml', null, true /* CLI allowed */, 'raw_data');
|
||||
if (! is_null($sInstallationXmlPath) && is_file($sInstallationXmlPath)) {
|
||||
$sInstallationBaseName = basename($sInstallationXmlPath);
|
||||
|
||||
$aSelectedExtensionsFromXmlSetup = $oParams->Get('selected_extensions', []);
|
||||
$bInstallationChoicesProvided = count($aSelectedExtensionsFromXmlSetup) !== 0;
|
||||
if ($bInstallationChoicesProvided) {
|
||||
$sMsg = "Modules to install computed based on $sInstallationBaseName file and installation choices (listed in section `selected_extensions` of $sXmlSetupBaseName file)";
|
||||
echo "$sMsg:\n".implode(',', $aSelectedExtensionsFromXmlSetup)."\n\n";
|
||||
SetupLog::Info($sMsg, null, $aSelectedExtensionsFromXmlSetup);
|
||||
} else {
|
||||
$sMsg = "Modules to install computed based on default installation choices inside $sInstallationBaseName (no choice specified in section `selected_extensions` of $sXmlSetupBaseName file).";
|
||||
echo "$sMsg\n\n";
|
||||
SetupLog::Info($sMsg);
|
||||
}
|
||||
|
||||
$oInstallationFileService = new InstallationFileService($sInstallationXmlPath, $sTargetEnvironment, $aSelectedExtensionsFromXmlSetup);
|
||||
$oInstallationFileService->Init();
|
||||
|
||||
$aComputedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions();
|
||||
if (! $bInstallationChoicesProvided) {
|
||||
$sMsg = "Computed installation choices";
|
||||
echo "$sMsg:\n".implode(',', $aComputedExtensions)."\n\n";
|
||||
SetupLog::Info($sMsg, null, $aComputedExtensions);
|
||||
$oParams->Set('selected_extensions', $aComputedExtensions);
|
||||
}
|
||||
|
||||
$aComputedModules = $oInstallationFileService->GetSelectedModules();
|
||||
$aSelectedModules = array_keys($aComputedModules);
|
||||
$oParams->Set('selected_modules', $aSelectedModules);
|
||||
|
||||
$sMsg = "Computed modules to install";
|
||||
} else {
|
||||
$aSelectedModules = $oParams->Get('selected_modules', []);
|
||||
$sMsg = "Modules to install listed in $sXmlSetupBaseName (selected_modules section)";
|
||||
}
|
||||
|
||||
sort($aSelectedModules);
|
||||
echo "$sMsg:\n".implode(',', $aSelectedModules)."\n\n";
|
||||
SetupLog::Info($sMsg, null, $aSelectedModules);
|
||||
|
||||
// Configuration file
|
||||
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
$bUseItopConfig = in_array('--use_itop_config', $argv);
|
||||
if ($bUseItopConfig && file_exists($sConfigFile)){
|
||||
//unattended run based on db settings coming from itop configuration
|
||||
copy($sConfigFile, "$sConfigFile.backup");
|
||||
|
||||
$oConfig = new Config($sConfigFile);
|
||||
$aDBXmlSettings = $oParams->Get('database', array());
|
||||
$aDBXmlSettings ['server'] = $oConfig->Get('db_host');
|
||||
$aDBXmlSettings ['user'] = $oConfig->Get('db_user');
|
||||
$aDBXmlSettings ['pwd'] = $oConfig->Get('db_pwd');
|
||||
$aDBXmlSettings ['name'] = $oConfig->Get('db_name');
|
||||
$aDBXmlSettings ['prefix'] = $oConfig->Get('db_subname');
|
||||
$aDBXmlSettings ['db_tls_enabled'] = $oConfig->Get('db_tls.enabled');
|
||||
//cannot be null or infinite loop triggered!
|
||||
$aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? "";
|
||||
$oParams->Set('database', $aDBXmlSettings);
|
||||
|
||||
$aFields = [
|
||||
'url' => 'app_root_url',
|
||||
'source_dir' => 'source_dir',
|
||||
'graphviz_path' => 'graphviz_path',
|
||||
];
|
||||
foreach($aFields as $sSetupField => $sConfField){
|
||||
$oParams->Set($sSetupField, $oConfig->Get($sConfField));
|
||||
}
|
||||
|
||||
$oParams->Set('mysql_bindir', $oConfig->GetModuleSetting('itop-backup', 'mysql_bindir', ""));
|
||||
$oParams->Set('language', $oConfig->GetDefaultLanguage());
|
||||
} else {
|
||||
//unattended run based on db settings coming from response_file (XML file)
|
||||
$aDBXmlSettings = $oParams->Get('database', array());
|
||||
}
|
||||
|
||||
$sDBServer = $aDBXmlSettings['server'];
|
||||
$sDBUser = $aDBXmlSettings['user'];
|
||||
$sDBPwd = $aDBXmlSettings['pwd'];
|
||||
$sDBName = $aDBXmlSettings['name'];
|
||||
$sDBPrefix = $aDBXmlSettings['prefix'];
|
||||
|
||||
if ($sMode == 'install')
|
||||
{
|
||||
echo "Installation mode detected.\n";
|
||||
|
||||
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
|
||||
if ($bClean)
|
||||
{
|
||||
echo "Cleanup mode detected.\n";
|
||||
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
|
||||
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
|
||||
unlink($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No config file to delete ($sConfigFile does not exist).\n";
|
||||
}
|
||||
|
||||
// Starting with iTop 2.7.0, a failed setup leaves some lock files, let's remove them
|
||||
$aLockFiles = array(
|
||||
'data/.readonly' => 'read-only lock file',
|
||||
'data/.maintenance' => 'maintenance mode lock file',
|
||||
);
|
||||
foreach($aLockFiles as $sFile => $sDescription)
|
||||
{
|
||||
$sLockFile = APPROOT.$sFile;
|
||||
if (file_exists($sLockFile))
|
||||
{
|
||||
echo "Trying to delete the $sDescription: '$sLockFile'.\n";
|
||||
unlink($sLockFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Starting with iTop 2.6.0, let's remove the cache directory as well
|
||||
// Can cause some strange issues in the setup (apparently due to the Dict class being automatically loaded ??)
|
||||
$sCacheDir = APPROOT.'data/cache-'.$sTargetEnvironment;
|
||||
if (file_exists($sCacheDir))
|
||||
{
|
||||
if (is_dir($sCacheDir))
|
||||
{
|
||||
echo "Emptying the cache directory '$sCacheDir'.\n";
|
||||
SetupUtils::tidydir($sCacheDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("ERROR the cache directory '$sCacheDir' exists, but is NOT a directory !!!\nExiting.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// env-xxx directory
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
|
||||
if (file_exists($sTargetDir))
|
||||
{
|
||||
if (is_dir($sTargetDir))
|
||||
{
|
||||
echo "Emptying the target directory '$sTargetDir'.\n";
|
||||
SetupUtils::tidydir($sTargetDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No target directory to delete ($sTargetDir does not exist).\n";
|
||||
}
|
||||
|
||||
if ($sDBPrefix != '')
|
||||
{
|
||||
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
|
||||
}
|
||||
|
||||
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
|
||||
if ($oMysqli->connect_errno)
|
||||
{
|
||||
die("Cannot connect to the MySQL server (".$oMysqli->connect_errno . ") ".$oMysqli->connect_error."\nExiting");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oMysqli->select_db($sDBName))
|
||||
{
|
||||
echo "Deleting database '$sDBName'\n";
|
||||
$oMysqli->query("DROP DATABASE `$sDBName`");
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//use settings from itop conf
|
||||
$sTargetEnvironment = $oParams->Get('target_env', '');
|
||||
if ($sTargetEnvironment == '')
|
||||
{
|
||||
$sTargetEnvironment = 'production';
|
||||
}
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
|
||||
}
|
||||
|
||||
$bHasErrors = false;
|
||||
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
|
||||
|
||||
$aSelectedModules = $oParams->Get('selected_modules');
|
||||
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
|
||||
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
|
||||
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
|
||||
|
||||
foreach($aChecks as $oCheckResult)
|
||||
{
|
||||
switch ($oCheckResult->iSeverity)
|
||||
{
|
||||
case CheckResult::ERROR:
|
||||
$bHasErrors = true;
|
||||
$sHeader = "Error";
|
||||
break;
|
||||
|
||||
case CheckResult::WARNING:
|
||||
$sHeader = "Warning";
|
||||
break;
|
||||
|
||||
case 3: // CheckResult::TRACE added in iTop 3.0.0
|
||||
// does nothing : those are old debug traces, see N°2214
|
||||
$sHeader = 'Trace';
|
||||
break;
|
||||
|
||||
case CheckResult::INFO:
|
||||
default:
|
||||
$sHeader = "Info";
|
||||
break;
|
||||
}
|
||||
echo $sHeader.": ".$oCheckResult->sLabel;
|
||||
if (strlen($oCheckResult->sDescription))
|
||||
{
|
||||
echo ' - '.$oCheckResult->sDescription;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if ($bHasErrors)
|
||||
{
|
||||
echo "Encountered stopper issues. Aborting...\n";
|
||||
$sLogMsg = "Encountered stopper issues. Aborting...";
|
||||
echo "$sLogMsg\n";
|
||||
SetupLog::Error($sLogMsg);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
$bFoundIssues = false;
|
||||
|
||||
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
|
||||
if ($bInstall)
|
||||
{
|
||||
echo "Starting the unattended installation...\n";
|
||||
$oWizard = new ApplicationInstaller($oParams);
|
||||
$bRes = $oWizard->ExecuteAllSteps();
|
||||
if (!$bRes)
|
||||
{
|
||||
echo "\nencountered installation issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
|
||||
if (!$oMysqli->connect_errno)
|
||||
{
|
||||
if ($oMysqli->select_db($sDBName))
|
||||
{
|
||||
// Check the presence of a table to record information about the MTP (from the Designer)
|
||||
$sDesignerUpdatesTable = $sDBPrefix.'priv_designer_update';
|
||||
$sSQL = "SELECT id FROM `$sDesignerUpdatesTable`";
|
||||
if ($oMysqli->query($sSQL) !== false)
|
||||
{
|
||||
// Record the Designer Udpates in the priv_designer_update table
|
||||
$sDeltaFile = APPROOT.'data/'.$sTargetEnvironment.'.delta.xml';
|
||||
if (is_readable($sDeltaFile))
|
||||
{
|
||||
// Retrieve the revision
|
||||
$oDoc = new DOMDocument();
|
||||
$oDoc->load($sDeltaFile);
|
||||
$iRevision = 0;
|
||||
$iRevision = $oDoc->firstChild->getAttribute('revision_id');
|
||||
if ($iRevision > 0) // Safety net, just in case...
|
||||
{
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
$sSQL = "INSERT INTO `$sDesignerUpdatesTable` (revision_id, compilation_date, comment) VALUES ($iRevision, '$sDate', 'Deployed using unattended.php.')";
|
||||
if ($oMysqli->query($sSQL) !== false)
|
||||
{
|
||||
echo "\nDesigner update (MTP at revision $iRevision) successfully recorded.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "\nFailed to record designer updates(".$oMysqli->error.").\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "\nFailed to read the revision from $sDeltaFile file. No designer update information will be recorded.\n";
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "\nNo $sDeltaFile file (or the file is not accessible). No designer update information to record.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No installation requested.\n";
|
||||
}
|
||||
if (!$bFoundIssues && $bCheckConsistency)
|
||||
{
|
||||
echo "Checking data model consistency.\n";
|
||||
ob_start();
|
||||
$sCheckRes = '';
|
||||
try
|
||||
{
|
||||
MetaModel::CheckDefinitions(false);
|
||||
$sCheckRes = ob_get_clean();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
|
||||
}
|
||||
if (strlen($sCheckRes) > 0)
|
||||
{
|
||||
echo $sCheckRes;
|
||||
echo "\nfound consistency issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $bFoundIssues)
|
||||
{
|
||||
// last line: used to check the install
|
||||
// the only way to track issues in case of Fatal error or even parsing error!
|
||||
$sLogMsg = "installed!";
|
||||
|
||||
if ($bUseItopConfig && is_file("$sConfigFile.backup"))
|
||||
{
|
||||
echo "\nuse config file provided by backup in $sConfigFile.";
|
||||
copy("$sConfigFile.backup", $sConfigFile);
|
||||
}
|
||||
|
||||
SetupLog::Info($sLogMsg);
|
||||
echo "\n$sLogMsg";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$sLogMsg = "installation failed!";
|
||||
SetupLog::Error($sLogMsg);
|
||||
echo "\n$sLogMsg\n";
|
||||
exit(-1);
|
||||
13
setup/unattended-install/web.config
Normal file
13
setup/unattended-install/web.config
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
|
||||
</requestFiltering>
|
||||
<authorization>
|
||||
<deny users="*" /> <!-- Denies all users -->
|
||||
</authorization>
|
||||
</security>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
41
setup/unattended-install/xml_setup/fresh-install.xml
Normal file
41
setup/unattended-install/xml_setup/fresh-install.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>install</mode>
|
||||
<preinstall>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.7.0</datamodel_version>
|
||||
<previous_configuration_file>/var/www/html/iTop/conf/production/config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server></server>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<name></name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url></url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<language></language>
|
||||
</admin_account>
|
||||
<language></language>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options type="array"/>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
</installation>
|
||||
53
setup/unattended-install/xml_setup/itil-fresh-install.xml
Normal file
53
setup/unattended-install/xml_setup/itil-fresh-install.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>install</mode>
|
||||
<preinstall>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.7.0</datamodel_version>
|
||||
<previous_configuration_file>/var/www/html/iTop/conf/production/config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server></server>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<name></name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url></url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<language></language>
|
||||
</admin_account>
|
||||
<language></language>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options type="array"/>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
<item>itop-config-mgmt-datacenter</item>
|
||||
<item>itop-config-mgmt-end-user</item>
|
||||
<item>itop-config-mgmt-storage</item>
|
||||
<item>itop-config-mgmt-virtualization</item>
|
||||
<item>itop-service-mgmt-enterprise</item>
|
||||
<item>itop-ticket-mgmt-itil</item>
|
||||
<item>itop-ticket-mgmt-itil-user-request</item>
|
||||
<item>itop-ticket-mgmt-itil-incident</item>
|
||||
<item>itop-ticket-mgmt-itil-enhanced-portal</item>
|
||||
<item>itop-change-mgmt-itil</item>
|
||||
<item>itop-config-mgmt-core</item>
|
||||
<item>itop-kown-error-mgmt</item>
|
||||
</selected_extensions>
|
||||
</installation>
|
||||
53
setup/unattended-install/xml_setup/itil-upgrade.xml
Normal file
53
setup/unattended-install/xml_setup/itil-upgrade.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>upgrade</mode>
|
||||
<preinstall>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.7.0</datamodel_version>
|
||||
<previous_configuration_file>/var/www/html/iTop/conf/production/config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server></server>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<name></name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url></url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<language></language>
|
||||
</admin_account>
|
||||
<language></language>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options type="array"/>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
<item>itop-config-mgmt-datacenter</item>
|
||||
<item>itop-config-mgmt-end-user</item>
|
||||
<item>itop-config-mgmt-storage</item>
|
||||
<item>itop-config-mgmt-virtualization</item>
|
||||
<item>itop-service-mgmt-enterprise</item>
|
||||
<item>itop-ticket-mgmt-itil</item>
|
||||
<item>itop-ticket-mgmt-itil-user-request</item>
|
||||
<item>itop-ticket-mgmt-itil-incident</item>
|
||||
<item>itop-ticket-mgmt-itil-enhanced-portal</item>
|
||||
<item>itop-change-mgmt-itil</item>
|
||||
<item>itop-config-mgmt-core</item>
|
||||
<item>itop-kown-error-mgmt</item>
|
||||
</selected_extensions>
|
||||
</installation>
|
||||
41
setup/unattended-install/xml_setup/upgrade.xml
Normal file
41
setup/unattended-install/xml_setup/upgrade.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>upgrade</mode>
|
||||
<preinstall>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.7.0</datamodel_version>
|
||||
<previous_configuration_file>/var/www/html/iTop/conf/production/config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server></server>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<name></name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url></url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user></user>
|
||||
<pwd></pwd>
|
||||
<language></language>
|
||||
</admin_account>
|
||||
<language></language>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options type="array"/>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
<selected_modules type="array">
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
</selected_extensions>
|
||||
</installation>
|
||||
@@ -2607,6 +2607,11 @@ class WizStepDone extends WizardStep
|
||||
$oProductionEnv->InitDataModel($oConfig, true);
|
||||
$sIframeUrl = $oConfig->GetModuleSetting('itop-hub-connector', 'setup_url', '');
|
||||
|
||||
$sSetupTokenFile = APPROOT.'data/.setup';
|
||||
$sSetupToken = bin2hex(random_bytes(12));
|
||||
file_put_contents($sSetupTokenFile, $sSetupToken);
|
||||
$sIframeUrl.= "&setup_token=$sSetupToken";
|
||||
|
||||
if ($sIframeUrl != '')
|
||||
{
|
||||
$oPage->add('<iframe id="fresh_content" style="border:0; width:100%; display:none;" src="'.$sIframeUrl.'"></iframe>');
|
||||
|
||||
@@ -20,8 +20,9 @@ class OAuthClientProviderAzure extends OAuthClientProviderAbstract
|
||||
'clientId' => $oOAuthClient->Get('client_id'),
|
||||
'clientSecret' => $oOAuthClient->Get('client_secret'),
|
||||
'redirectUri' => $oOAuthClient->Get('redirect_url'),
|
||||
'tenant' => $oOAuthClient->Get('tenant'),
|
||||
];
|
||||
|
||||
$this->oVendorProvider = new Azure($aOptions, $collaborators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace Combodo\iTop\Form;
|
||||
|
||||
use Combodo\iTop\Renderer\FormRenderer;
|
||||
use CoreException;
|
||||
|
||||
/**
|
||||
* Description of formmanager
|
||||
@@ -59,6 +60,12 @@ abstract class FormManager
|
||||
$oFormManager = new static();
|
||||
|
||||
$sFormRendererClass = $aJson['formrenderer_class'];
|
||||
// N°7455 - Ensure form renderer class extends FormRenderer
|
||||
if (false === is_a($sFormRendererClass, FormRenderer::class, true))
|
||||
{
|
||||
throw new CoreException('Form renderer class must extend '.FormRenderer::class);
|
||||
}
|
||||
|
||||
/** @var \Combodo\iTop\Renderer\FormRenderer $oFormRenderer */
|
||||
$oFormRenderer = new $sFormRendererClass();
|
||||
$oFormRenderer->SetEndpoint($aJson['formrenderer_endpoint']);
|
||||
|
||||
@@ -2077,7 +2077,7 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
'class_category' => '',
|
||||
'more_values' => '',
|
||||
'sql' => 'dest_class',
|
||||
'default_value' => 'Organization',
|
||||
'default_value' => '',
|
||||
'is_null_allowed' => true,
|
||||
'depends_on' => array(),
|
||||
)));
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div id="login-content">
|
||||
<h1>{{ 'UI:ResetPwd-Title'|dict_s }}</h1>
|
||||
{% if bNoUser %}
|
||||
<p>{{ 'UI:ResetPwd-Error-WrongLogin'|dict_format(sAuthUser) }}</p>
|
||||
<p>{{ 'UI:ResetPwd-EmailSent'|dict_s }}</p>
|
||||
{% elseif bBadToken %}
|
||||
<p>{{ 'UI:ResetPwd-Error-InvalidToken'|dict_s }}</p>
|
||||
{% else %}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div id="login-title">
|
||||
<h1>{{ 'UI:ResetPwd-Title'|dict_s }}</h1>
|
||||
{% if bNoUser and sErrorMessage is null %}
|
||||
<p>{{ 'UI:ResetPwd-Error-WrongLogin'|dict_format(sAuthUser) }}</p>
|
||||
<p>{{ 'UI:ResetPwd-EmailSent'|dict_s }}</p>
|
||||
{% elseif bBadToken and sErrorMessage is null %}
|
||||
<p>{{ 'UI:ResetPwd-Error-InvalidToken'|dict_s }}</p>
|
||||
{% else %}
|
||||
|
||||
@@ -5,7 +5,7 @@ php_version=7.2-apache
|
||||
db_version=5.7
|
||||
|
||||
[itop]
|
||||
itop_setup=tests/setup_params/default-params.xml
|
||||
;itop_setup=tests/setup_params/default-params.xml
|
||||
itop_backup=tests/backups/backup-itop.tar.gz
|
||||
|
||||
[phpunit]
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
"sempro/phpunit-pretty-print": "^1.4"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"unitary-tests/"
|
||||
],
|
||||
"psr-4": {
|
||||
"Combodo\\iTop\\Test\\UnitTest\\": "src/BaseTestCase/",
|
||||
"Combodo\\iTop\\Test\\UnitTest\\Hook\\": "src/Hook/",
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
printerClass="Sempro\PHPUnitPrettyPrinter\PrettyPrinter"
|
||||
>
|
||||
|
||||
<extensions>
|
||||
<extension class="Combodo\iTop\Test\UnitTest\Hook\TestsRunStartHook" />
|
||||
</extensions>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL"/>
|
||||
<ini name="display_errors" value="On"/>
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
printerClass="Sempro\PHPUnitPrettyPrinter\PrettyPrinter"
|
||||
>
|
||||
|
||||
<extensions>
|
||||
<extension class="Combodo\iTop\Test\UnitTest\Hook\TestsRunStartHook" />
|
||||
</extensions>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL"/>
|
||||
<ini name="display_errors" value="On"/>
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
printerClass="Sempro\PHPUnitPrettyPrinter\PrettyPrinter"
|
||||
>
|
||||
|
||||
<extensions>
|
||||
<extension class="Combodo\iTop\Test\UnitTest\Hook\TestsRunStartHook" />
|
||||
</extensions>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL"/>
|
||||
<ini name="display_errors" value="On"/>
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
namespace Combodo\iTop\Test\UnitTest;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\Hook\TestsRunStartHook;
|
||||
use Combodo\iTop\Test\UnitTest\Service\UnitTestRunTimeEnvironment;
|
||||
use Config;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
@@ -31,9 +29,9 @@ use utils;
|
||||
abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @var bool[]
|
||||
*/
|
||||
protected static $aReadyCustomEnvironments = [];
|
||||
* @var UnitTestRunTimeEnvironment
|
||||
*/
|
||||
protected $oEnvironment = null;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
@@ -51,11 +49,19 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
$this->setRunClassInSeparateProcess(true);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @return string Abs path to the XML delta to use for the tests of that class
|
||||
*/
|
||||
abstract public function GetDatamodelDeltaAbsPath(): string;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
static::LoadRequiredItopFiles();
|
||||
$this->oEnvironment = new UnitTestRunTimeEnvironment('production', $this->GetTestEnvironment());
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -93,40 +99,16 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark {@see \Combodo\iTop\Test\UnitTest\ItopDataTestCase::GetTestEnvironment()} as ready (compiled)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function MarkEnvironmentReady(): void
|
||||
{
|
||||
if (false === $this->IsEnvironmentReady()) {
|
||||
touch(static::GetTestEnvironmentFolderAbsPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the {@see \Combodo\iTop\Test\UnitTest\ItopDataTestCase::GetTestEnvironment()} is ready (compiled, but not started)
|
||||
*
|
||||
* @details Having the environment ready means that it has been compiled for this global tests run, not that it is a relic from a previous global tests run
|
||||
* @return bool True if the {@see \Combodo\iTop\Test\UnitTest\ItopDataTestCase::GetTestEnvironment()} is ready (compiled, up-to-date, but not necessarily started)
|
||||
*/
|
||||
final protected function IsEnvironmentReady(): bool
|
||||
{
|
||||
// As these test cases run in separate processes, the best way we found to let know a process if its environment was already prepared for **this run** was to compare the modification times of:
|
||||
// - its own env-<ENV> folder
|
||||
// - a file generated at the beginning of the global test run {@see \Combodo\iTop\Test\UnitTest\Hook\TestsRunStartHook}
|
||||
$sRunStartedFilePath = TestsRunStartHook::GetRunStartedFileAbsPath();
|
||||
$sEnvFolderPath = static::GetTestEnvironmentFolderAbsPath();
|
||||
|
||||
clearstatcache();
|
||||
if (false === file_exists($sRunStartedFilePath) || false === file_exists($sEnvFolderPath)) {
|
||||
if (false === file_exists($this->GetTestEnvironmentFolderAbsPath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$iRunStartedFileModificationTime = filemtime($sRunStartedFilePath);
|
||||
$iEnvFolderModificationTime = filemtime($sEnvFolderPath);
|
||||
|
||||
return $iEnvFolderModificationTime >= $iRunStartedFileModificationTime;
|
||||
}
|
||||
return $this->oEnvironment->IsUpToDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
@@ -141,6 +123,12 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
// Note: To improve performances, we compile all XML deltas from test cases derived from this class and make a single environment where everything will be ran at once.
|
||||
// This requires XML deltas to be compatible, but it is a known and accepted trade-off. See PR #457
|
||||
if (false === $this->IsEnvironmentReady()) {
|
||||
|
||||
$this->debug("Preparing custom environment '$sTestEnv' with the following datamodel files:");
|
||||
foreach ($this->oEnvironment->GetCustomDatamodelFiles() as $sCustomDatamodelFile) {
|
||||
$this->debug(" - $sCustomDatamodelFile");
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Clear any previous "$sTestEnv" environment
|
||||
//----------------------------------------------------
|
||||
@@ -153,14 +141,6 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
SetupUtils::tidydir($sConfFolder);
|
||||
}
|
||||
|
||||
// - Datamodel delta files
|
||||
// - Cache folder
|
||||
// - Compiled folder
|
||||
// We don't need to clean them as they are already by the compilation
|
||||
|
||||
// - Drop database
|
||||
// We don't do that now, it will be done before re-creating the DB, once the metamodel is started
|
||||
|
||||
//----------------------------------------------------
|
||||
// Prepare "$sTestEnv" environment
|
||||
//----------------------------------------------------
|
||||
@@ -179,7 +159,7 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
$oTestConfig->Set('db_name', $oTestConfig->Get('db_name').'_'.$sTestEnvSanitizedForDBName);
|
||||
|
||||
// - Compile env. based on the existing 'production' env.
|
||||
$oEnvironment = new UnitTestRunTimeEnvironment($sTestEnv);
|
||||
$oEnvironment = new UnitTestRunTimeEnvironment($sSourceEnv, $sTestEnv);
|
||||
$oEnvironment->WriteConfigFileSafe($oTestConfig);
|
||||
$oEnvironment->CompileFrom($sSourceEnv, false);
|
||||
|
||||
@@ -190,9 +170,11 @@ abstract class ItopCustomDatamodelTestCase extends ItopDataTestCase
|
||||
}
|
||||
CMDBSource::CreateDB($oTestConfig->Get('db_name'));
|
||||
MetaModel::Startup($sConfFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sTestEnv);
|
||||
// N°7446 For some reason we need to create the DB schema before starting the MM, then only we can create the tables.
|
||||
// In 2.7, we can't call MetaModel::DBCreate() directly as the views creation will fail
|
||||
$this->InvokeNonPublicStaticMethod(MetaModel::class, 'DBCreateTables', []);
|
||||
|
||||
$this->MarkEnvironmentReady();
|
||||
$this->debug('Preparation of custom environment "'.$sTestEnv.'" done.');
|
||||
$this->debug("Custom environment '$sTestEnv' is ready!");
|
||||
}
|
||||
|
||||
parent::PrepareEnvironment();
|
||||
|
||||
@@ -28,20 +28,8 @@ abstract class ItopTestCase extends TestCase
|
||||
|
||||
protected static $aBackupStaticProperties = [];
|
||||
|
||||
/** @noinspection UsingInclusionOnceReturnValueInspection avoid errors for approot includes */
|
||||
protected function setUp(): void
|
||||
{
|
||||
$sAppRootRelPath = 'approot.inc.php';
|
||||
$sDepthSeparator = '../';
|
||||
for ($iDepth = 0; $iDepth < 8; $iDepth++) {
|
||||
if (file_exists($sAppRootRelPath)) {
|
||||
require_once $sAppRootRelPath;
|
||||
break;
|
||||
}
|
||||
|
||||
$sAppRootRelPath = $sDepthSeparator.$sAppRootRelPath;
|
||||
}
|
||||
|
||||
$this->LoadRequiredItopFiles();
|
||||
$this->LoadRequiredTestFiles();
|
||||
}
|
||||
@@ -68,8 +56,9 @@ abstract class ItopTestCase extends TestCase
|
||||
*/
|
||||
protected function LoadRequiredItopFiles(): void
|
||||
{
|
||||
// Empty until we actually need to require some files in the class
|
||||
}
|
||||
// At least make sure that the autoloader will be loaded, and that the APPROOT constant is defined
|
||||
require_once __DIR__.'/../../../../approot.inc.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload this method to require necessary files through {@see \Combodo\iTop\Test\UnitTest\ItopTestCase::RequireOnceUnitTestFile()}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Hook;
|
||||
|
||||
require_once __DIR__ . '/../../../../approot.inc.php';
|
||||
|
||||
use PHPUnit\Runner\AfterLastTestHook;
|
||||
use PHPUnit\Runner\BeforeFirstTestHook;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class TestsRunStartHook
|
||||
*
|
||||
* IMPORTANT: This will no longer work in PHPUnit 10.0 and there is no alternative for now, so we will have to migrate it when the time comes
|
||||
* @link https://localheinz.com/articles/2023/02/14/extending-phpunit-with-its-new-event-system/#content-hooks-event-system
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @package Combodo\iTop\Test\UnitTest\Hook
|
||||
* @since N°6097 2.7.10 3.0.4 3.1.1
|
||||
*/
|
||||
class TestsRunStartHook implements BeforeFirstTestHook, AfterLastTestHook
|
||||
{
|
||||
/**
|
||||
* Use the modification time on this file to check whereas it is newer than the requirements in a test case
|
||||
*
|
||||
* @return string Abs. path to a file generated when the global tests run starts.
|
||||
*/
|
||||
public static function GetRunStartedFileAbsPath(): string
|
||||
{
|
||||
// Note: This can't be put in the cache-<ENV> folder as we have multiple <ENV> running across the test cases
|
||||
// We also don't want to put it in the unit tests folder as it is not supposed to be writable
|
||||
return APPROOT.'data/.php-unit-tests-run-started';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function executeBeforeFirstTest(): void
|
||||
{
|
||||
// Create / change modification timestamp of file marking the beginning of the tests run
|
||||
touch(static::GetRunStartedFileAbsPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function executeAfterLastTest(): void
|
||||
{
|
||||
// Cleanup of file marking the beginning of the tests run
|
||||
if (file_exists(static::GetRunStartedFileAbsPath())) {
|
||||
unlink(static::GetRunStartedFileAbsPath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,9 +9,13 @@ namespace Combodo\iTop\Test\UnitTest\Service;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||
use IssueLog;
|
||||
use LogChannels;
|
||||
use MFCoreModule;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use ReflectionClass;
|
||||
use RunTimeEnvironment;
|
||||
use utils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,62 +29,140 @@ use RunTimeEnvironment;
|
||||
class UnitTestRunTimeEnvironment extends RunTimeEnvironment
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $aCustomDatamodelFiles = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sSourceEnv;
|
||||
|
||||
public function __construct($sSourceEnv, $sTargetEnv)
|
||||
{
|
||||
parent::__construct($sTargetEnv);
|
||||
$this->sSourceEnv = $sSourceEnv;
|
||||
}
|
||||
|
||||
public function GetEnvironment(): string
|
||||
{
|
||||
return $this->sFinalEnv;
|
||||
}
|
||||
|
||||
public function IsUpToDate()
|
||||
{
|
||||
clearstatcache();
|
||||
$fLastCompilationTime = filemtime(APPROOT.'env-'.$this->sFinalEnv);
|
||||
$aModifiedFiles = [];
|
||||
$this->FindFilesModifiedAfter($fLastCompilationTime, APPROOT.'datamodels/2.x', $aModifiedFiles);
|
||||
$this->FindFilesModifiedAfter($fLastCompilationTime, APPROOT.'extensions', $aModifiedFiles);
|
||||
$this->FindFilesModifiedAfter($fLastCompilationTime, APPROOT.'data/production-modules', $aModifiedFiles);
|
||||
foreach ($this->GetCustomDatamodelFiles() as $sCustomDatamodelFile) {
|
||||
if (filemtime($sCustomDatamodelFile) > $fLastCompilationTime) {
|
||||
$aModifiedFiles[] = $sCustomDatamodelFile;
|
||||
}
|
||||
}
|
||||
if (count($aModifiedFiles) > 0) {
|
||||
echo "The following files have been modified after the last compilation:\n";
|
||||
foreach ($aModifiedFiles as $sFile) {
|
||||
echo " - $sFile\n";
|
||||
}
|
||||
}
|
||||
return (count($aModifiedFiles) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir)
|
||||
{
|
||||
$aRet = parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
||||
|
||||
/** @var string[] $aDeltaFiles Referential of loaded deltas. Mostly to avoid duplicates. */
|
||||
$aDeltaFiles = [];
|
||||
foreach (get_declared_classes() as $sClass) {
|
||||
// Filter on classes derived from this \Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCaseItopCustomDatamodelTestCase
|
||||
if (false === is_a($sClass, ItopCustomDatamodelTestCase::class, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
$oReflectionMethod = $oReflectionClass->getMethod('GetDatamodelDeltaAbsPath');
|
||||
|
||||
// Filter on classes with an actual XML delta (eg. not \Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase and maybe some other deriving from a class with a delta)
|
||||
if ($oReflectionMethod->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase $oTestClassInstance */
|
||||
$oTestClassInstance = new $sClass();
|
||||
|
||||
// Check test class is for desired environment
|
||||
if ($oTestClassInstance->GetTestEnvironment() !== $this->sFinalEnv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check XML delta actually exists
|
||||
$sDeltaFile = $oTestClassInstance->GetDatamodelDeltaAbsPath();
|
||||
if (false === is_file($sDeltaFile)) {
|
||||
$this->fail("Could not prepare '$this->sFinalEnv' as the XML delta file '$sDeltaFile' (used in $sClass) does not seem to exist");
|
||||
}
|
||||
|
||||
// Avoid duplicates
|
||||
if (in_array($sDeltaFile, $aDeltaFiles)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prepare fake module name for delta
|
||||
$sDeltaName = preg_replace('/[^\d\w]/', '', $sDeltaFile);
|
||||
// Note: We can't use \MFDeltaModule as we can't specify the ID which leads to only 1 delta being applied... In the future we might introduce a new MFXXXModule, but in the meantime it feels alright (GLA / RQU)
|
||||
$oDelta = new MFCoreModule($sDeltaName, $sDeltaName, $sDeltaFile);
|
||||
|
||||
IssueLog::Debug('XML delta found for unit tests', static::class, [
|
||||
'Unit test class' => $sClass,
|
||||
'Delta file path' => $sDeltaFile,
|
||||
]);
|
||||
|
||||
$aDeltaFiles[] = $sDeltaFile;
|
||||
$aRet[$sDeltaName] = $oDelta;
|
||||
foreach ($this->GetCustomDatamodelFiles() as $sDeltaFile) {
|
||||
$sDeltaId = preg_replace('/[^\d\w]/', '', $sDeltaFile);
|
||||
$sDeltaName = basename($sDeltaFile);
|
||||
$sDeltaDir = dirname($sDeltaFile);
|
||||
$oDelta = new MFCoreModule($sDeltaName, "$sDeltaDir/$sDeltaName", $sDeltaFile);
|
||||
$aRet[$sDeltaId] = $oDelta;
|
||||
}
|
||||
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function GetCustomDatamodelFiles()
|
||||
{
|
||||
if (!is_null($this->aCustomDatamodelFiles)) {
|
||||
return $this->aCustomDatamodelFiles;
|
||||
}
|
||||
$this->aCustomDatamodelFiles = [];
|
||||
|
||||
// Search for the PHP files implementing the method GetDatamodelDeltaAbsPath
|
||||
// and extract the delta file path from the method
|
||||
foreach(['unitary-tests', 'integration-tests'] as $sTestDir) {
|
||||
// Iterate on all PHP files in subdirectories
|
||||
// Note: grep is not available on Windows, so we will use the PHP Reflection API
|
||||
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__."/../../$sTestDir")) as $oFile) {
|
||||
if ($oFile->isDir()){
|
||||
continue;
|
||||
}
|
||||
if (pathinfo($oFile->getFilename(), PATHINFO_EXTENSION) !== 'php') {
|
||||
continue;
|
||||
}
|
||||
$sFile = $oFile->getPathname();
|
||||
$sContent = file_get_contents($sFile);
|
||||
if (strpos($sContent, 'GetDatamodelDeltaAbsPath') === false) {
|
||||
continue;
|
||||
}
|
||||
$sClass = '';
|
||||
$aMatches = [];
|
||||
if (preg_match('/namespace\s+([^;]+);/', $sContent, $aMatches)) {
|
||||
$sNamespace = $aMatches[1];
|
||||
$sClass = $sNamespace.'\\'.basename($sFile, '.php');
|
||||
}
|
||||
if (preg_match('/\s+class\s+([^ ]+)\s+/', $sContent, $aMatches)) {
|
||||
$sClass = $sNamespace.'\\'.$aMatches[1];
|
||||
}
|
||||
if ($sClass === '') {
|
||||
continue;
|
||||
}
|
||||
require_once $sFile;
|
||||
$oReflectionClass = new ReflectionClass($sClass);
|
||||
if ($oReflectionClass->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
// Check if the class extends ItopCustomDatamodelTestCase
|
||||
if (!$oReflectionClass->isSubclassOf(ItopCustomDatamodelTestCase::class)) {
|
||||
continue;
|
||||
}
|
||||
/** @var \Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase $oTestClassInstance */
|
||||
$oTestClassInstance = new $sClass();
|
||||
if ($oTestClassInstance->GetTestEnvironment() !== $this->sFinalEnv) {
|
||||
continue;
|
||||
}
|
||||
$sDeltaFile = $oTestClassInstance->GetDatamodelDeltaAbsPath();
|
||||
if (!is_file($sDeltaFile)) {
|
||||
throw new \Exception("Unknown delta file: $sDeltaFile, from test class '$sClass'");
|
||||
}
|
||||
if (!in_array($sDeltaFile, $this->aCustomDatamodelFiles)) {
|
||||
$this->aCustomDatamodelFiles[] = $sDeltaFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->aCustomDatamodelFiles;
|
||||
}
|
||||
|
||||
private function FindFilesModifiedAfter(float $fReferenceTimestamp, string $sPathToScan, array &$aModifiedFiles)
|
||||
{
|
||||
if (!is_dir($sPathToScan)) {
|
||||
return;
|
||||
}
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sPathToScan)) as $oFile) {
|
||||
if ($oFile->isDir()) {
|
||||
continue;
|
||||
}
|
||||
if (filemtime($oFile->getPathname()) > $fReferenceTimestamp) {
|
||||
$aModifiedFiles[] = $oFile->getPathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
tests/php-unit-tests/unitary-tests/application/LoginTest.php
Normal file
70
tests/php-unit-tests/unitary-tests/application/LoginTest.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class LoginTest extends ItopDataTestCase {
|
||||
protected $sConfigTmpBackupFile;
|
||||
protected $sConfigPath;
|
||||
protected $sLoginMode;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
clearstatcache();
|
||||
|
||||
// The test consists in requesting UI.php from outside iTop with a specific configuration
|
||||
// Hence the configuration file must be tweaked on disk (and restored)
|
||||
$this->sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
|
||||
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
|
||||
file_put_contents($this->sConfigTmpBackupFile, file_get_contents($this->sConfigPath));
|
||||
|
||||
$oConfig = new \Config($this->sConfigPath);
|
||||
$this->sLoginMode = "unimplemented_loginmode";
|
||||
$oConfig->AddAllowedLoginTypes($this->sLoginMode);
|
||||
|
||||
@chmod($this->sConfigPath, 0770);
|
||||
$oConfig->WriteToFile();
|
||||
@chmod($this->sConfigPath, 0440);
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
if (! is_null($this->sConfigTmpBackupFile) && is_file($this->sConfigTmpBackupFile)){
|
||||
//put config back
|
||||
@chmod($this->sConfigPath, 0770);
|
||||
file_put_contents($this->sConfigPath, file_get_contents($this->sConfigTmpBackupFile));
|
||||
@chmod($this->sConfigPath, 0440);
|
||||
@unlink($this->sConfigTmpBackupFile);
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testLoginInfiniteLoopFix() {
|
||||
$iTimeStamp = microtime(true);
|
||||
$sOutput = $this->CallItopUrlByCurl(sprintf("/pages/UI.php?login_mode=%s", $this->sLoginMode));
|
||||
$iElapsedInMs = (microtime(true) - $iTimeStamp) * 1000;
|
||||
$sMaxExecutionInS = 1;
|
||||
$this->assertTrue($iElapsedInMs < $sMaxExecutionInS * 1000, "iTop answered in $iElapsedInMs ms. it should do it in less than $sMaxExecutionInS seconds (max_execution_time)");
|
||||
$this->assertFalse(strpos($sOutput, "Fatal error"), "no fatal error due to max execution time should be returned" . $sOutput);
|
||||
}
|
||||
|
||||
protected function CallItopUrlByCurl($sUri, ?array $aPostFields=[]){
|
||||
$ch = curl_init();
|
||||
|
||||
$sUrl = MetaModel::GetConfig()->Get('app_root_url') . "/$sUri";
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
if (0 !== sizeof($aPostFields)){
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$sOutput = curl_exec($ch);
|
||||
curl_close ($ch);
|
||||
|
||||
return $sOutput;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class ExampleFor_iQueryModifier implements \iQueryModifier
|
||||
|
||||
public function GetFieldExpression(QueryBuilderContext &$oBuild, $sClass, $sAttCode, $sColId, Expression $oFieldSQLExp, SQLQuery &$oSelect)
|
||||
{
|
||||
// Do nothing, we just need the class to exists for the unit test
|
||||
return $oFieldSQLExp;
|
||||
}
|
||||
}
|
||||
]]></content>
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use CoreOqlMultipleResultsForbiddenException;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use Exception;
|
||||
use Expression;
|
||||
@@ -739,4 +740,110 @@ class DBSearchTest extends ItopDataTestCase
|
||||
$oSearch->MakeSelectQuery();
|
||||
self::assertTrue(true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @dataProvider QueriesProvider
|
||||
* @param $sOQL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testQueries($sOQL)
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->Count() > 0) {
|
||||
$aSelectedAliases = array_keys($oSearch->GetSelectedClasses());
|
||||
$aFirstRow = $oSet->FetchAssoc();
|
||||
$aAliases = array_keys($aFirstRow);
|
||||
$this->assertEquals($aSelectedAliases, $aAliases);
|
||||
}
|
||||
}
|
||||
|
||||
public function QueriesProvider()
|
||||
{
|
||||
return [
|
||||
['SELECT L,P FROM Person AS P JOIN Location AS L ON P.location_id=L.id'],
|
||||
['SELECT P,L FROM Person AS P JOIN Location AS L ON P.location_id=L.id'],
|
||||
];
|
||||
}
|
||||
public function SelectAttributeToArrayProvider()
|
||||
{
|
||||
return array(
|
||||
'select id from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'id',
|
||||
),
|
||||
'select name from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'name',
|
||||
),
|
||||
'select org_id from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'org_id',
|
||||
),
|
||||
'select organization_name from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'organization_name',
|
||||
),
|
||||
'select business_criticity from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'business_criticity',
|
||||
),
|
||||
'select org_id from FunctionalCI' => array(
|
||||
'SELECT FunctionalCI',
|
||||
'org_id',
|
||||
),
|
||||
'select email from Person' => array(
|
||||
'SELECT Person',
|
||||
'email',
|
||||
),
|
||||
'select phone from Person' => array(
|
||||
'SELECT Person',
|
||||
'phone',
|
||||
),
|
||||
'select picture from Person' => array(
|
||||
'SELECT Person',
|
||||
'picture',
|
||||
),
|
||||
'select description from Ticket' => array(
|
||||
'SELECT Ticket',
|
||||
'description',
|
||||
),
|
||||
'select start_date from Ticket' => array(
|
||||
'SELECT Ticket',
|
||||
'start_date',
|
||||
),
|
||||
'select private_log from Ticket' => array(
|
||||
'SELECT Ticket',
|
||||
'private_log',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider SelectAttributeToArrayProvider
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testSelectAttributeToArray($sQuery, $sField){
|
||||
|
||||
$oSearch = \DBObjectSearch::FromOQL($sQuery);
|
||||
$aResToDataArray=[];
|
||||
$oSet = new \DBObjectSet($oSearch);
|
||||
while ($oRecord = $oSet->Fetch()) {
|
||||
$aMappedRow[$sField] =$oRecord->Get($sField);
|
||||
$aResToDataArray[] = $aMappedRow;
|
||||
}
|
||||
array_multisort (array_column($aResToDataArray, $sField), SORT_DESC, $aResToDataArray);
|
||||
|
||||
$aResSelectColumnToArray = $oSearch->SelectAttributeToArray($sField);
|
||||
array_multisort (array_column($aResSelectColumnToArray, $sField), SORT_DESC, $aResSelectColumnToArray);
|
||||
|
||||
self::assertEquals( $aResToDataArray, $aResSelectColumnToArray, 'The array constructed using the OQL query and the result of testSelectAttributeToArray must be the same');
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class iTopConfigParserTest extends ItopTestCase
|
||||
|
||||
clearstatcache();
|
||||
$this->sConfigPath = utils::GetConfigFilePath();
|
||||
$this->tmpSavePath = tempnam('/tmp/', 'config-itop');
|
||||
$this->tmpSavePath = tempnam(sys_get_temp_dir(), 'config-itop');
|
||||
|
||||
$this->conf_exists = is_file($this->sConfigPath);
|
||||
if ($this->conf_exists)
|
||||
@@ -156,8 +156,8 @@ class iTopConfigParserTest extends ItopTestCase
|
||||
*/
|
||||
public function testConfigWriteToFile()
|
||||
{
|
||||
$tmpConfigFileBeforePath = tempnam( '/tmp/', 'config-itop');
|
||||
$tmpConfigFileAfterPath = tempnam( '/tmp/', 'config-itop');
|
||||
$tmpConfigFileBeforePath = tempnam( sys_get_temp_dir(), 'config-itop');
|
||||
$tmpConfigFileAfterPath = tempnam( sys_get_temp_dir(), 'config-itop');
|
||||
|
||||
//create new config file
|
||||
$sConfigFile = utils::GetConfig()->GetLoadedFile();
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\LaunchTest;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use TokenValidation;
|
||||
|
||||
class TokenValidationTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @param string $sSetupToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function createSetupTokenFile(string $sSetupToken): string
|
||||
{
|
||||
$sSetupTokenFile = APPROOT . 'data/.setup';
|
||||
file_put_contents($sSetupTokenFile, $sSetupToken);
|
||||
|
||||
return $sSetupTokenFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @group itop-community
|
||||
* @return void
|
||||
*/
|
||||
public function testLaunch()
|
||||
{
|
||||
$this->RequireOnceItopFile('datamodels/2.x/itop-hub-connector/TokenValidation.php');
|
||||
|
||||
$oTokenValidation = new TokenValidation();
|
||||
|
||||
$sSetupToken = bin2hex(random_bytes(12));
|
||||
$this->assertFalse($oTokenValidation->isSetupTokenValid('lol'));
|
||||
$this->assertFalse($oTokenValidation->isSetupTokenValid(''));
|
||||
$this->assertFalse($oTokenValidation->isSetupTokenValid($sSetupToken));
|
||||
$this->createSetupTokenFile($sSetupToken);
|
||||
$this->assertFalse($oTokenValidation->isSetupTokenValid('lol'));
|
||||
$this->createSetupTokenFile($sSetupToken);
|
||||
$this->assertFalse($oTokenValidation->isSetupTokenValid(''));
|
||||
$this->createSetupTokenFile($sSetupToken);
|
||||
$this->assertTrue($oTokenValidation->isSetupTokenValid($sSetupToken));
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,8 @@ class SetupUtilsTest extends ItopTestCase
|
||||
$this->assertContains($sLabel, $oCheck->sLabel);
|
||||
}
|
||||
|
||||
public function CheckGraphvizProvider(){
|
||||
if (substr(PHP_OS,0,3) === 'WIN'){
|
||||
public function CheckGraphvizProvider() {
|
||||
if (substr(PHP_OS, 0, 3) === 'WIN') {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -64,5 +64,46 @@ class SetupUtilsTest extends ItopTestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider HumanReadableSizeProvider
|
||||
*/
|
||||
public function testHumanReadableSize($fBytes, $sExpected)
|
||||
{
|
||||
$sOutput = SetupUtils::HumanReadableSize($fBytes);
|
||||
$this->assertEquals($sExpected, $sOutput);
|
||||
}
|
||||
|
||||
public function HumanReadableSizeProvider(): array
|
||||
{
|
||||
return [
|
||||
'10 bytes' => [
|
||||
10,
|
||||
'10 bytes',
|
||||
],
|
||||
'10 kilobytes' => [
|
||||
10 * 1024,
|
||||
'10.24 kB',
|
||||
],
|
||||
'10 megabytes' => [
|
||||
10 * 1024 * 1024,
|
||||
'10.49 MB',
|
||||
],
|
||||
'10 gigabytes' => [
|
||||
10 * 1024 * 1024 * 1024,
|
||||
'10.74 GB',
|
||||
],
|
||||
'10 terabytes' => [
|
||||
10 * 1024 * 1024 * 1024 * 1024,
|
||||
'11.00 TB',
|
||||
],
|
||||
'10 petabytes' => [
|
||||
10 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
'11.26 PB',
|
||||
],
|
||||
'10 exabytes' => [
|
||||
10 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
'11.53 EB',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup\UnattendedInstall;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use ItopExtensionsMap;
|
||||
use iTopExtension;
|
||||
use RunTimeEnvironment;
|
||||
use InstallationFileService;
|
||||
use ModuleDiscovery;
|
||||
|
||||
class InstallationFileServiceTest extends ItopTestCase {
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
require_once(dirname(__FILE__, 6) . '/setup/unattended-install/InstallationFileService.php');
|
||||
ModuleDiscovery::ResetCache();
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function GetInstallationPath() : string {
|
||||
return realpath(__DIR__ . '/resources/installation.xml');
|
||||
}
|
||||
|
||||
private function GetModuleData($sCategory, bool $bIsVisible, bool $bIsAutoSelect, bool $bProductionModulesInRootDir=false) : array {
|
||||
$sRootDir = $bProductionModulesInRootDir ? APPROOT.'data/production-modules/' : '';
|
||||
|
||||
$aModuleData = [
|
||||
'category' => $sCategory,
|
||||
'visible' => $bIsVisible,
|
||||
'root_dir' => $sRootDir,
|
||||
];
|
||||
|
||||
if ($bIsAutoSelect){
|
||||
$aModuleData['auto_select'] = true;
|
||||
}
|
||||
|
||||
return $aModuleData;
|
||||
}
|
||||
|
||||
public function ProcessDefaultModulesProvider() {
|
||||
parent::setUp();
|
||||
return [
|
||||
'root module' => [
|
||||
'aAllFoundModules' => [
|
||||
'_Root_' => $this->GetModuleData('authentication', false, false, true),
|
||||
],
|
||||
'aExpectedSelectedModules' => [],
|
||||
'aExpectedAutoSelectModules' => [],
|
||||
],
|
||||
'auto-select root module' => [
|
||||
'aAllFoundModules' => [
|
||||
'_Root_' => $this->GetModuleData('authentication', false, true, true),
|
||||
],
|
||||
'aExpectedSelectedModules' => [],
|
||||
'aExpectedAutoSelectModules' => [],
|
||||
],
|
||||
'autoselect module only' => [
|
||||
'aAllFoundModules' => [
|
||||
'autoselect-only' => $this->GetModuleData('mycategory', true, true),
|
||||
],
|
||||
'aExpectedSelectedModules' => [],
|
||||
'aExpectedAutoSelectModules' => ['autoselect-only'],
|
||||
],
|
||||
'autoselect/invisible module' => [
|
||||
'aAllFoundModules' => [
|
||||
'autoselect-only' => $this->GetModuleData('mycategory', false, true),
|
||||
],
|
||||
'aExpectedSelectedModules' => [],
|
||||
'aExpectedAutoSelectModules' => ['autoselect-only'],
|
||||
],
|
||||
'autoselect/invisible/in-root-dir module' => [
|
||||
'aAllFoundModules' => [
|
||||
'autoselect-only' => $this->GetModuleData('mycategory', false, true , true),
|
||||
],
|
||||
'aExpectedSelectedModules' => [],
|
||||
'aExpectedAutoSelectModules' => ['autoselect-only'],
|
||||
],
|
||||
'visible/authent module' => [
|
||||
'aAllFoundModules' => [
|
||||
'authent-module' => $this->GetModuleData('authentication', true, false , false),
|
||||
],
|
||||
'aExpectedSelectedModules' => ['authent-module'],
|
||||
'aExpectedAutoSelectModules' => [],
|
||||
],
|
||||
'invisible module' => [
|
||||
'aAllFoundModules' => [
|
||||
'visible-module' => $this->GetModuleData('mycategory', false, false , false),
|
||||
],
|
||||
'aExpectedSelectedModules' => ['visible-module'],
|
||||
'aExpectedAutoSelectModules' => [],
|
||||
],
|
||||
'in-root-dir module' => [
|
||||
'aAllFoundModules' => [
|
||||
'in-root-dir-module' => $this->GetModuleData('mycategory', true, false , true),
|
||||
],
|
||||
'aExpectedSelectedModules' => ['in-root-dir-module'],
|
||||
'aExpectedAutoSelectModules' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @dataProvider ProcessDefaultModulesProvider
|
||||
*/
|
||||
public function testProcessDefaultModules(array $aAllFoundModules, array $aExpectedSelectedModules, array $aExpectedAutoSelectModules) {
|
||||
$oInstallationFileService = new InstallationFileService('', 'production', [], true);
|
||||
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->once())
|
||||
->method('AnalyzeInstallation')
|
||||
->willReturn($aAllFoundModules);
|
||||
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
$oInstallationFileService->ProcessDefaultModules();
|
||||
|
||||
sort($aExpectedSelectedModules);
|
||||
$aModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
sort($aModules);
|
||||
|
||||
$this->assertEquals($aExpectedSelectedModules, $aModules);
|
||||
|
||||
$aAutoSelectModules = array_keys($oInstallationFileService->GetAutoSelectModules());
|
||||
sort($aAutoSelectModules);
|
||||
$this->assertEquals($aExpectedAutoSelectModules, $aAutoSelectModules);
|
||||
}
|
||||
|
||||
public function ProcessInstallationChoicesProvider() {
|
||||
return [
|
||||
'all checked' => [ true ],
|
||||
'only defaut + mandatory' => [ false ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ProcessInstallationChoicesProvider
|
||||
*/
|
||||
public function testProcessInstallationChoices($bInstallationOptionalChoicesChecked) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked);
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->never())
|
||||
->method('AnalyzeInstallation');
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
|
||||
$oInstallationFileService->ProcessInstallationChoices();
|
||||
$aExpectedModules = [
|
||||
"itop-config-mgmt",
|
||||
"itop-attachments",
|
||||
"itop-profiles-itil",
|
||||
"itop-welcome-itil",
|
||||
"itop-tickets",
|
||||
"itop-files-information",
|
||||
"combodo-db-tools",
|
||||
"itop-core-update",
|
||||
"itop-hub-connector",
|
||||
"itop-oauth-client",
|
||||
"itop-datacenter-mgmt",
|
||||
"itop-endusers-devices",
|
||||
"itop-storage-mgmt",
|
||||
"itop-virtualization-mgmt",
|
||||
"itop-service-mgmt",
|
||||
"itop-request-mgmt",
|
||||
"itop-portal",
|
||||
"itop-portal-base",
|
||||
"itop-change-mgmt",
|
||||
];
|
||||
|
||||
$aExpectedUnselectedModules = [
|
||||
'itop-change-mgmt-itil',
|
||||
'itop-incident-mgmt-itil',
|
||||
'itop-request-mgmt-itil',
|
||||
'itop-service-mgmt-provider',
|
||||
];
|
||||
|
||||
if ($bInstallationOptionalChoicesChecked){
|
||||
$aExpectedModules []= "itop-problem-mgmt";
|
||||
$aExpectedModules []= "itop-knownerror-mgmt";
|
||||
} else {
|
||||
$aExpectedUnselectedModules []= "itop-problem-mgmt";
|
||||
$aExpectedUnselectedModules []= "itop-knownerror-mgmt";
|
||||
}
|
||||
|
||||
sort($aExpectedModules);
|
||||
$aModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
sort($aModules);
|
||||
|
||||
$this->assertEquals($aExpectedModules, $aModules);
|
||||
|
||||
$aUnselectedModules = array_keys($oInstallationFileService->GetUnSelectedModules());
|
||||
sort($aExpectedUnselectedModules);
|
||||
sort($aUnselectedModules);
|
||||
$this->assertEquals($aExpectedUnselectedModules, $aUnselectedModules);
|
||||
|
||||
$aGetAfterComputationSelectedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions();
|
||||
sort($aGetAfterComputationSelectedExtensions);
|
||||
$aExpectedExtensions = [
|
||||
'itop-change-mgmt-simple',
|
||||
'itop-config-mgmt-core',
|
||||
'itop-config-mgmt-datacenter',
|
||||
'itop-config-mgmt-end-user',
|
||||
'itop-config-mgmt-storage',
|
||||
'itop-config-mgmt-virtualization',
|
||||
'itop-service-mgmt-enterprise',
|
||||
'itop-ticket-mgmt-simple-ticket',
|
||||
'itop-ticket-mgmt-simple-ticket-enhanced-portal',
|
||||
];
|
||||
if ($bInstallationOptionalChoicesChecked){
|
||||
$aExpectedExtensions []= "itop-problem-mgmt";
|
||||
$aExpectedExtensions []= 'itop-kown-error-mgmt';
|
||||
}
|
||||
sort($aExpectedExtensions);
|
||||
$this->assertEquals($aExpectedExtensions, $aGetAfterComputationSelectedExtensions);
|
||||
|
||||
$this->ValidateNonItilExtensionComputation($oInstallationFileService, $bInstallationOptionalChoicesChecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ItilExtensionProvider
|
||||
*/
|
||||
public function testProcessInstallationChoicesWithItilChoices(array $aSelectedExtensions, bool $bKnownMgtSelected, bool $bCoreMgtSelected) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', $aSelectedExtensions, false);
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->never())
|
||||
->method('AnalyzeInstallation');
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
|
||||
$oInstallationFileService->ProcessInstallationChoices();
|
||||
|
||||
$aExpectedInstallationModules = [
|
||||
"itop-config-mgmt",
|
||||
"itop-attachments",
|
||||
"itop-profiles-itil",
|
||||
"itop-welcome-itil",
|
||||
"itop-tickets",
|
||||
"itop-files-information",
|
||||
"combodo-db-tools",
|
||||
"itop-core-update",
|
||||
"itop-hub-connector",
|
||||
"itop-oauth-client",
|
||||
"itop-datacenter-mgmt",
|
||||
"itop-endusers-devices",
|
||||
"itop-storage-mgmt",
|
||||
"itop-virtualization-mgmt",
|
||||
"itop-service-mgmt",
|
||||
"itop-request-mgmt-itil",
|
||||
"itop-incident-mgmt-itil",
|
||||
"itop-portal",
|
||||
"itop-portal-base",
|
||||
"itop-change-mgmt-itil",
|
||||
];
|
||||
if ($bKnownMgtSelected){
|
||||
$aExpectedInstallationModules []= "itop-knownerror-mgmt";
|
||||
}
|
||||
|
||||
sort($aExpectedInstallationModules);
|
||||
$aModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
sort($aModules);
|
||||
|
||||
$this->assertEquals($aExpectedInstallationModules, $aModules);
|
||||
|
||||
$aExpectedUnselectedModules = [
|
||||
'itop-change-mgmt',
|
||||
'itop-problem-mgmt',
|
||||
'itop-request-mgmt',
|
||||
'itop-service-mgmt-provider',
|
||||
];
|
||||
if (!$bKnownMgtSelected){
|
||||
$aExpectedUnselectedModules[]='itop-knownerror-mgmt';
|
||||
}
|
||||
$aUnselectedModules = array_keys($oInstallationFileService->GetUnSelectedModules());
|
||||
sort($aExpectedUnselectedModules);
|
||||
sort($aUnselectedModules);
|
||||
$this->assertEquals($aExpectedUnselectedModules, $aUnselectedModules);
|
||||
|
||||
$this->ValidateItilExtensionComputation($oInstallationFileService, $bKnownMgtSelected, $bCoreMgtSelected);
|
||||
}
|
||||
|
||||
public function GetDefaultModulesProvider() {
|
||||
return [
|
||||
'check all possible modules' => [true],
|
||||
'only minimum defaul/mandatory from installation.xml' => [false],
|
||||
];
|
||||
}
|
||||
|
||||
private function GetMockListOfFoundModules() : array {
|
||||
$sJsonContent = file_get_contents(realpath(__DIR__ . '/resources/AnalyzeInstallation.json'));
|
||||
$sJsonContent = str_replace('ROOTDIR_TOREPLACE', addslashes(APPROOT), $sJsonContent);
|
||||
return json_decode($sJsonContent, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider GetDefaultModulesProvider
|
||||
*/
|
||||
public function testGetAllSelectedModules($bInstallationOptionalChoicesChecked=false) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked);
|
||||
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->once())
|
||||
->method('AnalyzeInstallation')
|
||||
->willReturn($this->GetMockListOfFoundModules());
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
|
||||
$oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class);
|
||||
$oItopExtensionsMap->expects($this->once())
|
||||
->method('GetAllExtensions')
|
||||
->willReturn([]);
|
||||
$oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap);
|
||||
|
||||
$oInstallationFileService->Init();
|
||||
|
||||
$aExpectedInstallationModules = [
|
||||
"itop-config-mgmt",
|
||||
"itop-attachments",
|
||||
"itop-profiles-itil",
|
||||
"itop-welcome-itil",
|
||||
"itop-tickets",
|
||||
"itop-files-information",
|
||||
"combodo-db-tools",
|
||||
"itop-core-update",
|
||||
"itop-hub-connector",
|
||||
"itop-oauth-client",
|
||||
"itop-datacenter-mgmt",
|
||||
"itop-endusers-devices",
|
||||
"itop-storage-mgmt",
|
||||
"itop-virtualization-mgmt",
|
||||
"itop-service-mgmt",
|
||||
"itop-request-mgmt",
|
||||
"itop-portal",
|
||||
"itop-portal-base",
|
||||
"itop-change-mgmt",
|
||||
'authent-cas',
|
||||
'authent-external',
|
||||
'authent-ldap',
|
||||
'authent-local',
|
||||
'itop-backup',
|
||||
'itop-config',
|
||||
'itop-sla-computation',
|
||||
'itop-bridge-virtualization-storage',
|
||||
];
|
||||
|
||||
if ($bInstallationOptionalChoicesChecked){
|
||||
$aExpectedInstallationModules []= "itop-problem-mgmt";
|
||||
$aExpectedInstallationModules []= "itop-knownerror-mgmt";
|
||||
}
|
||||
|
||||
sort($aExpectedInstallationModules);
|
||||
|
||||
$aSelectedModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
sort($aSelectedModules);
|
||||
$this->assertEquals($aExpectedInstallationModules, $aSelectedModules);
|
||||
|
||||
$this->ValidateNonItilExtensionComputation($oInstallationFileService, $bInstallationOptionalChoicesChecked);
|
||||
}
|
||||
|
||||
private function ValidateNonItilExtensionComputation($oInstallationFileService, bool $bInstallationOptionalChoicesChecked, array $aAdditionalExtensions=[]) {
|
||||
$aGetAfterComputationSelectedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions();
|
||||
sort($aGetAfterComputationSelectedExtensions);
|
||||
$aExpectedExtensions = array_merge($aAdditionalExtensions, [
|
||||
'itop-change-mgmt-simple',
|
||||
'itop-config-mgmt-core',
|
||||
'itop-config-mgmt-datacenter',
|
||||
'itop-config-mgmt-end-user',
|
||||
'itop-config-mgmt-storage',
|
||||
'itop-config-mgmt-virtualization',
|
||||
'itop-service-mgmt-enterprise',
|
||||
'itop-ticket-mgmt-simple-ticket',
|
||||
'itop-ticket-mgmt-simple-ticket-enhanced-portal',
|
||||
]);
|
||||
if ($bInstallationOptionalChoicesChecked){
|
||||
$aExpectedExtensions []= "itop-problem-mgmt";
|
||||
$aExpectedExtensions []= 'itop-kown-error-mgmt';
|
||||
}
|
||||
sort($aExpectedExtensions);
|
||||
$this->assertEquals($aExpectedExtensions, $aGetAfterComputationSelectedExtensions);
|
||||
}
|
||||
|
||||
private function ValidateItilExtensionComputation($oInstallationFileService, bool $bKnownMgtSelected, bool $bCoreMgtSelected) {
|
||||
$aGetAfterComputationSelectedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions();
|
||||
sort($aGetAfterComputationSelectedExtensions);
|
||||
$aExpectedExtensions = [
|
||||
'itop-change-mgmt-itil',
|
||||
'itop-config-mgmt-datacenter',
|
||||
'itop-config-mgmt-end-user',
|
||||
'itop-config-mgmt-storage',
|
||||
'itop-config-mgmt-virtualization',
|
||||
'itop-service-mgmt-enterprise',
|
||||
'itop-ticket-mgmt-itil',
|
||||
'itop-ticket-mgmt-itil-enhanced-portal',
|
||||
'itop-ticket-mgmt-itil-incident',
|
||||
'itop-ticket-mgmt-itil-user-request',
|
||||
];
|
||||
if ($bCoreMgtSelected){
|
||||
$aExpectedExtensions []= 'itop-config-mgmt-core';
|
||||
}
|
||||
if ($bKnownMgtSelected){
|
||||
$aExpectedExtensions []= 'itop-kown-error-mgmt';
|
||||
}
|
||||
sort($aExpectedExtensions);
|
||||
$this->assertEquals($aExpectedExtensions, $aGetAfterComputationSelectedExtensions);
|
||||
}
|
||||
|
||||
private function GetSelectedItilExtensions(bool $coreExtensionIncluded, bool $bKnownMgtIncluded) : array {
|
||||
$aExtensions = [
|
||||
'itop-config-mgmt-datacenter',
|
||||
'itop-config-mgmt-end-user',
|
||||
'itop-config-mgmt-storage',
|
||||
'itop-config-mgmt-virtualization',
|
||||
'itop-service-mgmt-enterprise',
|
||||
'itop-ticket-mgmt-itil',
|
||||
'itop-ticket-mgmt-itil-user-request',
|
||||
'itop-ticket-mgmt-itil-incident',
|
||||
'itop-ticket-mgmt-itil-enhanced-portal',
|
||||
'itop-change-mgmt-itil',
|
||||
];
|
||||
|
||||
if ($coreExtensionIncluded){
|
||||
$aExtensions[]= 'itop-config-mgmt-core';
|
||||
}
|
||||
|
||||
if ($bKnownMgtIncluded){
|
||||
$aExtensions[]= 'itop-kown-error-mgmt';
|
||||
}
|
||||
|
||||
return $aExtensions;
|
||||
|
||||
}
|
||||
|
||||
public function ItilExtensionProvider() {
|
||||
return [
|
||||
'all itil extensions + INCLUDING known-error-mgt' => [
|
||||
'aSelectedExtensions' => $this->GetSelectedItilExtensions(true, true),
|
||||
'bKnownMgtSelected' => true,
|
||||
'bCoreMgtSelected' => true,
|
||||
],
|
||||
'all itil extensions WITHOUT known-error-mgt' => [
|
||||
'aSelectedExtensions' => $this->GetSelectedItilExtensions(true, false),
|
||||
'bKnownMgtSelected' => false,
|
||||
'bCoreMgtSelected' => true,
|
||||
],
|
||||
'all itil extensions WITHOUT core mandatory ones + INCLUDING known-error-mgt' => [
|
||||
'aSelectedExtensions' => $this->GetSelectedItilExtensions(false, true),
|
||||
'bKnownMgtSelected' => true,
|
||||
'bCoreMgtSelected' => false,
|
||||
],
|
||||
'all itil extensions WITHOUT core mandatory ones and WITHOUT known-error-mgt' => [
|
||||
'aSelectedExtensions' => $this->GetSelectedItilExtensions(false, false),
|
||||
'bKnownMgtSelected' => false,
|
||||
'bCoreMgtSelected' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ItilExtensionProvider
|
||||
*/
|
||||
public function testGetAllSelectedModules_withItilExtensions(array $aSelectedExtensions, bool $bKnownMgtSelected, bool $bCoreMgtSelected) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', $aSelectedExtensions);
|
||||
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->once())
|
||||
->method('AnalyzeInstallation')
|
||||
->willReturn($this->GetMockListOfFoundModules());
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
|
||||
$oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class);
|
||||
$oItopExtensionsMap->expects($this->once())
|
||||
->method('GetAllExtensions')
|
||||
->willReturn([]);
|
||||
$oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap);
|
||||
|
||||
$oInstallationFileService->Init();
|
||||
|
||||
$aSelectedModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
$aExpectedInstallationModules = [
|
||||
"itop-config-mgmt",
|
||||
"itop-attachments",
|
||||
"itop-profiles-itil",
|
||||
"itop-welcome-itil",
|
||||
"itop-tickets",
|
||||
"itop-files-information",
|
||||
"combodo-db-tools",
|
||||
"itop-core-update",
|
||||
"itop-hub-connector",
|
||||
"itop-oauth-client",
|
||||
"itop-datacenter-mgmt",
|
||||
"itop-endusers-devices",
|
||||
"itop-storage-mgmt",
|
||||
"itop-virtualization-mgmt",
|
||||
"itop-service-mgmt",
|
||||
"itop-request-mgmt-itil",
|
||||
"itop-incident-mgmt-itil",
|
||||
"itop-portal",
|
||||
"itop-portal-base",
|
||||
"itop-change-mgmt-itil",
|
||||
"itop-full-itil",
|
||||
'authent-cas',
|
||||
'authent-external',
|
||||
'authent-ldap',
|
||||
'authent-local',
|
||||
'itop-backup',
|
||||
'itop-config',
|
||||
'itop-sla-computation',
|
||||
'itop-bridge-virtualization-storage',
|
||||
];
|
||||
if ($bKnownMgtSelected){
|
||||
$aExpectedInstallationModules []= "itop-knownerror-mgmt";
|
||||
}
|
||||
|
||||
sort($aExpectedInstallationModules);
|
||||
sort($aSelectedModules);
|
||||
$this->assertEquals($aExpectedInstallationModules, $aSelectedModules);
|
||||
|
||||
$this->ValidateItilExtensionComputation($oInstallationFileService, $bKnownMgtSelected, $bCoreMgtSelected);
|
||||
}
|
||||
|
||||
private function CreateItopExtension(string $sSource, string $sCode, array $aModules, array $aMissingDependencies, bool $bIsVisible) : iTopExtension{
|
||||
$oExtension = new iTopExtension();
|
||||
$oExtension->sCode = $sCode;
|
||||
$oExtension->sSource = $sSource;
|
||||
$oExtension->aModules = $aModules;
|
||||
$oExtension->aMissingDependencies = $aMissingDependencies;
|
||||
$oExtension->bVisible = $bIsVisible;
|
||||
return $oExtension;
|
||||
}
|
||||
|
||||
public function CanChooseUnpackageExtensionProvider() {
|
||||
return [
|
||||
'extension in SOURCE_REMOTE' => [
|
||||
'sCode' => "extension-from-designer",
|
||||
'bInstallationOptionalChoicesChecked' => false,
|
||||
'sSource' => 'data',
|
||||
'bExpectedRes' => true
|
||||
],
|
||||
'extension in SOURCE_WIZARD' => [
|
||||
'sCode' => 'extension-from-package',
|
||||
'bInstallationOptionalChoicesChecked' => true,
|
||||
'sSource' => 'datamodels',
|
||||
'bExpectedRes' => false
|
||||
],
|
||||
'extension in SOURCE_MANUAL + optional OK' => [
|
||||
'sCode' => 'extension-from-package',
|
||||
'bInstallationOptionalChoicesChecked' => true,
|
||||
'sSource' => 'extensions',
|
||||
'bExpectedRes' => true
|
||||
],
|
||||
'extension in SOURCE_MANUAL + optional NOT OK' => [
|
||||
'sCode' => 'extension-from-package',
|
||||
'bInstallationOptionalChoicesChecked' => false,
|
||||
'sSource' => 'extensions',
|
||||
'bExpectedRes' => false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider CanChooseUnpackageExtensionProvider
|
||||
*/
|
||||
public function testCanChooseUnpackageExtension(string $sCode, bool $bInstallationOptionalChoicesChecked, string $sSource, bool $bExpectedRes) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', [], $bInstallationOptionalChoicesChecked);
|
||||
|
||||
$oItopExtension = $this->CreateItopExtension($sSource, $sCode, [], [], true);
|
||||
$this->assertEquals($bExpectedRes, $oInstallationFileService->CanChooseUnpackageExtension($oItopExtension));
|
||||
}
|
||||
|
||||
public function ProcessExtensionModulesNotSpecifiedInChoicesProvider() {
|
||||
return [
|
||||
'extensions to install OK' => [
|
||||
'aExtensionData' => [
|
||||
'extension1' => [
|
||||
//'itop-request-mgmt-itil', //unselected
|
||||
'combodo-monitoring',
|
||||
'itop-config-mgmt', //already selected
|
||||
],
|
||||
'extension2' => [
|
||||
//'itop-incident-mgmt-itil', //unselected
|
||||
'combodo-monitoring2',
|
||||
'itop-attachments', //already selected
|
||||
]
|
||||
],
|
||||
'bExtensionCanBeChoosen' => true,
|
||||
'aMissingDependencies' => [],
|
||||
'bIsVisible' => true,
|
||||
'bExpectedAdditionalExtensions' => [
|
||||
'extension1', 'extension2'
|
||||
],
|
||||
'bExpectedAdditionalModules' => [
|
||||
'combodo-monitoring', 'combodo-monitoring2'
|
||||
]
|
||||
],
|
||||
'extensions to install cannot be choose,' => [
|
||||
'aExtensionData' => [
|
||||
'extension1' => [
|
||||
'combodo-monitoring',
|
||||
],
|
||||
'extension2' => [
|
||||
'combodo-monitoring2',
|
||||
]
|
||||
],
|
||||
'bExtensionCanBeChoosen' => false,
|
||||
'aMissingDependencies' => [],
|
||||
'bIsVisible' => true,
|
||||
'bExpectedAdditionalExtensions' => [],
|
||||
'bExpectedAdditionalModules' => []
|
||||
],
|
||||
'extensions to install not visible' => [
|
||||
'aExtensionData' => [
|
||||
'extension1' => [
|
||||
'combodo-monitoring',
|
||||
],
|
||||
'extension2' => [
|
||||
'combodo-monitoring2',
|
||||
]
|
||||
],
|
||||
'bExtensionCanBeChoosen' => true,
|
||||
'aMissingDependencies' => [],
|
||||
'bIsVisible' => false,
|
||||
'bExpectedAdditionalExtensions' => [],
|
||||
'bExpectedAdditionalModules' => []
|
||||
],
|
||||
'extensions to install with missing dependencies' => [
|
||||
'aExtensionData' => [
|
||||
'extension1' => [
|
||||
'combodo-monitoring',
|
||||
],
|
||||
'extension2' => [
|
||||
'combodo-monitoring2',
|
||||
]
|
||||
],
|
||||
'bExtensionCanBeChoosen' => true,
|
||||
'aMissingDependencies' => ['missing-module'],
|
||||
'bIsVisible' => true,
|
||||
'bExpectedAdditionalExtensions' => [],
|
||||
'bExpectedAdditionalModules' => []
|
||||
],
|
||||
'extensions to install with unselectable ITIL module' => [
|
||||
'aExtensionData' => [
|
||||
'extension1' => [
|
||||
'itop-request-mgmt-itil', //unselected
|
||||
'combodo-monitoring',
|
||||
],
|
||||
'extension2' => [
|
||||
'itop-incident-mgmt-itil', //unselected
|
||||
'combodo-monitoring2',
|
||||
]
|
||||
],
|
||||
'bExtensionCanBeChoosen' => true,
|
||||
'aMissingDependencies' => [],
|
||||
'bIsVisible' => true,
|
||||
'bExpectedAdditionalExtensions' => [],
|
||||
'bExpectedAdditionalModules' => []
|
||||
],
|
||||
'extensions already processed' => [
|
||||
'aExtensionData' => [
|
||||
'itop-config-mgmt-core' => [
|
||||
'itop-config-mgmt', //already selected
|
||||
],
|
||||
],
|
||||
'bExtensionCanBeChoosen' => true,
|
||||
'aMissingDependencies' => [],
|
||||
'bIsVisible' => true,
|
||||
'bExpectedAdditionalExtensions' => [
|
||||
],
|
||||
'bExpectedAdditionalModules' => [
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ProcessExtensionModulesNotSpecifiedInChoicesProvider
|
||||
*/
|
||||
public function testProcessExtensionModulesNotSpecifiedInChoices(array $aExtensionData, bool $bExtensionCanBeChoosen,
|
||||
array $aMissingDependencies, bool $bIsVisible, array $bExpectedAdditionalExtensions, array $bExpectedAdditionalModules) {
|
||||
$sPath = $this->GetInstallationPath();
|
||||
$oInstallationFileService = new InstallationFileService($sPath, 'production', [], true);
|
||||
|
||||
$oProductionEnv = $this->createMock(RunTimeEnvironment::class);
|
||||
$oProductionEnv->expects($this->once())
|
||||
->method('AnalyzeInstallation')
|
||||
->willReturn($this->GetMockListOfFoundModules());
|
||||
$oInstallationFileService->SetProductionEnv($oProductionEnv);
|
||||
|
||||
$oItopExtensionsMap = $this->createMock(ItopExtensionsMap::class);
|
||||
$aItopExtensionMap = [];
|
||||
|
||||
$sSource = $bExtensionCanBeChoosen ? iTopExtension::SOURCE_REMOTE : iTopExtension::SOURCE_WIZARD;
|
||||
foreach ($aExtensionData as $sExtensionCode => $aModules){
|
||||
$aItopExtensionMap[]= $this->CreateItopExtension($sSource, $sExtensionCode, $aModules, $aMissingDependencies, $bIsVisible);
|
||||
}
|
||||
$oItopExtensionsMap->expects($this->once())
|
||||
->method('GetAllExtensions')
|
||||
->willReturn($aItopExtensionMap);
|
||||
$oInstallationFileService->SetItopExtensionsMap($oItopExtensionsMap);
|
||||
|
||||
$oInstallationFileService->Init();
|
||||
|
||||
$aSelectedModules = array_keys($oInstallationFileService->GetSelectedModules());
|
||||
sort($aSelectedModules);
|
||||
$aExpectedInstallationModules = array_merge($bExpectedAdditionalModules, [
|
||||
"itop-config-mgmt",
|
||||
"itop-attachments",
|
||||
"itop-profiles-itil",
|
||||
"itop-welcome-itil",
|
||||
"itop-tickets",
|
||||
"itop-files-information",
|
||||
"combodo-db-tools",
|
||||
"itop-core-update",
|
||||
"itop-hub-connector",
|
||||
"itop-oauth-client",
|
||||
"itop-datacenter-mgmt",
|
||||
"itop-endusers-devices",
|
||||
"itop-storage-mgmt",
|
||||
"itop-virtualization-mgmt",
|
||||
"itop-service-mgmt",
|
||||
"itop-request-mgmt",
|
||||
"itop-portal",
|
||||
"itop-portal-base",
|
||||
"itop-change-mgmt",
|
||||
"itop-problem-mgmt",
|
||||
"itop-knownerror-mgmt",
|
||||
'authent-cas',
|
||||
'authent-external',
|
||||
'authent-ldap',
|
||||
'authent-local',
|
||||
'itop-backup',
|
||||
'itop-config',
|
||||
'itop-sla-computation',
|
||||
'itop-bridge-virtualization-storage',
|
||||
]);
|
||||
sort($aExpectedInstallationModules);
|
||||
|
||||
$this->assertEquals($aExpectedInstallationModules, $aSelectedModules);
|
||||
|
||||
$this->ValidateNonItilExtensionComputation($oInstallationFileService, true, $bExpectedAdditionalExtensions);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup\UnattendedInstall;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class UnattendedInstallTest extends ItopDataTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
$aFiles = [
|
||||
'web.config',
|
||||
'.htaccess',
|
||||
];
|
||||
foreach ($aFiles as $sFile){
|
||||
$sPath = APPROOT."setup/unattended-install/$sFile";
|
||||
if (is_file("$sPath.back")){
|
||||
rename("$sPath.back", $sPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function callUnattendedFromHttp() : string {
|
||||
$ch = curl_init();
|
||||
|
||||
$sUrl = \MetaModel::GetConfig()->Get('app_root_url');
|
||||
curl_setopt($ch, CURLOPT_URL, "$sUrl/setup/unattended-install/unattended-install.php");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, []);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$sJson = curl_exec($ch);
|
||||
curl_close ($ch);
|
||||
return $sJson;
|
||||
}
|
||||
public function testCallUnattendedInstallFromHttp(){
|
||||
$sJson = $this->callUnattendedFromHttp();
|
||||
if (false !== strpos($sJson, "403 Forbidden")){
|
||||
//.htaccess / webconfig effect
|
||||
$aFiles = [
|
||||
'web.config',
|
||||
'.htaccess',
|
||||
];
|
||||
foreach ($aFiles as $sFile){
|
||||
$sPath = APPROOT."setup/unattended-install/$sFile";
|
||||
if (is_file("$sPath")) {
|
||||
rename($sPath, "$sPath.back");
|
||||
}
|
||||
}
|
||||
|
||||
$sJson = $this->callUnattendedFromHttp();
|
||||
}
|
||||
|
||||
$this->assertEquals("Mode CLI only", $sJson, "even without HTTP protection, script should NOT be called directly by HTTP");
|
||||
}
|
||||
|
||||
public function testCallUnattendedInstallFromCLI() {
|
||||
$sCliPath = realpath(APPROOT."/setup/unattended-install/unattended-install.php");
|
||||
exec(sprintf("%s %s", PHP_BINARY, $sCliPath), $aOutput, $iCode);
|
||||
|
||||
$sOutput = implode('\n', $aOutput);
|
||||
var_dump($sOutput);
|
||||
$this->assertStringContainsString("Missing mandatory argument `--param-file`", $sOutput);
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
// Windows
|
||||
$this->assertEquals(-1, $iCode);
|
||||
} else {
|
||||
// Linux
|
||||
$this->assertEquals(255, $iCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<steps type="array">
|
||||
<step>
|
||||
<title>Configuration Management options</title>
|
||||
<description><![CDATA[<h2>The options below allow you to configure the type of elements that are to be managed inside iTop.</h2>]]></description>
|
||||
<banner>/images/modules.png</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-core</extension_code>
|
||||
<title>Configuration Management Core</title>
|
||||
<description>All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-config-mgmt</module>
|
||||
<module>itop-attachments</module>
|
||||
<module>itop-profiles-itil</module>
|
||||
<module>itop-welcome-itil</module>
|
||||
<module>itop-tickets</module>
|
||||
<module>itop-files-information</module>
|
||||
<module>combodo-db-tools</module>
|
||||
<module>itop-core-update</module>
|
||||
<module>itop-hub-connector</module>
|
||||
<module>itop-oauth-client</module>
|
||||
</modules>
|
||||
<mandatory>true</mandatory>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-datacenter</extension_code>
|
||||
<title>Data Center Devices</title>
|
||||
<description>Manage Data Center devices such as Racks, Enclosures, PDUs, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-datacenter-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-end-user</extension_code>
|
||||
<title>End-User Devices</title>
|
||||
<description>Manage devices related to end-users: PCs, Phones, Tablets, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-endusers-devices</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-storage</extension_code>
|
||||
<title>Storage Devices</title>
|
||||
<description>Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc.</description>
|
||||
<modules type="array">
|
||||
<module>itop-storage-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-config-mgmt-virtualization</extension_code>
|
||||
<title>Virtualization</title>
|
||||
<description>Manage Hypervisors, Virtual Machines and Farms.</description>
|
||||
<modules type="array">
|
||||
<module>itop-virtualization-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
<step>
|
||||
<title>Service Management options</title>
|
||||
<description><![CDATA[<h2>Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.</h2>]]></description>
|
||||
<banner>./wizard-icons/service.png</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-enterprise</extension_code>
|
||||
<title>Service Management for Enterprises</title>
|
||||
<description>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.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-service-mgmt-service-provider</extension_code>
|
||||
<title>Service Management for Service Providers</title>
|
||||
<description>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.</description>
|
||||
<modules type="array">
|
||||
<module>itop-service-mgmt-provider</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Tickets Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to respond to user requests and incidents.</h2>]]></description>
|
||||
<banner>./itop-incident-mgmt-itil/images/incident-escalated.png</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket</extension_code>
|
||||
<title>Simple Ticket Management</title>
|
||||
<description>Select this option to use one single type of tickets for all kind of requests.</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-simple-ticket-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil</extension_code>
|
||||
<title>ITIL Compliant Tickets Management</title>
|
||||
<description>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</description>
|
||||
<sub_options>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-user-request</extension_code>
|
||||
<title>User Request Management</title>
|
||||
<description>Manage User Request tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-request-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-incident</extension_code>
|
||||
<title>Incident Management</title>
|
||||
<description>Manage Incidents tickets in iTop</description>
|
||||
<modules type="array">
|
||||
<module>itop-incident-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-itil-enhanced-portal</extension_code>
|
||||
<title>Customer Portal</title>
|
||||
<description><![CDATA[Modern & responsive portal for the end-users]]></description>
|
||||
<modules type="array">
|
||||
<module>itop-portal</module>
|
||||
<module>itop-portal-base</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
</options>
|
||||
</sub_options>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-ticket-mgmt-none</extension_code>
|
||||
<title>No Tickets Management</title>
|
||||
<description>Don't manage incidents or user requests in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Change Management options</title>
|
||||
<description><![CDATA[<h2>Select the type of tickets you want to use in order to manage changes to the IT infrastructure.</h2>]]></description>
|
||||
<banner>./itop-change-mgmt/images/change.png</banner>
|
||||
<alternatives type="array">
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-simple</extension_code>
|
||||
<title>Simple Change Management</title>
|
||||
<description>Select this option to use one type of ticket for all kind of changes.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt</module>
|
||||
</modules>
|
||||
<default>true</default>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-itil</extension_code>
|
||||
<title>ITIL Change Management</title>
|
||||
<description>Select this option to use Normal/Routine/Emergency change tickets.</description>
|
||||
<modules type="array">
|
||||
<module>itop-change-mgmt-itil</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-change-mgmt-none</extension_code>
|
||||
<title>No Change Management</title>
|
||||
<description>Don't manage changes in iTop</description>
|
||||
<modules type="array">
|
||||
</modules>
|
||||
</choice>
|
||||
</alternatives>
|
||||
</step>
|
||||
<step>
|
||||
<title>Additional ITIL tickets</title>
|
||||
<description><![CDATA[<h2>Pick from the list below the additional ITIL processes that are to be implemented in iTop.</h2>]]></description>
|
||||
<banner>./itop-knownerror-mgmt/images/known-error.png</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||
<title>Known Errors Management</title>
|
||||
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-knownerror-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-problem-mgmt</extension_code>
|
||||
<title>Problem Management</title>
|
||||
<description>Select this option track "Problems" in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-problem-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
</options>
|
||||
</step>
|
||||
</steps>
|
||||
</installation>
|
||||
@@ -29,40 +29,8 @@
|
||||
</admin_account>
|
||||
<language>EN US</language>
|
||||
<selected_modules type="array">
|
||||
<item>authent-cas</item>
|
||||
<item>authent-external</item>
|
||||
<item>authent-local</item>
|
||||
<item>itop-backup</item>
|
||||
<item>itop-config</item>
|
||||
<item>itop-profiles-itil</item>
|
||||
<item>itop-sla-computation</item>
|
||||
<item>itop-tickets</item>
|
||||
<item>itop-welcome-itil</item>
|
||||
<item>itop-config-mgmt</item>
|
||||
<item>itop-attachments</item>
|
||||
<item>itop-datacenter-mgmt</item>
|
||||
<item>itop-endusers-devices</item>
|
||||
<item>itop-storage-mgmt</item>
|
||||
<item>itop-virtualization-mgmt</item>
|
||||
<item>itop-bridge-virtualization-storage</item>
|
||||
<item>itop-service-mgmt</item>
|
||||
<item>itop-request-mgmt</item>
|
||||
<item>itop-portal</item>
|
||||
<item>itop-portal-base</item>
|
||||
<item>itop-change-mgmt</item>
|
||||
<item>itop-knownerror-mgmt</item>
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
<item>itop-config-mgmt-core</item>
|
||||
<item>itop-config-mgmt-datacenter</item>
|
||||
<item>itop-config-mgmt-end-user</item>
|
||||
<item>itop-config-mgmt-storage</item>
|
||||
<item>itop-config-mgmt-virtualization</item>
|
||||
<item>itop-service-mgmt-enterprise</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket-enhanced-portal</item>
|
||||
<item>itop-change-mgmt-simple</item>
|
||||
<item>itop-kown-error-mgmt</item>
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
|
||||
@@ -211,13 +211,15 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
|
||||
|
||||
if (utils::IsModeCLI())
|
||||
{
|
||||
$oP = new CLIPage("iTop - Bulk import");
|
||||
$oP = new CLIPage("iTop IsModeCLI - Bulk import");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP = new CSVPage("iTop - Bulk import");
|
||||
}
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
utils::UseParamFile();
|
||||
@@ -231,9 +233,13 @@ catch(Exception $e)
|
||||
|
||||
if (utils::IsModeCLI())
|
||||
{
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
$bIsCli = ($sCleanName === 'cli');
|
||||
$oP->p("mode CLI bIsCli: $bIsCli sCleanName: $sCleanName");
|
||||
|
||||
// Next steps:
|
||||
// specific arguments: 'csvfile'
|
||||
//
|
||||
//
|
||||
$sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
|
||||
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
|
||||
$sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
|
||||
@@ -259,6 +265,10 @@ if (utils::IsModeCLI())
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
$bIsCli = ($sCleanName === 'cli');
|
||||
$oP->p("mode WEB bIsCli: $bIsCli sCleanName: $sCleanName");
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::ResetSession(true);
|
||||
$iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
|
||||
@@ -303,7 +313,7 @@ try
|
||||
//
|
||||
// Read parameters
|
||||
//
|
||||
$sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
|
||||
$sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
|
||||
$sSep = ReadParam($oP, 'separator', 'raw_data');
|
||||
$sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
|
||||
$sCharSet = ReadParam($oP, 'charset', 'raw_data');
|
||||
@@ -356,7 +366,7 @@ try
|
||||
{
|
||||
$sDateFormat = null;
|
||||
}
|
||||
|
||||
|
||||
if ($sCharSet == '')
|
||||
{
|
||||
$sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
@@ -474,7 +484,7 @@ try
|
||||
{
|
||||
$sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
$oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
|
||||
$oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
|
||||
|
||||
// Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
|
||||
$aRawFieldList = $oCSVParser->ListFields();
|
||||
@@ -496,7 +506,7 @@ try
|
||||
// Remove any trailing "star" character before the arrow (->)
|
||||
// A star character at the end can be used to indicate a mandatory field
|
||||
$sFieldName = $aMatches[1].'->'.$aMatches[2];
|
||||
}
|
||||
}
|
||||
if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames))
|
||||
{
|
||||
$aColumns = $aKnownColumnNames[strtolower($sFieldName)];
|
||||
@@ -518,7 +528,7 @@ try
|
||||
throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
|
||||
}
|
||||
}
|
||||
// Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
|
||||
// Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
|
||||
|
||||
$aAttList = array();
|
||||
$aExtKeys = array();
|
||||
@@ -753,7 +763,7 @@ try
|
||||
}
|
||||
CMDBObject::SetTrackInfo($sMoreInfo);
|
||||
CMDBObject::SetTrackOrigin('csv-import.php');
|
||||
|
||||
|
||||
$oMyChange = CMDBObject::GetCurrentChange();
|
||||
}
|
||||
|
||||
@@ -788,7 +798,7 @@ try
|
||||
break;
|
||||
case 'RowStatus_Issue':
|
||||
$iCountErrors++;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($bWritten)
|
||||
@@ -867,7 +877,7 @@ try
|
||||
$aDisplayConfig["$iCol"] = array("label"=>$sAttCode, "description"=>$sLabel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$aResultDisp = array(); // to be displayed
|
||||
foreach($aRes as $iRow => $aRowData)
|
||||
{
|
||||
@@ -894,11 +904,11 @@ try
|
||||
foreach($aRowData as $key => $value)
|
||||
{
|
||||
$sKey = (string) $key;
|
||||
|
||||
|
||||
if ($sKey == '__STATUS__') continue;
|
||||
if ($sKey == 'finalclass') continue;
|
||||
if ($sKey == 'id') continue;
|
||||
|
||||
|
||||
if (is_object($value))
|
||||
{
|
||||
$aRowDisp["$sKey"] = $value->GetDisplayableValue().$value->GetDescription();
|
||||
@@ -915,15 +925,15 @@ try
|
||||
}
|
||||
catch(BulkLoadException $e)
|
||||
{
|
||||
$oP->add_comment($e->getMessage());
|
||||
$oP->add_comment($e->getMessage());
|
||||
}
|
||||
catch(SecurityException $e)
|
||||
{
|
||||
$oP->add_comment($e->getMessage());
|
||||
$oP->add_comment($e->getMessage());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oP->add_comment((string)$e);
|
||||
$oP->add_comment((string)$e);
|
||||
}
|
||||
|
||||
$oP->output();
|
||||
|
||||
Reference in New Issue
Block a user