mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-18 23:44:19 +01:00
Compare commits
72 Commits
support/de
...
8231-proto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6105255d76 | ||
|
|
f3909eb50a | ||
|
|
7f2a1a6416 | ||
|
|
32dbf1490c | ||
|
|
2dc9f5dcc6 | ||
|
|
200b148c86 | ||
|
|
6fcbf68013 | ||
|
|
2fedfa1b4c | ||
|
|
755ac3a15c | ||
|
|
01e2d3b317 | ||
|
|
39fd258544 | ||
|
|
f58a2be620 | ||
|
|
e8c622a15d | ||
|
|
783fa01fc9 | ||
|
|
b1f49ae487 | ||
|
|
896069d64b | ||
|
|
85434578d3 | ||
|
|
e142fba0e6 | ||
|
|
c9f32311b4 | ||
|
|
a9e10742ec | ||
|
|
cb2a093498 | ||
|
|
944b1f557d | ||
|
|
affed69999 | ||
|
|
d5754fc568 | ||
|
|
44290db312 | ||
|
|
c49ceae75e | ||
|
|
8980f627e9 | ||
|
|
160bfd714b | ||
|
|
8d58372074 | ||
|
|
37fc1a5723 | ||
|
|
95aa444ee6 | ||
|
|
f5de808c7c | ||
|
|
e03033ce52 | ||
|
|
374b35f78a | ||
|
|
04bd8cc5ce | ||
|
|
8cb701bda3 | ||
|
|
1b29746806 | ||
|
|
fb9c317256 | ||
|
|
0904a21e3f | ||
|
|
82d11eeb47 | ||
|
|
142d6c8993 | ||
|
|
320922a13d | ||
|
|
f03d731b1d | ||
|
|
8be7628668 | ||
|
|
62caf16153 | ||
|
|
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 |
43
.github/workflows/action.yml
vendored
Normal file
43
.github/workflows/action.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Add PRs to Combodo PRs Dashboard
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add PR to Combodo Project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if author is a member of the organization
|
||||
id: check-membership
|
||||
run: |
|
||||
ORG="Combodo"
|
||||
AUTHOR=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
|
||||
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
"https://api.github.com/orgs/$ORG/members/$AUTHOR")
|
||||
if [ "$RESPONSE" == "404" ]; then
|
||||
echo "project_url=https://github.com/orgs/Combodo/projects/5" >> $GITHUB_ENV
|
||||
echo "is_member=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "project_url=https://github.com/orgs/Combodo/projects/4" >> $GITHUB_ENV
|
||||
echo "is_member=true" >> $GITHUB_ENV
|
||||
|
||||
fi
|
||||
|
||||
- name: Add internal tag if member
|
||||
if: env.is_member == 'true'
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
|
||||
-d '{"labels":["internal"]}'
|
||||
env:
|
||||
is_member: ${{ env.is_member }}
|
||||
|
||||
- name: Add PR to the appropriate project
|
||||
uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: ${{ env.project_url }}
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1316,6 +1316,11 @@ interface iRestServiceProvider
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||
}
|
||||
|
||||
interface iRestInputSanitizer
|
||||
{
|
||||
public function SanitizeJsonInput(string $sJsonInput): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||
*
|
||||
@@ -1405,6 +1410,14 @@ class RestResult
|
||||
* @api
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* Sanitize the content of this result to hide sensitive information
|
||||
*/
|
||||
public function SanitizeContent()
|
||||
{
|
||||
// The default implementation does nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1193,12 +1193,12 @@ EOF
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$sId = utils::HtmlEntities($this->sId);
|
||||
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
|
||||
$sId = json_encode($this->sId);
|
||||
$sLayoutClass = json_encode($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = utils::HtmlEntities($this->sTitle);
|
||||
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
|
||||
$sTitle = json_encode($this->sTitle);
|
||||
$sFile = json_encode($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
@@ -1250,15 +1250,15 @@ $('#dashboard_editor').dialog({
|
||||
});
|
||||
|
||||
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
||||
dashboard_id: '$sId',
|
||||
layout_class: '$sLayoutClass',
|
||||
title: '$sTitle',
|
||||
dashboard_id: $sId,
|
||||
layout_class: $sLayoutClass,
|
||||
title: $sTitle,
|
||||
auto_reload: $sAutoReload,
|
||||
auto_reload_sec: $sAutoReloadSec,
|
||||
submit_to: '$sUrl',
|
||||
submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
render_to: '$sUrl',
|
||||
render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||
new_dashlet_parameters: {operation: 'new_dashlet'}
|
||||
});
|
||||
|
||||
|
||||
@@ -199,16 +199,8 @@ 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));
|
||||
return ($sCleanName === 'cli');
|
||||
}
|
||||
|
||||
protected static $bPageMode = null;
|
||||
@@ -316,13 +308,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 +330,7 @@ class utils
|
||||
$retValue = $defaultValue;
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,11 +473,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 +486,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 +499,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 +607,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 +802,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 +821,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 +1240,7 @@ class utils
|
||||
{
|
||||
$aArguments['param_file'] = $sParamFile;
|
||||
}
|
||||
|
||||
|
||||
$aArgs = array();
|
||||
foreach($aArguments as $sName => $value)
|
||||
{
|
||||
@@ -1257,7 +1249,7 @@ class utils
|
||||
$aArgs[] = "--$sName=".escapeshellarg($value);
|
||||
}
|
||||
$sArgs = implode(' ', $aArgs);
|
||||
|
||||
|
||||
$sScript = realpath(APPROOT.$sScriptName);
|
||||
if (!file_exists($sScript))
|
||||
{
|
||||
@@ -1348,7 +1340,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 +1365,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 +1379,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 +1393,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 +1457,7 @@ class utils
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
$oPage->add_linked_script($sLinkedScript);
|
||||
@@ -1602,7 +1594,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 +1604,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 +1621,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 +1659,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 +1685,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 +1697,7 @@ class utils
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
@@ -1746,7 +1738,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 +1768,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 +1798,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 +1811,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 +1874,7 @@ class utils
|
||||
|
||||
return $sCss;
|
||||
}
|
||||
|
||||
|
||||
public static function GetImageSize($sImageData)
|
||||
{
|
||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||
@@ -1939,7 +1931,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 +1945,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 +1960,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 +2029,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 +2060,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sProperty The name of the property to retrieve
|
||||
* @param mixed $defaultvalue
|
||||
@@ -2078,7 +2070,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 +2079,7 @@ class utils
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given path/url is an http(s) URL
|
||||
* @param string $sPath
|
||||
@@ -2102,7 +2094,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 +2143,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 +2187,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 +2243,7 @@ class utils
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
@@ -2265,7 +2257,7 @@ class utils
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
{
|
||||
$aCleanHeaders = array();
|
||||
@@ -2290,7 +2282,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 +2437,7 @@ class utils
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ define('APPCONF', APPROOT.'conf/');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '2.7.10');
|
||||
define('ITOP_CORE_VERSION', '2.7.12');
|
||||
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -138,7 +138,7 @@ abstract class AttributeDefinition
|
||||
|
||||
protected $aCSSClasses;
|
||||
|
||||
public function GetType()
|
||||
public function GetType()
|
||||
{
|
||||
return Dict::S('Core:'.get_class($this));
|
||||
}
|
||||
@@ -3775,7 +3775,7 @@ class AttributeFinalClass extends AttributeString
|
||||
*/
|
||||
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
@@ -3851,7 +3851,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
*/
|
||||
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
static $sKey = null; // Encryption key used for all encrypted fields
|
||||
static $sLibrary = null; // Encryption library used for all encrypted fields
|
||||
@@ -9243,7 +9243,7 @@ class AttributeSubItem extends AttributeDefinition
|
||||
*/
|
||||
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
|
||||
@@ -95,7 +95,7 @@ class MySQLHasGoneAwayException extends MySQLException
|
||||
{
|
||||
return array(
|
||||
2006,
|
||||
2013
|
||||
2013,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1296,8 +1296,8 @@ class CMDBSource
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
{
|
||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
|
||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||
{
|
||||
@@ -1734,8 +1734,20 @@ class CMDBSource
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
public static function GetClusterNb()
|
||||
{
|
||||
$result = 0;
|
||||
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['Value'];
|
||||
}
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.5.0 N°1001 switch to utf8mb4
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
*/
|
||||
class ObjectResult
|
||||
{
|
||||
public $code;
|
||||
use SanitizeTrait;
|
||||
|
||||
public $code;
|
||||
public $message;
|
||||
public $class;
|
||||
public $key;
|
||||
@@ -122,6 +124,19 @@ class ObjectResult
|
||||
{
|
||||
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
|
||||
}
|
||||
|
||||
public function SanitizeContent()
|
||||
{
|
||||
foreach($this->fields as $sFieldAttCode => $fieldValue)
|
||||
{
|
||||
try {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->class, $sFieldAttCode);
|
||||
} catch (Exception $e) { // for special cases like ID
|
||||
continue;
|
||||
}
|
||||
$this->SanitizeFieldIfSensitive($this->fields, $sFieldAttCode, $fieldValue, $oAttDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +196,16 @@ class RestResultWithObjects extends RestResult
|
||||
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$this->objects[$sObjKey] = $oObjRes;
|
||||
}
|
||||
|
||||
public function SanitizeContent()
|
||||
{
|
||||
parent::SanitizeContent();
|
||||
|
||||
foreach($this->objects as $sObjKey => $oObjRes)
|
||||
{
|
||||
$oObjRes->SanitizeContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RestResultWithRelations extends RestResultWithObjects
|
||||
@@ -247,9 +272,10 @@ class RestDelete
|
||||
*
|
||||
* @package Core
|
||||
*/
|
||||
class CoreServices implements iRestServiceProvider
|
||||
class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||
{
|
||||
/**
|
||||
use SanitizeTrait;
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
@@ -663,6 +689,33 @@ class CoreServices implements iRestServiceProvider
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
public function SanitizeJsonInput(string $sJsonInput): string
|
||||
{
|
||||
$sSanitizedJsonInput = $sJsonInput;
|
||||
$aJsonData = json_decode($sSanitizedJsonInput, true);
|
||||
$sOperation = $aJsonData['operation'];
|
||||
|
||||
switch ($sOperation) {
|
||||
case 'core/check_credentials':
|
||||
if (isset($aJsonData['password'])) {
|
||||
$aJsonData['password'] = '*****';
|
||||
}
|
||||
break;
|
||||
case 'core/update':
|
||||
case 'core/create':
|
||||
default :
|
||||
$sClass = $aJsonData['class'];
|
||||
if (isset($aJsonData['fields'])) {
|
||||
foreach ($aJsonData['fields'] as $sFieldAttCode => $fieldValue) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sFieldAttCode);
|
||||
$this->SanitizeFieldIfSensitive($aJsonData['fields'], $sFieldAttCode, $fieldValue, $oAttDef);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return json_encode($aJsonData, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for object deletion
|
||||
@@ -802,3 +855,50 @@ class CoreServices implements iRestServiceProvider
|
||||
return $iLimit * max(0, $iPage - 1);
|
||||
}
|
||||
}
|
||||
|
||||
trait SanitizeTrait
|
||||
{
|
||||
/**
|
||||
* Sanitize a field if it is sensitive.
|
||||
*
|
||||
* @param array $fields The fields array
|
||||
* @param string $sFieldAttCode The attribute code
|
||||
* @param mixed $oAttDef The attribute definition
|
||||
* @throws Exception
|
||||
*/
|
||||
private function SanitizeFieldIfSensitive(array &$fields, string $sFieldAttCode, $fieldValue, $oAttDef): void
|
||||
{
|
||||
// for simple attribute
|
||||
if ($oAttDef instanceof iAttributeNoGroupBy) // iAttributeNoGroupBy is equivalent to sensitive attribute
|
||||
{
|
||||
$fields[$sFieldAttCode] = '*****';
|
||||
return;
|
||||
}
|
||||
// for 1-n / n-n relation
|
||||
if ($oAttDef instanceof AttributeLinkedSet) {
|
||||
foreach ($fieldValue as $i => $aLnkValues) {
|
||||
foreach ($aLnkValues as $sLnkAttCode => $sLnkValue) {
|
||||
$oLnkAttDef = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sLnkAttCode);
|
||||
if ($oLnkAttDef instanceof iAttributeNoGroupBy) { // 1-n relation
|
||||
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeLinkedSetIndirect && $oLnkAttDef instanceof AttributeExternalField) { // for n-n relation
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLnkAttDef->GetTargetClass(), $oLnkAttDef->GetExtAttCode());
|
||||
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
|
||||
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// for external attribute
|
||||
if ($oAttDef instanceof AttributeExternalField) {
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
|
||||
$fields[$sFieldAttCode] = '*****';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,7 +537,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
throw new Exception('graphviz not found');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -592,7 +592,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
throw new Exception('graphviz not found');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||
$version: "v2.7.10";
|
||||
$version: "v2.7.12";
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
// Base colors
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-cas/2.7.10',
|
||||
'authent-cas/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-external/2.7.10',
|
||||
'authent-external/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-ldap/2.7.10',
|
||||
'authent-ldap/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-local/2.7.10',
|
||||
'authent-local/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'combodo-db-tools/2.7.10',
|
||||
'combodo-db-tools/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-attachments/2.7.10',
|
||||
'itop-attachments/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-backup/2.7.10',
|
||||
'itop-backup/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-bridge-virtualization-storage/2.7.10',
|
||||
'itop-bridge-virtualization-storage/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-change-mgmt-itil/2.7.10',
|
||||
'itop-change-mgmt-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-change-mgmt/2.7.10',
|
||||
'itop-change-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-config-mgmt/2.7.10',
|
||||
'itop-config-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-config/2.7.10',
|
||||
'itop-config/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-core-update/2.7.10',
|
||||
'itop-core-update/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-datacenter-mgmt/2.7.10',
|
||||
'itop-datacenter-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-endusers-devices/2.7.10',
|
||||
'itop-endusers-devices/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-files-information/2.7.10',
|
||||
'itop-files-information/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-full-itil/2.7.10',
|
||||
'itop-full-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
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:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-hub-connector/2.7.10',
|
||||
'itop-hub-connector/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-incident-mgmt-itil/2.7.10',
|
||||
'itop-incident-mgmt-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-knownerror-mgmt/2.7.10',
|
||||
'itop-knownerror-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-oauth-client/2.7.10',
|
||||
'itop-oauth-client/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-portal-base/2.7.10', array(
|
||||
'itop-portal-base/2.7.12', array(
|
||||
// Identification
|
||||
'label' => 'Portal Development Library',
|
||||
'category' => 'Portal',
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
p_user_profile_brick_edit_person:
|
||||
path: '/user/edit_person'
|
||||
defaults:
|
||||
_controller: 'Combodo\iTop\Portal\Controller\UserProfileBrickController::EditPerson'
|
||||
|
||||
p_user_profile_brick:
|
||||
path: '/user/{sBrickId}'
|
||||
defaults:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1246,7 +1246,12 @@ class ObjectController extends BrickController
|
||||
$bIgnoreSilos = $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass);
|
||||
$aParams = array('objects_id' => $aObjectIds);
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT $sObjectClass WHERE id IN (:objects_id)");
|
||||
if ($bIgnoreSilos === true)
|
||||
if (!$oScopeValidator->AddScopeToQuery($oSearch, $sObjectClass)
|
||||
) {
|
||||
IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to read ' . $sObjectClass . ' object.');
|
||||
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
|
||||
}
|
||||
if ($bIgnoreSilos === true)
|
||||
{
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
use Dict;
|
||||
/**
|
||||
* Class UserProfileBrickController
|
||||
*
|
||||
@@ -66,34 +66,9 @@ class UserProfileBrickController extends BrickController
|
||||
$oRequestManipulator = $this->get('request_manipulator');
|
||||
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $ObjectFormHandler */
|
||||
$ObjectFormHandler = $this->get('object_form_handler');
|
||||
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
|
||||
$oBrickCollection = $this->get('brick_collection');
|
||||
$oBrick = $this->GetBrick($sBrickId);
|
||||
|
||||
// If the brick id was not specified, we get the first one registered that is an instance of UserProfileBrick as default
|
||||
if ($sBrickId === null)
|
||||
{
|
||||
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oTmpBrick */
|
||||
foreach ($oBrickCollection->GetBricks() as $oTmpBrick)
|
||||
{
|
||||
if ($oTmpBrick instanceof UserProfileBrick)
|
||||
{
|
||||
$oBrick = $oTmpBrick;
|
||||
}
|
||||
}
|
||||
|
||||
// We make sure a UserProfileBrick was found
|
||||
if (!isset($oBrick) || $oBrick === null)
|
||||
{
|
||||
$oBrick = new UserProfileBrick();
|
||||
//throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
|
||||
}
|
||||
|
||||
$aData = array();
|
||||
$aData = array();
|
||||
|
||||
// Setting form mode regarding the demo mode parameter
|
||||
$bDemoMode = MetaModel::GetConfig()->Get('demo_mode');
|
||||
@@ -130,11 +105,12 @@ class UserProfileBrickController extends BrickController
|
||||
$oCurContact = UserRights::GetContactObject();
|
||||
$sCurContactClass = get_class($oCurContact);
|
||||
$sCurContactId = $oCurContact->GetKey();
|
||||
|
||||
$aForm = $oBrick->GetForm();
|
||||
$aForm['submit_endpoint'] = $this->generateUrl('p_user_profile_brick_edit_person', ['sBrickId' => $sBrickId]);
|
||||
// Preparing forms
|
||||
$aData['forms']['contact'] = $ObjectFormHandler->HandleForm($oRequest, $sFormMode, $sCurContactClass, $sCurContactId,
|
||||
$oBrick->GetForm());
|
||||
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
|
||||
$aData['forms']['contact'] = $ObjectFormHandler->HandleForm($oRequest, $sFormMode, $sCurContactClass, $sCurContactId,
|
||||
$aForm);
|
||||
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
|
||||
// - If user can change password, we display the form
|
||||
$aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null;
|
||||
|
||||
@@ -150,6 +126,35 @@ class UserProfileBrickController extends BrickController
|
||||
return $oResponse;
|
||||
}
|
||||
|
||||
public function EditPerson(Request $oRequest)
|
||||
{
|
||||
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
|
||||
$oObjectFormHandler = $this->get('object_form_handler');
|
||||
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
|
||||
$oSecurityHelper = $this->get('security_helper');
|
||||
|
||||
$oCurContact = UserRights::GetContactObject();
|
||||
$sObjectClass = get_class($oCurContact);
|
||||
$sObjectId = $oCurContact->GetKey();
|
||||
|
||||
// Checking security layers
|
||||
// Warning : This is a dirty quick fix to allow editing its own contact information
|
||||
$bAllowWrite = ($sObjectClass === 'Person' && $sObjectId == UserRights::GetContactId());
|
||||
if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sObjectClass, $sObjectId) && !$bAllowWrite) {
|
||||
IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to modify ' . $sObjectClass . '::' . $sObjectId . ' object.');
|
||||
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
|
||||
}
|
||||
|
||||
$aForm = $this->GetBrick()->GetForm();
|
||||
$aForm['submit_endpoint'] = $this->generateUrl('p_user_profile_brick_edit_person');
|
||||
|
||||
$aData = ['sMode' => 'edit'];
|
||||
$aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId, $aForm);
|
||||
|
||||
return new JsonResponse($aData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $oRequest
|
||||
* @param string $sFormMode
|
||||
@@ -388,4 +393,34 @@ class UserProfileBrickController extends BrickController
|
||||
return $aFormData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sBrickId
|
||||
* @return \Combodo\iTop\Portal\Brick\PortalBrick|UserProfileBrick
|
||||
* @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException
|
||||
*/
|
||||
public function GetBrick($sBrickId = null)
|
||||
{
|
||||
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
|
||||
$oBrickCollection = $this->get('brick_collection');
|
||||
|
||||
// If the brick id was not specified, we get the first one registered that is an instance of UserProfileBrick as default
|
||||
if ($sBrickId === null) {
|
||||
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oTmpBrick */
|
||||
foreach ($oBrickCollection->GetBricks() as $oTmpBrick) {
|
||||
if ($oTmpBrick instanceof UserProfileBrick) {
|
||||
$oBrick = $oTmpBrick;
|
||||
}
|
||||
}
|
||||
|
||||
// We make sure a UserProfileBrick was found
|
||||
if (!isset($oBrick) || $oBrick === null) {
|
||||
$oBrick = new UserProfileBrick();
|
||||
//throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.');
|
||||
}
|
||||
} else {
|
||||
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
|
||||
}
|
||||
return $oBrick;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -132,10 +132,8 @@ class ObjectFormHandlerHelper
|
||||
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
|
||||
|
||||
// - Retrieve form properties
|
||||
if ($aFormProperties === null)
|
||||
{
|
||||
$aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
|
||||
}
|
||||
$aFormProperties = $aFormProperties ?? ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
|
||||
|
||||
// - Create and
|
||||
if (empty($sOperation))
|
||||
{
|
||||
@@ -243,13 +241,17 @@ class ObjectFormHandlerHelper
|
||||
case static::ENUM_MODE_CREATE:
|
||||
case static::ENUM_MODE_EDIT:
|
||||
case static::ENUM_MODE_VIEW:
|
||||
$sFormEndpoint = $this->oUrlGenerator->generate(
|
||||
'p_object_'.$sMode,
|
||||
array(
|
||||
'sObjectClass' => $sObjectClass,
|
||||
'sObjectId' => $sObjectId,
|
||||
)
|
||||
);
|
||||
if(array_key_exists('submit_endpoint', $aFormProperties)) {
|
||||
$sFormEndpoint = $aFormProperties['submit_endpoint'];
|
||||
} else {
|
||||
$sFormEndpoint = $this->oUrlGenerator->generate(
|
||||
'p_object_' . $sMode,
|
||||
array(
|
||||
'sObjectClass' => $sObjectClass,
|
||||
'sObjectId' => $sObjectId,
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case static::ENUM_MODE_APPLY_STIMULUS:
|
||||
@@ -282,7 +284,8 @@ class ObjectFormHandlerHelper
|
||||
->SetActionRulesToken($sActionRulesToken)
|
||||
->SetRenderer($oFormRenderer)
|
||||
->SetFormProperties($aFormProperties);
|
||||
|
||||
$oFormManager->PrepareFormAndHTMLDocument();
|
||||
$oFormManager->PrepareFields();
|
||||
$oFormManager->Build();
|
||||
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
|
||||
// Check the number of editable fields
|
||||
|
||||
@@ -64,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 %}
|
||||
@@ -464,8 +464,8 @@
|
||||
sBody = '{{ 'Error:XHR:Fail'|dict_format(constant('ITOP_APPLICATION_SHORT'))|escape('js') }}';
|
||||
}
|
||||
var oModalElem = $('#modal-for-alert');
|
||||
oModalElem.find('.modal-content .modal-header .modal-title').html(sTitle);
|
||||
oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').html(sBody);
|
||||
oModalElem.find('.modal-content .modal-header .modal-title').text(sTitle);
|
||||
oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').text(sBody);
|
||||
oModalElem.modal('show');
|
||||
};
|
||||
{% endblock %}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-portal/2.7.10', array(
|
||||
'itop-portal/2.7.12', array(
|
||||
// Identification
|
||||
'label' => 'Enhanced Customer Portal',
|
||||
'category' => 'Portal',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-problem-mgmt/2.7.10',
|
||||
'itop-problem-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-profiles-itil/2.7.10',
|
||||
'itop-profiles-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-request-mgmt-itil/2.7.10',
|
||||
'itop-request-mgmt-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-request-mgmt/2.7.10',
|
||||
'itop-request-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-service-mgmt-provider/2.7.10',
|
||||
'itop-service-mgmt-provider/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-service-mgmt/2.7.10',
|
||||
'itop-service-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-sla-computation/2.7.10',
|
||||
'itop-sla-computation/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-storage-mgmt/2.7.10',
|
||||
'itop-storage-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__,
|
||||
'itop-tickets/2.7.10',
|
||||
'itop-tickets/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-virtualization-mgmt/2.7.10',
|
||||
'itop-virtualization-mgmt/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-welcome-itil/2.7.10',
|
||||
'itop-welcome-itil/2.7.12',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<information>
|
||||
<version>2.7.10</version>
|
||||
<version>2.7.12</version>
|
||||
</information>
|
||||
|
||||
@@ -81,7 +81,7 @@ function DisplayPreferences($oP)
|
||||
$oP->add('<fieldset><legend>'.Dict::S('UI:FavoriteOtherSettings').'</legend>');
|
||||
$oP->add('<form method="post" onsubmit="return ValidateOtherSettings()">');
|
||||
|
||||
$iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$iDefaultPageSize = (int)appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$oP->add('<p>'.Dict::Format('UI:Favorites:Default_X_ItemsPerPage', '<input id="default_page_size" name="default_page_size" type="text" size="3" value="'.$iDefaultPageSize.'"/><span id="v_default_page_size"></span>').'</p>');
|
||||
|
||||
$bShow = utils::IsArchiveMode() || appUserPreferences::GetPref('show_obsolete_data', MetaModel::GetConfig()->Get('obsolescence.show_obsolete_data'));
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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."'");
|
||||
@@ -1116,7 +1118,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load data from XML files for the selected modules (structural data and/or sample data)
|
||||
* @param array[] $aAvailableModules All available modules and their definition
|
||||
@@ -1126,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)
|
||||
@@ -1173,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
|
||||
@@ -1185,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;
|
||||
@@ -1199,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
|
||||
@@ -1225,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
|
||||
@@ -1265,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
|
||||
|
||||
@@ -1230,6 +1230,12 @@ EOF
|
||||
$aResult['checks'][] = new CheckResult(CheckResult::INFO, "MySQL server's max_connections is set to $iMaxConnections.");
|
||||
}
|
||||
|
||||
$iClusters = $oDBSource->GetClusterNb();
|
||||
if ($iClusters > 0) {
|
||||
SetupLog::Warning('Warning - Using Galera will cause malfunctions and data corruptions. Combodo does not support this type of infrastructure.');
|
||||
$aResult['checks'][] = new CheckResult(CheckResult::WARNING, 'Using Galera will cause malfunctions and data corruptions. Combodo does not support this type of infrastructure.');
|
||||
}
|
||||
|
||||
try {
|
||||
$aResult['databases'] = $oDBSource->ListDB();
|
||||
}
|
||||
|
||||
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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
setup/unattended-install/README.md
Normal file
73
setup/unattended-install/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 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)
|
||||
|
||||
# unattended-install.php
|
||||
|
||||
## Usage
|
||||
|
||||
Execution of the unattended installation
|
||||
> Note:
|
||||
> Because the installation runs from the command line, make sure that the current user has enough rights to access the different locations and that the web server will be able to access the files and directories created during the scripted installation. In order to exactly emulate the behavior of
|
||||
the interactive installation it may be a good practice to run this installation from the user account used for running the web server process.
|
||||
|
||||
Launch the script with the following command: ```bash php unattended_install.php --param-file=fresh-install.xml ```
|
||||
|
||||
Where: `fresh-install.xml` is the response file containing your desired settings for the installation (there are 4 models available in the folder `xml_setup`: fresh-install.xml, itil-fresh-install.xml, itil-upgrade.xml, upgrade.xml)
|
||||
|
||||
Fresh installation parameters
|
||||
> Important:
|
||||
> In the case of a fresh installation (<mode>install</mode>), do not forget to complete below mandatory parameters before:
|
||||
|
||||
```xml
|
||||
<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>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
To get all available options of the script, you can perform the following command :
|
||||
```php unattended-install.php --help```
|
||||
|
||||
# install-itop.sh
|
||||
|
||||
## Usage
|
||||
|
||||
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>');
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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"/>
|
||||
@@ -45,7 +41,9 @@
|
||||
<testsuite name="Setup">
|
||||
<directory>unitary-tests/setup</directory>
|
||||
</testsuite>
|
||||
<!-- Note: The unitary-tests/sources/application/TwigBase is omitted for now as the test is not working -->
|
||||
<testsuite name="SourcesApplicationTwigBase">
|
||||
<directory>unitary-tests/sources/application/TwigBase</directory>
|
||||
</testsuite>
|
||||
<testsuite name="SourcesApplicationSearch">
|
||||
<directory>unitary-tests/sources/application/search</directory>
|
||||
</testsuite>
|
||||
|
||||
@@ -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()}
|
||||
@@ -96,23 +85,6 @@ abstract class ItopTestCase extends TestCase
|
||||
require_once APPROOT . $sFileRelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to load a module file. The caller test must be in that module !
|
||||
* Will browse dir up to find a module.*.php
|
||||
*
|
||||
* @param string $sFileRelPath for example 'portal/src/Helper/ApplicationHelper.php'
|
||||
* @since 2.7.10 3.1.1 3.2.0 N°6709 method creation
|
||||
*/
|
||||
protected function RequireOnceCurrentModuleFile(string $sFileRelPath): void
|
||||
{
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
$sCallerFileFullPath = $aStack[0]['file'];
|
||||
$sCallerDir = dirname($sCallerFileFullPath);
|
||||
|
||||
$sModuleRootPath = static::GetFirstDirUpContainingFile($sCallerDir, 'module.*.php');
|
||||
require_once $sModuleRootPath . $sFileRelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require once a unit test file (eg. a mock class) from its relative path from the *current* dir.
|
||||
* This ensure that required files don't crash when unit tests dir is moved in the iTop structure (see N°5608)
|
||||
@@ -130,26 +102,6 @@ abstract class ItopTestCase extends TestCase
|
||||
require_once $sCallerDirAbsPath . DIRECTORY_SEPARATOR . $sFileRelPath;
|
||||
}
|
||||
|
||||
private static function GetFirstDirUpContainingFile(string $sSearchPath, string $sFileToFindGlobPattern): ?string
|
||||
{
|
||||
for ($iDepth = 0; $iDepth < 8; $iDepth++) {
|
||||
$aGlobFiles = glob($sSearchPath . '/' . $sFileToFindGlobPattern);
|
||||
if (is_array($aGlobFiles) && (count($aGlobFiles) > 0)) {
|
||||
return $sSearchPath . '/';
|
||||
}
|
||||
$iOffsetSep = strrpos($sSearchPath, '/');
|
||||
if ($iOffsetSep === false) {
|
||||
$iOffsetSep = strrpos($sSearchPath, '\\');
|
||||
if ($iOffsetSep === false) {
|
||||
// Do not throw an exception here as PHPUnit will not show it clearly when determing the list of test to perform
|
||||
return 'Could not find the approot file in ' . $sSearchPath;
|
||||
}
|
||||
}
|
||||
$sSearchPath = substr($sSearchPath, 0, $iOffsetSep);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function debug($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST) {
|
||||
@@ -164,7 +116,7 @@ abstract class ItopTestCase extends TestCase
|
||||
|
||||
public function GetMicroTime()
|
||||
{
|
||||
list($uSec, $sec) = explode(" ", microtime());
|
||||
[$uSec, $sec] = explode(" ", microtime());
|
||||
return ((float)$uSec + (float)$sec);
|
||||
}
|
||||
|
||||
@@ -206,7 +158,7 @@ abstract class ItopTestCase extends TestCase
|
||||
/**
|
||||
* @param string $sObjectClass for example DBObject::class
|
||||
* @param string $sMethodName
|
||||
* @param object $oObject
|
||||
* @param object|null $oObject
|
||||
* @param array $aArgs
|
||||
*
|
||||
* @return mixed method result
|
||||
@@ -249,7 +201,7 @@ abstract class ItopTestCase extends TestCase
|
||||
* @throws \ReflectionException
|
||||
* @since 2.7.8 3.0.3 3.1.0
|
||||
*/
|
||||
public function GetNonPublicProperty(object $oObject, string $sProperty)
|
||||
public function GetNonPublicProperty($oObject, string $sProperty)
|
||||
{
|
||||
$oProperty = $this->GetProperty(get_class($oObject), $sProperty);
|
||||
|
||||
@@ -318,7 +270,7 @@ abstract class ItopTestCase extends TestCase
|
||||
* @throws \ReflectionException
|
||||
* @since 2.7.8 3.0.3 3.1.0
|
||||
*/
|
||||
public function SetNonPublicProperty(object $oObject, string $sProperty, $value)
|
||||
public function SetNonPublicProperty($oObject, string $sProperty, $value)
|
||||
{
|
||||
$oProperty = $this->GetProperty(get_class($oObject), $sProperty);
|
||||
$oProperty->setValue($oObject, $value);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
|
||||
<classes>
|
||||
<class id="TestServer" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>bizmodel</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>test_server</db_table>
|
||||
<db_key_field>id</db_key_field>
|
||||
</properties>
|
||||
<presentation/>
|
||||
<methods/>
|
||||
<fields>
|
||||
<field id="contact_list" xsi:type="AttributeLinkedSetIndirect">
|
||||
<linked_class>lnkContactTestToServer</linked_class>
|
||||
<ext_key_to_me>test_server_id</ext_key_to_me>
|
||||
<ext_key_to_remote>contact_test_id</ext_key_to_remote>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
</field>
|
||||
<field id="password_list" xsi:type="AttributeLinkedSet">
|
||||
<linked_class>PasswordTest</linked_class>
|
||||
<ext_key_to_me>server_test_id</ext_key_to_me>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
</field>
|
||||
<field id="name" xsi:type="AttributeString">
|
||||
<sql>name</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
|
||||
|
||||
<class id="ContactTest" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>bizmodel</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>contact_test</db_table>
|
||||
<db_key_field>id</db_key_field>
|
||||
</properties>
|
||||
<presentation/>
|
||||
<methods/>
|
||||
<fields>
|
||||
<field id="password" xsi:type="AttributeEncryptedString">
|
||||
<sql>password</sql>
|
||||
</field>
|
||||
<field id="server_test_list" xsi:type="AttributeLinkedSetIndirect">
|
||||
<linked_class>lnkContactTestToServer</linked_class>
|
||||
<ext_key_to_me>contact_test_id</ext_key_to_me>
|
||||
<ext_key_to_remote>test_server_id</ext_key_to_remote>
|
||||
<is_null_allowed>true</is_null_allowed>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
|
||||
|
||||
<class id="lnkContactTestToServer" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>bizmodel</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>lnk_contact_server_test</db_table>
|
||||
<db_key_field>id</db_key_field>
|
||||
</properties>
|
||||
<presentation/>
|
||||
<methods/>
|
||||
<fields>
|
||||
<field id="contact_test_password" xsi:type="AttributeExternalField" _delta="define">
|
||||
<extkey_attcode>contact_test_id</extkey_attcode>
|
||||
<target_attcode>password</target_attcode>
|
||||
</field>
|
||||
<field id="test_server_id" xsi:type="AttributeExternalKey" _delta="define">
|
||||
<target_class>TestServer</target_class>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
<sql>test_server</sql>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
|
||||
</field>
|
||||
<field id="contact_test_id" xsi:type="AttributeExternalKey" _delta="define">
|
||||
<target_class>ContactTest</target_class>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
<sql>contact_test</sql>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="PasswordTest" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>bizmodel</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>password_test</db_table>
|
||||
<db_key_field>id</db_key_field>
|
||||
</properties>
|
||||
<presentation/>
|
||||
<methods/>
|
||||
<fields>
|
||||
<field id="server_test_id" xsi:type="AttributeExternalKey" _delta="define">
|
||||
<target_class>TestServer</target_class>
|
||||
<sql>server_test_id</sql>
|
||||
<on_target_delete>DEL_MANUAL</on_target_delete>
|
||||
</field>
|
||||
<field id="password" xsi:type="AttributeEncryptedString" _delta="define">
|
||||
<sql>password</sql>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
</itop_design>
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use ArchivedObjectException;
|
||||
use AttributeEncryptedString;
|
||||
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||
use CoreException;
|
||||
use CoreUnexpectedValue;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use ormLinkSet;
|
||||
use PasswordTest;
|
||||
use RestResultWithObjects;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class RestServicesSanitizeOutputTest extends ItopCustomDatamodelTestCase
|
||||
{
|
||||
private const SIMPLE_PASSWORD = '123456';
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
// Workaround to cope with inconsistent settings in itop-config files from the CI
|
||||
AttributeEncryptedString::$sKey = '6eb9d9afa3ee0fbcebe622a33bf57aaeafb7c37998fd24c403c2522c2d60117f';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function testSanitizeAttributeOnRequestedObject()
|
||||
{
|
||||
$oContactTest = MetaModel::NewObject('ContactTest', [
|
||||
'password' => self::SIMPLE_PASSWORD]
|
||||
);
|
||||
$oRestResultWithObject = new RestResultWithObjects();
|
||||
$oRestResultWithObject->AddObject(0, 'ok', $oContactTest, ['ContactTest' => ['password']]);
|
||||
$oRestResultWithObject->SanitizeContent();
|
||||
static::assertEquals(
|
||||
'{"objects":{"ContactTest::-1":{"code":0,"message":"ok","class":"ContactTest","key":-1,"fields":{"password":"*****"}}},"code":0,"message":null}',
|
||||
json_encode($oRestResultWithObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanitizeAttributeExternalFieldOnLink()
|
||||
{
|
||||
$oContactTest = $this->createObject('ContactTest', [
|
||||
'password' => self::SIMPLE_PASSWORD]
|
||||
);
|
||||
|
||||
$oTestServer = $this->createObject('TestServer', [
|
||||
'name' => 'test_server',
|
||||
]);
|
||||
|
||||
|
||||
// create lnkContactTestToServer
|
||||
$oLnkContactTestToServer = $this->createObject('lnkContactTestToServer', [
|
||||
'contact_test_id' => $oContactTest->GetKey(),
|
||||
'test_server_id' => $oTestServer->GetKey()
|
||||
]);
|
||||
|
||||
$oRestResultWithObject = new RestResultWithObjects();
|
||||
$oRestResultWithObject->AddObject(0, 'ok', $oLnkContactTestToServer,
|
||||
['lnkContactTestToServer' => ['contact_test_password']]);
|
||||
|
||||
$oRestResultWithObject->SanitizeContent();
|
||||
|
||||
static::assertContains(
|
||||
'*****',
|
||||
json_encode($oRestResultWithObject));
|
||||
|
||||
static::assertNotContains(
|
||||
self::SIMPLE_PASSWORD,
|
||||
json_encode($oRestResultWithObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanitizeAttributeOnObjectRelatedThroughNNRelation()
|
||||
{
|
||||
$oContactTest = $this->createObject('ContactTest', [
|
||||
'password' => self::SIMPLE_PASSWORD]);
|
||||
|
||||
$oTestServer = $this->createObject('TestServer', [
|
||||
'name' => 'test_server',
|
||||
]);
|
||||
|
||||
// create lnkContactTestToServer
|
||||
$this->createObject('lnkContactTestToServer', [
|
||||
'contact_test_id' => $oContactTest->GetKey(),
|
||||
'test_server_id' => $oTestServer->GetKey()
|
||||
]);
|
||||
|
||||
$oRestResultWithObject = new RestResultWithObjects();
|
||||
$oRestResultWithObject->AddObject(0, 'ok', $oTestServer,
|
||||
['TestServer' => ['contact_list']]);
|
||||
|
||||
$oRestResultWithObject->SanitizeContent();
|
||||
static::assertContains(
|
||||
'*****',
|
||||
json_encode($oRestResultWithObject));
|
||||
|
||||
static::assertNotContains(
|
||||
self::SIMPLE_PASSWORD,
|
||||
json_encode($oRestResultWithObject));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws ArchivedObjectException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanitizeOnObjectRelatedThrough1NRelation()
|
||||
{
|
||||
$oTestServer = $this->createObject('TestServer', [
|
||||
'name' => 'my_server',
|
||||
]);
|
||||
|
||||
$oPassword = new PasswordTest();
|
||||
$oPassword->Set('password', self::SIMPLE_PASSWORD);
|
||||
$oPassword->Set('server_test_id', $oTestServer->GetKey());
|
||||
|
||||
/** @var ormLinkSet $oContactList */
|
||||
$oContactList = $oTestServer->Get('password_list');
|
||||
$oContactList->AddItem($oPassword);
|
||||
$oTestServer->Set('password_list', $oContactList);
|
||||
|
||||
$oRestResultWithObject = new RestResultWithObjects();
|
||||
$oRestResultWithObject->AddObject(0, 'ok', $oTestServer, ['TestServer' => ['id', 'password_list']]);
|
||||
$oRestResultWithObject->SanitizeContent();
|
||||
|
||||
static::assertContains(
|
||||
'*****',
|
||||
json_encode($oRestResultWithObject));
|
||||
|
||||
static::assertNotContains(
|
||||
self::SIMPLE_PASSWORD,
|
||||
json_encode($oRestResultWithObject));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Abs path to the XML delta to use for the tests of that class
|
||||
*/
|
||||
public function GetDatamodelDeltaAbsPath(): string
|
||||
{
|
||||
return __DIR__ . '/Delta/delta_test_sanitize_output.xml';
|
||||
}
|
||||
}
|
||||
125
tests/php-unit-tests/unitary-tests/core/RestServicesTest.php
Normal file
125
tests/php-unit-tests/unitary-tests/core/RestServicesTest.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use CoreException;
|
||||
use CoreServices;
|
||||
use CoreUnexpectedValue;
|
||||
use RestResultWithObjects;
|
||||
use UserLocal;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class RestServicesTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
* @dataProvider providerTestSanitizeJsonInput
|
||||
*/
|
||||
public function testSanitizeJsonInput($sJsonData, $sExpectedJsonDataSanitized)
|
||||
{
|
||||
$oRS = new CoreServices();
|
||||
$sOutputJson = $oRS->SanitizeJsonInput($sJsonData);
|
||||
static::assertEquals($sExpectedJsonDataSanitized, $sOutputJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function providerTestSanitizeJsonInput(): array
|
||||
{
|
||||
return [
|
||||
'core/check_credentials' => [
|
||||
'{"operation": "core/check_credentials", "user": "admin", "password": "admin"}',
|
||||
'{
|
||||
"operation": "core/check_credentials",
|
||||
"user": "admin",
|
||||
"password": "*****"
|
||||
}'
|
||||
],
|
||||
'core/update' => [
|
||||
'{"operation": "core/update", "comment": "Update user", "class": "UserLocal", "key": {"id":1}, "output_fields": "first_name, password", "fields": {"password" : "123456"}}',
|
||||
'{
|
||||
"operation": "core/update",
|
||||
"comment": "Update user",
|
||||
"class": "UserLocal",
|
||||
"key": {
|
||||
"id": 1
|
||||
},
|
||||
"output_fields": "first_name, password",
|
||||
"fields": {
|
||||
"password": "*****"
|
||||
}
|
||||
}'
|
||||
],
|
||||
'core/create' => [
|
||||
'{"operation": "core/create", "comment": "Create user", "class": "UserLocal", "fields": {"first_name": "John", "last_name": "Doe", "email": "jd@example/com", "password" : "123456"}}',
|
||||
'{
|
||||
"operation": "core/create",
|
||||
"comment": "Create user",
|
||||
"class": "UserLocal",
|
||||
"fields": {
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"email": "jd@example/com",
|
||||
"password": "*****"
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sOperation
|
||||
* @param $aJsonData
|
||||
* @param $sExpectedJsonDataSanitized
|
||||
* @return void
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @dataProvider providerTestSanitizeJsonOutput
|
||||
*/
|
||||
public function testSanitizeJsonOutput($sOperation, $aJsonData, $sExpectedJsonDataSanitized)
|
||||
{
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('password', '123456');
|
||||
$oRestResultWithObject = new RestResultWithObjects();
|
||||
$oRestResultWithObject->AddObject(0, 'ok', $oUser, ['UserLocal' => ['login', 'password']]);
|
||||
$oRestResultWithObject->SanitizeContent();
|
||||
static::assertEquals($sExpectedJsonDataSanitized, json_encode($oRestResultWithObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function providerTestSanitizeJsonOutput(): array
|
||||
{
|
||||
return [
|
||||
|
||||
'core/update' => [
|
||||
'core/update',
|
||||
['comment' => 'Update user', 'class' => 'UserLocal', 'key' => ['login' => 'my_example'], 'output_fields' => 'password', 'fields' => ['password' => 'opkB!req57']],
|
||||
'{"objects":{"UserLocal::-1":{"code":0,"message":"ok","class":"UserLocal","key":-1,"fields":{"login":"","password":"*****"}}},"code":0,"message":null}'
|
||||
],
|
||||
'core/create' => [
|
||||
'core/create',
|
||||
['comment' => 'Create user', 'class' => 'UserLocal', 'fields' => ['password' => 'Azertyuiiop*12', 'login' => 'toto', 'profile_list' => [1]]],
|
||||
'{"objects":{"UserLocal::-1":{"code":0,"message":"ok","class":"UserLocal","key":-1,"fields":{"login":"","password":"*****"}}},"code":0,"message":null}'
|
||||
],
|
||||
'core/get' => [
|
||||
'core/get',
|
||||
['comment' => 'Get user', 'class' => 'UserLocal', 'key' => ['login' => 'my_example'], 'output_fields' => 'first_name, password'],
|
||||
'{"objects":{"UserLocal::-1":{"code":0,"message":"ok","class":"UserLocal","key":-1,"fields":{"login":"","password":"*****"}}},"code":0,"message":null}'
|
||||
],
|
||||
'core/check_credentials' => [
|
||||
'core/check_credentials',
|
||||
['user' => 'admin', 'password' => 'admin'],
|
||||
'{"objects":{"UserLocal::-1":{"code":0,"message":"ok","class":"UserLocal","key":-1,"fields":{"login":"","password":"*****"}}},"code":0,"message":null}'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,58 +1,44 @@
|
||||
<?php
|
||||
namespace Combodo\iTop\Test\UnitTest;
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application\TwigBase;
|
||||
|
||||
use Combodo\iTop\Portal\Twig\AppExtension;
|
||||
use Twig_Environment;
|
||||
use Twig_Loader_Array;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class TwigTest extends ItopDataTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('core/config.class.inc.php');
|
||||
}
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('core/config.class.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the fix for ticket N°4384
|
||||
*
|
||||
* @dataProvider TemplateProvider
|
||||
*
|
||||
*/
|
||||
public function testTemplate($sFileName, $sExpected)
|
||||
{
|
||||
$sId = 'TestTwig';
|
||||
$oAppExtension = new AppExtension();
|
||||
/**
|
||||
* @covers N°4384 N°7810
|
||||
*
|
||||
*/
|
||||
public function testTemplate()
|
||||
{
|
||||
// Creating sandbox twig env. to load and test the custom form template
|
||||
$oTwig = new Environment(new FilesystemLoader(__DIR__.'/'));
|
||||
|
||||
// Creating sandbox twig env. to load and test the custom form template
|
||||
$oTwig = new Twig_Environment(new Twig_Loader_Array([$sId => $sFileName]));
|
||||
// Manually registering filters and functions as we didn't find how to do it automatically
|
||||
$oAppExtension = new AppExtension();
|
||||
$aFilters = $oAppExtension->getFilters();
|
||||
foreach ($aFilters as $oFilter)
|
||||
{
|
||||
$oTwig->addFilter($oFilter);
|
||||
}
|
||||
$aFunctions = $oAppExtension->getFunctions();
|
||||
foreach ($aFunctions as $oFunction)
|
||||
{
|
||||
$oTwig->addFunction($oFunction);
|
||||
}
|
||||
|
||||
// Manually registering filters and functions as we didn't find how to do it automatically
|
||||
$aFilters = $oAppExtension->getFilters();
|
||||
foreach ($aFilters as $oFilter)
|
||||
{
|
||||
$oTwig->addFilter($oFilter);
|
||||
}
|
||||
$aFunctions = $oAppExtension->getFunctions();
|
||||
foreach ($aFunctions as $oFunction)
|
||||
{
|
||||
$oTwig->addFunction($oFunction);
|
||||
}
|
||||
}
|
||||
$sOutput = $oTwig->render('test.html.twig');
|
||||
|
||||
public static function testTemplateProvider()
|
||||
{
|
||||
$aReturn = array();
|
||||
$aReturn['filter_system'] = [
|
||||
'sFileName' => 'test.html',
|
||||
'expected' =>file_get_contents(dirname(__FILE__).'/test.html'),
|
||||
];
|
||||
|
||||
return $aReturn;
|
||||
}
|
||||
$this->assertEquals(file_get_contents(__DIR__.'/test.html'), $sOutput);
|
||||
}
|
||||
}
|
||||
@@ -42,5 +42,8 @@ Smith, Dupond
|
||||
<div>['echo',1]|sort('system')|join</div>
|
||||
echo1
|
||||
|
||||
<div>[['id','']|sort('system')</div>
|
||||
id
|
||||
|
||||
POST /subscribe?0=cat+/etc/passwd HTTP/1.1
|
||||
email=""@attacker.tld
|
||||
@@ -47,5 +47,8 @@
|
||||
<div>['echo',1]|sort('system')|join</div>
|
||||
{{ ['echo',1]|sort('system')|join }}
|
||||
|
||||
<div>[['id','']|sort('system')</div>
|
||||
{{['id','']|sort('system')|join}}
|
||||
|
||||
POST /subscribe?0=cat+/etc/passwd HTTP/1.1
|
||||
email="{{ app.request.query.filter(0,0,1024,{'options':'system'}) }}"@attacker.tld
|
||||
@@ -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>
|
||||
|
||||
@@ -209,6 +209,13 @@ try
|
||||
/** @var iRestServiceProvider $oRS */
|
||||
$oRS = $aOpToRestService[$sOperation]['service_provider'];
|
||||
$sProvider = get_class($oRS);
|
||||
|
||||
if ($oRS instanceof iRestInputSanitizer) {
|
||||
$sSanitizedJsonInput = $oRS->SanitizeJsonInput($sJsonString);
|
||||
}
|
||||
else {
|
||||
$sSanitizedJsonInput = $sJsonString;
|
||||
}
|
||||
|
||||
CMDBObject::SetTrackOrigin('webservice-rest');
|
||||
$oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
|
||||
@@ -234,6 +241,7 @@ catch(Exception $e)
|
||||
//
|
||||
$sResponse = json_encode($oResult);
|
||||
|
||||
|
||||
if ($sResponse === false)
|
||||
{
|
||||
$oJsonIssue = new RestResult();
|
||||
@@ -267,7 +275,7 @@ if (MetaModel::GetConfig()->Get('log_rest_service'))
|
||||
$oLog->SetTrim('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('version', $sVersion);
|
||||
$oLog->Set('operation', $sOperation);
|
||||
$oLog->SetTrim('json_input', $sJsonString);
|
||||
$oLog->SetTrim('json_input', $sSanitizedJsonInput);
|
||||
|
||||
$oLog->Set('provider', $sProvider);
|
||||
$sMessage = $oResult->message;
|
||||
@@ -277,7 +285,8 @@ if (MetaModel::GetConfig()->Get('log_rest_service'))
|
||||
}
|
||||
$oLog->SetTrim('message', $sMessage);
|
||||
$oLog->Set('code', $oResult->code);
|
||||
$oLog->SetTrim('json_output', $sResponse);
|
||||
$oResult->SanitizeContent();
|
||||
$oLog->SetTrim('json_output', json_encode($oResult, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$oLog->DBInsertNoReload();
|
||||
$oKPI->ComputeAndReport('Log inserted');
|
||||
|
||||
Reference in New Issue
Block a user