mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-18 18:04:11 +01:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d229e08f02 | ||
|
|
b5adb2e82b | ||
|
|
386c90c601 | ||
|
|
5d0c61178b | ||
|
|
3bcae734e5 | ||
|
|
842e8f9e01 | ||
|
|
52cd4f7c5e | ||
|
|
995619af9b | ||
|
|
c842162fe2 | ||
|
|
83f99642e0 | ||
|
|
ae6a264d6d | ||
|
|
a06bf6ea7c | ||
|
|
bb8d4a92cb | ||
|
|
1429792690 | ||
|
|
1f26b59d90 | ||
|
|
7b093a6bba | ||
|
|
d4607ee815 | ||
|
|
5c0e92d51a | ||
|
|
cd4b3fdaab | ||
|
|
0030d5c2b8 | ||
|
|
95a0efedcf | ||
|
|
13a1d32f56 | ||
|
|
35155e4b7a | ||
|
|
77710f1613 | ||
|
|
2763b99142 | ||
|
|
db13c105ad | ||
|
|
2276539f24 | ||
|
|
9b7cd20d47 | ||
|
|
e1d644c33b | ||
|
|
c601082a5e | ||
|
|
5836be7131 | ||
|
|
6f40bb4c35 | ||
|
|
241bd1cdeb | ||
|
|
71c5f47cd8 | ||
|
|
74246a8278 | ||
|
|
c450c9426c | ||
|
|
46f9fe743c | ||
|
|
c31df5fff3 | ||
|
|
6e0af1a3b7 | ||
|
|
e9e18513be | ||
|
|
9d2fc883b8 | ||
|
|
913ea0cef2 | ||
|
|
82ba7f25b0 | ||
|
|
bb877a244b | ||
|
|
a12959d60e | ||
|
|
83434b5506 | ||
|
|
dcd4abe72b | ||
|
|
571520815a | ||
|
|
e9cff0920b | ||
|
|
905ee19519 | ||
|
|
0b95220d1b | ||
|
|
e1b2a767f5 | ||
|
|
3058b2eb00 | ||
|
|
38bc2d9d58 | ||
|
|
c8e8778d7b | ||
|
|
656fa3208a | ||
|
|
f647ce61c2 | ||
|
|
6b76e5a853 | ||
|
|
dbb6e43751 | ||
|
|
f07f0ba1c7 | ||
|
|
a5894c1a4c | ||
|
|
e06996a2e4 | ||
|
|
2f0e7c6d29 | ||
|
|
7115a6ae7d | ||
|
|
765560d1f5 | ||
|
|
bc024d9ed0 | ||
|
|
37a4a3eb47 | ||
|
|
54e9bd5c8e | ||
|
|
066a6d8b36 | ||
|
|
4123c6213d | ||
|
|
8265b9b034 | ||
|
|
c4756e8cec | ||
|
|
37351d6b3e | ||
|
|
57a085eec1 | ||
|
|
0019595923 | ||
|
|
4d61c14f80 | ||
|
|
cf1b613923 | ||
|
|
1304e2eb2d | ||
|
|
3cf16627c1 | ||
|
|
4aaa237bf9 | ||
|
|
cece15d10c | ||
|
|
aa15e009cb | ||
|
|
b9ca2ac13d | ||
|
|
80e1e0e61a | ||
|
|
ecebe4ecd5 | ||
|
|
8bfcb14d0c | ||
|
|
1cf1473d6b | ||
|
|
aa43425df3 | ||
|
|
35d77ff642 | ||
|
|
539fa43503 | ||
|
|
eb537f45f4 | ||
|
|
a2a4cd4e7a |
@@ -6,19 +6,20 @@ end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 140
|
||||
max_line_length = 300
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = 80,120
|
||||
ij_visual_guides = 300
|
||||
ij_wrap_on_typing = true
|
||||
|
||||
[*.css]
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_css_align_closing_brace_with_properties = false
|
||||
ij_css_blank_lines_around_nested_selector = 1
|
||||
ij_css_blank_lines_between_blocks = 1
|
||||
@@ -38,7 +39,9 @@ ij_css_use_double_quotes = true
|
||||
ij_css_value_alignment = do_not_align
|
||||
|
||||
[*.scss]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_visual_guides = none
|
||||
ij_scss_align_closing_brace_with_properties = false
|
||||
ij_scss_blank_lines_around_nested_selector = 1
|
||||
ij_scss_blank_lines_between_blocks = 1
|
||||
@@ -58,8 +61,8 @@ ij_scss_use_double_quotes = true
|
||||
ij_scss_value_alignment = 0
|
||||
|
||||
[*.twig]
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_wrap_on_typing = false
|
||||
ij_twig_keep_indents_on_empty_lines = false
|
||||
ij_twig_spaces_inside_comments_delimiters = true
|
||||
@@ -67,6 +70,7 @@ ij_twig_spaces_inside_delimiters = true
|
||||
ij_twig_spaces_inside_variable_delimiters = true
|
||||
|
||||
[.editorconfig]
|
||||
ij_visual_guides = none
|
||||
ij_editorconfig_align_group_field_declarations = false
|
||||
ij_editorconfig_space_after_colon = false
|
||||
ij_editorconfig_space_after_comma = true
|
||||
@@ -74,10 +78,12 @@ ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
|
||||
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul, phpunit.xml.dist}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_wrap_on_typing = false
|
||||
ij_xml_align_attributes = true
|
||||
ij_xml_align_text = false
|
||||
ij_xml_attribute_wrap = normal
|
||||
@@ -93,11 +99,12 @@ ij_xml_line_comment_at_first_column = true
|
||||
ij_xml_space_after_tag_name = false
|
||||
ij_xml_space_around_equals_in_attribute = false
|
||||
ij_xml_space_inside_empty_tag = false
|
||||
ij_xml_text_wrap = normal
|
||||
ij_xml_text_wrap = off
|
||||
|
||||
[{*.bash,*.sh,*.zsh}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_visual_guides = none
|
||||
ij_shell_binary_ops_start_line = false
|
||||
ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
@@ -108,6 +115,7 @@ ij_shell_switch_cases_indented = false
|
||||
indent_style = tab
|
||||
ij_continuation_indent_size = 4
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_javascript_align_imports = false
|
||||
ij_javascript_align_multiline_array_initializer_expression = false
|
||||
ij_javascript_align_multiline_binary_operation = false
|
||||
@@ -141,7 +149,7 @@ ij_javascript_chained_call_dot_on_new_line = true
|
||||
ij_javascript_class_brace_style = end_of_line
|
||||
ij_javascript_comma_on_new_line = false
|
||||
ij_javascript_do_while_brace_force = always
|
||||
ij_javascript_else_on_new_line = true
|
||||
ij_javascript_else_on_new_line = false
|
||||
ij_javascript_enforce_trailing_comma = keep
|
||||
ij_javascript_extends_keyword_wrap = off
|
||||
ij_javascript_extends_list_wrap = off
|
||||
@@ -297,6 +305,7 @@ ij_php_array_initializer_new_line_after_left_brace = true
|
||||
ij_php_array_initializer_right_brace_on_new_line = true
|
||||
ij_php_array_initializer_wrap = on_every_item
|
||||
ij_php_assignment_wrap = off
|
||||
ij_php_attributes_wrap = off
|
||||
ij_php_author_weight = 8
|
||||
ij_php_binary_operation_sign_on_next_line = false
|
||||
ij_php_binary_operation_wrap = off
|
||||
@@ -385,6 +394,7 @@ ij_php_new_line_after_php_opening_tag = false
|
||||
ij_php_null_type_position = in_the_end
|
||||
ij_php_package_weight = 28
|
||||
ij_php_param_weight = 5
|
||||
ij_php_parameters_attributes_wrap = off
|
||||
ij_php_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_php_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_php_phpdoc_blank_line_before_tags = true
|
||||
@@ -406,6 +416,7 @@ ij_php_see_weight = 3
|
||||
ij_php_since_weight = 28
|
||||
ij_php_sort_phpdoc_elements = true
|
||||
ij_php_space_after_colon = true
|
||||
ij_php_space_after_colon_in_named_argument = true
|
||||
ij_php_space_after_colon_in_return_type = true
|
||||
ij_php_space_after_comma = true
|
||||
ij_php_space_after_for_semicolon = true
|
||||
@@ -419,6 +430,7 @@ ij_php_space_before_catch_parentheses = true
|
||||
ij_php_space_before_class_left_brace = true
|
||||
ij_php_space_before_closure_left_parenthesis = true
|
||||
ij_php_space_before_colon = true
|
||||
ij_php_space_before_colon_in_named_argument = false
|
||||
ij_php_space_before_colon_in_return_type = false
|
||||
ij_php_space_before_comma = false
|
||||
ij_php_space_before_do_left_brace = true
|
||||
@@ -486,6 +498,7 @@ ij_php_while_on_new_line = false
|
||||
|
||||
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
ij_json_keep_line_breaks = true
|
||||
@@ -500,7 +513,8 @@ ij_json_wrap_long_lines = false
|
||||
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_visual_guides = none
|
||||
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
@@ -527,10 +541,16 @@ ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
[{*.yaml, *.yml}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
ij_yaml_autoinsert_sequence_marker = true
|
||||
ij_yaml_block_mapping_on_new_line = false
|
||||
ij_yaml_indent_sequence_value = true
|
||||
ij_yaml_keep_indents_on_empty_lines = false
|
||||
ij_yaml_keep_line_breaks = true
|
||||
ij_yaml_space_before_colon = true
|
||||
ij_yaml_sequence_on_new_line = false
|
||||
ij_yaml_space_before_colon = false
|
||||
ij_yaml_spaces_within_braces = true
|
||||
ij_yaml_spaces_within_brackets = true
|
||||
|
||||
@@ -41,10 +41,8 @@ class ajax_page extends WebPage implements iTabbedPage
|
||||
parent::__construct($s_title, $bPrintable);
|
||||
$this->m_sReadyScript = "";
|
||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
$this->m_oTabs = new TabManager();
|
||||
$this->sContentType = 'text/html';
|
||||
$this->sContentDisposition = 'inline';
|
||||
|
||||
@@ -3256,16 +3256,15 @@ EOF
|
||||
*/
|
||||
public function DisplayDocumentInline(WebPage $oPage, $sAttCode)
|
||||
{
|
||||
/** @var \ormDocument $oDoc */
|
||||
$oDoc = $this->Get($sAttCode);
|
||||
$sClass = get_class($this);
|
||||
$Id = $this->GetKey();
|
||||
switch ($oDoc->GetMainMimeType())
|
||||
{
|
||||
switch ($oDoc->GetMainMimeType()) {
|
||||
case 'text':
|
||||
case 'html':
|
||||
$data = $oDoc->GetData();
|
||||
switch ($oDoc->GetMimeType())
|
||||
{
|
||||
switch ($oDoc->GetMimeType()) {
|
||||
case 'text/xml':
|
||||
$oPage->add("<iframe id='preview_$sAttCode' src=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode\" width=\"100%\" height=\"400\">Loading...</iframe>\n");
|
||||
break;
|
||||
|
||||
@@ -32,10 +32,8 @@ class CSVPage extends WebPage
|
||||
function __construct($s_title) {
|
||||
parent::__construct($s_title);
|
||||
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
//$this->add_header("Content-Transfer-Encoding: binary");
|
||||
}
|
||||
|
||||
|
||||
@@ -459,17 +459,21 @@ EOF
|
||||
$sAttType = $aTargetAttCodes[$sTargetAttCode];
|
||||
$sExtFieldAttCode = $sTargetAttCode;
|
||||
}
|
||||
if (is_a($sAttType, 'AttributeLinkedSet', true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (is_a($sAttType, 'AttributeFriendlyName', true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (is_a($sAttType, 'AttributeOneWayPassword', true))
|
||||
{
|
||||
continue;
|
||||
|
||||
$aForbidenAttType = [
|
||||
'AttributeLinkedSet',
|
||||
'AttributeFriendlyName',
|
||||
|
||||
'iAttributeNoGroupBy', //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
|
||||
'AttributeOneWayPassword',
|
||||
'AttributeEncryptedString',
|
||||
'AttributePassword',
|
||||
];
|
||||
foreach ($aForbidenAttType as $sForbidenAttType) {
|
||||
if (is_a($sAttType, $sForbidenAttType, true))
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
|
||||
@@ -446,8 +446,21 @@ class DisplayBlock
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
|
||||
}
|
||||
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
switch($this->m_sStyle)
|
||||
{
|
||||
|
||||
switch($this->m_sStyle) {
|
||||
case 'list_search':
|
||||
case 'list':
|
||||
break;
|
||||
default:
|
||||
// N°3473: except for 'list_search' and 'list' (which have more granularity, see the other switch below),
|
||||
// refuse to render if the user is not allowed to see the class.
|
||||
if (! UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) {
|
||||
$sHtml .= $oPage->GetP(Dict::Format('UI:Error:ReadNotAllowedOn_Class', $this->m_oSet->GetClass()));
|
||||
return $sHtml;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($this->m_sStyle) {
|
||||
case 'count':
|
||||
if (isset($aExtraParams['group_by']))
|
||||
{
|
||||
|
||||
@@ -60,8 +60,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
// Create a breadcrumb entry for the current page, but get its title as late as possible (page title could be changed later)
|
||||
$this->bBreadCrumbEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$this->bBreadCrumbEnabled = false;
|
||||
}
|
||||
|
||||
@@ -71,10 +70,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
$this->m_aMessages = array();
|
||||
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
||||
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
|
||||
|
||||
@@ -84,10 +84,8 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
}
|
||||
|
||||
public function SetStyleSheet()
|
||||
@@ -687,7 +685,7 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
public static function HTTPReload()
|
||||
{
|
||||
$sOriginURL = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
|
||||
$sOriginURL = utils::GetCurrentAbsoluteUrl();
|
||||
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
|
||||
{
|
||||
// If the found URL does not start with the configured AppRoot URL
|
||||
|
||||
@@ -297,11 +297,19 @@ class privUITransactionFile
|
||||
* Cleanup old transactions which have been pending since more than 24 hours
|
||||
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
|
||||
*/
|
||||
protected static function CleanupOldTransactions()
|
||||
protected static function CleanupOldTransactions($sTransactionDir = null)
|
||||
{
|
||||
$iLimit = time() - 24*3600;
|
||||
$iThreshold = (int) MetaModel::GetConfig()->Get('transactions_gc_threshold');
|
||||
$iThreshold = min(100, $iThreshold);
|
||||
$iThreshold = max(1, $iThreshold);
|
||||
if ((100 != $iThreshold) && (rand(1, 100) > $iThreshold)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/*-*');
|
||||
$iLimit = time() - 24*3600;
|
||||
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : APPROOT.'data/transactions/*';
|
||||
$aTransactions = glob($sPattern);
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
if (filemtime($sFileName) < $iLimit)
|
||||
|
||||
@@ -782,22 +782,42 @@ class utils
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool The boolean value of the conf. "behind_reverse_proxy" (except if there is no REMOTE_ADDR int his case, it return false)
|
||||
*
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public static function IsProxyTrusted()
|
||||
{
|
||||
if (empty($_SERVER['REMOTE_ADDR'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$bTrustProxies = (bool) self::GetConfig()->Get('behind_reverse_proxy');
|
||||
|
||||
return $bTrustProxies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL to the application root path
|
||||
*
|
||||
* @param bool $bForceTrustProxy
|
||||
*
|
||||
* @return string The absolute URL to the application root, without the first slash
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 2.7.4 $bForceTrustProxy param added
|
||||
*/
|
||||
public static function GetAbsoluteUrlAppRoot()
|
||||
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
|
||||
{
|
||||
static $sUrl = null;
|
||||
if ($sUrl === null)
|
||||
if ($sUrl === null || $bForceTrustProxy)
|
||||
{
|
||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||
if ($sUrl == '')
|
||||
{
|
||||
$sUrl = self::GetDefaultUrlAppRoot();
|
||||
$sUrl = self::GetDefaultUrlAppRoot($bForceTrustProxy);
|
||||
}
|
||||
elseif (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
|
||||
{
|
||||
@@ -821,31 +841,116 @@ class utils
|
||||
* For most usages, when an root url is needed, use utils::GetAbsoluteUrlAppRoot() instead as uses this only as a fallback when the
|
||||
* app_root_url conf parameter is not defined.
|
||||
*
|
||||
* @param bool $bForceTrustProxy
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetDefaultUrlAppRoot()
|
||||
*
|
||||
* @since 2.7.4 $bForceTrustProxy param added
|
||||
*/
|
||||
public static function GetDefaultUrlAppRoot($bForceTrustProxy = false)
|
||||
{
|
||||
$sAbsoluteUrl = self::GetCurrentAbsoluteUrl($bForceTrustProxy, true);
|
||||
|
||||
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
|
||||
$sAppRoot = realpath(APPROOT);
|
||||
|
||||
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the current absolute URL from the server's variables.
|
||||
*
|
||||
* For almost every usage, you should use the more secure utils::GetAbsoluteUrlAppRoot() : instead of reading the current uri, it provide you the configured application's root URL (this is done during the setup and chn be changed in the configuration file)
|
||||
*
|
||||
* @see utils::GetAbsoluteUrlAppRoot
|
||||
*
|
||||
* @param bool $bForceTrustProxy
|
||||
* @param bool $bTrimQueryString
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public static function GetCurrentAbsoluteUrl($bForceTrustProxy = false, $bTrimQueryString = false)
|
||||
{
|
||||
// Build an absolute URL to this page on this server/port
|
||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
$sProtocol = self::IsConnectionSecure() ? 'https' : 'http';
|
||||
$iPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
||||
if ($sProtocol == 'http')
|
||||
{
|
||||
$sServerName = self::GetServerName($bForceTrustProxy);
|
||||
$bIsSecure = self::IsConnectionSecure($bForceTrustProxy);
|
||||
$sProtocol = $bIsSecure ? 'https' : 'http';
|
||||
$iPort = self::GetServerPort($bForceTrustProxy);
|
||||
if ($bIsSecure) {
|
||||
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
|
||||
} else {
|
||||
$sPort = ($iPort == 80) ? '' : ':'.$iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
|
||||
|
||||
$sPath = self::GetRequestUri($bForceTrustProxy);
|
||||
|
||||
if ($bTrimQueryString) {
|
||||
// remove all the parameters from the query string
|
||||
$iQuestionMarkPos = strpos($sPath, '?');
|
||||
if ($iQuestionMarkPos !== false) {
|
||||
$sPath = substr($sPath, 0, $iQuestionMarkPos);
|
||||
}
|
||||
}
|
||||
|
||||
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
|
||||
|
||||
return $sAbsoluteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bForceTrustProxy
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public static function GetServerName($bForceTrustProxy = false)
|
||||
{
|
||||
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
|
||||
|
||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
|
||||
if ($bTrustProxy) {
|
||||
$sServerName = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $sServerName;
|
||||
}
|
||||
|
||||
return $sServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bForceTrustProxy
|
||||
*
|
||||
* @return int|mixed
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public static function GetServerPort($bForceTrustProxy = false)
|
||||
{
|
||||
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
|
||||
|
||||
$sServerPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
||||
|
||||
if ($bTrustProxy) {
|
||||
$sServerPort = isset($_SERVER['HTTP_X_FORWARDED_PORT']) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $sServerPort;
|
||||
}
|
||||
|
||||
return $sServerPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public static function GetRequestUri()
|
||||
{
|
||||
// $_SERVER['REQUEST_URI'] is empty when running on IIS
|
||||
// Let's use Ivan Tcholakov's fix (found on www.dokeos.com)
|
||||
if (!empty($_SERVER['REQUEST_URI']))
|
||||
{
|
||||
$sPath = $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
else
|
||||
if (empty($_SERVER['REQUEST_URI']))
|
||||
{
|
||||
$sPath = $_SERVER['SCRIPT_NAME'];
|
||||
if (!empty($_SERVER['QUERY_STRING']))
|
||||
@@ -856,18 +961,7 @@ class utils
|
||||
}
|
||||
$sPath = $_SERVER['REQUEST_URI'];
|
||||
|
||||
// remove all the parameters from the query string
|
||||
$iQuestionMarkPos = strpos($sPath, '?');
|
||||
if ($iQuestionMarkPos !== false)
|
||||
{
|
||||
$sPath = substr($sPath, 0, $iQuestionMarkPos);
|
||||
}
|
||||
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
|
||||
|
||||
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
|
||||
$sAppRoot = realpath(APPROOT);
|
||||
|
||||
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
|
||||
return $sPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -911,19 +1005,36 @@ class utils
|
||||
/**
|
||||
* Helper to handle the variety of HTTP servers
|
||||
* See N°286 (fixed in [896]), and N°634 (this fix)
|
||||
*
|
||||
*
|
||||
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
|
||||
* nginx set it to an empty string
|
||||
* Others might leave it unset (no array entry)
|
||||
*/
|
||||
public static function IsConnectionSecure()
|
||||
* Others might leave it unset (no array entry)
|
||||
*
|
||||
* @param bool $bForceTrustProxy
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 2.7.4 reverse proxies handling
|
||||
*/
|
||||
public static function IsConnectionSecure($bForceTrustProxy = false)
|
||||
{
|
||||
$bSecured = false;
|
||||
|
||||
if (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
|
||||
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
|
||||
|
||||
if ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
|
||||
{
|
||||
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
|
||||
}
|
||||
elseif ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL']))
|
||||
{
|
||||
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
|
||||
}
|
||||
elseif ((!empty($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) != 'off'))
|
||||
{
|
||||
$bSecured = true;
|
||||
}
|
||||
|
||||
return $bSecured;
|
||||
}
|
||||
|
||||
@@ -2337,4 +2448,13 @@ class utils
|
||||
$e = new CoreException($sMessage, null, '', $oException);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool : indicate whether we run under a windows environnement or not
|
||||
* @since 2.7.4 : N°3412
|
||||
*/
|
||||
public static function IsWindowsEnvironment(){
|
||||
return (substr(PHP_OS,0,3) === 'WIN');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -482,6 +482,23 @@ class WebPage implements Page
|
||||
$this->a_headers[] = $s_header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the config parameter value.
|
||||
*
|
||||
* @since 2.7.3 3.0.0 N°3416
|
||||
* @uses security_header_xframe config parameter
|
||||
* @uses \utils::GetConfig()
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
*/
|
||||
public function add_xframe_options($sHeaderValue = null)
|
||||
{
|
||||
if (is_null($sHeaderValue)) {
|
||||
$sHeaderValue = utils::GetConfig()->Get('security_header_xframe');
|
||||
}
|
||||
|
||||
$this->add_header('X-Frame-Options: '.$sHeaderValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add needed headers to the page so that it will no be cached
|
||||
*/
|
||||
@@ -490,7 +507,6 @@ class WebPage implements Page
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,10 +43,8 @@ class XMLPage extends WebPage
|
||||
$this->m_bPassThrough = $bPassThrough;
|
||||
$this->m_bHeaderSent = false;
|
||||
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
$this->add_header("Content-location: export.xml");
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,14 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
|
||||
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
|
||||
|
||||
/**
|
||||
* Attributes implementing this interface won't be accepted as `group by` field
|
||||
* @since 2.7.4 N°3473
|
||||
*/
|
||||
interface iAttributeNoGroupBy
|
||||
{
|
||||
//no method, just a contract on implement
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute definition API, implemented in and many flavours (Int, String, Enum, etc.)
|
||||
@@ -3761,7 +3769,7 @@ class AttributeFinalClass extends AttributeString
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributePassword extends AttributeString
|
||||
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
@@ -3837,7 +3845,7 @@ class AttributePassword extends AttributeString
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeEncryptedString extends AttributeString
|
||||
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
@@ -9217,7 +9225,7 @@ class AttributeSubItem extends AttributeDefinition
|
||||
/**
|
||||
* One way encrypted (hashed) password
|
||||
*/
|
||||
class AttributeOneWayPassword extends AttributeDefinition
|
||||
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
|
||||
@@ -1181,17 +1181,24 @@ class CMDBSource
|
||||
}
|
||||
|
||||
/**
|
||||
* There may have some differences between DB : for example in MySQL 5.7 we have "INT", while in MariaDB >= 10.2 you get "int DEFAULT 'NULL'"
|
||||
* There may have some differences between DB ! For example in :
|
||||
* * MySQL 5.7 we have `INT`
|
||||
* * MariaDB >= 10.2 you get `int DEFAULT 'NULL'`
|
||||
*
|
||||
* We still do a case sensitive comparison for enum values !
|
||||
* We still need to do a case sensitive comparison for enum values !
|
||||
*
|
||||
* A better solution would be to generate SQL field definitions ({@link GetFieldSpec} method) based on the DB used... But for
|
||||
* now (N°2490 / SF #1756 / PR #91) we did implement this simpler solution
|
||||
*
|
||||
* @param string $sItopGeneratedFieldType
|
||||
* @see GetFieldDataTypeAndOptions extracts all info from the SQL field definition
|
||||
*
|
||||
* @param string $sDbFieldType
|
||||
*
|
||||
* @param string $sItopGeneratedFieldType
|
||||
*
|
||||
* @return bool true if same type and options (case sensitive comparison only for type options), false otherwise
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @since 2.7.0 N°2490
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
@@ -1239,24 +1246,68 @@ class CMDBSource
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sCompleteFieldType sql field type, for example 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
|
||||
* @see \self::GetEnumOptions() specific processing for ENUM fields
|
||||
*
|
||||
* @param string $sCompleteFieldType sql field type, for example `VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0`
|
||||
*
|
||||
* @return string[] consisting of 3 items :
|
||||
* 1. data type : for example 'VARCHAR'
|
||||
* 2. type value : for example '255'
|
||||
* 3. other options : for example ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
|
||||
* 1. data type : for example `VARCHAR`
|
||||
* 2. type value : for example `255`
|
||||
* 3. other options : for example `CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0`
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private static function GetFieldDataTypeAndOptions($sCompleteFieldType)
|
||||
{
|
||||
preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?/', $sCompleteFieldType, $aMatches);
|
||||
|
||||
$sDataType = isset($aMatches[1]) ? $aMatches[1] : '';
|
||||
|
||||
if (strcasecmp($sDataType, 'ENUM') === 0){
|
||||
try{
|
||||
return self::GetEnumOptions($sDataType, $sCompleteFieldType);
|
||||
}catch(CoreException $e){
|
||||
//do nothing ; especially do not block setup.
|
||||
IssueLog::Warning("enum was not parsed properly: $sCompleteFieldType. it should not happen during setup.");
|
||||
}
|
||||
}
|
||||
|
||||
$sTypeOptions = isset($aMatches[2]) ? $aMatches[3] : '';
|
||||
$sOtherOptions = isset($aMatches[4]) ? $aMatches[4] : '';
|
||||
|
||||
return array($sDataType, $sTypeOptions, $sOtherOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDataType for example `ENUM`
|
||||
* @param string $sCompleteFieldType Example:
|
||||
* `ENUM('CSP A','CSP (aaaa) M','NA','OEM(ROC)','OPEN(VL)','RETAIL (Boite)') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
|
||||
*
|
||||
* @return string[] consisting of 3 items :
|
||||
* 1. data type : ENUM or enum here
|
||||
* 2. type value : in-between EUM parenthesis
|
||||
* 3. other options : for example ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
|
||||
* @throws \CoreException
|
||||
* @since 2.7.4 N°3065 specific processing for enum fields : fix no alter table when enum values containing parenthesis
|
||||
* Handle ENUM options
|
||||
*/
|
||||
private static function GetEnumOptions($sDataType, $sCompleteFieldType)
|
||||
{
|
||||
$iFirstOpeningParenthesis = strpos($sCompleteFieldType, '(');
|
||||
$iLastEndingParenthesis = strrpos($sCompleteFieldType, ')');
|
||||
|
||||
if ($iFirstOpeningParenthesis === false || $iLastEndingParenthesis === false ){
|
||||
//should never happen as GetFieldDataTypeAndOptions regexp matched.
|
||||
//except if regexp is modiied/broken somehow one day...
|
||||
throw new CoreException("GetEnumOptions issue with $sDataType parsing : " . $sCompleteFieldType);
|
||||
}
|
||||
|
||||
$sTypeOptions = substr($sCompleteFieldType, $iFirstOpeningParenthesis + 1, $iLastEndingParenthesis - 1);
|
||||
$sOtherOptions = substr($sCompleteFieldType, $iLastEndingParenthesis + 1);
|
||||
|
||||
return array($sDataType, $sTypeOptions, $sOtherOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
* @param string $sField
|
||||
|
||||
@@ -1065,6 +1065,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'transactions_gc_threshold' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
|
||||
'default' => 10, // added in itop 2.7.4, before the GC was always called
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'log_transactions' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to enable the debug log for the transactions.',
|
||||
@@ -1249,6 +1257,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'security_header_xframe' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Value of the X-Frame-Options HTTP header sent by iTop. Possible values : DENY, SAMEORIGIN, or empty string.',
|
||||
'default' => 'SAMEORIGIN',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'behind_reverse_proxy' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -1186,6 +1186,30 @@ abstract class DBSearch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($aGroupByExpr))
|
||||
{
|
||||
foreach($aGroupByExpr as $sAlias => $oGroupByExp)
|
||||
{
|
||||
/** @var \Expression $oGroupByExp */
|
||||
|
||||
$aFields = $oGroupByExp->ListRequiredFields();
|
||||
foreach($aFields as $sFieldAlias)
|
||||
{
|
||||
$aMatches = array();
|
||||
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
|
||||
{
|
||||
$sFieldClass = $this->GetClassName($aMatches[1]);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
|
||||
if ( $oAttDef instanceof iAttributeNoGroupBy)
|
||||
{
|
||||
throw new Exception("Grouping on '$sFieldClass' fields is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
|
||||
$oSQLQuery->SetSourceOQL($oSearch->ToOQL());
|
||||
|
||||
|
||||
@@ -2602,5 +2602,18 @@ class DBObjectSearch extends DBSearch
|
||||
return $oExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aAttCodes array of attCodes to search into
|
||||
* @param string $sNeedle one word to be searched
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle)
|
||||
{
|
||||
}
|
||||
|
||||
public function ListParameters()
|
||||
{
|
||||
return $this->GetCriteria()->ListParameters();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4166,11 +4166,7 @@ abstract class MetaModel
|
||||
}
|
||||
if (count($aCurrentUser) > 0)
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT User WHERE id = :id");
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('id' => UserRights::GetUserId()));
|
||||
$oSet->OptimizeColumnLoad($aCurrentUser);
|
||||
$oUser = $oSet->fetch();
|
||||
$oUser = MetaModel::GetObject("User", UserRights::GetUserId(),true,true);
|
||||
$aPlaceholders['current_user->object()'] = $oUser;
|
||||
foreach ($aCurrentUser as $sField)
|
||||
{
|
||||
@@ -4179,10 +4175,7 @@ abstract class MetaModel
|
||||
}
|
||||
if (count($aCurrentContact) > 0)
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Contact WHERE id = :id");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('id' => UserRights::GetContactId()));
|
||||
$oSet->OptimizeColumnLoad(['Contact' => $aCurrentContact]);
|
||||
$oUser = $oSet->fetch();
|
||||
$oUser = MetaModel::GetObject("Person", UserRights::GetContactId(),true,true);
|
||||
foreach ($aCurrentContact as $sField)
|
||||
{
|
||||
$aPlaceholders['current_contact->'.$sField] = $oUser->Get($sField);
|
||||
@@ -5552,15 +5545,10 @@ abstract class MetaModel
|
||||
|
||||
// The field already exists, does it have the relevant properties?
|
||||
//
|
||||
$bToBeChanged = false;
|
||||
$sActualFieldSpec = CMDBSource::GetFieldSpec($sTable, $sField);
|
||||
if (!CMDBSource::IsSameFieldTypes($sDBFieldSpec, $sActualFieldSpec))
|
||||
{
|
||||
$bToBeChanged = true;
|
||||
$aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found <code>$sActualFieldSpec</code> while expecting <code>$sDBFieldSpec</code>";
|
||||
}
|
||||
if ($bToBeChanged)
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
|
||||
@@ -1672,6 +1672,39 @@ class FieldExpression extends UnaryExpression
|
||||
// Has been resolved into an SQL expression
|
||||
class FieldExpressionResolved extends FieldExpression
|
||||
{
|
||||
protected $m_aAdditionalExpressions;
|
||||
|
||||
public function __construct($mExpression, $sParent = '')
|
||||
{
|
||||
$this->m_aAdditionalExpressions = array();
|
||||
if (is_array($mExpression))
|
||||
{
|
||||
foreach ($mExpression as $sSuffix => $sExpression)
|
||||
{
|
||||
if ($sSuffix == '')
|
||||
{
|
||||
$sName = $sExpression;
|
||||
}
|
||||
$this->m_aAdditionalExpressions[$sSuffix] = new FieldExpressionResolved($sExpression, $sParent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sName = $mExpression;
|
||||
}
|
||||
|
||||
parent::__construct($sName, $sParent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of additional expressions for muti-column attributes
|
||||
* @since 2.7.4
|
||||
*/
|
||||
public function AdditionalExpressions()
|
||||
{
|
||||
return $this->m_aAdditionalExpressions;
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -331,4 +331,12 @@ class OQLJoin
|
||||
return $this->sRightField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetLeftField()
|
||||
{
|
||||
return $this->sLeftField;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,15 @@ class OQLClassTreeOptimizer
|
||||
{
|
||||
if ($oJoin->IsOutbound())
|
||||
{
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
||||
// If joined class in not the same class than the external key target class
|
||||
// then the join cannot be removed because it is used to filter the request
|
||||
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
|
||||
$sExtKeyAttCode = $oJoin->GetLeftField();
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
|
||||
if ($sJoinedClass == $oExtKeyAttDef->GetTargetClass()) {
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -178,6 +178,14 @@ class QueryBuilderExpressions
|
||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
||||
{
|
||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
if ($this->m_aSelectExpr[$sColAlias] instanceof FieldExpressionResolved)
|
||||
{
|
||||
// Split the field with the relevant alias
|
||||
foreach ($this->m_aSelectExpr[$sColAlias]->AdditionalExpressions() as $sSuffix => $oAdditionalExpr)
|
||||
{
|
||||
$this->m_aSelectExpr[$sColAlias.$sSuffix] = $oAdditionalExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->m_aGroupByExpr)
|
||||
{
|
||||
|
||||
@@ -199,8 +199,8 @@ EOF
|
||||
// Integration within MS-Excel web queries + HTTPS + IIS:
|
||||
// MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
|
||||
// Then the fix is to force the reset of header values Pragma and Cache-control
|
||||
$oPage->add_header("Pragma:", true);
|
||||
$oPage->add_header("Cache-control:", true);
|
||||
$oPage->add_header("Pragma:");
|
||||
$oPage->add_header("Cache-control:");
|
||||
}
|
||||
|
||||
public function GetHeader()
|
||||
|
||||
@@ -239,24 +239,16 @@ class SQLObjectQueryBuilder
|
||||
continue;
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
||||
$oFieldSQLExp = new FieldExpressionResolved($oAttDef->GetSQLExpressions(), $sClassAlias);
|
||||
/**
|
||||
* @var string $sPluginClass
|
||||
* @var iQueryModifier $oQueryModifier
|
||||
*/
|
||||
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
||||
{
|
||||
if (!empty($sColId))
|
||||
{
|
||||
// Multi column attributes
|
||||
$oBuild->m_oQBExpressions->AddSelect($sSelectedClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
|
||||
}
|
||||
$oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sClassAlias);
|
||||
/**
|
||||
* @var string $sPluginClass
|
||||
* @var iQueryModifier $oQueryModifier
|
||||
*/
|
||||
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
||||
{
|
||||
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, $sColId, $oFieldSQLExp, $oBaseSQLQuery);
|
||||
}
|
||||
$aTranslation[$sClassAlias][$sAttCode.$sColId] = $oFieldSQLExp;
|
||||
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, '', $oFieldSQLExp, $oBaseSQLQuery);
|
||||
}
|
||||
$aTranslation[$sClassAlias][$sAttCode] = $oFieldSQLExp;
|
||||
}
|
||||
|
||||
// Translate the selected columns
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
*/
|
||||
|
||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||
$version: "v2.7.2";
|
||||
$version: "v2.7.4";
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
// Base colors
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$gray-dark: #444 !default;
|
||||
$gray: #777 !default;
|
||||
$gray-light: #808080 !default;
|
||||
$gray-lighter: #ddd !default;
|
||||
$gray-extra-light: #F1F1F1 !default;
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$gray-dark: #444 !default;
|
||||
$gray: #777 !default;
|
||||
$gray-light: #808080 !default;
|
||||
$gray-lighter: #ddd !default;
|
||||
$gray-extra-light: #F1F1F1 !default;
|
||||
|
||||
$white: #FFFFFF !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
|
||||
'authent-cas/2.7.0',
|
||||
'authent-cas/2.7.4',
|
||||
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.0',
|
||||
'authent-external/2.7.4',
|
||||
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.0',
|
||||
'authent-ldap/2.7.4',
|
||||
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.1',
|
||||
'authent-local/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -212,9 +212,9 @@ function DisplayInconsistenciesReport($aResults)
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: multipart/x-zip');
|
||||
header('Content-Disposition: inline; filename="'.basename($sZipReport).'"');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Content-Length: '.filesize($sZipReport));
|
||||
readfile($sZipReport);
|
||||
unlink($sZipReport);
|
||||
|
||||
@@ -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.1',
|
||||
'combodo-db-tools/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -61,7 +61,6 @@ try
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
|
||||
|
||||
@@ -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.1',
|
||||
'itop-attachments/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
@@ -80,6 +80,31 @@ if (!class_exists('AttachmentInstaller'))
|
||||
return $oConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.4 N°3788
|
||||
* @param string $sTableName
|
||||
* @param int $iBulkSize
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function GetOrphanAttachmentIds($sTableName, $iBulkSize){
|
||||
$sSqlQuery = <<<SQL
|
||||
SELECT id as attachment_id FROM `$sTableName` WHERE (`item_id`='' OR `item_id` IS NULL) LIMIT {$iBulkSize};
|
||||
SQL;
|
||||
/** @var \mysqli_result $oQueryResult */
|
||||
$oQueryResult = CMDBSource::Query($sSqlQuery);
|
||||
|
||||
$aIds = [];
|
||||
while($aRow = $oQueryResult->fetch_array()){
|
||||
$aIds[] = $aRow['attachment_id'];
|
||||
}
|
||||
|
||||
return $aIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler called before creating or upgrading the database schema
|
||||
* @param $oConfiguration Config The new configuration of the application
|
||||
@@ -97,10 +122,32 @@ if (!class_exists('AttachmentInstaller'))
|
||||
$iCount = CMDBSource::QueryToScalar($sCountQuery);
|
||||
if ($iCount > 0)
|
||||
{
|
||||
SetupPage::log_info("Cleanup of orphan attachments that cannot be migrated to the new ObjKey model: $iCount record(s) must be deleted.");
|
||||
$sRepairQuery = "DELETE FROM `$sTableName` WHERE (`item_id`='' OR `item_id` IS NULL)";
|
||||
$iRet = CMDBSource::Query($sRepairQuery); // Throws an exception in case of error
|
||||
SetupPage::log_info("Cleanup of orphan attachments successfully completed.");
|
||||
SetupPage::log_info("Cleanup of orphan attachments that cannot be migrated to the new ObjKey model: $iCount record(s) must be deleted.");
|
||||
|
||||
$iBulkSize = 100;
|
||||
$iMaxDuration = 30;
|
||||
$iDeletedCount = 0;
|
||||
$fStartTime = microtime(true);
|
||||
$aIds = self::GetOrphanAttachmentIds($sTableName, $iBulkSize);
|
||||
|
||||
while (count($aIds) !== 0) {
|
||||
$sCleanupQuery = sprintf("DELETE FROM `$sTableName` WHERE `id` IN (%s)", implode(",", $aIds));
|
||||
CMDBSource::Query($sCleanupQuery); // Throws an exception in case of error
|
||||
|
||||
$iDeletedCount += count($aIds);
|
||||
$fElapsed = microtime(true) - $fStartTime;
|
||||
|
||||
if ($fElapsed > $iMaxDuration){
|
||||
SetupPage::log_info(sprintf("Cleanup of orphan attachments interrupted after %.3f s. $iDeletedCount records were deleted among $iCount.", $fElapsed));
|
||||
break;
|
||||
}
|
||||
|
||||
$aIds = self::GetOrphanAttachmentIds($sTableName, $iBulkSize);
|
||||
}
|
||||
|
||||
if (count($aIds) === 0){
|
||||
SetupPage::log_info("Cleanup of orphan attachments successfully completed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -108,7 +155,7 @@ if (!class_exists('AttachmentInstaller'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler called after the creation/update of the database schema
|
||||
* @param $oConfiguration Config The new configuration of the application
|
||||
@@ -123,6 +170,7 @@ if (!class_exists('AttachmentInstaller'))
|
||||
// Prerequisite: change null into 0 (workaround to the fact that we cannot use IS NULL in OQL)
|
||||
SetupPage::log_info("Initializing attachment/item_org_id - null to zero");
|
||||
$sTableName = MetaModel::DBGetTable('Attachment');
|
||||
|
||||
$sRepair = "UPDATE `$sTableName` SET `item_org_id` = 0 WHERE `item_org_id` IS NULL";
|
||||
CMDBSource::Query($sRepair);
|
||||
|
||||
@@ -132,7 +180,13 @@ if (!class_exists('AttachmentInstaller'))
|
||||
$iUpdated = 0;
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
if (empty($oAttachment->Get('item_class'))) {
|
||||
//do not treat orphan attachment
|
||||
continue;
|
||||
}
|
||||
|
||||
$oContainer = MetaModel::GetObject($oAttachment->Get('item_class'), $oAttachment->Get('item_id'), false /* must be found */, true /* allow all data */);
|
||||
|
||||
if ($oContainer)
|
||||
{
|
||||
$oAttachment->SetItem($oContainer, true /*updateonchange*/);
|
||||
|
||||
@@ -51,7 +51,6 @@ function DisplayErrorAndDie($oPage, $sHtmlErrorMessage, $exitCode = null)
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
|
||||
$oPage = new ajax_page('');
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('text/html');
|
||||
|
||||
|
||||
|
||||
@@ -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.1',
|
||||
'itop-backup/2.7.4',
|
||||
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.0',
|
||||
'itop-bridge-virtualization-storage/2.7.4',
|
||||
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.0',
|
||||
'itop-change-mgmt-itil/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -237,9 +237,9 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Class:NormalChange' => 'Normale Change',
|
||||
'Class:NormalChange+' => '',
|
||||
'Class:NormalChange/Attribute:acceptance_date' => 'Datum goedkeuring',
|
||||
'Class:NormalChange/Attribute:acceptance_date' => 'Datum acceptatie',
|
||||
'Class:NormalChange/Attribute:acceptance_date+' => '',
|
||||
'Class:NormalChange/Attribute:acceptance_comment' => 'Commentaar goedkeuring',
|
||||
'Class:NormalChange/Attribute:acceptance_comment' => 'Commentaar acceptatie',
|
||||
'Class:NormalChange/Attribute:acceptance_comment+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_validate' => 'Valideer',
|
||||
'Class:NormalChange/Stimulus:ev_validate+' => '',
|
||||
|
||||
@@ -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.0',
|
||||
'itop-change-mgmt/2.7.4',
|
||||
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.1',
|
||||
'itop-config-mgmt/2.7.4',
|
||||
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.0',
|
||||
'itop-config/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -75,6 +75,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'Application can be updated',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'Application cannot be updated: %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Warning: application update can fail: %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => 'You must use the <a href="%1$s">setup</a> to update the application.<br />Some modified files were detected, a partial update cannot be executed.',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Ready to start',
|
||||
|
||||
@@ -75,6 +75,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'iTopUpdate:UI:CanCoreUpdate:Yes' => 'L\'application peut être mise à jour',
|
||||
'iTopUpdate:UI:CanCoreUpdate:No' => 'L\'application ne peut pas être mise à jour : %1$s',
|
||||
'iTopUpdate:UI:CanCoreUpdate:Warning' => 'Attention : la mise à jour de l\'application peut échouer : %1$s',
|
||||
'iTopUpdate:UI:CannotUpdateUseSetup' => 'Vous devez utiliser la page <a href="%1$s">d\'installation</a> pour mettre à jour l\'application.<br />Des fichiers modifiés ont été détectés, une mise à jour partielle ne peut pas être effectuée.',
|
||||
|
||||
// Setup Messages
|
||||
'iTopUpdate:UI:SetupMessage:Ready' => 'Prêt pour l\\installation',
|
||||
|
||||
@@ -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.1',
|
||||
'itop-core-update/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -37,7 +37,8 @@ class AjaxController extends Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['sMessage'] = Dict::Format("iTopUpdate:UI:CanCoreUpdate:{$sCanUpdateCore}", $sMessage);
|
||||
$sLink = utils::GetAbsoluteUrlAppRoot().'setup/';
|
||||
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CannotUpdateUseSetup', $sLink);
|
||||
}
|
||||
} catch (FileNotExistException $e)
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<fieldset id="form-update-outer">
|
||||
<legend>{{ 'iTopUpdate:UI:SelectUpdateFile'|dict_s }}</legend>
|
||||
|
||||
<div class="details">
|
||||
|
||||
@@ -22,6 +22,9 @@ $.ajax({
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#check-update").prop("disabled", true);
|
||||
$("#file").prop("disabled", true);
|
||||
$('#form-update-outer').slideUp(600);
|
||||
oRequirements.addClass("message_error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.0',
|
||||
'itop-datacenter-mgmt/2.7.4',
|
||||
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.0',
|
||||
'itop-endusers-devices/2.7.4',
|
||||
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.1',
|
||||
'itop-files-information/2.7.4',
|
||||
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.0',
|
||||
'itop-full-itil/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -111,7 +111,6 @@ function DoBackup($sTargetFile)
|
||||
function ReportStatus($sMessage, $bSuccess, $iErrorCode = 0, $aMoreFields = array())
|
||||
{
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('application/json');
|
||||
$aResult = array(
|
||||
'code' => $iErrorCode,
|
||||
|
||||
@@ -4,20 +4,18 @@ class HubConnectorPage extends NiceWebPage
|
||||
{
|
||||
public function __construct($sTitle)
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
parent::__construct($sTitle);
|
||||
|
||||
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||
$this->add_header('Pragma: no-cache');
|
||||
$this->add_header('Expires: 0');
|
||||
$this->add_header('X-Frame-Options: deny');
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
|
||||
$sImagesDir = utils::GetAbsoluteUrlAppRoot().'images';
|
||||
$sModuleImagesDir = utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/images';
|
||||
|
||||
$sUserPrefs = appUserPreferences::GetAsJSON();
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
$sImagesDir = utils::GetAbsoluteUrlAppRoot().'images';
|
||||
$sModuleImagesDir = utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/images';
|
||||
|
||||
$sUserPrefs = appUserPreferences::GetAsJSON();
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
var oUserPreferences = $sUserPrefs;
|
||||
EOF
|
||||
);
|
||||
|
||||
@@ -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.0',
|
||||
'itop-hub-connector/2.7.4',
|
||||
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.0',
|
||||
'itop-incident-mgmt-itil/2.7.4',
|
||||
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.0',
|
||||
'itop-knownerror-mgmt/2.7.4',
|
||||
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.1', array(
|
||||
'itop-portal-base/2.7.4', array(
|
||||
// Identification
|
||||
'label' => 'Portal Development Library',
|
||||
'category' => 'Portal',
|
||||
|
||||
@@ -33,6 +33,7 @@ $(function()
|
||||
category: 'redirect',
|
||||
url: null,
|
||||
modal: false,
|
||||
timeout_duration: 400,
|
||||
},
|
||||
cancel_rule: {
|
||||
category: 'close',
|
||||
@@ -56,7 +57,7 @@ $(function()
|
||||
this.options.submit_rule.url = this.options.submit_url;
|
||||
if((this.options.cancel_url !== null) && (this.options.cancel_url !== ''))
|
||||
this.options.cancel_rule.url = this.options.cancel_url;
|
||||
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
@@ -143,12 +144,14 @@ $(function()
|
||||
// Determine where we go in case validation is successful
|
||||
var sRuleType = me.options.submit_rule.category;
|
||||
var bRedirectInModal = me.options.submit_rule.modal;
|
||||
var iRedirectTimeout = me.options.submit_rule.timeout_duration;
|
||||
var sRedirectUrl = me.options.submit_rule.url;
|
||||
// - The validation might want us to be redirect elsewhere
|
||||
if(oValidation.valid)
|
||||
{
|
||||
// Checking if we have to redirect to another page
|
||||
// Typically this happens when applying a stimulus, we redirect to the transition form
|
||||
// This code let the ajax response override the initial parameters
|
||||
if(oValidation.redirection !== undefined)
|
||||
{
|
||||
var oRedirection = oValidation.redirection;
|
||||
@@ -160,6 +163,10 @@ $(function()
|
||||
{
|
||||
sRedirectUrl = oRedirection.url;
|
||||
}
|
||||
if(oRedirection.timeout_duration !== undefined)
|
||||
{
|
||||
iRedirectTimeout = oRedirection.timeout_duration;
|
||||
}
|
||||
sRuleType = 'redirect';
|
||||
}
|
||||
}
|
||||
@@ -241,7 +248,7 @@ $(function()
|
||||
// Checking if we have to redirect to another page
|
||||
if(sRuleType === 'redirect')
|
||||
{
|
||||
me._applyRedirectRule(sRedirectUrl, bRedirectInModal);
|
||||
me._applyRedirectRule(sRedirectUrl, bRedirectInModal, iRedirectTimeout);
|
||||
}
|
||||
// Close rule only needs to be applied to non modal forms (modal is always closed on submit)
|
||||
else if(sRuleType === 'close')
|
||||
@@ -360,10 +367,13 @@ $(function()
|
||||
{
|
||||
$('#page_overlay').fadeOut(200);
|
||||
},
|
||||
_applyRedirectRule: function(sRedirectUrl, bRedirectInModal)
|
||||
_applyRedirectRule: function(sRedirectUrl, bRedirectInModal, iRedirectTimeout)
|
||||
{
|
||||
var me = this;
|
||||
|
||||
//optional argument
|
||||
iRedirectTimeout = (typeof iRedirectTimeout !== 'undefined' && iRedirectTimeout != null ) ? iRedirectTimeout : 400;
|
||||
|
||||
// Always close current modal
|
||||
if(this.options.is_modal)
|
||||
{
|
||||
@@ -391,8 +401,8 @@ $(function()
|
||||
// Showing loader while redirecting, otherwise user tend to click somewhere in the page.
|
||||
// Note: We use a timeout because .always() is called right after here and will hide the loader
|
||||
setTimeout(function(){ me._disableFormBeforeLoading(); }, 50);
|
||||
// Redirecting after a few ms so the user can see what happend
|
||||
setTimeout(function() { location.href = sRedirectUrl; }, 400);
|
||||
// Redirecting after a few ms so the user can see what happened
|
||||
setTimeout(function() { location.href = sRedirectUrl; }, iRedirectTimeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -106,7 +106,11 @@ var CombodoPortalToolbox = {
|
||||
if (oOptions.base_modal.usage === 'clone')
|
||||
{
|
||||
oModalElem = oSelectorElem.clone();
|
||||
oModalElem.attr('id', oOptions.id)
|
||||
|
||||
// Force modal to have an HTML ID, otherwise it can lead to complications, especially with the portal_leave_handle.js
|
||||
// See N°3469
|
||||
var sModalID = (oOptions.id !== null) ? oOptions.id : 'modal-with-generated-id-'+Date.now();
|
||||
oModalElem.attr('id', sModalID)
|
||||
.appendTo('body');
|
||||
}
|
||||
// - Get an existing modal in the DOM
|
||||
|
||||
@@ -252,6 +252,7 @@ class ManageBrickController extends BrickController
|
||||
$oExporter->SetObjectList($oSearch);
|
||||
$oExporter->SetFormat($sFormat);
|
||||
$oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
|
||||
$oExporter->SetLocalizeOutput(true);
|
||||
$oExporter->SetFields($sFields);
|
||||
|
||||
$aData = array(
|
||||
|
||||
@@ -211,6 +211,7 @@ class UserProfileBrickController extends BrickController
|
||||
{
|
||||
$aFormData['validation']['redirection'] = array(
|
||||
'url' => $oUrlGenerator->generate('p_user_profile_brick'),
|
||||
'timeout_duration' => 1000, //since there are several ajax request, we use a longer timeout in hope that they will all be finished in time. A promise would have been more reliable, but since this change is made in a minor version, this approach is less error prone.
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1048,6 +1048,26 @@ class ObjectFormManager extends FormManager
|
||||
$this->CancelAttachments();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function CheckTransaction(&$aData)
|
||||
{
|
||||
$isTransactionValid = \utils::IsTransactionValid($this->oForm->GetTransactionId(), false); //The transaction token is kept in order to preserve BC with ajax forms (the second call would fail if the token is deleted). (The GC will take care of cleaning the token for us later on)
|
||||
if (!$isTransactionValid) {
|
||||
if ($this->oObject->IsNew()) {
|
||||
$sError = Dict::S('UI:Error:ObjectAlreadyCreated');
|
||||
} else {
|
||||
$sError = Dict::S('UI:Error:ObjectAlreadyUpdated');
|
||||
}
|
||||
|
||||
$aData['messages']['error'] += [
|
||||
'_main' => [$sError]
|
||||
];
|
||||
$aData['valid'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the form and returns an array with the validation status and the messages.
|
||||
* If the form is valid, creates/updates the object.
|
||||
@@ -1070,124 +1090,116 @@ class ObjectFormManager extends FormManager
|
||||
*/
|
||||
public function OnSubmit($aArgs = null)
|
||||
{
|
||||
$aData = array(
|
||||
'valid' => true,
|
||||
'messages' => array(
|
||||
'success' => array(),
|
||||
'warnings' => array(), // Not used as of today, just to show that the structure is ready for change like this.
|
||||
'error' => array(),
|
||||
),
|
||||
);
|
||||
$aData = parent::OnSubmit($aArgs);
|
||||
|
||||
if (! $aData['valid']) {
|
||||
return $aData;
|
||||
}
|
||||
|
||||
// Update object and form
|
||||
$this->OnUpdate($aArgs);
|
||||
|
||||
// Check if form valid
|
||||
if ($this->oForm->Validate())
|
||||
{
|
||||
// The try catch is essentially to start a MySQL transaction in order to ensure that all or none objects are persisted when creating an object with links
|
||||
try
|
||||
{
|
||||
$sObjectClass = get_class($this->oObject);
|
||||
|
||||
// Starting transaction
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
|
||||
$bAllowWrite = ($sObjectClass === 'Person' && $this->oObject->GetKey() == UserRights::GetContactId());
|
||||
if ($bAllowWrite)
|
||||
{
|
||||
$this->oObject->AllowWrite(true);
|
||||
}
|
||||
|
||||
// Writing object to DB
|
||||
$bActivateTriggers = (!$this->oObject->IsNew() && $this->oObject->IsModified());
|
||||
$bWasModified = $this->oObject->IsModified();
|
||||
try
|
||||
{
|
||||
$this->oObject->DBWrite();
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
throw new Exception($e->getHtmlMessage());
|
||||
}
|
||||
// Finalizing images link to object, otherwise it will be cleaned by the GC
|
||||
InlineImage::FinalizeInlineImages($this->oObject);
|
||||
// Finalizing attachments link to object
|
||||
// TODO : This has to be refactored when the function from itop-attachments has been migrated into the core
|
||||
if (isset($aArgs['attachmentIds']))
|
||||
{
|
||||
$this->FinalizeAttachments($aArgs['attachmentIds']);
|
||||
}
|
||||
|
||||
// Ending transaction with a commit as everything was fine
|
||||
CMDBSource::Query('COMMIT');
|
||||
|
||||
// Checking if we have to apply a stimulus
|
||||
if (isset($aArgs['applyStimulus']))
|
||||
{
|
||||
$this->oObject->ApplyStimulus($aArgs['applyStimulus']['code']);
|
||||
}
|
||||
// Activating triggers only on update
|
||||
if ($bActivateTriggers)
|
||||
{
|
||||
$sTriggersQuery = $this->oContainer->getParameter('combodo.portal.instance.conf')['properties']['triggers_query'];
|
||||
if ($sTriggersQuery !== null)
|
||||
{
|
||||
$aParentClasses = MetaModel::EnumParentClasses($sObjectClass, ENUM_PARENT_CLASSES_ALL);
|
||||
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL($sTriggersQuery), array(),
|
||||
array('parent_classes' => $aParentClasses));
|
||||
/** @var \Trigger $oTrigger */
|
||||
while ($oTrigger = $oTriggerSet->Fetch())
|
||||
{
|
||||
try
|
||||
{
|
||||
$oTrigger->DoActivate($this->oObject->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resetting caselog fields value, otherwise the value will stay in it after submit.
|
||||
$this->oForm->ResetCaseLogFields();
|
||||
|
||||
if ($bWasModified)
|
||||
{
|
||||
//=if (isNew) because $bActivateTriggers = (!$this->oObject->IsNew() && $this->oObject->IsModified())
|
||||
if(!$bActivateTriggers)
|
||||
{
|
||||
$aData['messages']['success'] += array( '_main' => array(Dict::Format('UI:Title:Object_Of_Class_Created', $this->oObject->GetName(),MetaModel::GetName(get_class($this->oObject)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aData['messages']['success'] += array('_main' => array(Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($this->oObject)), $this->oObject->GetName())));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// End transaction with a rollback as something failed
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
$aData['valid'] = false;
|
||||
$aData['messages']['error'] += array('_main' => array($e->getMessage()));
|
||||
IssueLog::Error(__METHOD__.' at line '.__LINE__.' : Rollback during submit ('.$e->getMessage().')');
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Removing transaction id
|
||||
utils::RemoveTransaction($this->oForm->GetTransactionId());
|
||||
}
|
||||
}
|
||||
else
|
||||
if (! $this->oForm->Validate())
|
||||
{
|
||||
// Handle errors
|
||||
$aData['valid'] = false;
|
||||
$aData['messages']['error'] += $this->oForm->GetErrorMessages();
|
||||
return $aData;
|
||||
}
|
||||
|
||||
// The try catch is essentially to start a MySQL transaction in order to ensure that all or none objects are persisted when creating an object with links
|
||||
try
|
||||
{
|
||||
$sObjectClass = get_class($this->oObject);
|
||||
|
||||
// Starting transaction
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
// Forcing allowed writing on the object if necessary. This is used in some particular cases.
|
||||
$bAllowWrite = ($sObjectClass === 'Person' && $this->oObject->GetKey() == UserRights::GetContactId());
|
||||
if ($bAllowWrite)
|
||||
{
|
||||
$this->oObject->AllowWrite(true);
|
||||
}
|
||||
|
||||
// Writing object to DB
|
||||
$bActivateTriggers = (!$this->oObject->IsNew() && $this->oObject->IsModified());
|
||||
$bWasModified = $this->oObject->IsModified();
|
||||
try
|
||||
{
|
||||
$this->oObject->DBWrite();
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
throw new Exception($e->getHtmlMessage());
|
||||
}
|
||||
// Finalizing images link to object, otherwise it will be cleaned by the GC
|
||||
InlineImage::FinalizeInlineImages($this->oObject);
|
||||
// Finalizing attachments link to object
|
||||
// TODO : This has to be refactored when the function from itop-attachments has been migrated into the core
|
||||
if (isset($aArgs['attachmentIds']))
|
||||
{
|
||||
$this->FinalizeAttachments($aArgs['attachmentIds']);
|
||||
}
|
||||
|
||||
// Ending transaction with a commit as everything was fine
|
||||
CMDBSource::Query('COMMIT');
|
||||
|
||||
// Checking if we have to apply a stimulus
|
||||
if (isset($aArgs['applyStimulus']))
|
||||
{
|
||||
$this->oObject->ApplyStimulus($aArgs['applyStimulus']['code']);
|
||||
}
|
||||
// Activating triggers only on update
|
||||
if ($bActivateTriggers)
|
||||
{
|
||||
$sTriggersQuery = $this->oContainer->getParameter('combodo.portal.instance.conf')['properties']['triggers_query'];
|
||||
if ($sTriggersQuery !== null)
|
||||
{
|
||||
$aParentClasses = MetaModel::EnumParentClasses($sObjectClass, ENUM_PARENT_CLASSES_ALL);
|
||||
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL($sTriggersQuery), array(),
|
||||
array('parent_classes' => $aParentClasses));
|
||||
/** @var \Trigger $oTrigger */
|
||||
while ($oTrigger = $oTriggerSet->Fetch())
|
||||
{
|
||||
try
|
||||
{
|
||||
$oTrigger->DoActivate($this->oObject->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resetting caselog fields value, otherwise the value will stay in it after submit.
|
||||
$this->oForm->ResetCaseLogFields();
|
||||
|
||||
if ($bWasModified)
|
||||
{
|
||||
//=if (isNew) because $bActivateTriggers = (!$this->oObject->IsNew() && $this->oObject->IsModified())
|
||||
if(!$bActivateTriggers)
|
||||
{
|
||||
$aData['messages']['success'] += array( '_main' => array(Dict::Format('UI:Title:Object_Of_Class_Created', $this->oObject->GetName(),MetaModel::GetName(get_class($this->oObject)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aData['messages']['success'] += array('_main' => array(Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($this->oObject)), $this->oObject->GetName())));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// End transaction with a rollback as something failed
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
$aData['valid'] = false;
|
||||
$aData['messages']['error'] += array('_main' => array($e->getMessage()));
|
||||
IssueLog::Error(__METHOD__.' at line '.__LINE__.' : Rollback during submit ('.$e->getMessage().')');
|
||||
}
|
||||
|
||||
|
||||
return $aData;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ class PasswordFormManager extends FormManager
|
||||
// Building the form
|
||||
$oForm = new Form('change_password');
|
||||
|
||||
$oForm->SetTransactionId(\utils::GetNewTransactionId());
|
||||
|
||||
// Adding hidden field with form type
|
||||
$oField = new HiddenField('form_type');
|
||||
$oField->SetCurrentValue('change_password');
|
||||
@@ -93,14 +95,11 @@ class PasswordFormManager extends FormManager
|
||||
*/
|
||||
public function OnSubmit($aArgs = null)
|
||||
{
|
||||
$aData = array(
|
||||
'valid' => true,
|
||||
'messages' => array(
|
||||
'success' => array(),
|
||||
'warnings' => array(), // Not used as of today, just to show that the structure is ready for change like this.
|
||||
'error' => array(),
|
||||
),
|
||||
);
|
||||
$aData = parent::OnSubmit($aArgs);
|
||||
|
||||
if (! $aData['valid']) {
|
||||
return $aData;
|
||||
}
|
||||
|
||||
// Update object and form
|
||||
$this->OnUpdate($aArgs);
|
||||
|
||||
@@ -49,6 +49,8 @@ class PreferencesFormManager extends FormManager
|
||||
// Building the form
|
||||
$oForm = new Form('preferences');
|
||||
|
||||
$oForm->SetTransactionId(\utils::GetNewTransactionId());
|
||||
|
||||
// Adding hidden field with form type
|
||||
$oField = new HiddenField('form_type');
|
||||
$oField->SetCurrentValue('preferences');
|
||||
@@ -97,14 +99,11 @@ class PreferencesFormManager extends FormManager
|
||||
*/
|
||||
public function OnSubmit($aArgs = null)
|
||||
{
|
||||
$aData = array(
|
||||
'valid' => true,
|
||||
'messages' => array(
|
||||
'success' => array(),
|
||||
'warnings' => array(), // Not used as of today, just to show that the structure is ready for change like this.
|
||||
'error' => array(),
|
||||
),
|
||||
);
|
||||
$aData = parent::OnSubmit($aArgs);
|
||||
|
||||
if (! $aData['valid']) {
|
||||
return $aData;
|
||||
}
|
||||
|
||||
// Update object and form
|
||||
$this->OnUpdate($aArgs);
|
||||
|
||||
@@ -227,16 +227,10 @@
|
||||
$('#user-profile-wrapper .form_field .help-block > p').remove();
|
||||
|
||||
// Submiting contact form through AJAX
|
||||
//if($('#{{ oContactForm.id }} .field_set').field_set('hasTouchedFields'))
|
||||
//{
|
||||
$('#{{ oContactForm.id }}').portal_form_handler('submit', oEvent);
|
||||
//}
|
||||
$('#{{ oContactForm.id }}').portal_form_handler('submit', oEvent);
|
||||
|
||||
// Submiting preferences form through AJAX
|
||||
//if($('#{{ oPreferencesForm.id }} .field_set').field_set('hasTouchedFields'))
|
||||
//{
|
||||
$('#{{ oPreferencesForm.id }}').portal_form_handler('submit', oEvent);
|
||||
//}
|
||||
$('#{{ oPreferencesForm.id }}').portal_form_handler('submit', oEvent);
|
||||
|
||||
{% if oPasswordForm is not null %}
|
||||
// Submiting password form through AJAX
|
||||
|
||||
@@ -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.0', array(
|
||||
'itop-portal/2.7.4', 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.0',
|
||||
'itop-problem-mgmt/2.7.4',
|
||||
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.0',
|
||||
'itop-profiles-itil/2.7.4',
|
||||
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.0',
|
||||
'itop-request-mgmt-itil/2.7.4',
|
||||
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.0',
|
||||
'itop-request-mgmt/2.7.4',
|
||||
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.1',
|
||||
'itop-service-mgmt-provider/2.7.4',
|
||||
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.1',
|
||||
'itop-service-mgmt/2.7.4',
|
||||
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.0',
|
||||
'itop-sla-computation/2.7.4',
|
||||
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.0',
|
||||
'itop-storage-mgmt/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__,
|
||||
'itop-tickets/2.7.0',
|
||||
'itop-tickets/2.7.4',
|
||||
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.0',
|
||||
'itop-virtualization-mgmt/2.7.4',
|
||||
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.1',
|
||||
'itop-welcome-itil/2.7.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<information>
|
||||
<version>2.7.2</version>
|
||||
<version>2.7.4</version>
|
||||
</information>
|
||||
|
||||
@@ -1045,4 +1045,14 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:AsyncTask/Attribute:event_id+' => '',
|
||||
'Class:AsyncTask/Attribute:finalclass' => 'Final class',
|
||||
'Class:AsyncTask/Attribute:finalclass+' => '',
|
||||
'Class:AsyncTask/Attribute:status' => 'Status',
|
||||
'Class:AsyncTask/Attribute:status+' => '',
|
||||
'Class:AsyncTask/Attribute:remaining_retries' => 'Remaining retries',
|
||||
'Class:AsyncTask/Attribute:remaining_retries+' => '',
|
||||
'Class:AsyncTask/Attribute:last_error_code' => 'Last error code',
|
||||
'Class:AsyncTask/Attribute:last_error_code+' => '',
|
||||
'Class:AsyncTask/Attribute:last_error' => 'Last error',
|
||||
'Class:AsyncTask/Attribute:last_error+' => '',
|
||||
'Class:AsyncTask/Attribute:last_attempt' => 'Last attempt',
|
||||
'Class:AsyncTask/Attribute:last_attempt+' => '',
|
||||
));
|
||||
|
||||
@@ -457,6 +457,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Error:ObjectsAlreadyDeleted' => 'Error: objects have already been deleted!',
|
||||
'UI:Error:BulkDeleteNotAllowedOn_Class' => 'You are not allowed to perform a bulk delete of objects of class %1$s',
|
||||
'UI:Error:DeleteNotAllowedOn_Class' => 'You are not allowed to delete objects of class %1$s',
|
||||
'UI:Error:ReadNotAllowedOn_Class' => 'You are not allowed to view objects of class %1$s',
|
||||
'UI:Error:BulkModifyNotAllowedOn_Class' => 'You are not allowed to perform a bulk update of objects of class %1$s',
|
||||
'UI:Error:ObjectAlreadyCloned' => 'Error: the object has already been cloned!',
|
||||
'UI:Error:ObjectAlreadyCreated' => 'Error: the object has already been created!',
|
||||
@@ -465,6 +466,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Error:InvalidDashboard' => 'Error: invalid dashboard',
|
||||
'UI:Error:MaintenanceMode' => 'Application is currently in maintenance',
|
||||
'UI:Error:MaintenanceTitle' => 'Maintenance',
|
||||
'UI:Error:InvalidToken' => 'Error: the requested operation has already been performed (CSRF token not found)',
|
||||
|
||||
'UI:GroupBy:Count' => 'Count',
|
||||
'UI:GroupBy:Count+' => 'Number of elements',
|
||||
@@ -1559,6 +1561,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
|
||||
'UI:Search:Criteria:Raw:Filtered' => 'Filtered',
|
||||
'UI:Search:Criteria:Raw:FilteredOn' => 'Filtered on %1$s',
|
||||
|
||||
'UI:StateChanged' => 'State changed',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -1043,6 +1043,18 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:AsyncTask/Attribute:event_id+' => '',
|
||||
'Class:AsyncTask/Attribute:finalclass' => 'Sous-classe de tâche asynchrone',
|
||||
'Class:AsyncTask/Attribute:finalclass+' => '',
|
||||
'Class:AsyncTask/Attribute:status' => 'Statut',
|
||||
'Class:AsyncTask/Attribute:status+' => '',
|
||||
'Class:AsyncTask/Attribute:remaining_retries' => 'Essais restants',
|
||||
'Class:AsyncTask/Attribute:remaining_retries+' => '',
|
||||
'Class:AsyncTask/Attribute:last_error_code' => 'Dernier code d\'erreur',
|
||||
'Class:AsyncTask/Attribute:last_error_code+' => '',
|
||||
'Class:AsyncTask/Attribute:last_error' => 'Dernière erreur',
|
||||
'Class:AsyncTask/Attribute:last_error+' => '',
|
||||
'Class:AsyncTask/Attribute:last_attempt' => 'Dernière tentative',
|
||||
'Class:AsyncTask/Attribute:last_attempt+' => '',
|
||||
|
||||
|
||||
));
|
||||
|
||||
// Additional language entries not present in English dict
|
||||
|
||||
@@ -439,7 +439,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:Error:ObjectCannotBeUpdated' => 'Erreur: l\'objet ne peut pas être mis à jour.',
|
||||
'UI:Error:ObjectsAlreadyDeleted' => 'Erreur: les objets ont déjà été supprimés !',
|
||||
'UI:Error:BulkDeleteNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé à faire une suppression massive sur les objets de type %1$s',
|
||||
'UI:Error:DeleteNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé supprimer des objets de type %1$s',
|
||||
'UI:Error:DeleteNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé à supprimer des objets de type %1$s',
|
||||
'UI:Error:ReadNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé à voir des objets de type %1$s',
|
||||
'UI:Error:BulkModifyNotAllowedOn_Class' => 'Vous n\'êtes pas autorisé à faire une modification massive sur les objets de type %1$s',
|
||||
'UI:Error:ObjectAlreadyCloned' => 'Erreur: l\'objet a déjà été dupliqué !',
|
||||
'UI:Error:ObjectAlreadyCreated' => 'Erreur: l\'objet a déjà été créé !',
|
||||
@@ -448,6 +449,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:Error:InvalidDashboard' => 'Erreur: Le tableau de bord est invalide',
|
||||
'UI:Error:MaintenanceMode' => 'L\'application est en maintenance',
|
||||
'UI:Error:MaintenanceTitle' => 'Maintenance',
|
||||
'UI:Error:InvalidToken' => 'Erreur: l\'opération a déjà été effectuée (CSRF token not found)',
|
||||
|
||||
'UI:GroupBy:Count' => 'Nombre',
|
||||
'UI:GroupBy:Count+' => 'Nombre d\'éléments',
|
||||
@@ -1539,6 +1541,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
|
||||
'UI:Search:Criteria:Raw:Filtered' => 'Filtré',
|
||||
'UI:Search:Criteria:Raw:FilteredOn' => 'Filtré sur %1$s',
|
||||
|
||||
'UI:StateChanged' => 'Etat modifié',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -813,7 +813,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'UI:Delete:WillBeDeletedAutomatically' => 'Zal automatisch verwijderd worden',
|
||||
'UI:Delete:MustBeDeletedManually' => 'Moet handmatig verwijderd worden',
|
||||
'UI:Delete:CannotUpdateBecause_Issue' => 'Zou automatisch moeten geüpdatet worden, maar: %1$s',
|
||||
'UI:Delete:WillAutomaticallyUpdate_Fields' => 'Zal automatisch aangeapst worden (reset: %1$s)',
|
||||
'UI:Delete:WillAutomaticallyUpdate_Fields' => 'Zal automatisch aangepast worden (reset: %1$s)',
|
||||
'UI:Delete:Count_Objects/LinksReferencing_Object' => '%1$d objecten/links verwijzen naar %2$s',
|
||||
'UI:Delete:Count_Objects/LinksReferencingTheObjects' => '%1$d objecten/links verwijzen naar sommige objecten die verwijderd worden',
|
||||
'UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity' => 'Elke verdere verwijzing moet verwijderd worden om de integriteit van de database te verzekeren',
|
||||
|
||||
@@ -192,7 +192,7 @@ function activateFirstTabWithError(sFormId) {
|
||||
if ($fieldsWithError.length > 0)
|
||||
{
|
||||
$tabsContainer.tabs("option", "active", index);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1210,7 +1210,7 @@ HTML
|
||||
$sOrigState = utils::ReadPostedParam('obj_state_orig', '');
|
||||
if ($sTargetState != $sOrigState)
|
||||
{
|
||||
$aWarnings[] = 'State changed';
|
||||
$aWarnings[] = Dict::S('UI:StateChanged');
|
||||
}
|
||||
$oObj->Set($sStateAttCode, $sTargetState);
|
||||
}
|
||||
|
||||
@@ -242,7 +242,6 @@ try
|
||||
{
|
||||
case 'parser_preview':
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('text/html');
|
||||
$sSeparator = utils::ReadParam('separator', ',', false, 'raw_data');
|
||||
if ($sSeparator == 'tab') $sSeparator = "\t";
|
||||
|
||||
@@ -38,7 +38,6 @@ try
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
$operation = utils::ReadParam('operation', '');
|
||||
$sClass = utils::ReadParam('class', 'MissingAjaxParam', false, 'class');
|
||||
@@ -62,9 +61,14 @@ try
|
||||
ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'attachment');
|
||||
if ($iCacheSec > 0)
|
||||
{
|
||||
$oPage->add_header("Expires: "); // Reset the value set in ajax_page
|
||||
$oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec");
|
||||
$oPage->add_header("Pragma: cache"); // Reset the value set .... where ?
|
||||
$oPage->add_header("Expires: "); // Reset the value set in ajax_page
|
||||
|
||||
// X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page
|
||||
// so we're resetting its value ! (see N°3416)
|
||||
$oPage->add_xframe_options('');
|
||||
|
||||
$oPage->add_header("Last-Modified: Wed, 15 Jun 2015 13:21:15 GMT"); // An arbitrary date in the past is ok
|
||||
}
|
||||
}
|
||||
@@ -76,12 +80,16 @@ try
|
||||
$id = utils::ReadParam('id', '');
|
||||
$sSecret = utils::ReadParam('s', '');
|
||||
$iCacheSec = 31556926; // One year ahead: an inline image cannot change
|
||||
if (!empty($id) && !empty($sSecret))
|
||||
{
|
||||
if (!empty($id) && !empty($sSecret)) {
|
||||
ormDocument::DownloadDocument($oPage, 'InlineImage', $id, 'contents', 'inline', 'secret', $sSecret);
|
||||
$oPage->add_header("Expires: "); // Reset the value set in ajax_page
|
||||
$oPage->add_header("Cache-Control: no-transform,public,max-age=$iCacheSec,s-maxage=$iCacheSec");
|
||||
$oPage->add_header("Pragma: cache"); // Reset the value set .... where ?
|
||||
$oPage->add_header("Expires: "); // Reset the value set in ajax_page
|
||||
|
||||
// X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page
|
||||
// so we're resetting its value ! (see N°3416)
|
||||
$oPage->add_xframe_options('');
|
||||
|
||||
$oPage->add_header("Last-Modified: Wed, 15 Jun 2016 13:21:15 GMT"); // An arbitrary date in the past is ok
|
||||
}
|
||||
break;
|
||||
@@ -92,6 +100,11 @@ try
|
||||
$oPage->SetContentType('text/javascript');
|
||||
$oPage->add_header('Cache-control: public, max-age=86400'); // Cache for 24 hours
|
||||
$oPage->add_header("Pragma: cache"); // Reset the value set .... where ?
|
||||
|
||||
// X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page
|
||||
// so we're resetting its value ! (see N°3416)
|
||||
$oPage->add_xframe_options('');
|
||||
|
||||
$oPage->add(file_get_contents(Utils::GetCachePath().$sSignature.'.js'));
|
||||
break;
|
||||
|
||||
|
||||
@@ -68,8 +68,6 @@ try
|
||||
LoginWebPage::DoLoginEx($sRequestedPortalId, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
|
||||
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
|
||||
$sEncoding = utils::ReadParam('encoding', 'serialize');
|
||||
@@ -889,13 +887,12 @@ try
|
||||
case 'chart':
|
||||
// Workaround for IE8 + IIS + HTTPS
|
||||
// See TRAC #363, fix described here: http://forums.codecharge.com/posts.php?post_id=97771
|
||||
$oPage->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT");
|
||||
$oPage->add_header("Cache-Control: cache, must-revalidate");
|
||||
$oPage->add_header("Pragma: public");
|
||||
$oPage->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT");
|
||||
|
||||
$aParams = utils::ReadParam('params', array(), false, 'raw_data');
|
||||
if ($sFilter != '')
|
||||
{
|
||||
if ($sFilter != '') {
|
||||
$oFilter = DBSearch::unserialize($sFilter);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'chart_ajax', false);
|
||||
@@ -961,6 +958,11 @@ try
|
||||
if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
// X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page
|
||||
// so we're resetting its value ! (see N°3416)
|
||||
$oPage->add_xframe_options('');
|
||||
|
||||
ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'inline');
|
||||
$oKPI->ComputeAndReport('Data fetch and format');
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ try
|
||||
}
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('text/html');
|
||||
|
||||
$sListParams = utils::ReadParam('list_params', '{}', false, 'raw_data');
|
||||
|
||||
@@ -727,10 +727,16 @@ class ApplicationInstaller
|
||||
SetupPage::log_info("There are $iOrphanCount useless records in {$sDBPrefix}priv_change (".sprintf('%.2f', ((100.0*$iOrphanCount)/$iTotalCount))."%)");
|
||||
if ($iOrphanCount > 0)
|
||||
{
|
||||
SetupPage::log_info("Removing the orphan records...");
|
||||
$sCleanup = "DELETE FROM `{$sDBPrefix}priv_change` USING `{$sDBPrefix}priv_change` LEFT JOIN `{$sDBPrefix}priv_changeop` ON `{$sDBPrefix}priv_change`.id = `{$sDBPrefix}priv_changeop`.changeid WHERE `{$sDBPrefix}priv_changeop`.id IS NULL;";
|
||||
CMDBSource::Query($sCleanup);
|
||||
SetupPage::log_info("Cleanup completed successfully.");
|
||||
//N°3793
|
||||
if ($iOrphanCount > 100000)
|
||||
{
|
||||
SetupPage::log_warning("There are too much useless records ($iOrphanCount) in {$sDBPrefix}priv_change. Cleanup cannot be done during setup.");
|
||||
} else {
|
||||
SetupPage::log_info("Removing the orphan records...");
|
||||
$sCleanup = "DELETE FROM `{$sDBPrefix}priv_change` USING `{$sDBPrefix}priv_change` LEFT JOIN `{$sDBPrefix}priv_changeop` ON `{$sDBPrefix}priv_change`.id = `{$sDBPrefix}priv_changeop`.changeid WHERE `{$sDBPrefix}priv_changeop`.id IS NULL;";
|
||||
CMDBSource::Query($sCleanup);
|
||||
SetupPage::log_info("Cleanup completed successfully.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -33,10 +33,14 @@ LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be
|
||||
$sOperation = Utils::ReadParam('operation', 'step1');
|
||||
$oP = new SetupPage('iTop email test utility');
|
||||
|
||||
// Although this page doesn't expose sensitive info, with it we can send multiple emails
|
||||
// So we're adding this http header to reduce CSRF exposure...
|
||||
$oP->add_xframe_options('DENY');
|
||||
|
||||
|
||||
/**
|
||||
* Helper to check server setting required to send an email
|
||||
*/
|
||||
*/
|
||||
function CheckEmailSetting($oP)
|
||||
{
|
||||
$bRet = true;
|
||||
@@ -255,11 +259,11 @@ try
|
||||
break;
|
||||
|
||||
case 'step2':
|
||||
$oP->no_cache();
|
||||
$sTo = Utils::ReadParam('to', '', false, 'raw_data');
|
||||
$sFrom = Utils::ReadParam('from', '', false, 'raw_data');
|
||||
DisplayStep2($oP, $sFrom, $sTo);
|
||||
break;
|
||||
$oP->no_cache();
|
||||
$sTo = Utils::ReadParam('to', '', false, 'raw_data');
|
||||
$sFrom = Utils::ReadParam('from', '', false, 'raw_data');
|
||||
DisplayStep2($oP, $sFrom, $sTo);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oP->error("Error: unsupported operation '$sOperation'");
|
||||
|
||||
@@ -61,24 +61,29 @@ class iTopExtension
|
||||
* @var bool
|
||||
*/
|
||||
public $bVisible;
|
||||
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public $aModules;
|
||||
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public $aModuleVersion;
|
||||
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public $aModuleInfo;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $sSourceDir;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $aMissingDependencies;
|
||||
@@ -96,6 +101,7 @@ class iTopExtension
|
||||
$this->sInstalledVersion = '';
|
||||
$this->aModules = array();
|
||||
$this->aModuleVersion = array();
|
||||
$this->aModuleInfo = array();
|
||||
$this->sSourceDir = '';
|
||||
$this->bVisible = true;
|
||||
$this->aMissingDependencies = array();
|
||||
@@ -310,11 +316,11 @@ class iTopExtensionsMap
|
||||
$sModuleVersion = '0.0.1';
|
||||
}
|
||||
|
||||
if (($sParentExtensionId !== null) && (array_key_exists($sParentExtensionId, $this->aExtensions)) && ($this->aExtensions[$sParentExtensionId] instanceof iTopExtension))
|
||||
{
|
||||
if (($sParentExtensionId !== null) && (array_key_exists($sParentExtensionId, $this->aExtensions)) && ($this->aExtensions[$sParentExtensionId] instanceof iTopExtension)) {
|
||||
// Already inside an extension, let's add this module the list of modules belonging to this extension
|
||||
$this->aExtensions[$sParentExtensionId]->aModules[] = $sModuleName;
|
||||
$this->aExtensions[$sParentExtensionId]->aModuleVersion[$sModuleName] = $sModuleVersion;
|
||||
$this->aExtensions[$sParentExtensionId]->aModuleInfo[$sModuleName] = $aModuleInfo[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -329,8 +335,6 @@ class iTopExtensionsMap
|
||||
}
|
||||
|
||||
// Let's create a "fake" extension from this module (containing just this module) for backwards compatibility
|
||||
$sExtensionId = $sModuleId;
|
||||
|
||||
$oExtension = new iTopExtension();
|
||||
$oExtension->sCode = $sModuleName;
|
||||
$oExtension->sLabel = $aModuleInfo[2]['label'];
|
||||
@@ -341,9 +345,10 @@ class iTopExtensionsMap
|
||||
$oExtension->sMoreInfoUrl = $aModuleInfo[2]['doc.more_information'];
|
||||
$oExtension->aModules = array($sModuleName);
|
||||
$oExtension->aModuleVersion[$sModuleName] = $sModuleVersion;
|
||||
$oExtension->aModuleInfo[$sModuleName] = $aModuleInfo[2];
|
||||
$oExtension->sSourceDir = $sSearchDir;
|
||||
$oExtension->bVisible = $bVisible;
|
||||
$this->AddExtension($oExtension);
|
||||
$this->AddExtension($oExtension);
|
||||
}
|
||||
|
||||
closedir($hDir);
|
||||
|
||||
@@ -54,5 +54,7 @@ if (!function_exists('json_decode'))
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
//N°3671 setup context: force $bForceTrustProxy to be persisted in next calls
|
||||
utils::GetAbsoluteUrlAppRoot(true);
|
||||
$oWizard = new WizardController('WizStepWelcome');
|
||||
$oWizard->Run();
|
||||
|
||||
@@ -551,14 +551,25 @@ class SetupUtils
|
||||
SetupPage::log('Info - PHP functions disabled: '.implode(', ', $aDisabled));
|
||||
if (in_array('exec', $aDisabled))
|
||||
{
|
||||
$aResult[] = new CheckResult(CheckResult::ERROR, "The PHP exec() function has been disabled on this server");
|
||||
return new CheckResult(CheckResult::ERROR, "The PHP exec() function has been disabled on this server");
|
||||
}
|
||||
|
||||
// availability of dot / dot.exe
|
||||
if (empty($sGraphvizPath))
|
||||
{
|
||||
$sGraphvizPath = 'dot';
|
||||
} else {
|
||||
clearstatcache();
|
||||
if (!is_file($sGraphvizPath) || !is_executable($sGraphvizPath)) {
|
||||
//N°3412 avoid shell injection
|
||||
return new CheckResult(CheckResult::WARNING, "$sGraphvizPath could not be executed: Please make sure it is installed and in the path");
|
||||
}
|
||||
|
||||
if (!utils::IsWindowsEnvironment()){
|
||||
$sGraphvizPath = escapeshellcmd($sGraphvizPath);
|
||||
}
|
||||
}
|
||||
|
||||
$sCommand = "\"$sGraphvizPath\" -V 2>&1";
|
||||
|
||||
$aOutput = array();
|
||||
@@ -568,10 +579,6 @@ class SetupUtils
|
||||
{
|
||||
$oResult = new CheckResult(CheckResult::INFO, "dot is present: ".$aOutput[0]);
|
||||
}
|
||||
elseif ($iRetCode == 1)
|
||||
{
|
||||
$oResult = new CheckResult(CheckResult::WARNING, "dot could not be found: ".implode(' ', $aOutput)." - Please make sure it is installed and in the path.");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult = new CheckResult(CheckResult::WARNING, "dot could not be executed (retcode=$iRetCode): Please make sure it is installed and in the path");
|
||||
|
||||
@@ -716,7 +716,7 @@ class WizStepLicense extends WizardStep
|
||||
$aLicenses = SetupUtils::GetLicenses();
|
||||
$oPage->add_style(
|
||||
<<<EOF
|
||||
fieldset {
|
||||
fieldset ul{
|
||||
max-height: 18em;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -978,7 +978,7 @@ class WizStepMiscParams extends WizardStep
|
||||
public function Display(WebPage $oPage)
|
||||
{
|
||||
$sDefaultLanguage = $this->oWizard->GetParameter('default_language', $this->oWizard->GetParameter('admin_language'));
|
||||
$sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetDefaultUrlAppRoot());
|
||||
$sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetDefaultUrlAppRoot(true));
|
||||
$sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot';
|
||||
$sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath);
|
||||
$sSampleData = $this->oWizard->GetParameter('sample_data', 'yes');
|
||||
@@ -996,14 +996,14 @@ class WizStepMiscParams extends WizardStep
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>Application URL</legend>');
|
||||
$oPage->add('<table>');
|
||||
$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'"><span id="v_application_url"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2">Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.</td><tr>');
|
||||
$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'" style="width: 100%;box-sizing: border-box;"><span id="v_application_url"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><div class="message message-warning">Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.</div></td><tr>');
|
||||
$oPage->add('</table>');
|
||||
$oPage->add('</fieldset>');
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>Path to Graphviz\' dot application</legend>');
|
||||
$oPage->add('<table style="width: 100%;">');
|
||||
$oPage->add('<tr><td>Path: </td><td><input id="graphviz_path" name="graphviz_path" type="text" size="35" maxlength="1024" value="'.htmlentities($sGraphvizPath, ENT_QUOTES, 'UTF-8').'"><span id="v_graphviz_path"/></td><tr>');
|
||||
$oPage->add('<tr><td>Path: </td><td><input id="graphviz_path" name="graphviz_path" type="text" size="35" maxlength="1024" value="'.htmlentities($sGraphvizPath, ENT_QUOTES, 'UTF-8').'" style="width: 100%;box-sizing: border-box;"><span id="v_graphviz_path"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><a href="http://www.graphviz.org" target="_blank">Graphviz</a> is required to display the impact analysis graph (i.e. impacts / depends on).</td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><span id="graphviz_status"></span></td><tr>');
|
||||
$oPage->add('</table>');
|
||||
@@ -1125,21 +1125,21 @@ class WizStepUpgradeMiscParams extends WizardStep
|
||||
|
||||
public function Display(WebPage $oPage)
|
||||
{
|
||||
$sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetDefaultUrlAppRoot());
|
||||
$sApplicationURL = $this->oWizard->GetParameter('application_url', utils::GetAbsoluteUrlAppRoot(true)); //Preserve existing configuration.
|
||||
$sDefaultGraphvizPath = (strtolower(substr(PHP_OS, 0, 3)) === 'win') ? 'C:\\Program Files\\Graphviz\\bin\\dot.exe' : '/usr/bin/dot';
|
||||
$sGraphvizPath = $this->oWizard->GetParameter('graphviz_path', $sDefaultGraphvizPath);
|
||||
$oPage->add('<h2>Additional parameters</h2>');
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>Application URL</legend>');
|
||||
$oPage->add('<table>');
|
||||
$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'"><span id="v_application_url"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2">Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.</td><tr>');
|
||||
$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'" style="width: 100%;box-sizing: border-box;"><span id="v_application_url"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><div class="message message-warning">Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.</div></td><tr>');
|
||||
$oPage->add('</table>');
|
||||
$oPage->add('</fieldset>');
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>Path to Graphviz\' dot application</legend>');
|
||||
$oPage->add('<table style="width: 100%;">');
|
||||
$oPage->add('<tr><td>Path: </td><td><input id="graphviz_path" name="graphviz_path" type="text" size="35" maxlength="1024" value="'.htmlentities($sGraphvizPath, ENT_QUOTES, 'UTF-8').'"><span id="v_graphviz_path"/></td><tr>');
|
||||
$oPage->add('<tr><td>Path: </td><td><input id="graphviz_path" name="graphviz_path" type="text" size="35" maxlength="1024" value="'.htmlentities($sGraphvizPath, ENT_QUOTES, 'UTF-8').'" style="width: 100%;box-sizing: border-box;"><span id="v_graphviz_path"/></td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><a href="http://www.graphviz.org" target="_blank">Graphviz</a> is required to display the impact analysis graph (i.e. impacts / depends on).</td><tr>');
|
||||
$oPage->add('<tr><td colspan="2"><span id="graphviz_status"></span></td><tr>');
|
||||
$oPage->add('</table>');
|
||||
@@ -1376,14 +1376,14 @@ class WizStepModulesChoice extends WizardStep
|
||||
if (substr($sBannerPath, 0, 1) == '/')
|
||||
{
|
||||
// absolute path, means relative to APPROOT
|
||||
$sBannerUrl = utils::GetDefaultUrlAppRoot().$sBannerPath;
|
||||
$sBannerUrl = utils::GetDefaultUrlAppRoot(true).$sBannerPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
// relative path: i.e. relative to the directory containing the XML file
|
||||
$sFullPath = dirname($this->GetSourceFilePath()).'/'.$sBannerPath;
|
||||
$sRealPath = realpath($sFullPath);
|
||||
$sBannerUrl = utils::GetDefaultUrlAppRoot().str_replace(realpath(APPROOT), '', $sRealPath);
|
||||
$sBannerUrl = utils::GetDefaultUrlAppRoot(true).str_replace(realpath(APPROOT), '', $sRealPath);
|
||||
}
|
||||
$oPage->add('<td><img src="'.$sBannerUrl.'"/><td>');
|
||||
}
|
||||
@@ -1666,10 +1666,12 @@ EOF
|
||||
|
||||
/**
|
||||
* Converts the list of selected "choices" into a list of "modules": take into account the selected and the mandatory modules
|
||||
* @param hash $aInfo Info about the "choice" array('options' => array(...), 'alternatives' => array(...))
|
||||
* @param hash $aSelectedChoices List of selected choices array('name' => 'selected_value_id')
|
||||
* @param hash $aModules Return parameter: List of selected modules array('module_id' => true)
|
||||
*
|
||||
* @param array $aInfo Info about the "choice" array('options' => array(...), 'alternatives' => array(...))
|
||||
* @param array $aSelectedChoices List of selected choices array('name' => 'selected_value_id')
|
||||
* @param array $aModules Return parameter: List of selected modules array('module_id' => true)
|
||||
* @param string $sParentId Used for recursion
|
||||
*
|
||||
* @return string A text representation of what will be installed
|
||||
*/
|
||||
protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sParentId = '', $sDisplayChoices = '', &$aSelectedExtensions = null)
|
||||
@@ -1690,35 +1692,55 @@ EOF
|
||||
}
|
||||
}
|
||||
$aOptions = isset($aInfo['options']) ? $aInfo['options'] : array();
|
||||
foreach($aOptions as $index => $aChoice)
|
||||
{
|
||||
foreach($aOptions as $index => $aChoice) {
|
||||
$sChoiceId = $sParentId.self::$SEP.$index;
|
||||
if ( (isset($aChoice['mandatory']) && $aChoice['mandatory']) ||
|
||||
(isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId)) )
|
||||
{
|
||||
$aModuleInfo = [];
|
||||
// Get the extension corresponding to the choice
|
||||
foreach ($this->oExtensionsMap->GetAllExtensions() as $sExtensionVersion => $oExtension) {
|
||||
if (utils::StartsWith($sExtensionVersion, $aChoice['extension_code'].'/')) {
|
||||
$aModuleInfo = $oExtension->aModuleInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((isset($aChoice['mandatory']) && $aChoice['mandatory']) ||
|
||||
(isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId))) {
|
||||
$sDisplayChoices .= '<li>'.$aChoice['title'].'</li>';
|
||||
if (isset($aChoice['modules']))
|
||||
{
|
||||
foreach($aChoice['modules'] as $sModuleId)
|
||||
{
|
||||
$aModules[$sModuleId] = true; // store the Id of the selected module
|
||||
if (isset($aChoice['modules'])) {
|
||||
foreach ($aChoice['modules'] as $sModuleId) {
|
||||
$bSelected = true;
|
||||
if (isset($aModuleInfo[$sModuleId])) {
|
||||
// Test if module has 'auto_select'
|
||||
$aInfo = $aModuleInfo[$sModuleId];
|
||||
if (isset($aInfo['auto_select'])) {
|
||||
// Check the module selection
|
||||
try {
|
||||
$bSelected = false;
|
||||
SetupInfo::SetSelectedModules($aModules);
|
||||
eval('$bSelected = ('.$aInfo['auto_select'].');');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$bSelected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bSelected) {
|
||||
$aModules[$sModuleId] = true; // store the Id of the selected module
|
||||
SetupInfo::SetSelectedModules($aModules);
|
||||
}
|
||||
}
|
||||
}
|
||||
$sChoiceType = isset($aChoice['type']) ? $aChoice['type'] : 'wizard_option';
|
||||
if ($aSelectedExtensions !== null)
|
||||
{
|
||||
if ($aSelectedExtensions !== null) {
|
||||
$aSelectedExtensions[] = $aChoice['extension_code'];
|
||||
}
|
||||
// Recurse only for selected choices
|
||||
if (isset($aChoice['sub_options']))
|
||||
{
|
||||
if (isset($aChoice['sub_options'])) {
|
||||
$sDisplayChoices .= '<ul>';
|
||||
$sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices, $aSelectedExtensions);
|
||||
$sDisplayChoices .= '</ul>';
|
||||
}
|
||||
$sDisplayChoices .= '</li>';
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
$aAlternatives = isset($aInfo['alternatives']) ? $aInfo['alternatives'] : array();
|
||||
@@ -1754,7 +1776,6 @@ EOF
|
||||
}
|
||||
$sDisplayChoices .= '</li>';
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
if ($sParentId == '')
|
||||
{
|
||||
@@ -2494,7 +2515,7 @@ class WizStepDone extends WizardStep
|
||||
$aManualSteps = array();
|
||||
$aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard);
|
||||
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot(true);
|
||||
$aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true);
|
||||
foreach($aSelectedModules as $sModuleId)
|
||||
{
|
||||
@@ -2526,7 +2547,7 @@ class WizStepDone extends WizardStep
|
||||
{
|
||||
// To mitigate security risks: pass only the filename without the extension, the download will add the extension itself
|
||||
$oPage->p('Your backup is ready');
|
||||
$oPage->p('<a style="background:transparent;" href="'.utils::GetAbsoluteUrlAppRoot().'setup/ajax.dataloader.php?operation=async_action&step_class=WizStepDone¶ms[backup]='.urlencode($sBackupDestination).'&authent='.$this->oWizard->GetParameter('authent','').'" target="_blank"><img src="../images/tar.png" style="border:0;vertical-align:middle;"> Download '.basename($sBackupDestination).'</a>');
|
||||
$oPage->p('<a style="background:transparent;" href="'.utils::GetAbsoluteUrlAppRoot(true).'setup/ajax.dataloader.php?operation=async_action&step_class=WizStepDone¶ms[backup]='.urlencode($sBackupDestination).'&authent='.$this->oWizard->GetParameter('authent','').'" target="_blank"><img src="../images/tar.png" style="border:0;vertical-align:middle;"> Download '.basename($sBackupDestination).'</a>');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -435,18 +435,16 @@ abstract class Controller
|
||||
$sFileMimeType = utils::GetFileMimeType($sFilePath);
|
||||
header('Content-Type: '.$sFileMimeType);
|
||||
|
||||
if ($bFileTransfer)
|
||||
{
|
||||
if ($bFileTransfer) {
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Disposition: inline; filename="'.$sDownloadArchiveName);
|
||||
}
|
||||
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
|
||||
foreach ($aHeaders as $sKey => $sValue)
|
||||
{
|
||||
foreach ($aHeaders as $sKey => $sValue) {
|
||||
header($sKey.': '.$sValue);
|
||||
}
|
||||
|
||||
@@ -558,7 +556,7 @@ abstract class Controller
|
||||
{
|
||||
case 'html':
|
||||
$this->m_oPage = new iTopWebPage($this->GetOperationTitle());
|
||||
$this->m_oPage->add_header('X-Frame-Options: deny');
|
||||
$this->m_oPage->add_xframe_options();
|
||||
break;
|
||||
|
||||
case 'ajax':
|
||||
|
||||
@@ -160,9 +160,41 @@ abstract class FormManager
|
||||
/**
|
||||
* @param array|null $aArgs
|
||||
*
|
||||
* @return mixed
|
||||
* @return array
|
||||
*
|
||||
* @since 2.7.4 3.0.0 N°3430
|
||||
*/
|
||||
abstract public function OnSubmit($aArgs = null);
|
||||
public function OnSubmit($aArgs = null)
|
||||
{
|
||||
$aData = array(
|
||||
'valid' => true,
|
||||
'messages' => array(
|
||||
'success' => array(),
|
||||
'warnings' => array(), // Not used as of today, just to show that the structure is ready for change like this.
|
||||
'error' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->CheckTransaction($aData);
|
||||
|
||||
return $aData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aData
|
||||
*
|
||||
* @since 2.7.4 3.0.0 N°3430
|
||||
*/
|
||||
public function CheckTransaction(&$aData)
|
||||
{
|
||||
$isTransactionValid = \utils::IsTransactionValid($this->oForm->GetTransactionId(), false); //The transaction token is kept in order to preserve BC with ajax forms (the second call would fail if the token is deleted). (The GC will take care of cleaning the token for us later on)
|
||||
if (!$isTransactionValid) {
|
||||
$aData['messages']['error'] += [
|
||||
'_main' => [\Dict::S('UI:Error:InvalidToken')] //This message is generic, if you override this method you should use a more precise message. @see \Combodo\iTop\Portal\Form\ObjectFormManager::CheckTransaction
|
||||
];
|
||||
$aData['valid'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $aArgs
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{% if bFailedLogin %}
|
||||
<p class="hilite">{{ sMessage }}</p>
|
||||
{% else %}
|
||||
<p>{{ sMessage }}</p>
|
||||
<p>{{ sMessage|raw }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock login_title %}
|
||||
|
||||
@@ -73,7 +73,7 @@ class ItopDataTestCase extends ItopTestCase
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
//require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
|
||||
require_once(APPROOT.'application/utils.inc.php');
|
||||
|
||||
@@ -408,8 +408,12 @@ class ItopDataTestCase extends ItopTestCase
|
||||
* @return \DBObject
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function CreateUser($sLogin, $iProfileId)
|
||||
protected function CreateUser($sLogin, $iProfileId, $sPassword=null)
|
||||
{
|
||||
if (empty($sPassword)){
|
||||
$sPassword = $sLogin;
|
||||
}
|
||||
|
||||
$oUserProfile = new URP_UserProfile();
|
||||
$oUserProfile->Set('profileid', $iProfileId);
|
||||
$oUserProfile->Set('reason', 'UNIT Tests');
|
||||
@@ -417,7 +421,7 @@ class ItopDataTestCase extends ItopTestCase
|
||||
$oUser = $this->createObject('UserLocal', array(
|
||||
'contactid' => 2,
|
||||
'login' => $sLogin,
|
||||
'password' => $sLogin,
|
||||
'password' => $sPassword,
|
||||
'language' => 'EN US',
|
||||
'profile_list' => $oSet,
|
||||
));
|
||||
@@ -426,6 +430,29 @@ class ItopDataTestCase extends ItopTestCase
|
||||
return $oUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oUser
|
||||
* @param int $iProfileId
|
||||
*
|
||||
* @return \DBObject
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function AddProfileToUser($oUser, $iProfileId)
|
||||
{
|
||||
$oUserProfile = new URP_UserProfile();
|
||||
$oUserProfile->Set('profileid', $iProfileId);
|
||||
$oUserProfile->Set('reason', 'UNIT Tests');
|
||||
/** @var DBObjectSet $oSet */
|
||||
$oSet = $oUser->Get('profile_list');
|
||||
$oSet->AddObject($oUserProfile);
|
||||
$oUser = $this->updateObject('UserLocal', $oUser->GetKey(), array(
|
||||
'profile_list' => $oSet,
|
||||
));
|
||||
$this->debug("Updated {$oUser->GetName()} ({$oUser->GetKey()})");
|
||||
|
||||
return $oUser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a Hypervisor in database
|
||||
|
||||
@@ -93,7 +93,31 @@ class ItopTestCase extends TestCase
|
||||
{
|
||||
$sId = str_replace('"', '', $this->getName());
|
||||
$sId = str_replace(' ', '_', $sId);
|
||||
|
||||
return $sId;
|
||||
}
|
||||
|
||||
public function InvokeNonPublicStaticMethod($sObjectClass, $sMethodName, $aArgs)
|
||||
{
|
||||
return $this->InvokeNonPublicMethod($sObjectClass, $sMethodName, null, $aArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sObjectClass for example DBObject::class
|
||||
* @param string $sMethodName
|
||||
* @param object $oObject
|
||||
* @param array $aArgs
|
||||
*
|
||||
* @return mixed method result
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function InvokeNonPublicMethod($sObjectClass, $sMethodName, $oObject, $aArgs)
|
||||
{
|
||||
$class = new \ReflectionClass($sObjectClass);
|
||||
$method = $class->getMethod($sMethodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invokeArgs($oObject, $aArgs);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user