Compare commits

..

60 Commits

Author SHA1 Message Date
rquetiez
1004dd9afb N°1649 - Support blobs and images as external fields 2020-07-03 22:11:11 +02:00
acognet
9628a1d028 N°2284 - Replace JQuery Autocompleter plugin by JQuery UI Autocomplete widget and start of bug 2390 - Auto-complete - Relevant results in first 2020-07-02 11:46:51 +02:00
acognet
5585385d08 N°2393 - API : Font Awesome remove v4 compatibility 2020-07-02 11:03:05 +02:00
acognet
98870b06e3 2548 - API : remove \DBObject::GetRelationQueries overrides in default datamodel 2020-07-01 16:28:20 +02:00
bruno DA SILVA
46d91322c1 n°2556 - fix errors in the merge of support/2.7 into develop
The cherry picks resulted in an out of order apply of the commits, the result was that the wrong code was keeped
2020-07-01 15:41:46 +02:00
acognet
ca28f8f3c4 2393 - API : Font Awesome remove v4 compatibility 2020-06-30 16:28:15 +02:00
acognet
0aaa55b35b 3009 - PHP Minimum version raised to 7.1 2020-06-30 10:10:00 +02:00
acognet
e9a1167da6 N°2363 - API : deprecate old linkedset update pattern 2020-06-30 09:02:42 +02:00
acognet
a67fce66fc N°2999 - Optimize OQL 2020-06-26 16:42:03 +02:00
acognet
491d1d7d53 N°API : remove CMDBSource::GetNextInsertId 2020-06-26 16:20:15 +02:00
acognet
54b48dc908 N°2372 - API : remove \MetaModel::EnumLinksClasses and \MetaModel::EnumLinkingClasses 2020-06-26 16:17:04 +02:00
acognet
dadeab58eb N°852 - Cleanup: remove deprecated impact analysis algorithm and clean up old broken test 2020-06-26 15:03:10 +02:00
acognet
0ecdc6620b N°852 - Cleanup: remove deprecated impact analysis algorithm 2020-06-26 15:02:30 +02:00
acognet
80161b909e N°2362 - API : remove DBInsertTracked / DBUpdateTracked 2020-06-26 14:16:14 +02:00
Pierre Goiffon
888232e8c3 Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	test/core/HTMLDOMSanitizerTest.php
2020-06-26 10:53:49 +02:00
Pierre Goiffon
d904883bdd 📝 CONTRIBUTING : fix release branch naming 2020-06-23 17:48:42 +02:00
bruno DA SILVA
35d2c3afac uses the conventionnal host 2020-06-23 15:10:09 +02:00
Molkobain
7cee8c3cd0 Update README.md 2020-06-23 15:05:41 +02:00
Molkobain
27622bbcec Update readme.txt 2020-06-23 15:05:38 +02:00
bruno DA SILVA
219270ce81 Removes latest versions to ease maintenance 2020-06-23 14:59:11 +02:00
OИUЯd da silva
16dd47a3b9 simplify the readme (#143)
Removes latest versions to ease maintenance
2020-06-23 14:51:52 +02:00
Molkobain
70835984de Add "software requirements" section to README
At first I added a complete section with a table and all currently supported versions of iTop, then @bruno-ds pointed out that it would only be redundant with the wiki page and just another source of information to maintain (which is totally true, thanks!). So instead it's just a simple link in the "resources" section.
2020-06-23 14:23:15 +02:00
Pierre Goiffon
d9224f43f2 AttributeImage : remove useless override
The previous code was removed in d5b0bb02, and until then the method was just calling the parent doing nothing more
2020-06-23 11:39:51 +02:00
Molkobain
5b9643d6fb Update PHPDoc 2020-06-22 16:06:21 +02:00
bruno DA SILVA
d3525190d5 N°2556 - Html sanitization preserve content of removed tags (except for a forbidden list)
forbidden list: see $aTagsContentRemovableList

(cherry picked from commit 746b47bb0e)
(cherry picked from commit 79909fadc0)
2020-06-22 11:40:38 +02:00
Pierre Goiffon
f20808d929 Merge remote-tracking branch 'origin/support/2.7' into develop 2020-06-22 11:39:44 +02:00
Pierre Goiffon
aee80e41ca N°2214 Fix typo in method name
Introduced in b7136c0b7a
Many Thanks @jbostoen !
2020-06-19 13:47:15 +02:00
Pierre Goiffon
56ea6c8848 N°2214 fix @since after cherry-pick 2020-06-15 16:22:04 +02:00
Pierre Goiffon
b7136c0b7a N°2214 Add PHP check in CLI scripts
It is quite common that the PHP interpreter that is launched in CLI is different that the one used by the webserver. So iTop code launched by CLI could run in a context that doesn't meet iTop requirements !

This adds in the following scripts the same control that is done on the setup wizard first step :
* cron.php
* backup, check-backup
* export, exportv2
* bulk import
* synchro-exec, synchro-import

If the check throws at least one error then the script is stopped with an appropriate message, and a log is made (IssueLog, Error level, CLI channel)

(cherry picked from commit c768e18e2b : no risk taken for 2.7.1, so cherry picked for 2.8.0)
2020-06-15 15:20:17 +02:00
Pierre Goiffon
ea94986247 Merge remote-tracking branch 'origin/support/2.7' into develop 2020-06-15 15:19:12 +02:00
odain
d8363067e6 ci 2 new options: coverture + run only one test 💚
try to have coverture option
2020-06-15 10:32:47 +02:00
Thomas Casteleyn
b302569feb Update Dutch SynchroAttribute::update_policy translation 2020-06-08 13:02:57 +02:00
Pierre Goiffon
44008fc179 Merge remote-tracking branch 'origin/support/2.7' into develop 2020-05-26 12:26:44 +02:00
Thomas Casteleyn
c2f62a13e6 Fix duplicate version loading (#141) 2020-05-13 14:53:47 +02:00
Pierre Goiffon
accda04a37 Hierarchical selection popup : now collapsed by default, and collapse all / expand all buttons (#132)
The collapse all / expand all is not printed if no child exists
Combodo implementation of PR #87
2020-04-27 11:21:37 +02:00
Pierre Goiffon
f364e7b043 N°2923 Datatable : fix var name typo
Thanks @bruno-ds !
2020-04-24 18:28:50 +02:00
Pierre Goiffon
42afe033ef N°2923 Datatable : use container id instead of externally generated id 2020-04-24 17:16:47 +02:00
Pierre Goiffon
8de4c0360d Merge remote-tracking branch 'origin/support/2.7' into develop 2020-04-20 16:08:15 +02:00
Molkobain
24130dd94f Change version number to 2.8.0-dev 2020-04-14 18:01:30 +02:00
Pierre Goiffon
59cc6d3f76 📝 CONTRIBUTING : fix unecessary escape 2020-04-07 15:33:26 +02:00
Pierre Goiffon
ee37373cfa 📝 CONTRIBUTING : branch model paragraph small changes 2020-04-07 15:24:56 +02:00
Pierre Goiffon
56d9653f15 📝 CONTRIBUTING : modify branch model
We are renaming the master branch, so using a custom GitFlow branch model :)
2020-04-06 09:32:23 +02:00
Pierre Goiffon
d5670abdcc 📝 Fix PHPDoc for \MFElement::_FindNode
Introduced in 4688c92e
2020-04-06 08:57:23 +02:00
Pierre Goiffon
0360a3160d Merge remote-tracking branch 'origin/releases/germanium' into develop
# Conflicts:
#	setup/modelfactory.class.inc.php
2020-04-06 08:47:22 +02:00
Thomas Casteleyn
bcd21aefb4 📝 DBObject fix wrong PHPDoc (#133)
Thanks to @Hipska !
2020-04-06 08:32:31 +02:00
Pierre Goiffon
3cbcdd4f13 🎨 MFElement : fix access modifiers & PHPDoc 2020-03-31 08:53:22 +02:00
odain
c46d0f5662 N°2888 Impossibility to import iTop User with password policy 2020-03-30 17:48:01 +02:00
Pierre Goiffon
b2454d44ae 👥 Added @ousret to the contributor list
See #99
Thanks to him !
2020-03-30 08:39:35 +02:00
TAHRI Ahmed R
79cfb95f6e Support array for json_data posted in rest/json service (#99)
Previous syntax :
```
CURLOPT_POSTFIELDS => array(
	'auth_user' => 'admin',
	'auth_pwd' => 'admin',
	'json_data' => '{
	   "operation": "core/get",
	   "class": "Person",
	   "key": "SELECT Person", "limit": "10", "page": "1"
	}'
);
```

Now we can also use :
```
CURLOPT_POSTFIELDS => array(
	'auth_user' => 'admin',
	'auth_pwd' => 'admin',
	"json_data[operation]" => "core/get",
	"json_data[class]" => "Person",
	"json_data[key]" => "SELECT Person",
	"json_data[limit]" => 10,
	"json_data[page]" => 1
);
```
2020-03-27 18:11:09 +01:00
Pierre Goiffon
ff2e1a3507 Fix syntax error in core/email.class.inc.php
Missing ";" at the end of line :/
Introduced by 503afb98
2020-03-27 16:43:03 +01:00
Lars Hippler
503afb9831 Make it possible to add return path for mails (#95) 2020-03-27 16:20:59 +01:00
Pierre Goiffon
b6772917ae Merge branch 'release/2.7.0' into develop
# Conflicts:
#	.make/license/gen-community-license.sh
#	setup/licenses/community-licenses.xml
2020-03-27 15:20:08 +01:00
bruno DA SILVA
00971f9ec7 rollback on two composer options: adding them made no sense since this file is not meant to handle dependencies but just use the autoloader (dependencies are handled by the one a the root of the project) 2020-03-21 16:17:45 +01:00
bruno DA SILVA
a3a97fa228 added missing composer config for the portal's composer.json
- php 5.6+
 - dump the autoloader as optimized as possible
2020-03-21 16:13:28 +01:00
bruno DA SILVA
18c4ca9131 🐛 fix Cannot connect to the MySQL server for the CI's unattended_install 2020-03-20 15:15:29 +01:00
odain
466ddf768e Fix license generation tool 2020-03-17 18:59:29 +01:00
odain
76d26e8ef9 Fix license generation tool 2020-03-17 18:59:18 +01:00
odain
b526d6422b Adding a test to cover selectin/cmdb code
cleanup
2020-03-12 10:55:18 +01:00
Pierre Goiffon
63c02ff33d 📝 Fix PHPDoc typo 2020-03-09 16:00:15 +01:00
Molkobain
4688c92e7c Internal: PHPDoc and warnings suppression 2020-02-25 15:44:26 +01:00
2423 changed files with 42456 additions and 220064 deletions

View File

@@ -1,30 +1,26 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 300
max_line_length = 140
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 = 300
ij_visual_guides = 80, 120, 140
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
ij_css_brace_placement = end_of_line
ij_css_enforce_quotes_on_format = false
ij_css_brace_placement = 0
ij_css_hex_color_long_format = false
ij_css_hex_color_lower_case = false
ij_css_hex_color_short_format = false
@@ -35,18 +31,59 @@ ij_css_keep_single_line_blocks = false
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_css_space_after_colon = true
ij_css_space_before_opening_brace = true
ij_css_use_double_quotes = true
ij_css_value_alignment = do_not_align
ij_css_value_alignment = 0
[*.csv]
max_line_length = 2147483647
ij_wrap_on_typing = false
ij_csv_wrap_long_lines = false
[*.feature]
indent_size = 2
ij_gherkin_keep_indents_on_empty_lines = false
[*.less]
indent_size = 2
ij_less_align_closing_brace_with_properties = false
ij_less_blank_lines_around_nested_selector = 1
ij_less_blank_lines_between_blocks = 1
ij_less_brace_placement = 0
ij_less_hex_color_long_format = false
ij_less_hex_color_lower_case = false
ij_less_hex_color_short_format = false
ij_less_hex_color_upper_case = false
ij_less_keep_blank_lines_in_code = 2
ij_less_keep_indents_on_empty_lines = false
ij_less_keep_single_line_blocks = false
ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_less_space_after_colon = true
ij_less_space_before_opening_brace = true
ij_less_value_alignment = 0
[*.sass]
indent_size = 2
ij_sass_align_closing_brace_with_properties = false
ij_sass_blank_lines_around_nested_selector = 1
ij_sass_blank_lines_between_blocks = 1
ij_sass_brace_placement = 0
ij_sass_hex_color_long_format = false
ij_sass_hex_color_lower_case = false
ij_sass_hex_color_short_format = false
ij_sass_hex_color_upper_case = false
ij_sass_keep_blank_lines_in_code = 2
ij_sass_keep_indents_on_empty_lines = false
ij_sass_keep_single_line_blocks = false
ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_sass_space_after_colon = true
ij_sass_space_before_opening_brace = true
ij_sass_value_alignment = 0
[*.scss]
indent_size = 2
tab_width = 2
ij_visual_guides = none
indent_style = tab
ij_scss_align_closing_brace_with_properties = false
ij_scss_blank_lines_around_nested_selector = 1
ij_scss_blank_lines_between_blocks = 1
ij_scss_brace_placement = 0
ij_scss_enforce_quotes_on_format = false
ij_scss_hex_color_long_format = false
ij_scss_hex_color_lower_case = false
ij_scss_hex_color_short_format = false
@@ -57,20 +94,17 @@ ij_scss_keep_single_line_blocks = false
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_scss_space_after_colon = true
ij_scss_space_before_opening_brace = true
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
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
@@ -78,45 +112,10 @@ 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}]
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
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = true
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 = 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
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true
[{*.cjs,*.js}]
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
@@ -135,13 +134,13 @@ ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*
ij_javascript_blank_lines_after_imports = 1
ij_javascript_blank_lines_around_class = 1
ij_javascript_blank_lines_around_field = 0
ij_javascript_blank_lines_around_function = 1
ij_javascript_blank_lines_around_method = 1
ij_javascript_block_brace_style = end_of_line
ij_javascript_block_brace_style = next_line
ij_javascript_call_parameters_new_line_after_left_paren = false
ij_javascript_call_parameters_right_paren_on_new_line = false
ij_javascript_call_parameters_wrap = off
@@ -149,15 +148,15 @@ ij_javascript_catch_on_new_line = false
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 = false
ij_javascript_do_while_brace_force = never
ij_javascript_else_on_new_line = true
ij_javascript_enforce_trailing_comma = keep
ij_javascript_extends_keyword_wrap = off
ij_javascript_extends_list_wrap = off
ij_javascript_field_prefix = _
ij_javascript_file_name_style = relaxed
ij_javascript_finally_on_new_line = false
ij_javascript_for_brace_force = always
ij_javascript_for_brace_force = never
ij_javascript_for_statement_new_line_after_left_paren = false
ij_javascript_for_statement_right_paren_on_new_line = false
ij_javascript_for_statement_wrap = off
@@ -193,9 +192,6 @@ ij_javascript_parentheses_expression_new_line_after_left_paren = false
ij_javascript_parentheses_expression_right_paren_on_new_line = false
ij_javascript_place_assignment_sign_on_next_line = false
ij_javascript_prefer_as_type_cast = false
ij_javascript_prefer_explicit_types_function_expression_returns = false
ij_javascript_prefer_explicit_types_function_returns = false
ij_javascript_prefer_explicit_types_vars_fields = false
ij_javascript_prefer_parameters_wrap = false
ij_javascript_reformat_c_style_comments = false
ij_javascript_space_after_colon = true
@@ -276,11 +272,11 @@ ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = always
ij_javascript_while_brace_force = never
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
@@ -289,8 +285,8 @@ ij_php_align_assignments = false
ij_php_align_class_constants = false
ij_php_align_group_field_declarations = false
ij_php_align_inline_comments = false
ij_php_align_key_value_pairs = true
ij_php_align_multiline_array_initializer_expression = true
ij_php_align_key_value_pairs = false
ij_php_align_multiline_array_initializer_expression = false
ij_php_align_multiline_binary_operation = false
ij_php_align_multiline_chained_methods = false
ij_php_align_multiline_extends_list = false
@@ -300,14 +296,12 @@ ij_php_align_multiline_parameters_in_calls = false
ij_php_align_multiline_ternary_operation = false
ij_php_align_phpdoc_comments = false
ij_php_align_phpdoc_param_names = false
ij_php_anonymous_brace_style = end_of_line
ij_php_api_weight = 1
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_author_weight = 7
ij_php_binary_operation_sign_on_next_line = false
ij_php_binary_operation_wrap = off
ij_php_blank_lines_after_class_header = 0
@@ -324,8 +318,7 @@ ij_php_blank_lines_before_imports = 1
ij_php_blank_lines_before_method_body = 0
ij_php_blank_lines_before_package = 1
ij_php_blank_lines_before_return_statement = 1
ij_php_blank_lines_between_imports = 0
ij_php_block_brace_style = end_of_line
ij_php_block_brace_style = next_line
ij_php_call_parameters_new_line_after_left_paren = false
ij_php_call_parameters_right_paren_on_new_line = false
ij_php_call_parameters_wrap = normal
@@ -335,11 +328,11 @@ ij_php_class_brace_style = next_line
ij_php_comma_after_last_array_element = true
ij_php_concat_spaces = false
ij_php_copyright_weight = 28
ij_php_deprecated_weight = 2
ij_php_deprecated_weight = 28
ij_php_do_while_brace_force = always
ij_php_else_if_style = as_is
ij_php_else_on_new_line = false
ij_php_example_weight = 4
ij_php_else_on_new_line = true
ij_php_example_weight = 3
ij_php_extends_keyword_wrap = off
ij_php_extends_list_wrap = off
ij_php_fields_default_visibility = private
@@ -350,8 +343,6 @@ ij_php_for_statement_new_line_after_left_paren = false
ij_php_for_statement_right_paren_on_new_line = false
ij_php_for_statement_wrap = off
ij_php_force_short_declaration_array_style = false
ij_php_getters_setters_naming_style = camel_case
ij_php_getters_setters_order_style = getters_first
ij_php_global_weight = 28
ij_php_group_use_wrap = on_every_item
ij_php_if_brace_force = always
@@ -371,8 +362,7 @@ ij_php_keep_control_statement_in_one_line = true
ij_php_keep_first_column_comment = true
ij_php_keep_indents_on_empty_lines = false
ij_php_keep_line_breaks = true
ij_php_keep_rparen_and_lbrace_on_one_line = false
ij_php_keep_simple_classes_in_one_line = false
ij_php_keep_rparen_and_lbrace_on_one_line = true
ij_php_keep_simple_methods_in_one_line = false
ij_php_lambda_brace_style = end_of_line
ij_php_license_weight = 28
@@ -380,7 +370,6 @@ ij_php_line_comment_add_space = false
ij_php_line_comment_at_first_column = true
ij_php_link_weight = 28
ij_php_lower_case_boolean_const = true
ij_php_lower_case_keywords = true
ij_php_lower_case_null_const = true
ij_php_method_brace_style = next_line
ij_php_method_call_chain_wrap = off
@@ -391,11 +380,9 @@ ij_php_method_weight = 28
ij_php_modifier_list_wrap = false
ij_php_multiline_chained_calls_semicolon_on_new_line = false
ij_php_namespace_brace_style = 1
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_param_weight = 4
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
@@ -412,12 +399,11 @@ ij_php_property_read_weight = 28
ij_php_property_weight = 28
ij_php_property_write_weight = 28
ij_php_return_type_on_new_line = false
ij_php_return_weight = 6
ij_php_see_weight = 3
ij_php_return_weight = 5
ij_php_see_weight = 2
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
@@ -431,7 +417,6 @@ 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
@@ -448,7 +433,6 @@ ij_php_space_before_method_call_parentheses = false
ij_php_space_before_method_left_brace = true
ij_php_space_before_method_parentheses = false
ij_php_space_before_quest = true
ij_php_space_before_short_closure_left_parenthesis = false
ij_php_space_before_switch_left_brace = true
ij_php_space_before_switch_parentheses = true
ij_php_space_before_try_left_brace = true
@@ -481,11 +465,11 @@ ij_php_spaces_within_parentheses = false
ij_php_spaces_within_short_echo_tags = true
ij_php_spaces_within_switch_parentheses = false
ij_php_spaces_within_while_parentheses = false
ij_php_special_else_if_treatment = true
ij_php_special_else_if_treatment = false
ij_php_subpackage_weight = 28
ij_php_ternary_operation_signs_on_next_line = false
ij_php_ternary_operation_wrap = off
ij_php_throws_weight = 7
ij_php_throws_weight = 6
ij_php_todo_weight = 28
ij_php_unknown_tag_weight = 28
ij_php_upper_case_boolean_const = false
@@ -497,24 +481,9 @@ ij_php_version_weight = 28
ij_php_while_brace_force = always
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
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
[{*.sht,*.htm,*.html,*.shtm,*.shtml}]
indent_style = tab
ij_smart_tabs = true
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
@@ -534,38 +503,209 @@ ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = none
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false
[{*.markdown,*.md}]
ij_visual_guides = none
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
[{*.ts,*.ats}]
ij_continuation_indent_size = 4
ij_typescript_align_imports = false
ij_typescript_align_multiline_array_initializer_expression = false
ij_typescript_align_multiline_binary_operation = false
ij_typescript_align_multiline_chained_methods = false
ij_typescript_align_multiline_extends_list = false
ij_typescript_align_multiline_for = true
ij_typescript_align_multiline_parameters = true
ij_typescript_align_multiline_parameters_in_calls = false
ij_typescript_align_multiline_ternary_operation = false
ij_typescript_align_object_properties = 0
ij_typescript_align_union_types = false
ij_typescript_align_var_statements = 0
ij_typescript_array_initializer_new_line_after_left_brace = false
ij_typescript_array_initializer_right_brace_on_new_line = false
ij_typescript_array_initializer_wrap = off
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**/*
ij_typescript_blank_lines_after_imports = 1
ij_typescript_blank_lines_around_class = 1
ij_typescript_blank_lines_around_field = 0
ij_typescript_blank_lines_around_field_in_interface = 0
ij_typescript_blank_lines_around_function = 1
ij_typescript_blank_lines_around_method = 1
ij_typescript_blank_lines_around_method_in_interface = 1
ij_typescript_block_brace_style = end_of_line
ij_typescript_call_parameters_new_line_after_left_paren = false
ij_typescript_call_parameters_right_paren_on_new_line = false
ij_typescript_call_parameters_wrap = off
ij_typescript_catch_on_new_line = false
ij_typescript_chained_call_dot_on_new_line = true
ij_typescript_class_brace_style = end_of_line
ij_typescript_comma_on_new_line = false
ij_typescript_do_while_brace_force = never
ij_typescript_else_on_new_line = false
ij_typescript_enforce_trailing_comma = keep
ij_typescript_extends_keyword_wrap = off
ij_typescript_extends_list_wrap = off
ij_typescript_field_prefix = _
ij_typescript_file_name_style = relaxed
ij_typescript_finally_on_new_line = false
ij_typescript_for_brace_force = never
ij_typescript_for_statement_new_line_after_left_paren = false
ij_typescript_for_statement_right_paren_on_new_line = false
ij_typescript_for_statement_wrap = off
ij_typescript_force_quote_style = false
ij_typescript_force_semicolon_style = false
ij_typescript_function_expression_brace_style = end_of_line
ij_typescript_if_brace_force = never
ij_typescript_import_merge_members = global
ij_typescript_import_prefer_absolute_path = global
ij_typescript_import_sort_members = true
ij_typescript_import_sort_module_name = false
ij_typescript_import_use_node_resolution = true
ij_typescript_imports_wrap = on_every_item
ij_typescript_indent_case_from_switch = true
ij_typescript_indent_chained_calls = true
ij_typescript_indent_package_children = 0
ij_typescript_jsdoc_include_types = false
ij_typescript_jsx_attribute_value = braces
ij_typescript_keep_blank_lines_in_code = 2
ij_typescript_keep_first_column_comment = true
ij_typescript_keep_indents_on_empty_lines = false
ij_typescript_keep_line_breaks = true
ij_typescript_keep_simple_blocks_in_one_line = false
ij_typescript_keep_simple_methods_in_one_line = false
ij_typescript_line_comment_add_space = true
ij_typescript_line_comment_at_first_column = false
ij_typescript_method_brace_style = end_of_line
ij_typescript_method_call_chain_wrap = off
ij_typescript_method_parameters_new_line_after_left_paren = false
ij_typescript_method_parameters_right_paren_on_new_line = false
ij_typescript_method_parameters_wrap = off
ij_typescript_object_literal_wrap = on_every_item
ij_typescript_parentheses_expression_new_line_after_left_paren = false
ij_typescript_parentheses_expression_right_paren_on_new_line = false
ij_typescript_place_assignment_sign_on_next_line = false
ij_typescript_prefer_as_type_cast = false
ij_typescript_prefer_parameters_wrap = false
ij_typescript_reformat_c_style_comments = false
ij_typescript_space_after_colon = true
ij_typescript_space_after_comma = true
ij_typescript_space_after_dots_in_rest_parameter = false
ij_typescript_space_after_generator_mult = true
ij_typescript_space_after_property_colon = true
ij_typescript_space_after_quest = true
ij_typescript_space_after_type_colon = true
ij_typescript_space_after_unary_not = false
ij_typescript_space_before_async_arrow_lparen = true
ij_typescript_space_before_catch_keyword = true
ij_typescript_space_before_catch_left_brace = true
ij_typescript_space_before_catch_parentheses = true
ij_typescript_space_before_class_lbrace = true
ij_typescript_space_before_class_left_brace = true
ij_typescript_space_before_colon = true
ij_typescript_space_before_comma = false
ij_typescript_space_before_do_left_brace = true
ij_typescript_space_before_else_keyword = true
ij_typescript_space_before_else_left_brace = true
ij_typescript_space_before_finally_keyword = true
ij_typescript_space_before_finally_left_brace = true
ij_typescript_space_before_for_left_brace = true
ij_typescript_space_before_for_parentheses = true
ij_typescript_space_before_for_semicolon = false
ij_typescript_space_before_function_left_parenth = true
ij_typescript_space_before_generator_mult = false
ij_typescript_space_before_if_left_brace = true
ij_typescript_space_before_if_parentheses = true
ij_typescript_space_before_method_call_parentheses = false
ij_typescript_space_before_method_left_brace = true
ij_typescript_space_before_method_parentheses = false
ij_typescript_space_before_property_colon = false
ij_typescript_space_before_quest = true
ij_typescript_space_before_switch_left_brace = true
ij_typescript_space_before_switch_parentheses = true
ij_typescript_space_before_try_left_brace = true
ij_typescript_space_before_type_colon = false
ij_typescript_space_before_unary_not = false
ij_typescript_space_before_while_keyword = true
ij_typescript_space_before_while_left_brace = true
ij_typescript_space_before_while_parentheses = true
ij_typescript_spaces_around_additive_operators = true
ij_typescript_spaces_around_arrow_function_operator = true
ij_typescript_spaces_around_assignment_operators = true
ij_typescript_spaces_around_bitwise_operators = true
ij_typescript_spaces_around_equality_operators = true
ij_typescript_spaces_around_logical_operators = true
ij_typescript_spaces_around_multiplicative_operators = true
ij_typescript_spaces_around_relational_operators = true
ij_typescript_spaces_around_shift_operators = true
ij_typescript_spaces_around_unary_operator = false
ij_typescript_spaces_within_array_initializer_brackets = false
ij_typescript_spaces_within_brackets = false
ij_typescript_spaces_within_catch_parentheses = false
ij_typescript_spaces_within_for_parentheses = false
ij_typescript_spaces_within_if_parentheses = false
ij_typescript_spaces_within_imports = false
ij_typescript_spaces_within_interpolation_expressions = false
ij_typescript_spaces_within_method_call_parentheses = false
ij_typescript_spaces_within_method_parentheses = false
ij_typescript_spaces_within_object_literal_braces = false
ij_typescript_spaces_within_object_type_braces = true
ij_typescript_spaces_within_parentheses = false
ij_typescript_spaces_within_switch_parentheses = false
ij_typescript_spaces_within_type_assertion = false
ij_typescript_spaces_within_union_types = true
ij_typescript_spaces_within_while_parentheses = false
ij_typescript_special_else_if_treatment = true
ij_typescript_ternary_operation_signs_on_next_line = false
ij_typescript_ternary_operation_wrap = off
ij_typescript_union_types_wrap = on_every_item
ij_typescript_use_chained_calls_group_indents = false
ij_typescript_use_double_quotes = true
ij_typescript_use_explicit_js_extension = global
ij_typescript_use_path_mapping = always
ij_typescript_use_public_modifier = false
ij_typescript_use_semicolon_after_statement = true
ij_typescript_var_declaration_wrap = normal
ij_typescript_while_brace_force = never
ij_typescript_while_on_new_line = false
ij_typescript_wrap_comments = false
[{*.yaml,*.yml}]
[{*.yml,*.yaml}]
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_continuation_indent_size = 2
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = 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
[{*.zsh,*.bash,*.sh}]
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
[{.stylelintrc,.eslintrc,.babelrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json}]
indent_size = 2
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{phpunit.xml.dist,*.jhm,*.rng,*.wsdl,*.fxml,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.xml}]
indent_size = 2
indent_style = tab
tab_width = 2
ij_smart_tabs = true
ij_xml_block_comment_at_first_column = true
ij_xml_keep_indents_on_empty_lines = false
ij_xml_line_comment_at_first_column = true

48
.gitattributes vendored
View File

@@ -1,48 +0,0 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.bash text eol=lf
*.bat text eol=lf
*.cmd text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.dist text eol=lf
.editorconfig text eol=lf
.env* text eol=lf
.gitignore text eol=lf
.htaccess text eol=lf
*.htm text eol=lf
*.html text eol=lf
*.ini text eol=lf
*.js text eol=lf
*.json text eol=lf
*.lock text eol=lf
*.md text eol=lf
*.php text eol=lf
*.php_cs text eol=lf
*.php8 text eol=lf
*.plex text eol=lf
*.sh text eol=lf
*.svg text eol=lf
*.ts text eol=lf
*.twig text eol=lf
*.txt text eol=lf
*.xml text eol=lf
*.xsd text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpeg binary
*.jpg binary
*.gif binary
*.ico binary
*.pdf binary
*.swf binary
*.zip binary
*.ttf binary
*.woff binary
*.woff2 binary

9
.gitflow Normal file
View File

@@ -0,0 +1,9 @@
[gitflow "branch"]
master = master
develop = develop
[gitflow "prefix"]
feature = feature/
release = release/
hotfix = hotfix/
versiontag =
support = support/

View File

@@ -1,83 +0,0 @@
<!--
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
Any PRs not following the guidelines or with missing information will not be considered.
-->
## Base information
| Question | Answer
|---------------------------------------------------------------|--------
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
| Type of change? | Bug fix / Enhancement / Translations
## Symptom (bug) / Objective (enhancement)
<!--
If it's a bug
- Explain the symptom in details
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
If it's an enhancement
- Describe what is blocking you, what is the objective with as much details as possible.
- Add screenshots if it's related to UI.
-->
## Reproduction procedure (bug)
<!--
Remove this section only if it's NOT a bug.
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
2. First go there
2. Then do that
3. ...
4. Finally, see that...
## Cause (bug)
<!--
Remove this section only if it's NOT a bug.
Otherwise, explain what is the cause of the issue (where in the code and why)
-->
## Proposed solution (bug and enhancement)
<!--
Explain in details how you are proposing to solve this:
- What did you do in the code and why
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
-->
## Checklist before requesting a review
<!--
Don't remove these lines, check them once done.
-->
- [ ] I have performed a self-review of my code
- [ ] I have tested all changes I made on an iTop instance
- [ ] I have added a unit test, otherwise I have explained why I couldn't
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
## Checklist of things to do before PR is ready to merge
<!--
Things that needs to be done in the PR before it can be considered as ready to be merged
Examples:
- Changes requested in the review
- Unit test to add
- Dictionary entries to translate
- ...
-->
- [ ] ...
- [ ] ...
- [ ] ...

View File

@@ -1,43 +0,0 @@
name: Add PRs to Combodo PRs Dashboard
on:
pull_request_target:
types:
- opened
jobs:
add-to-project:
name: Add PR to Combodo Project
runs-on: ubuntu-latest
steps:
- name: Check if author is a member of the organization
id: check-membership
run: |
ORG="Combodo"
AUTHOR=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
"https://api.github.com/orgs/$ORG/members/$AUTHOR")
if [ "$RESPONSE" == "404" ]; then
echo "project_url=https://github.com/orgs/Combodo/projects/5" >> $GITHUB_ENV
echo "is_member=false" >> $GITHUB_ENV
else
echo "project_url=https://github.com/orgs/Combodo/projects/4" >> $GITHUB_ENV
echo "is_member=true" >> $GITHUB_ENV
fi
- name: Add internal tag if member
if: env.is_member == 'true'
run: |
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
-d '{"labels":["internal"]}'
env:
is_member: ${{ env.is_member }}
- name: Add PR to the appropriate project
uses: actions/add-to-project@v1.0.2
with:
project-url: ${{ env.project_url }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}

23
.gitignore vendored
View File

@@ -6,14 +6,19 @@
# maintenance mode (N°2240)
/.maintenance
# listing prevention in conf directory
/conf/**
!/conf/.htaccess
!/conf/index.php
!/conf/web.config
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
tests/*/vendor/*
test/vendor/*
# all conf but listing prevention
/conf/**
!/conf/.htaccess
!/conf/index.php
!/conf/web.config
# all datas but listing prevention
@@ -24,9 +29,7 @@ tests/*/vendor/*
# iTop extensions
/extensions/**
!/extensions/.htaccess
!/extensions/readme.txt
!/extensions/web.config
# all logs but listing prevention
/log/**
@@ -34,15 +37,14 @@ tests/*/vendor/*
!/log/index.php
!/log/web.config
# PHPUnit: Cache file, local XML working copies
/tests/php-unit-tests/.phpunit.result.cache
/tests/php-unit-tests/phpunit.xml
/tests/php-unit-tests/postbuild_integration.xml
# Jetbrains
/.idea/**
!/.idea/IntelliLang.xml
!/.idea/encodings.xml
!/.idea/codeStyles
!/.idea/codeStyles/*
!/.idea/inspectionProfiles
!/.idea/inspectionProfiles/*
# doc. generation
/.doc/vendor
@@ -137,3 +139,4 @@ local.properties
.cache-main
.scala_dependencies
.worksheet

15
.idea/IntelliLang.xml generated
View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="LanguageInjectionConfiguration">
<injection language="InjectablePHP" injector-id="xml">
<display-name>iTop - Class method code</display-name>
<place><![CDATA[xmlTag().withLocalName(string().equalTo("code"))]]></place>
<xpath-condition>name(..) = 'method' and count(/itop_design) = 1</xpath-condition>
</injection>
<injection language="InjectablePHP" injector-id="xml">
<display-name>iTop - Snippet code</display-name>
<place><![CDATA[xmlTag().withLocalName(string().equalTo("snippet"))]]></place>
<xpath-condition>name(..) = 'snippets' and count(/itop_design) = 1</xpath-condition>
</injection>
</component>
</project>

74
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,74 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="140" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="SOFT_MARGINS" value="140" />
<HTMLCodeStyleSettings>
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,style,script,head" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
</JSCodeStyleSettings>
<PHPCodeStyleSettings>
<option name="CONCAT_SPACES" value="false" />
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
<option name="THROWS_WEIGHT" value="6" />
<option name="PARAM_WEIGHT" value="4" />
<option name="RETURN_WEIGHT" value="5" />
<option name="AUTHOR_WEIGHT" value="7" />
<option name="INTERNAL_WEIGHT" value="0" />
<option name="API_WEIGHT" value="1" />
<option name="EXAMPLE_WEIGHT" value="3" />
<option name="SEE_WEIGHT" value="2" />
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
<option name="LOWER_CASE_NULL_CONST" value="true" />
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
<option name="PHPDOC_USE_FQCN" value="true" />
</PHPCodeStyleSettings>
<XML>
<option name="XML_TEXT_WRAP" value="0" />
<option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_KEEP_WHITE_SPACES_INSIDE_CDATA" value="true" />
</XML>
<codeStyleSettings language="JavaScript">
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="SPACE_AROUND_ADDITIVE_OPERATORS" value="false" />
<option name="IF_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="WRAP_ON_TYPING" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Combodo" />
</state>
</component>

6
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

171
.idea/inspectionProfiles/Combodo.xml generated Normal file
View File

@@ -0,0 +1,171 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Combodo" />
<inspection_tool class="CascadeStringReplacementInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="ERROR" enabled_by_default="true">
<option name="configuration">
<list>
<option value="\Codeception\Util\Debug::debug" />
<option value="\Codeception\Util\Debug::pause" />
<option value="\Doctrine\Common\Util\Debug::dump" />
<option value="\Doctrine\Common\Util\Debug::export" />
<option value="\Illuminate\Support\Debug\Dumper::dump" />
<option value="\Symfony\Component\Debug\Debug::enable" />
<option value="\Symfony\Component\Debug\DebugClassLoader::enable" />
<option value="\Symfony\Component\Debug\ErrorHandler::register" />
<option value="\Symfony\Component\Debug\ExceptionHandler::register" />
<option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" />
<option value="\Zend\Debug\Debug::dump" />
<option value="\Zend\Di\Display\Console::export" />
<option value="dd" />
<option value="debug_print_backtrace" />
<option value="debug_zval_dump" />
<option value="dpm" />
<option value="dpq" />
<option value="dsm" />
<option value="dump" />
<option value="dvm" />
<option value="error_log" />
<option value="kpr" />
<option value="phpinfo" />
<option value="print_r" />
<option value="var_dump" />
<option value="var_export" />
<option value="xdebug_break" />
<option value="xdebug_call_class" />
<option value="xdebug_call_file" />
<option value="xdebug_call_function" />
<option value="xdebug_call_line" />
<option value="xdebug_code_coverage_started" />
<option value="xdebug_debug_zval" />
<option value="xdebug_debug_zval_stdout" />
<option value="xdebug_dump_superglobals" />
<option value="xdebug_enable" />
<option value="xdebug_get_code_coverage" />
<option value="xdebug_get_collected_errors" />
<option value="xdebug_get_declared_vars" />
<option value="xdebug_get_function_stack" />
<option value="xdebug_get_headers" />
<option value="xdebug_get_monitored_functions" />
<option value="xdebug_get_profiler_filename" />
<option value="xdebug_get_stack_depth" />
<option value="xdebug_get_tracefile_name" />
<option value="xdebug_is_enabled" />
<option value="xdebug_memory_usage" />
<option value="xdebug_peak_memory_usage" />
<option value="xdebug_print_function_stack" />
<option value="xdebug_start_code_coverage" />
<option value="xdebug_start_error_collection" />
<option value="xdebug_start_function_monitor" />
<option value="xdebug_start_trace" />
<option value="xdebug_stop_code_coverage" />
<option value="xdebug_stop_error_collection" />
<option value="xdebug_stop_function_monitor" />
<option value="xdebug_stop_trace" />
<option value="xdebug_time_index" />
<option value="xdebug_var_dump" />
</list>
</option>
<option name="migratedIntoUserSpace" value="true" />
</inspection_tool>
<inspection_tool class="HtmlRequiredAltAttribute" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="HtmlRequiredLangAttribute" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IsNullFunctionUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpComposerExtensionStubsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedLocalVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_INSIDE_LIST" value="true" />
</inspection_tool>
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="optionConfiguration">
<list>
<option value="barryvdh/laravel-debugbar" />
<option value="behat/behat" />
<option value="brianium/paratest" />
<option value="codeception/codeception" />
<option value="codedungeon/phpunit-result-printer" />
<option value="composer/composer" />
<option value="doctrine/coding-standard" />
<option value="filp/whoops" />
<option value="friendsofphp/php-cs-fixer" />
<option value="humbug/humbug" />
<option value="infection/infection" />
<option value="jakub-onderka/php-parallel-lint" />
<option value="johnkary/phpunit-speedtrap" />
<option value="kalessil/production-dependencies-guard" />
<option value="mikey179/vfsStream" />
<option value="mockery/mockery" />
<option value="mybuilder/phpunit-accelerator" />
<option value="orchestra/testbench" />
<option value="pdepend/pdepend" />
<option value="phan/phan" />
<option value="phing/phing" />
<option value="phpcompatibility/php-compatibility" />
<option value="phpmd/phpmd" />
<option value="phpro/grumphp" />
<option value="phpspec/phpspec" />
<option value="phpspec/prophecy" />
<option value="phpstan/phpstan" />
<option value="phpunit/phpunit" />
<option value="povils/phpmnd" />
<option value="roave/security-advisories" />
<option value="satooshi/php-coveralls" />
<option value="sebastian/phpcpd" />
<option value="slevomat/coding-standard" />
<option value="spatie/phpunit-watcher" />
<option value="squizlabs/php_codesniffer" />
<option value="sstalle/php7cc" />
<option value="symfony/debug" />
<option value="symfony/maker-bundle" />
<option value="symfony/phpunit-bridge" />
<option value="symfony/var-dumper" />
<option value="vimeo/psalm" />
<option value="wimg/php-compatibility" />
<option value="wp-coding-standards/wpcs" />
<option value="yiisoft/yii2-coding-standards" />
<option value="yiisoft/yii2-debug" />
<option value="yiisoft/yii2-gii" />
<option value="zendframework/zend-coding-standard" />
<option value="zendframework/zend-debug" />
<option value="zendframework/zend-test" />
</list>
</option>
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Combodo" />
<version value="1.0" />
</settings>
</component>

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -x
# create target dirs
mkdir -p var
mkdir -p toolkit
# cleanup target dirs
rm -rf toolkit/*
# fill target dirs
curl https://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip > toolkit.zip
unzip toolkit.zip
rm toolkit.zip
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -x
# on the root dir
# composer install -a # => Not needed anymore (libs were added to git with N°2435)
# under the test dir
cd test
composer install

15
.jenkins/bin/init/debug.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -x
whoami
pwd
ls
echo "$BRANCH_NAME:${BRANCH_NAME}"
echo "printenv :"
printenv

41
.jenkins/bin/tests/phpunit.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -x
cd test
export DEBUG_UNIT_TEST=0
RUN_NONREG_TESTS=0
#USAGE ${debugMode} ${runNonRegOQLTests} "${coverture}" "${testFile}"
if [ $# -ge 1 -a "x$1" == "xtrue" ]
then
export DEBUG_UNIT_TEST=1
else
export DEBUG_UNIT_TEST=0
fi
set -x
OPTION=""
if [ $# -ge 3 -a "x$3" == "xtrue" ]
then
##coverture
OPTION="-dxdebug.coverage_enable=1 --coverage-clover ../var/test/coverage.xml"
fi
TESTFILE="$4"
if [ "x$TESTFILE" != "x" ]
then
# shellcheck disable=SC2001
TESTFILE=$(echo "$TESTFILE" | sed 's|test/||1')
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION $TESTFILE --teamcity
exit 0
fi
if [ $# -ge 2 -a "x$2" == "xtrue" ]
then
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION --teamcity
else
#echo php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml $OPTION --exclude-group OQL --teamcity
fi

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -x
chmod 666 conf/production/config-itop.php
cd toolkit
php unattended_install.php --response_file=default-params.xml --clean=true

View File

@@ -0,0 +1,284 @@
<?php
/**
*
* Configuration file, generated by the iTop configuration wizard
*
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
*
*/
$MySettings = array(
// access_message: Message displayed to the users when there is any access restriction
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
// default: 3
'access_mode' => 3,
'allowed_login_types' => 'form|basic|external',
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
// default: true
'apc_cache.enabled' => true,
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
// default: 3600
'apc_cache.query_ttl' => 3600,
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
// default: ''
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
// buttons_position: Position of the forms buttons: bottom | top | both
// default: 'both'
'buttons_position' => 'both',
// cas_include_path: The path where to find the phpCAS library
// default: '/usr/share/php'
'cas_include_path' => '/usr/share/php',
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
// default: 600
'cron_max_execution_time' => 600,
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
// default: 'ISO-8859-1'
'csv_file_default_charset' => 'ISO-8859-1',
'csv_import_charsets' => array (
),
// csv_import_history_display: Display the history tab in the import wizard
// default: false
'csv_import_history_display' => false,
// date_and_time_format: Format for date and time display (per language)
// default: array (
// 'default' =>
// array (
// 'date' => 'Y-m-d',
// 'time' => 'H:i:s',
// 'date_time' => '$date $time',
// ),
// )
'date_and_time_format' => array (
'default' =>
array (
'date' => 'Y-m-d',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
'FR FR' =>
array (
'date' => 'd/m/Y',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
),
'db_host' => '',
'db_name' => 'itop_ci',
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
'db_subname' => '',
'db_user' => 'jenkins_itop',
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
// default: '$difference$'
'deadline_format' => '$difference$',
'default_language' => 'EN US',
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
// default: 3600
'draft_attachments_lifetime' => 3600,
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
// default: false
'email_asynchronous' => false,
// email_default_sender_address: Default address provided in the email from header field.
// default: ''
'email_default_sender_address' => '',
// email_default_sender_label: Default label provided in the email from header field.
// default: ''
'email_default_sender_label' => '',
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
// default: 'PHPMail'
'email_transport' => 'SMTP',
// email_transport_smtp.host: host name or IP address (optional)
// default: 'localhost'
'email_transport_smtp.host' => 'smtp.combodo.com',
// email_transport_smtp.password: Authentication password (optional)
// default: ''
'email_transport_smtp.password' => 'IDoNotWork',
// email_transport_smtp.port: port number (optional)
// default: 25
'email_transport_smtp.port' => 25,
// email_transport_smtp.username: Authentication user (optional)
// default: ''
'email_transport_smtp.username' => 'test2@combodo.com',
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
'encryption_key' => '@iT0pEncr1pti0n!',
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
'fast_reload_interval' => '60',
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
// default: '/usr/bin/dot'
'graphviz_path' => '/usr/bin/dot',
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
// default: '250'
'inline_image_max_display_width' => 250,
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
// default: '1600'
'inline_image_max_storage_width' => 1600,
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
// default: '\''
'link_set_attribute_qualifier' => '\'',
// link_set_attribute_separator: Link set from string: attribute separator
// default: ';'
'link_set_attribute_separator' => ';',
// link_set_item_separator: Link set from string: line separator
// default: '|'
'link_set_item_separator' => '|',
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
// default: ':'
'link_set_value_separator' => ':',
'log_global' => true,
'log_issue' => true,
'log_notification' => true,
'log_web_service' => true,
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
// default: 50
'max_combo_length' => 50,
'max_display_limit' => '15',
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
// default: 100
'max_linkset_output' => 100,
'min_display_limit' => '10',
// online_help: Hyperlink to the online-help web page
// default: 'http://www.combodo.com/itop-help'
'online_help' => 'http://www.combodo.com/itop-help',
// php_path: Path to the php executable in CLI mode
// default: 'php'
'php_path' => 'php',
// portal_tickets: CSV list of classes supported in the portal
// default: 'UserRequest'
'portal_tickets' => 'UserRequest',
'query_cache_enabled' => true,
// search_manual_submit: Force manual submit of search requests (class => true)
// default: false
'search_manual_submit' => array (
'Person' => true,
),
'secure_connection_required' => false,
// session_name: The name of the cookie used to store the PHP session id
// default: 'iTop'
'session_name' => 'iTop',
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
// default: 'UI:Menu:Modify,UI:Menu:New'
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
// default: ''
'source_dir' => 'datamodels/2.x/',
'standard_reload_interval' => '300',
// synchro_trace: Synchronization details: none, display, save (includes 'display')
// default: 'none'
'synchro_trace' => 'none',
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
// default: 'Europe/Paris'
'timezone' => 'Europe/Paris',
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
// default: 1
'tracking_level_linked_set_default' => 0,
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
);
/**
*
* Modules specific settings
*
*/
$MyModuleSettings = array(
'authent-local' => array (
'password_validation.pattern' => '',
),
'itop-attachments' => array (
'allowed_classes' => array (
0 => 'Ticket',
),
'position' => 'relations',
'preview_max_width' => 290,
),
'itop-backup' => array (
'mysql_bindir' => '',
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
'time' => '23:30',
'retention_count' => 5,
'enabled' => true,
'debug' => false,
),
'molkobain-console-tooltips' => array (
'decoration_class' => 'fas fa-question',
'enabled' => true,
),
);
/**
*
* Data model modules to be loaded. Names are specified as relative paths
*
*/
$MyModules = array(
'addons' => array (
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
),
);
?>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<installation>
<!-- On manual installs, this file is generated in setup/install-*.xml -->
<mode>upgrade</mode>
<preinstall>
<copies type="array"/>
</preinstall>
<source_dir>datamodels/2.x/</source_dir>
<datamodel_version>2.5.0</datamodel_version>
<previous_configuration_file>default-config-itop.php</previous_configuration_file>
<extensions_dir>extensions</extensions_dir>
<target_env>production</target_env>
<workspace_dir></workspace_dir>
<database>
<server></server>
<user>jenkins_itop</user>
<pwd>IKnowYouSeeMeInJenkinsConf</pwd>
<name>itop_ci</name>
<db_tls_enabled></db_tls_enabled>
<db_tls_ca></db_tls_ca>
<prefix></prefix>
</database>
<url>http://127.0.0.1/itop/svn/trunk/</url>
<graphviz_path>/usr/bin/dot</graphviz_path>
<admin_account>
<user>admin</user>
<pwd>admin</pwd>
<language>EN US</language>
</admin_account>
<language>EN US</language>
<selected_modules type="array">
<item>authent-external</item>
<item>authent-local</item>
<item>itop-backup</item>
<item>itop-config</item>
<item>itop-profiles-itil</item>
<item>itop-sla-computation</item>
<item>itop-tickets</item>
<item>itop-welcome-itil</item>
<item>itop-config-mgmt</item>
<item>itop-attachments</item>
<item>itop-datacenter-mgmt</item>
<item>itop-endusers-devices</item>
<item>itop-storage-mgmt</item>
<item>itop-virtualization-mgmt</item>
<item>itop-bridge-virtualization-storage</item>
<item>itop-service-mgmt</item>
<item>itop-request-mgmt</item>
<item>itop-portal</item>
<item>itop-portal-base</item>
<item>itop-change-mgmt</item>
<item>itop-knownerror-mgmt</item>
</selected_modules>
<selected_extensions type="array">
<item>itop-config-mgmt-core</item>
<item>itop-config-mgmt-datacenter</item>
<item>itop-config-mgmt-end-user</item>
<item>itop-config-mgmt-storage</item>
<item>itop-config-mgmt-virtualization</item>
<item>itop-service-mgmt-enterprise</item>
<item>itop-ticket-mgmt-simple-ticket</item>
<item>itop-ticket-mgmt-simple-ticket-enhanced-portal</item>
<item>itop-change-mgmt-simple</item>
<item>itop-kown-error-mgmt</item>
</selected_extensions>
<sample_data>1</sample_data>
<old_addon></old_addon>
<options>
<generate_config>1</generate_config>
</options>
<mysql_bindir></mysql_bindir>
</installation>

View File

@@ -0,0 +1,208 @@
<?php
/**
* Copyright (C) 2013-2019 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
//this scrit will be run under the ./toolkit directory, relatively to the document root
require_once('../approot.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/application/clipage.class.inc.php');
require_once(APPROOT.'/core/config.class.inc.php');
require_once(APPROOT.'/core/log.class.inc.php');
require_once(APPROOT.'/core/kpi.class.inc.php');
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
require_once(APPROOT.'/setup/setuppage.class.inc.php');
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
/////////////////////////////////////////////////
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
$oParams = new XMLParameters($sParamFile);
$sMode = $oParams->Get('mode');
if ($sMode == 'install')
{
echo "Installation mode detected.\n";
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
if ($bClean)
{
echo "Cleanup mode detected.\n";
$sTargetEnvironment = $oParams->Get('target_env', '');
if ($sTargetEnvironment == '')
{
$sTargetEnvironment = 'production';
}
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
// Configuration file
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
if (file_exists($sConfigFile))
{
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
unlink($sConfigFile);
}
else
{
echo "No config file to delete ($sConfigFile does not exist).\n";
}
// env-xxx directory
if (file_exists($sTargetDir))
{
if (is_dir($sTargetDir))
{
echo "Emptying the target directory '$sTargetDir'.\n";
SetupUtils::tidydir($sTargetDir);
}
else
{
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
}
}
else
{
echo "No target directory to delete ($sTargetDir does not exist).\n";
}
// Database
$aDBSettings = $oParams->Get('database', array());
$sDBServer = $aDBSettings['server'];
$sDBUser = $aDBSettings['user'];
$sDBPwd = $aDBSettings['pwd'];
$sDBName = $aDBSettings['name'];
$sDBPrefix = $aDBSettings['prefix'];
if ($sDBPrefix != '')
{
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
}
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
if ($oMysqli->connect_errno)
{
die("Cannot connect to the MySQL server (".$oMysqli->connect_errno . ") ".$oMysqli->connect_error."\nExiting");
}
else
{
if ($oMysqli->select_db($sDBName))
{
echo "Deleting database '$sDBName'\n";
$oMysqli->query("DROP DATABASE `$sDBName`");
}
else
{
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
}
}
}
}
$bHasErrors = false;
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
$aSelectedModules = $oParams->Get('selected_modules');
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
foreach($aChecks as $oCheckResult)
{
switch($oCheckResult->iSeverity)
{
case CheckResult::ERROR:
$bHasErrors = true;
$sHeader = "Error";
break;
case CheckResult::WARNING:
$sHeader = "Warning";
break;
case CheckResult::INFO:
default:
$sHeader = "Info";
break;
}
echo $sHeader.": ".$oCheckResult->sLabel;
if (strlen($oCheckResult->sDescription))
{
echo ' - '.$oCheckResult->sDescription;
}
echo "\n";
}
if ($bHasErrors)
{
echo "Encountered stopper issues. Aborting...\n";
die;
}
$bFoundIssues = false;
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
if ($bInstall)
{
echo "Starting the unattended installation...\n";
$oWizard = new ApplicationInstaller($oParams);
$bRes = $oWizard->ExecuteAllSteps();
if (!$bRes)
{
echo "\nencountered installation issues!";
$bFoundIssues = true;
}
}
else
{
echo "No installation requested.\n";
}
if (!$bFoundIssues && $bCheckConsistency)
{
echo "Checking data model consistency.\n";
ob_start();
$sCheckRes = '';
try
{
MetaModel::CheckDefinitions(false);
$sCheckRes = ob_get_clean();
}
catch(Exception $e)
{
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
}
if (strlen($sCheckRes) > 0)
{
echo $sCheckRes;
echo "\nfound consistency issues!";
$bFoundIssues = true;
}
}
if (!$bFoundIssues)
{
// last line: used to check the install
// the only way to track issues in case of Fatal error or even parsing error!
echo "\ninstalled!";
exit;
}

View File

@@ -1,90 +0,0 @@
<?php
$iBeginTime = time();
chdir(__DIR__);
$aCommands = [
'php composer/rmDeniedTestDir.php',
'php build/commands/setupCssCompiler.php',
// 'bash /tmp/gabuzomeu.sh',
];
$aFailedCommands=[];
foreach ($aCommands as $sCommand)
{
if (!ExecCommand($sCommand))
{
$aFailedCommands[] = $sCommand;
}
}
$iElapsed = time() - $iBeginTime;
if (count($aFailedCommands))
{
fwrite(STDERR, "\nafterBuild execution failed! (in ${iElapsed}s)\n");
fwrite(STDERR, "List of failling commands:\n - " . implode("\n - ", $aFailedCommands) . "\n");
exit(1);
}
echo "\nDone (${iElapsed}s)\n";
exit(0);
/**
* Executes a command and returns an array with exit code, stdout and stderr content
*
* @param string $cmd - Command to execute
*
* @return bool
* @throws \Exception
*/
function ExecCommand($cmd) {
$iBeginTime = time();
echo sprintf("command: %s", str_pad("$cmd ", 50));
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
$process = proc_open($cmd, $descriptorspec, $pipes, __DIR__ . '/..', null);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$iCode = proc_close($process);
$bSuccess = (0 === $iCode);
$iElapsed = time() - $iBeginTime;
if (!$bSuccess) {
fwrite(STDERR, sprintf(
"\nCOMMAND FAILED! (%s) \n - status:%s \n - stderr:%s \n - stdout: %s\n - elapsed:%ss\n\n",
$cmd,
$iCode,
rtrim($stderr),
rtrim($stdout),
$iElapsed
));
}
else
{
echo "| elapsed:${iElapsed}s \n";
}
if (!empty($stderr))
{
fwrite(STDERR, "$stderr\n");
}
if (!empty($stdout))
{
echo "stdout :$stdout\n\n";
}
return $bSuccess;
}

View File

@@ -1,51 +0,0 @@
<?php
/**
* Copyright (C) 2010-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
*
*/
use Combodo\iTop\Composer\iTopComposer;
$iTopFolder = __DIR__."/../../../";
require_once("$iTopFolder/approot.inc.php");
require_once(APPROOT."/application/utils.inc.php");
if (PHP_SAPI !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
$sCssFile = APPROOT.'/css/setup.css';
if (file_exists($sCssFile))
{
fwrite(STDERR, "$sCssFile already exists (it should not), removing it.");
if (!unlink($sCssFile))
{
fwrite(STDERR, "Failed to remove $sCssFile, exiting.");
exit(1);
}
}
$sCssRelPath = utils::GetCSSFromSASS('css/setup.scss');
if (!file_exists($sCssFile))
{
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
exit(1);
}

View File

@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
require_once ("$iTopFolder/approot.inc.php");
require_once (APPROOT."/setup/setuputils.class.inc.php");
if (PHP_SAPI !== 'cli')
if (php_sapi_name() !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
@@ -36,38 +36,22 @@ clearstatcache();
$oiTopComposer = new iTopComposer();
$aDeniedButStillPresent = $oiTopComposer->ListDeniedButStillPresent();
echo "\n";
foreach ($aDeniedButStillPresent as $sDir)
{
if (false === iTopComposer::IsTestDir($sDir))
if (! preg_match('#[tT]ests?/?$#', $sDir))
{
echo "ERROR found INVALID denied test dir: '$sDir'\n";
echo "\nfound INVALID denied test dir: '$sDir'\n";
throw new \Exception("$sDir must end with /Test/ or /test/");
}
if (false === file_exists($sDir)) {
echo "INFO $sDir is in denied list, but not existing on disk => skipping !\n";
continue;
}
try {
try
{
SetupUtils::rrmdir($sDir);
echo "OK Remove denied test dir: '$sDir'\n";
echo "Remove denied test dir: '$sDir'\n";
}
catch (\Exception $e) {
catch (\Exception $e)
{
echo "\nFAILED to remove denied test dir: '$sDir'\n";
}
}
$aAllowedAndDeniedDirs = array_merge(
$oiTopComposer->ListAllowedTestDir(),
$oiTopComposer->ListDeniedTestDir()
);
$aExistingDirs = $oiTopComposer->ListAllTestDir();
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
if (false === empty($aMissing)) {
echo "Some new tests dirs exists !\n"
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
.' List of dirs:'."\n".var_export($aMissing, true);
}
}

View File

@@ -1,42 +1,20 @@
<?php
/**
* script used to sort license file (useful for autogeneration)
*
* Requirements :
* * bash (on Windows, use Git Bash)
* * composer (if you use the phar version, mind to create a `Composer` alias !)
* * JQ command
* to install on Windows :
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
* this is a Windows port : https://stedolan.github.io/jq/
*
* Licenses sources :
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
* ⚠ If licenses were added manually, they might be removed by this tool ! Be very careful to check for the result before pushing !
*
* To launch, check requirements and run `php updateLicenses.php`
* The target license file path is in `$xmlFilePath`
* script used to sort license file (usefull for autogeneration)
* Example: php
*/
$iTopFolder = __DIR__."/../../";
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
$iTopFolder = __DIR__ . "/../../" ;
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
if ((null === $jqExec) || (false === $jqExec)) {
echo "/!\ JQ is required but cannot be launched :( \n";
echo "Check this script PHPDoc block for instructions\n";
die(-1);
}
function get_scope($product_node) {
function get_scope($product_node)
{
$scope = $product_node->getAttribute("scope");
if ($scope === "") { //put iTop first
if ($scope === "")
{ //put iTop first
return "aaaaaaaaa";
}
return $scope;
}
@@ -73,83 +51,39 @@ function get_license_nodes($file_path)
$xp = new DOMXPath($dom);
$licenseList = $xp->query('/licenses/license');
$licenses = iterator_to_array($licenseList);
$licenses = iterator_to_array($licenseList);
usort($licenses, 'sort_by_product');
return $licenses;
}
/** @noinspection SuspiciousAssignmentsInspection */
function fix_product_name(DOMNode &$oProductNode)
{
$sProductNameOrig = $oProductNode->nodeValue;
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//lib/symfony/polyfill-ctype`
$sProductNameFixed = remove_dir_from_string($sProductNameOrig, 'lib/');
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas`
$sProductNameFixed = remove_dir_from_string($sProductNameFixed, 'vendor/');
$oProductNode->nodeValue = $sProductNameFixed;
}
function remove_dir_from_string($sString, $sNeedle)
{
if (strpos($sString, $sNeedle) === false) {
return $sString;
}
$sStringTmp = strstr($sString, $sNeedle);
$sStringFixed = str_replace($sNeedle, '', $sStringTmp);
// DEBUG trace O:)
// echo "$sNeedle = $sString => $sStringFixed\n";
return $sStringFixed;
}
$old_licenses = get_license_nodes($xmlFilePath);
//generate file with updated licenses
$generated_license_file_path = __DIR__."/provfile.xml";
echo "- Generating licences...";
exec("bash ".__DIR__."/gen-community-license.sh $iTopFolder > ".$generated_license_file_path);
echo "OK!\n";
echo "- Get licenses nodes...";
exec("bash " . __DIR__ . "/gen-community-license.sh $iTopFolder > ". $generated_license_file_path);
$new_licenses = get_license_nodes($generated_license_file_path);
unlink($generated_license_file_path);
exec("rm -f ". $generated_license_file_path);
foreach ($old_licenses as $b) {
$aProductNode = get_product_node($b);
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels") {
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
{
$new_licenses[] = $b;
}
}
usort($new_licenses, 'sort_by_product');
echo "OK!\n";
echo "- Overwritting Combodo license file...";
$new_dom = new DOMDocument("1.0");
$new_dom->formatOutput = true;
$root = $new_dom->createElement("licenses");
$new_dom->appendChild($root);
foreach ($new_licenses as $b) {
$node = $new_dom->importNode($b, true);
// N°3870 fix when running script in Windows
// fix should be in gen-community-license.sh but it is easier to do it here !
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
$oProductNodeOrig = get_product_node($node);
fix_product_name($oProductNodeOrig);
}
$root->appendChild($node);
$node = $new_dom->importNode($b,true);
$root->appendChild($new_dom->importNode($b,true));
}
$new_dom->save($xmlFilePath);
echo "OK!\n";
$new_dom->save($xmlFilePath);

View File

@@ -1,77 +0,0 @@
<?php
/**
* Usage :
* `php changelog.php 2.7.4`
*
* As argument is passed the git ref (tag name or sha1) we want to use as reference
*
* Outputs :
*
* 1. List of bugs as CSV :
* bug ref;link
* Example :
* <code>
* Bug_ref;Bug_URL;sha1
* 1234;https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=1234;949b213f9|b1ca1f263|a1271da74
* </code>
*
* 2. List of commits sha1/message without bug ref
* Example :
* <code>
* sha1;subject
* a6aa183e2;:bookmark: Prepare 2.7.5
* </code>
*/
if (count($argv) === 1) {
echo '⚠ You must pass the base tag/sha1 as parameter';
exit(1);
}
$sBaseReference = $argv[1];
//--- Get log
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
$sGitLogRaw = shell_exec($sGitLogCommand);
//--- Analyze log
$aGitLogLines = preg_split('/\n/', trim($sGitLogRaw));;
$aLogLinesWithBugRef = [];
$aLogLineNoBug = [];
foreach ($aGitLogLines as $sLogLine) {
$sBugRef = preg_match('/[nN]°(\d{3,4})/', $sLogLine, $aLineBugRef);
if (($sBugRef === false) || empty($aLineBugRef)) {
$aLogLineNoBug[] = $sLogLine;
continue;
}
$iBugId = $aLineBugRef[1];
$sSha = substr($sLogLine, 0, 9);
if (array_key_exists($iBugId, $aLogLinesWithBugRef)) {
$aBugShaRefs = $aLogLinesWithBugRef[$iBugId];
$aBugShaRefs[] = $sSha;
$aLogLinesWithBugRef[$iBugId] = $aBugShaRefs;
} else {
$aLogLinesWithBugRef[$iBugId] = [$sSha];
}
}
$aBugsList = array_keys($aLogLinesWithBugRef);
sort($aBugsList, SORT_NUMERIC);
//-- Output results
echo "# Bugs included\n";
echo "Bug_ref;Bug_URL;sha1\n";
foreach ($aBugsList as $sBugRef) {
$sShaRefs = implode('|', $aLogLinesWithBugRef[$sBugRef]);
echo "{$sBugRef};https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id={$sBugRef};$sShaRefs\n";
}
echo "\n";
echo "# Logs line without bug referenced\n";
echo "sha1;subject\n";
foreach ($aLogLineNoBug as $sLogLine) {
echo "$sLogLine\n";
}

View File

@@ -27,7 +27,6 @@ $aFilesUpdaters = array(
new iTopVersionFileUpdater(),
new CssVariablesFileUpdater(),
new DatamodelsModulesFiles(),
new ConstantFileUpdater('ITOP_CORE_VERSION', 'approot.inc.php'),
);
if (count($argv) === 1)

View File

@@ -69,40 +69,6 @@ abstract class AbstractSingleFileVersionUpdater extends FileVersionUpdater
}
}
/**
* @since 2.7.7 3.0.1 3.1.0 N°4714
*/
class ConstantFileUpdater extends AbstractSingleFileVersionUpdater {
/** @var string */
private $sConstantName;
/**
* @param $sConstantName constant to search, for example `ITOP_CORE_VERSION`
* @param $sFileToUpdate file containing constant definition
*/
public function __construct($sConstantName, $sFileToUpdate)
{
$this->sConstantName = $sConstantName;
parent::__construct($sFileToUpdate);
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
$sConstantSearchPattern = <<<REGEXP
/define\('{$this->sConstantName}', ?'[^']+'\);/
REGEXP;
return preg_replace(
$sConstantSearchPattern,
"define('{$this->sConstantName}', '{$sVersionLabel}');",
$sFileContent
);
}
}
class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
{
public function __construct()

View File

@@ -60,7 +60,7 @@ For example, if no version is currently prepared for shipping we could have:
In this example, when 2.8.0-beta is shipped that will become:
- `develop`: future 2.9.0 version
- `release/2.8`: 2.8.0-beta
- `release/2.8.0`: 2.8.0-beta
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
@@ -111,9 +111,9 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.dev/)).
Beware to use the code (for example `:bug:`) and not the character (🐛) as Unicode support in git clients is very poor for now...
Emoji examples :
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/)).
Beware to use the code (for example `:bug:`) and not the character (🐛) as Unicode support in git clients is very poor for now...
Emoji examples :
* 🌐 `:globe_with_meridians:` for translations
* 🎨 `:art:` when improving the format/structure of the code
* ⚡️ `:zap:` when improving performance
@@ -132,7 +132,7 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
When your code is working, please:
* squash as much as possible your commits,
* stash as much as possible your commits,
* rebase your branch on our repo last commit,
* create a pull request.

84
Jenkinsfile vendored
View File

@@ -1,19 +1,73 @@
def infra
pipeline {
agent any
parameters {
booleanParam(name: 'debugMode', defaultValue: 'false', description: 'Debug mode?')
string(name: 'testFile', defaultValue: '', description: 'Provide test file to execute. Example: test/core/LogAPITest.php')
booleanParam(name: 'coverture', defaultValue: 'false', description: 'Test coverture?')
booleanParam(name: 'runNonRegOQLTests', defaultValue: 'false', description: 'Do You want to run legacy OQL regression tests?')
}
stages {
node(){
properties([
buildDiscarder(
logRotator(
daysToKeepStr: "28",
numToKeepStr: "500")
)
])
stage('init') {
parallel {
stage('debug') {
steps {
sh './.jenkins/bin/init/debug.sh'
}
}
stage('append files to project') {
steps {
sh './.jenkins/bin/init/append_files.sh'
}
}
stage('composer install') {
steps {
sh './.jenkins/bin/init/composer_install.sh'
}
}
}
}
checkout scm
stage('unattended_install') {
parallel {
stage('unattended_install default env') {
steps {
sh './.jenkins/bin/unattended_install/default_env.sh'
}
}
}
}
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
stage('test') {
parallel {
stage('phpunit') {
steps {
sh './.jenkins/bin/tests/phpunit.sh ${debugMode} ${runNonRegOQLTests} "${coverture}" "${testFile}"'
}
}
}
}
}
post {
always {
archiveArtifacts allowEmptyArchive:true, excludes: '.gitkeep', artifacts: 'var/test/*.xml'
junit 'var/test/phpunit-log.junit.xml'
}
failure {
slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME_UNESCAPED} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
fixed {
slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME_UNESCAPED} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
environment {
DEBUG_UNIT_TEST = '0'
JOB_NAME_UNESCAPED = env.JOB_NAME.replaceAll("%2F", "/")
}
options {
timeout(time: 20, unit: 'MINUTES')
}
}
infra.call()

View File

@@ -23,10 +23,10 @@ iTop also offers mass import tools and web services to integrate with your IT
## Latest release
- [Changes since the previous version][62]
- [New features][63]
- [Installation notes][64]
- [Download][65]
- [Changes since the previous version][62]
- [New features][63]
- [Installation notes][64]
- [Download][65]
[62]: https://www.itophub.io/wiki/page?id=latest:release:change_log
[63]: https://www.itophub.io/wiki/page?id=latest:release:start
@@ -39,16 +39,17 @@ iTop also offers mass import tools and web services to integrate with your IT
- [iTop Forums][1]: community support
- [iTop Tickets][2]: for feature requests and bug reports
- [Releases download][3]
- [Documentation][4] covering both iTop and its official extensions
- [iTop Hub][5] : discover and install extensions !
- [Software requirements][4]
- [Documentation][5] covering both iTop and its official extensions
- [iTop Hub][6] : discover and install extensions !
[1]: https://sourceforge.net/p/itop/discussion/
[2]: https://sourceforge.net/p/itop/tickets/
[3]: https://sourceforge.net/projects/itop/files/itop/
[4]: https://www.itophub.io/wiki
[5]: https://store.itophub.io/en_US/
[4]: https://www.itophub.io/wiki/page?id=latest:install:upgrading_itop
[5]: https://www.itophub.io/wiki
[6]: https://store.itophub.io/en_US/
[10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing
@@ -95,9 +96,8 @@ We would like to give a special thank you to the people from the community who c
- Lassiter, Dennis
- Lazcano, Federico
- Lucas, Jonathan
- Malik, Remie
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
- Rosenke, Stephan
- Malik, Remie
- Rosenke, Stephan
- Seki, Shoji
- Shilov, Vladimir
- Tulio, Marco

View File

@@ -23,7 +23,7 @@ define('PORTAL_PROFILE_NAME', 'Portal user');
class UserRightsBaseClassGUI extends cmdbAbstractObject
{
// Whenever something changes, reload the privileges
protected function AfterInsert()
{
UserRights::FlushPrivileges();
@@ -73,7 +73,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
}
protected static $m_aCacheProfiles = null;
public static function DoCreateProfile($sName, $sDescription)
{
if (is_null(self::$m_aCacheProfiles))
@@ -85,7 +85,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
self::$m_aCacheProfiles[$oProfile->Get('name')] = $oProfile->GetKey();
}
}
}
$sCacheKey = $sName;
if (isset(self::$m_aCacheProfiles[$sCacheKey]))
@@ -96,10 +96,10 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oNewObj->Set('name', $sName);
$oNewObj->Set('description', $sDescription);
$iId = $oNewObj->DBInsertNoReload();
self::$m_aCacheProfiles[$sCacheKey] = $iId;
self::$m_aCacheProfiles[$sCacheKey] = $iId;
return $iId;
}
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
{
$bGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
@@ -116,7 +116,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
}
}
function DoShowGrantSumary($oPage)
{
if ($this->GetRawName() == "Administrator")
@@ -128,7 +128,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
// Note: for sure, we assume that the instance is derived from UserRightsProfile
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
{
@@ -137,12 +137,12 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$bGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if ($bGrant === true)
{
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
$aDisplayData[] = array(
'class' => MetaModel::GetName($sClass),
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'r'),
@@ -154,7 +154,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
'stimuli' => $sStimuli,
);
}
$aDisplayConfig = array();
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
@@ -214,7 +214,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
* @return integer Flags: the binary combination of the flags applicable to this attribute
*/
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
@@ -397,7 +397,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
$oUser = UserRights::GetUserObject();
$oUser = UserRights::GetUserObject();
$oAddon = UserRights::GetModuleInstance();
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)
@@ -425,12 +425,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
UR_ACTION_BULK_DELETE => 'bd',
);
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
private $aUsersProfilesList = [];
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
@@ -440,7 +434,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
$oOrg = MetaModel::NewObject('Organization');
$oOrg = new Organization();
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$iOrgId = $oOrg->DBInsertNoReload();
@@ -448,13 +442,17 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
$oContact = MetaModel::NewObject('Person');
$oContact = new Person();
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
if (MetaModel::IsValidAttCode('Person', 'org_id'))
{
$oContact->Set('org_id', $iOrgId);
}
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org');
$iContactId = $oContact->DBInsertNoReload();
}
@@ -527,7 +525,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$oUserOrgSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserOrg = $oUserOrgSet->Fetch())
{
@@ -563,7 +561,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return bool
* @return array
*/
public function IsAdministrator($oUser)
{
@@ -573,22 +571,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return bool
* @return array
*/
public function IsPortalUser($oUser)
{
// UserRights caches the list for us
return UserRights::HasProfile(PORTAL_PROFILE_NAME, $oUser);
}
/**
* @param $oUser User
*
* @return array
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @return bool
*/
public function ListProfiles($oUser)
{
@@ -652,20 +644,14 @@ class UserRightsProfile extends UserRightsAddOnAPI
// load and cache permissions for the current user on the given class
//
$iUser = $oUser->GetKey();
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])){
$aTest = $this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
if (is_array($aTest)) return $aTest;
}
$aTest = @$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
if (is_array($aTest)) return $aTest;
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Call the API of UserRights because it caches the list for us
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -791,16 +777,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))
@@ -829,10 +810,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
/**
* @param string $sClass
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{
$sAttCode = null;

View File

@@ -535,7 +535,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
$oOrg = MetaModel::NewObject('Organization');
$oOrg = new Organization();
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$oOrg::SetCurrentChange($oChange);
@@ -544,13 +544,17 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
$oContact = MetaModel::NewObject('Person');
$oContact = new Person();
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
if (MetaModel::IsValidAttCode('Person', 'org_id'))
{
$oContact->Set('org_id', $iOrgId);
}
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org');
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
@@ -604,10 +608,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param User $oUser
* @param string $sClass (not used here but can be used in overloads)
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array keys of the User allowed org
* @return array
* @throws \CoreException
* @throws \Exception
*/
@@ -707,7 +711,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function LoadCache()
{
if (!is_null($this->m_aProfiles)) return false;
if (!is_null($this->m_aProfiles)) return;
// Could be loaded in a shared memory (?)
$oKPI = new ExecutionKPI();

View File

@@ -24,42 +24,31 @@ class ajax_page extends WebPage implements iTabbedPage
/**
* Jquery style ready script
* @var array
*/
*/
protected $m_sReadyScript;
protected $m_oTabs;
private $m_sMenu; // If set, then the menu will be updated
/**
* constructor for the web page
*
* @param string $s_title Not used
*/
function __construct($s_title) {
/**
* constructor for the web page
* @param string $s_title Not used
*/
function __construct($s_title)
{
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
$this->m_sReadyScript = "";
parent::__construct($s_title, $bPrintable);
$this->m_sReadyScript = "";
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->no_cache();
$this->add_http_headers();
$this->add_header("Cache-control: no-cache");
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
/**
* Disabling sending the header so that resource won't be blocked by CORB. See parent method documentation.
* @return void
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
*/
public function add_xcontent_type_options()
{
// Nothing to do !
}
}
/**
* @inheritDoc

View File

@@ -29,9 +29,9 @@ require_once(APPROOT.'application/newsroomprovider.class.inc.php');
* You may implement such interfaces in a module file (e.g. main.mymodule.php)
*
* @api
* @package LoginExtensibilityAPI
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @package Extensibility
* @since 2.7.0
*/
interface iLoginExtension
@@ -39,16 +39,12 @@ interface iLoginExtension
/**
* Return the list of supported login modes for this plugin
*
* @api
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes();
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
interface iLoginFSMExtension extends iLoginExtension
@@ -60,7 +56,6 @@ interface iLoginFSMExtension extends iLoginExtension
* if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
* if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or state
*
* @api
* @param string $sLoginState (see LoginWebPage::LOGIN_STATE_...)
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
@@ -70,8 +65,6 @@ interface iLoginFSMExtension extends iLoginExtension
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
@@ -79,14 +72,15 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
/**
* @inheritDoc
*/
abstract public function ListSupportedLoginModes();
public abstract function ListSupportedLoginModes();
/**
* @inheritDoc
*/
public function LoginAction($sLoginState, &$iErrorCode)
{
switch ($sLoginState) {
switch ($sLoginState)
{
case LoginWebPage::LOGIN_STATE_START:
return $this->OnStart($iErrorCode);
@@ -118,7 +112,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
/**
* Initialization
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -132,7 +125,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* Detect login mode explicitly without respecting configured order (legacy mode)
* In most case do nothing here
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -149,7 +141,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* 1 - display login form
* 2 - read the values posted by the user
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -163,7 +154,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* Control the validity of the data provided by the user
* Automatic user provisioning can be done here
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -174,7 +164,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -185,7 +174,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -196,7 +184,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -207,7 +194,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -219,28 +205,22 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
interface iLogoutExtension extends iLoginExtension
{
/**
* Execute all actions to log out properly
* @api
*/
public function LogoutAction();
}
/**
* @api
* @package UIExtensibilityAPI
* @since 2.7.0
*/
interface iLoginUIExtension extends iLoginExtension
{
/**
* @api
* @return LoginTwigContext
*/
public function GetTwigContext();
@@ -248,20 +228,18 @@ interface iLoginUIExtension extends iLoginExtension
/**
* @api
* @package PreferencesExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
interface iPreferencesExtension
{
/**
* @api
* @param \WebPage $oPage
*
*/
public function DisplayPreferences(WebPage $oPage);
/**
* @api
* @param \WebPage $oPage
* @param string $sOperation
*
@@ -274,7 +252,7 @@ interface iPreferencesExtension
* Extend this class instead of implementing iPreferencesExtension if you don't need to overload all methods
*
* @api
* @package PreferencesExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractPreferencesExtension implements iPreferencesExtension
@@ -320,7 +298,7 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members.
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
*/
interface iApplicationUIExtension
{
@@ -342,7 +320,6 @@ interface iApplicationUIExtension
* }
* </code>
*
* @api
* @param DBObject $oObject The object being displayed
* @param WebPage $oPage The output context
* @param boolean $bEditMode True if the edition form is being displayed
@@ -356,7 +333,6 @@ interface iApplicationUIExtension
*
* The method is called rigth after all the tabs have been displayed
*
* @api
* @param DBObject $oObject The object being displayed
* @param WebPage $oPage The output context
* @param boolean $bEditMode True if the edition form is being displayed
@@ -371,7 +347,6 @@ interface iApplicationUIExtension
* The method is called after the changes from the standard form have been
* taken into account, and before saving the changes into the database.
*
* @api
* @param DBObject $oObject The object being edited
* @param string $sFormPrefix Prefix given to the HTML form inputs
*
@@ -386,7 +361,6 @@ interface iApplicationUIExtension
* javascript into the edition form, and if that code requires to store temporary data
* (this is the case when a file must be uploaded).
*
* @api
* @param string $sTempId Unique temporary identifier made of session_id and transaction_id. It identifies the object in a unique way.
*
* @return void
@@ -398,7 +372,6 @@ interface iApplicationUIExtension
*
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return string[] desc
@@ -410,7 +383,6 @@ interface iApplicationUIExtension
*
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return string Path of the icon, relative to the modules directory.
@@ -430,7 +402,6 @@ interface iApplicationUIExtension
* * HILIGHT_CLASS_OK
* * HILIGHT_CLASS_NONE
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return integer The value representing the mood of the object
@@ -457,7 +428,6 @@ interface iApplicationUIExtension
*
* See also iPopupMenuExtension for greater flexibility
*
* @api
* @param DBObjectSet $oSet A set of persistent objects (DBObject)
*
* @return string[string]
@@ -469,7 +439,7 @@ interface iApplicationUIExtension
* Extend this class instead of implementing iApplicationUIExtension if you don't need to overload
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
@@ -543,7 +513,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI.
*
* @api
* @package ORMExtensibilityAPI
* @package Extensibility
*/
interface iApplicationObjectExtension
{
@@ -556,7 +526,6 @@ interface iApplicationObjectExtension
* If the extension returns false, then the framework will perform the usual evaluation.
* Otherwise, the answer is definitively "yes, the object has changed".
*
* @api
* @param \cmdbAbstractObject $oObject The target object
*
* @return boolean True if something has changed for the target object
@@ -569,7 +538,6 @@ interface iApplicationObjectExtension
* The GUI calls this verb and reports any issue.
* Anyhow, this API can be called in other contexts such as the CSV import tool.
*
* @api
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -583,7 +551,6 @@ interface iApplicationObjectExtension
*
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
*
* @api
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -599,7 +566,6 @@ interface iApplicationObjectExtension
* * {@see DBObject::ListPreviousValuesForUpdatedAttributes()} : list of changed attributes and their values before the change
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @api
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -615,7 +581,6 @@ interface iApplicationObjectExtension
*
* The method is called right <b>after</b> the object has been written to the database.
*
* @api
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -629,7 +594,6 @@ interface iApplicationObjectExtension
*
* The method is called right <b>before</b> the object will be deleted from the database.
*
* @api
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -643,7 +607,7 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @package ORMExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractApplicationObjectExtension implements iApplicationObjectExtension
@@ -703,7 +667,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
* by the application, as long as the class definition is included somewhere in the code
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
interface iPopupMenuExtension
@@ -712,21 +676,18 @@ interface iPopupMenuExtension
* Insert an item into the Actions menu of a list
*
* $param is a DBObjectSet containing the list of objects
* @api
*/
const MENU_OBJLIST_ACTIONS = 1;
/**
* Insert an item into the Toolkit menu of a list
*
* $param is a DBObjectSet containing the list of objects
* @api
*/
const MENU_OBJLIST_TOOLKIT = 2;
/**
* Insert an item into the Actions menu on an object details page
*
* $param is a DBObject instance: the object currently displayed
* @api
*/
const MENU_OBJDETAILS_ACTIONS = 3;
/**
@@ -736,14 +697,12 @@ interface iPopupMenuExtension
* is being displayed.
*
* $param is a Dashboard instance: the dashboard currently displayed
* @api
*/
const MENU_DASHBOARD_ACTIONS = 4;
/**
* Insert an item into the User menu (upper right corner)
*
* $param is null
* @api
*/
const MENU_USER_ACTIONS = 5;
/**
@@ -751,7 +710,6 @@ interface iPopupMenuExtension
*
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on
* the current line)
* @api
*/
const PORTAL_OBJLISTITEM_ACTIONS = 7;
/**
@@ -759,7 +717,6 @@ interface iPopupMenuExtension
*
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object
* currently displayed)
* @api
*/
const PORTAL_OBJDETAILS_ACTIONS = 8;
@@ -797,7 +754,6 @@ interface iPopupMenuExtension
* This method is called by the framework for each menu.
* The items will be inserted in the menu in the order of the returned array.
*
* @api
* @param int $iMenuId The identifier of the type of menu, as listed by the constants MENU_xxx
* @param mixed $param Depends on $iMenuId, see the constants defined above
*
@@ -810,7 +766,7 @@ interface iPopupMenuExtension
* Base class for the various types of custom menus
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
abstract class ApplicationPopupMenuItem
@@ -871,7 +827,6 @@ abstract class ApplicationPopupMenuItem
}
/**
* @api
* @param $aCssClasses
*/
public function SetCssClasses($aCssClasses)
@@ -882,7 +837,6 @@ abstract class ApplicationPopupMenuItem
/**
* Adds a CSS class to the CSS classes that will be put on the menu item
*
* @api
* @param $sCssClass
*/
public function AddCssClass($sCssClass)
@@ -909,7 +863,7 @@ abstract class ApplicationPopupMenuItem
* Class for adding an item into a popup menu that browses to the given URL
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class URLPopupMenuItem extends ApplicationPopupMenuItem
@@ -922,7 +876,6 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
/**
* Constructor
*
* @api
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
* @param string $sLabel The display label of the menu (must be localized)
* @param string $sURL If the menu is an hyperlink, provide the absolute hyperlink here
@@ -946,7 +899,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
* Class for adding an item into a popup menu that triggers some Javascript code
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class JSPopupMenuItem extends ApplicationPopupMenuItem
@@ -998,7 +951,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
* will automatically reduce several consecutive separators to just one
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
@@ -1007,7 +960,6 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
/**
* Constructor
* @api
*/
public function __construct()
{
@@ -1025,7 +977,7 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
* Class for adding an item as a button that browses to the given URL
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class URLButtonItem extends URLPopupMenuItem
@@ -1037,7 +989,7 @@ class URLButtonItem extends URLPopupMenuItem
* Class for adding an item as a button that runs some JS code
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class JSButtonItem extends JSPopupMenuItem
@@ -1061,7 +1013,7 @@ class JSButtonItem extends JSPopupMenuItem
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
interface iPageUIExtension
@@ -1069,7 +1021,6 @@ interface iPageUIExtension
/**
* Add content to the North pane
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1079,7 +1030,6 @@ interface iPageUIExtension
/**
* Add content to the South pane
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1089,7 +1039,6 @@ interface iPageUIExtension
/**
* Add content to the "admin banner"
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1101,7 +1050,7 @@ interface iPageUIExtension
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractPageUIExtension implements iPageUIExtension
@@ -1135,11 +1084,11 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
/**
* Implement this interface to add content to any enhanced portal page
*
* @api
* @package PortalExtensibilityAPI
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
*
* @since 2.4.0 interface creation
* @since 2.7.0 change method signatures due to Silex to Symfony migration
* @api
* @package Extensibility
* @since 2.4.0
*/
interface iPortalUIExtension
{
@@ -1150,7 +1099,6 @@ interface iPortalUIExtension
/**
* Returns an array of CSS file urls
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
@@ -1160,7 +1108,6 @@ interface iPortalUIExtension
/**
* Returns inline (raw) CSS
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1170,7 +1117,6 @@ interface iPortalUIExtension
/**
* Returns an array of JS file urls
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
@@ -1180,7 +1126,6 @@ interface iPortalUIExtension
/**
* Returns raw JS code
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1190,7 +1135,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the <body> tag
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1200,7 +1144,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the #main-wrapper element
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1210,7 +1153,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1219,11 +1161,7 @@ interface iPortalUIExtension
}
/**
* Extend this class instead of iPortalUIExtension if you don't need to overload all methods
*
* @api
* @package PortalExtensibilityAPI
* @since 2.4.0
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
*/
abstract class AbstractPortalUIExtension implements iPortalUIExtension
{
@@ -1288,7 +1226,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
* Implement this interface to add new operations to the REST/JSON web service
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
* @since 2.0.1
*/
interface iRestServiceProvider
@@ -1296,7 +1234,6 @@ interface iRestServiceProvider
/**
* Enumerate services delivered by this class
*
* @api
* @param string $sVersion The version (e.g. 1.0) supported by the services
*
* @return array An array of hash 'verb' => verb, 'description' => description
@@ -1306,7 +1243,6 @@ interface iRestServiceProvider
/**
* Enumerate services delivered by this class
*
* @api
* @param string $sVersion The version (e.g. 1.0) supported by the services
* @param string $sVerb
* @param array $aParams
@@ -1316,115 +1252,81 @@ interface iRestServiceProvider
public function ExecOperation($sVersion, $sVerb, $aParams);
}
interface iRestInputSanitizer
{
public function SanitizeJsonInput(string $sJsonInput): string;
}
/**
* Minimal REST response structure. Derive this structure to add response data and error codes.
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
* @since 2.0.1
*/
class RestResult
{
/**
* Result: no issue has been encountered
* @api
*/
const OK = 0;
/**
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
* @api
*/
const UNAUTHORIZED = 1;
/**
* Result: the parameter 'version' is missing
* @api
*/
const MISSING_VERSION = 2;
/**
* Result: the parameter 'json_data' is missing
* @api
*/
const MISSING_JSON = 3;
/**
* Result: the input structure is not a valid JSON string
* @api
*/
const INVALID_JSON = 4;
/**
* Result: the parameter 'auth_user' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_USER = 5;
/**
* Result: the parameter 'auth_pwd' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_PWD = 6;
/**
* Result: no operation is available for the specified version
* @api
*/
const UNSUPPORTED_VERSION = 10;
/**
* Result: the requested operation is not valid for the specified version
* @api
*/
const UNKNOWN_OPERATION = 11;
/**
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
* @api
*/
const UNSAFE = 12;
/**
* Result: the request page number is not valid. It must be an integer greater than 0
* @api
*/
const INVALID_PAGE = 13;
/**
* Result: the operation could not be performed, see the message for troubleshooting
* @api
*/
const INTERNAL_ERROR = 100;
/**
* Default constructor - ok!
* @api
*/
public function __construct()
{
$this->code = RestResult::OK;
}
/**
* @var int
* @api
*/
public $code;
/**
* @var string
* @api
*/
public $message;
/**
* Sanitize the content of this result to hide sensitive information
*/
public function SanitizeContent()
{
// The default implementation does nothing
}
}
/**
* Helpers for implementing REST services
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
*/
class RestUtils
{
@@ -1573,7 +1475,6 @@ class RestUtils
/**
* Read and interpret object search criteria from a Rest/Json structure
*
* @api
* @param string $sClass Name of the class
* @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the
* attriute)
@@ -1634,8 +1535,6 @@ class RestUtils
*
* @return DBObject The object found
* @throws Exception If the input structure is not valid or it could not find exactly one object
*
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
*/
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
{
@@ -1685,7 +1584,6 @@ class RestUtils
/**
* Search objects from a polymorph search specification (Rest/Json)
*
* @api
* @param string $sClass Name of the class
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
* @param int $iLimit The limit of results to return
@@ -1722,16 +1620,8 @@ class RestUtils
elseif (is_string($key))
{
// OQL
try {
$oSearch = DBObjectSearch::FromOQL($key);
} catch (Exception $e) {
throw new CoreOqlException('Query failed to execute', [
'query' => $key,
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
]);
}
}
$oSearch = DBObjectSearch::FromOQL($key);
}
else
{
throw new Exception("Wrong format for key");
@@ -1880,28 +1770,4 @@ class RestUtils
interface iModuleExtension
{
public function __construct();
}
/**
* KPI logging extensibility point
*
* KPI Logger extension
*/
interface iKPILoggerExtension
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKPILogData
*
* @return mixed
*/
public function LogOperation($oKPILogData);
}

View File

@@ -29,9 +29,13 @@ require_once(APPROOT."/application/webpage.class.inc.php");
class CLIPage implements Page
{
function __construct($s_title)
/** @var string */
public $s_title;
function __construct($s_title)
{
}
$this->s_title = $s_title;
}
public function output()
{
@@ -48,22 +52,22 @@ class CLIPage implements Page
public function add($sText)
{
echo $sText;
}
}
public function p($sText)
{
echo $sText."\n";
}
}
public function pre($sText)
{
echo $sText."\n";
}
}
public function add_comment($sText)
{
echo "#".$sText."\n";
}
}
public function table($aConfig, $aData, $aParams = array())
{
@@ -93,5 +97,3 @@ class CLIPage implements Page
}
}
}
?>

View File

@@ -541,7 +541,7 @@ EOF
{
continue;
}
$oPage->AddAjaxTab( 'Class:'.$sClass.'/Attribute:'.$sAttCode, utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode(), true, $oAttDef->GetLabel());
$oPage->AddAjaxTab($oAttDef->GetLabel(), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode(), true, 'Class:'.$sClass.'/Attribute:'.$sAttCode);
continue;
}
@@ -558,6 +558,12 @@ EOF
$oLinkSet = $oOrmLinkSet->ToDBObjectSet(utils::ShowObsoleteData());
$iCount = $oLinkSet->Count();
$sCount = '';
if ($iCount != 0)
{
$sCount = " ($iCount)";
}
$oPage->SetCurrentTab('Class:'.$sClass.'/Attribute:'.$sAttCode, $oAttDef->GetLabel().$sCount);
if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
@@ -603,9 +609,6 @@ EOF
continue;
}
$sCount = ($iCount != 0) ? " ($iCount)" : "";
$oPage->SetCurrentTab('Class:'.$sClass.'/Attribute:'.$sAttCode, $oAttDef->GetLabel().$sCount);
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
@@ -1557,9 +1560,6 @@ HTML
* @param array $aParams
*
* @throws \Exception
* only used in old and deprecated export.php
*
* @internal Only to be used by `/webservices/export.php` : this is a legacy method that produces wrong HTML (no TR on table body rows)
*/
public static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
@@ -1580,8 +1580,6 @@ HTML
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*
* @internal Only to be used by `/webservices/export.php` : this is a legacy method that produces wrong HTML (no TR on table body rows)
*/
public static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
{
@@ -3218,13 +3216,13 @@ EOF
if ($oAttDef->GetEditClass() == 'Document')
{
$oDocument = $this->Get($sAttCode);
if (is_object($oDocument) && !$oDocument->IsEmpty())
if (!$oDocument->IsEmpty())
{
$sDisplayValue = $this->GetAsHTML($sAttCode);
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
}
else
{
@@ -3261,15 +3259,16 @@ 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;
@@ -3797,24 +3796,19 @@ EOF
break;
case 'Image':
$value = null;
$oImage = utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents');
if (!is_null($oImage->GetData()))
{
$aSize = utils::GetImageSize($oImage->GetData());
$oImage = utils::ResizeImageToFit(
$oImage,
$aSize[0],
$aSize[1],
$oAttDef->Get('storage_max_width'),
$oAttDef->Get('storage_max_height')
);
}
$aSize = utils::GetImageSize($oImage->GetData());
$oImage = utils::ResizeImageToFit($oImage, $aSize[0], $aSize[1], $oAttDef->Get('storage_max_width'),
$oAttDef->Get('storage_max_height'));
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
if (is_array($aOtherData))
{
$value = array('fcontents' => $oImage, 'remove' => $aOtherData['remove']);
}
else
{
$value = null;
}
break;
case 'RedundancySetting':
@@ -4003,9 +3997,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
}
return $res;
@@ -4022,16 +4014,13 @@ EOF
protected function DBCloneTracked_Internal($newKey = null)
{
/** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
$oNewObj = parent::DBCloneTracked_Internal($newKey);
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
return $oNewObj;
@@ -4059,9 +4048,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
}
}
catch (Exception $e)
@@ -4107,9 +4094,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4127,10 +4112,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified)
if ($oExtensionInstance->OnIsModified($this))
{
return true;
}
@@ -4174,9 +4156,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
{
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
@@ -4224,9 +4204,7 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
if (is_array($aNewIssues) && count($aNewIssues) > 0)
{
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
@@ -4738,7 +4716,7 @@ EOF
$bResult = (count($aErrors) == 0);
if ($bResult)
{
[$bResult, $aErrors] = $oObj->CheckToWrite();
list($bResult, $aErrors) = $oObj->CheckToWrite();
}
if ($bPreview)
{
@@ -4751,24 +4729,15 @@ EOF
$sCSSClass = $bResult ? HILIGHT_CLASS_NONE : HILIGHT_CLASS_CRITICAL;
$sChecked = $bResult ? 'checked' : '';
$sDisabled = $bResult ? '' : 'disabled';
$aErrorsToDisplay = array_map(function($sError) {
return utils::HtmlEntities($sError);
}, $aErrors);
$aRows[] = array(
'form::select' => "<input type=\"checkbox\" class=\"selectList\" $sChecked $sDisabled\"></input>",
'object' => $oObj->GetHyperlink(),
'status' => $sStatus,
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrors)).'</p>',
'@class' => $sCSSClass,
);
if ($bResult && (!$bPreview))
{
// doing the check will load multiple times same objects :/
// but it shouldn't cost too much on execution time
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷‍♂️
$oObj->CheckChangedExtKeysValues();
$oObj->DBUpdate();
}
}

View File

@@ -29,13 +29,13 @@ require_once(APPROOT."/application/webpage.class.inc.php");
class CSVPage extends WebPage
{
function __construct($s_title) {
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_http_headers();
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");
//$this->add_header("Content-Transfer-Encoding: binary");
}
}
public function output()
{

View File

@@ -529,7 +529,10 @@ EOF
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
if (!array_key_exists('dashboard_div_id', $aExtraParams))
{
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
@@ -849,29 +852,28 @@ class RuntimeDashboard extends Dashboard
{
$bCustomized = false;
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
if (false === $sDashboardFileSanitized) {
throw new SecurityException('Invalid dashboard file !');
}
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
{
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0) {
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
if ($sDashboardDefinition !== false)
@@ -879,7 +881,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
$oDashboard->SetDefinitionFile($sDashboardFile);
}
else
{
@@ -999,7 +1001,7 @@ EOF
$sSelectorHtml .= '</div>';
$sSelectorHtml = addslashes($sSelectorHtml);
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = json_encode($this->GetReloadURL());
$sReloadURL = $this->GetReloadURL();
$oPage->add_ready_script(
<<<EOF
@@ -1056,7 +1058,7 @@ EOF
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-pencil-alt\"></i><ul>";
$aActions = array();
$sFile = addslashes($this->sDefinitionFile);
$sJSExtraParams = json_encode($aExtraParams);
@@ -1088,7 +1090,6 @@ EOF
EOF
);
$sReloadURL = json_encode($this->GetReloadURL());
$oPage->add_script(
<<<EOF
function EditDashboard(sId, sDashboardFile, aExtraParams)
@@ -1186,19 +1187,19 @@ EOF
$oPage->add('</div>');
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = json_encode($this->sId);
$sLayoutClass = json_encode($this->sLayoutClass);
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = json_encode($this->sTitle);
$sFile = json_encode($this->GetDefinitionFile());
$sTitle = addslashes($this->sTitle);
$sFile = addslashes($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = json_encode($this->GetReloadURL());
$sReloadURL = $this->GetReloadURL();
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
@@ -1248,15 +1249,15 @@ $('#dashboard_editor').dialog({
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: $sId,
layout_class: $sLayoutClass,
title: $sTitle,
dashboard_id: '$sId',
layout_class: '$sLayoutClass',
title: '$sTitle',
auto_reload: $sAutoReload,
auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl',
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl',
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
@@ -1476,29 +1477,6 @@ JS
return $this->sDefinitionFile;
}
/**
* @param string $sDashboardFileRelative can also be an absolute path (compatibility with old URL)
*
* @return string full path to the Dashboard file
* @throws \SecurityException if path isn't under approot
* @uses utils::RealPath()
* @since 2.7.8 3.0.3 3.1.0 N°4449 remove FPD
*/
public static function GetDashboardFileFromRelativePath($sDashboardFileRelative)
{
if (utils::RealPath($sDashboardFileRelative, APPROOT)) {
// compatibility with old URL containing absolute path !
return $sDashboardFileRelative;
}
$sDashboardFile = APPROOT.$sDashboardFileRelative;
if (false === utils::RealPath($sDashboardFile, APPROOT)) {
throw new SecurityException('Invalid dashboard file !');
}
return $sDashboardFile;
}
/**
* @param string $sDefinitionFile
*/

View File

@@ -255,7 +255,7 @@ abstract class Dashlet
catch(OqlException $e)
{
$oPage->add('<div class="dashlet-content">');
$oPage->p(utils::HtmlEntities($e->GetUserFriendlyDescription()));
$oPage->p($e->GetUserFriendlyDescription());
$oPage->add('</div>');
}
catch(Exception $e)
@@ -459,21 +459,17 @@ EOF
$sAttType = $aTargetAttCodes[$sTargetAttCode];
$sExtFieldAttCode = $sTargetAttCode;
}
$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;
}
if (is_a($sAttType, 'AttributeLinkedSet', true))
{
continue;
}
if (is_a($sAttType, 'AttributeFriendlyName', true))
{
continue;
}
if (is_a($sAttType, 'AttributeOneWayPassword', true))
{
continue;
}
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<portals>
<portal id="backoffice" _delta="define">
<url>pages/UI.php</url>

View File

@@ -372,7 +372,7 @@ EOF;
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(

View File

@@ -324,10 +324,8 @@ class DisplayBlock
* @throws DictExceptionMissingString
* @throws MySQLException
* @throws Exception
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 add type hinting to $aExtraParams
*/
public function GetRenderContent(WebPage $oPage, array $aExtraParams, $sId)
public function GetRenderContent(WebPage $oPage, $aExtraParams, $sId)
{
$sHtml = '';
// Add the extra params into the filter if they make sense for such a filter
@@ -448,21 +446,8 @@ class DisplayBlock
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
}
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
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) {
switch($this->m_sStyle)
{
case 'count':
if (isset($aExtraParams['group_by']))
{
@@ -853,7 +838,7 @@ class DisplayBlock
foreach($aStates as $sStateValue)
{
$sHtmlValue=$aGroupBy['group1']->MakeValueLabel($this->m_oFilter, $sStateValue, $sStateValue);
$aStateLabels[$sStateValue] = strip_tags($sHtmlValue);
$aStateLabels[$sStateValue] = html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8');
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
? $aCountsQueryResults[$sStateValue]
@@ -1009,7 +994,6 @@ EOF
$iTotalCount = 0;
$aValues = array();
$aURLs = array();
foreach ($aRes as $iRow => $aRow)
{
$sValue = $aRow['grouped_by_1'];
@@ -1017,8 +1001,7 @@ EOF
$aGroupBy[(int)$iRow] = (int) $aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow[$sFctVar]);
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
@@ -1032,23 +1015,16 @@ EOF
{
case 'bars':
$aNames = array();
$iMaxNbCharsInLabel = 0;
foreach($aValues as $idx => $aValue)
{
$aNames[$idx] = $aValue['label'];
if ($iMaxNbCharsInLabel < mb_strlen($aValue['label'])) {
$iMaxNbCharsInLabel = mb_strlen($aValue['label']);
}
}
$sJSNames = json_encode($aNames);
$sJson = json_encode($aValues);
$oPage->add_ready_script(
<<<EOF
var iChartDefaultHeight = 200,
iChartLegendHeight = 6 * $iMaxNbCharsInLabel,
iChartTotalHeight = iChartDefaultHeight + iChartLegendHeight;
$('#my_chart_$sId').height(iChartTotalHeight+ 'px');
var chart = c3.generate({
bindto: d3.select('#my_chart_$sId'),
data: {
@@ -1116,19 +1092,8 @@ EOF
}
$sJSColumns = json_encode($aColumns);
$sJSNames = json_encode($aNames);
$iNbLinesToAddForName = 0;
if (count($aNames) > 50) {
// Calculation of the number of legends line add to the height of the graph to have a maximum of 5 legend columns
$iNbLinesIncludedInChartHeight = 10;
$iNbLinesToAddForName = ceil(count($aNames) / 5) - $iNbLinesIncludedInChartHeight;
}
$oPage->add_ready_script(
<<<EOF
// Calculate height of graph : 200px (minimum height for the chart) + 20*iNbLinesToAddForName for the legend
var iChartDefaultHeight = 200,
iChartLegendHeight = 20 * $iNbLinesToAddForName,
iChartTotalHeight = (iChartDefaultHeight + iChartLegendHeight);
$('#my_chart_$sId').height(iChartTotalHeight + 'px');
var chart = c3.generate({
bindto: d3.select('#my_chart_$sId'),
data: {
@@ -1440,25 +1405,8 @@ class HistoryBlock extends DisplayBlock
$this->iLimitStart = $iStart;
$this->iLimitCount = $iCount;
}
/**
* @param \WebPage $oPage
* @param array $aExtraParams
* @param string $sId
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aExtraParams and add type hinting for PHP 8.0 compatibility
* (var is unused, and all calls were already made using a default value)
*/
public function GetRenderContent(WebPage $oPage, array $aExtraParams, $sId)
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
$sHtml = '';
$bTruncated = false;
@@ -1597,10 +1545,8 @@ class MenuBlock extends DisplayBlock
* @throws \Exception
* @throws \MissingQueryArgument
* @throws \MySQLException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value and add type hinting on $aExtraParams for PHP 8.0 compatibility
*/
public function GetRenderContent(WebPage $oPage, array $aExtraParams, $sId)
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
if ($this->m_sStyle == 'popup') // popup is a synonym of 'list' for backward compatibility
{
@@ -1935,13 +1881,11 @@ class MenuBlock extends DisplayBlock
{
if (count($aFavoriteActions) > 0)
{
$sActionsMenuLabel = Dict::S('UI:Menu:OtherActions');
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li aria-label=\"{$sActionsMenuLabel}\">{$sActionsMenuLabel}<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
}
else
{
$sActionsMenuLabel = Dict::S('UI:Menu:Actions');
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li aria-label=\"{$sActionsMenuLabel}\">{$sActionsMenuLabel}<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
}
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);

View File

@@ -1223,7 +1223,7 @@ class DesignerComboField extends DesignerFormField
$sChecked = $this->defaultValue ? 'checked' : '';
$sMandatory = $this->bMandatory ? 'true' : 'false';
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
if ($this->IsSorted() )
if ($this->IsSorted())
{
asort($this->aAllowedValues);
}
@@ -1271,14 +1271,18 @@ class DesignerComboField extends DesignerFormField
$sHtml .= "<option value=\"\">".$this->sNullLabel."</option>";
}
}
foreach ($this->aAllowedValues as $sKey => $sDisplayValue) {
if ($this->bMultipleSelection) {
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
{
if ($this->bMultipleSelection)
{
$sSelected = in_array($sKey, $this->defaultValue) ? 'selected' : '';
} else {
}
else
{
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
}
// Quick and dirty: display the menu parents as a tree
$sHtmlValue = str_replace(' ', '&nbsp;', $sDisplayValue);
$sHtmlValue = str_replace(' ', '&nbsp;', htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8'));
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
}
$sHtml .= "</select>";

View File

@@ -60,7 +60,8 @@ 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;
}
@@ -70,22 +71,18 @@ 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->no_cache();
$this->add_http_headers();
$this->add_header("Cache-control: no-cache");
$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");
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
$this->add_linked_stylesheet("../css/magnific-popup.css");
$this->add_linked_stylesheet("../css/c3.min.css");
$this->add_linked_stylesheet("../css/font-awesome/css/all.min.css");
$this->add_linked_stylesheet("../css/font-awesome/css/v4-shims.min.css");
$this->add_linked_stylesheet("../js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css");
$this->add_linked_script('../js/jquery.layout.min.js');
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
$this->add_linked_script("../js/jquery.treeview.js");
$this->add_linked_script("../js/jquery.autocomplete.js");
$this->add_linked_script("../js/date.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js");
@@ -354,15 +351,14 @@ JS
);
// Highlight code content created with CKEditor
// Note: We check for the <code> tag inside the <pre> tag to only target code from CKEditor, otherwise we might highlight some others things. See N°3810
$this->add_ready_script(
<<<JS
// Highlight code content for HTML AttributeText
$("[data-attribute-type='AttributeText'] .HTML pre > code").parent().each(function(i, block) {
$("[data-attribute-type='AttributeText'] .HTML pre").each(function(i, block) {
hljs.highlightBlock(block);
});
// Highlight code content for CaseLogs
$("[data-attribute-type='AttributeCaseLog'] .caselog_entry_html pre > code").parent().each(function(i, block) {
$("[data-attribute-type='AttributeCaseLog'] .caselog_entry_html pre").each(function(i, block) {
hljs.highlightBlock(block);
});
JS
@@ -624,7 +620,7 @@ JS
ShowDebug();
$('#logOffBtn>ul').popupmenu();
$('body').on('click', '.caselog_header', function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
$(document).ajaxSend(function(event, jqxhr, options) {
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
@@ -1219,7 +1215,7 @@ EOF;
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
}
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li aria-label=\"" . Dict::S("UI:PowerMenu") . "\"><i class=\"top-right-icon icon-additional-arrow fas fa-power-off\"></i><ul>";
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-power-off\"></i><ul>";
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
$aActions = array();

View File

@@ -51,7 +51,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
if ($_SESSION['login_mode'] == 'basic')
{
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
@@ -59,17 +59,15 @@ class LoginBasic extends AbstractLoginFSMExtension
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// Save the checked user
$_SESSION['auth_user'] = $sAuthUser;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
if ($_SESSION['login_mode'] == 'basic')
{
$sAuthUser = $_SESSION['auth_user'];
list($sAuthUser) = $this->GetAuthUserAndPassword();
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -77,13 +75,8 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
if ($_SESSION['login_mode'] == 'basic')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -91,7 +84,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
if ($_SESSION['login_mode'] == 'basic')
{
$_SESSION['can_logoff'] = true;
return LoginWebPage::CheckLoggedUser($iErrorCode);

View File

@@ -77,7 +77,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
if ($iOnExit == LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
@@ -93,12 +93,6 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
if (!isset($_SESSION['login_mode']))
{
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// If no plugin validated the user, exit
self::ResetLoginSession();
exit();
@@ -117,11 +111,6 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnConnected(&$iErrorCode)
{
unset($_SESSION['login_temp_auth_user']);
if (is_null(UserRights::GetUserObject())){
//N°7085 avoid infinite loop
IssueLog::Error("No user logged in. exit");
exit(-1);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
@@ -137,4 +126,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
}
}
}
}
}

View File

@@ -35,7 +35,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
if ($_SESSION['login_mode'] == 'external')
{
$sAuthUser = $this->GetAuthUser();
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
@@ -43,17 +43,15 @@ class LoginExternal extends AbstractLoginFSMExtension
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// Save the checked user
$_SESSION['auth_user'] = $sAuthUser;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
if ($_SESSION['login_mode'] == 'external')
{
$sAuthUser = $_SESSION['auth_user'];
$sAuthUser = $this->GetAuthUser();
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -61,7 +59,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
if ($_SESSION['login_mode'] == 'external')
{
$_SESSION['can_logoff'] = false;
return LoginWebPage::CheckLoggedUser($iErrorCode);
@@ -71,13 +69,8 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
if ($_SESSION['login_mode'] == 'external')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -43,10 +43,6 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
exit;
}
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
// No credentials yet, display the form
$oPage = LoginWebPage::NewLoginWebPage();
$oPage->DisplayLoginForm($this->bForceFormOnError);
@@ -66,7 +62,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCheckCredentials(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
if ($_SESSION['login_mode'] == 'form')
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
@@ -75,8 +71,6 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// Save the checked user
$_SESSION['auth_user'] = $sAuthUser;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
@@ -86,9 +80,17 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCredentialsOK(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
if ($_SESSION['login_mode'] == 'form')
{
$sAuthUser = $_SESSION['auth_user'];
if (isset($_SESSION['auth_user']))
{
// If FSM reenter this state (example 2FA) then the auth_user is not resubmitted
$sAuthUser = $_SESSION['auth_user'];
}
else
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
}
// Store 'auth_user' in session for further use
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
@@ -100,7 +102,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnError(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
if ($_SESSION['login_mode'] == 'form')
{
$this->bForceFormOnError = true;
}
@@ -112,7 +114,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnConnected(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
if ($_SESSION['login_mode'] == 'form')
{
$_SESSION['can_logoff'] = true;
return LoginWebPage::CheckLoggedUser($iErrorCode);

View File

@@ -40,7 +40,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
if ($_SESSION['login_mode'] == 'url')
{
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
}
@@ -49,7 +49,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
if ($_SESSION['login_mode'] == 'url')
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
@@ -58,17 +58,15 @@ class LoginURL extends AbstractLoginFSMExtension
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// Save the checked user
$_SESSION['auth_user'] = $sAuthUser;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
if ($_SESSION['login_mode'] == 'url')
{
$sAuthUser = $_SESSION['auth_user'];
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -76,7 +74,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
if ($_SESSION['login_mode'] == 'url')
{
$this->bErrorOccurred = true;
}

View File

@@ -32,7 +32,7 @@ class LoginWebPage extends NiceWebPage
{
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN = 2; // Non interactive mode (ajax, rest, ...)
const EXIT_RETURN = 2;
const EXIT_CODE_OK = 0;
const EXIT_CODE_MISSINGLOGIN = 1;
@@ -78,21 +78,20 @@ class LoginWebPage extends NiceWebPage
public function __construct($sTitle = null)
{
if ($sTitle === null) {
$sTitle = Dict::S('UI:Login:Title');
}
if($sTitle === null)
{
$sTitle = Dict::S('UI:Login:Title');
}
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->no_cache();
$this->add_http_headers();
$this->add_header("Cache-control: no-cache");
}
public function SetStyleSheet()
{
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/login.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/v4-shims.min.css');
}
public static function SetLoginFailedMessage($sMessage)
@@ -100,6 +99,44 @@ class LoginWebPage extends NiceWebPage
self::$m_sLoginFailedMessage = $sMessage;
}
/**
* @param $oUser
* @param array $aProfiles
*
* @return array
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
public static function SynchroniseProfiles(&$oUser, array $aProfiles, $sOrigin)
{
$oProfilesSet = $oUser->Get(profile_list);
//delete old profiles
$aExistingProfiles = [];
while ($oProfile = $oProfilesSet->Fetch())
{
array_push($aExistingProfiles, $oProfile->Get('profileid'));
$iArrayKey = array_search($oProfile->Get('profileid'), $aProfiles);
if (!$iArrayKey)
{
$oProfilesSet->RemoveItem($oProfile->Get('profileid'));
}
else
{
unset($aProfiles[$iArrayKey]);
}
}
//add profiles not already linked with user
foreach ($aProfiles as $iProfileId)
{
$oLink = new URP_UserProfile();
$oLink->Set('profileid', $iProfileId);
$oLink->Set('reason', $sOrigin);
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin)));
}
$oUser->Set('profile_list', $oProfilesSet);
}
public function DisplayLoginHeader($bMainAppLogo = false)
{
$sLogo = 'itop-logo-external.png';
@@ -208,7 +245,7 @@ class LoginWebPage extends NiceWebPage
}
// This token allows the user to change the password without knowing the previous one
$sToken = bin2hex(random_bytes(32));
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
$oUser->Set('reset_pwd_token', $sToken);
CMDBObject::SetTrackInfo('Reset password');
$oUser->AllowWrite(true);
@@ -352,20 +389,14 @@ class LoginWebPage extends NiceWebPage
$this->output();
}
public static function ResetSession($bFullCleanup = false)
public static function ResetSession()
{
if ($bFullCleanup) {
// Unset all of the session variables.
foreach (array_keys($_SESSION) as $sKey) {
unset($_SESSION[$sKey]);
}
} else {
unset($_SESSION['auth_user']);
unset($_SESSION['login_state']);
unset($_SESSION['can_logoff']);
unset($_SESSION['archive_mode']);
unset($_SESSION['impersonate_user']);
}
// Unset all of the session variables.
unset($_SESSION['auth_user']);
unset($_SESSION['login_state']);
unset($_SESSION['can_logoff']);
unset($_SESSION['archive_mode']);
unset($_SESSION['impersonate_user']);
UserRights::_ResetSessionCache();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
@@ -691,7 +722,7 @@ class LoginWebPage extends NiceWebPage
public static function HTTPReload()
{
$sOriginURL = utils::GetCurrentAbsoluteUrl();
$sOriginURL = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
{
// If the found URL does not start with the configured AppRoot URL
@@ -892,20 +923,12 @@ class LoginWebPage extends NiceWebPage
}
// Now synchronize the profiles
$oProfilesSet = DBObjectSet::FromScratch('URP_UserProfile');
$sOrigin = 'External User provisioning';
if (isset($_SESSION['login_mode']))
{
$sOrigin .= " ({$_SESSION['login_mode']})";
}
foreach ($aProfiles as $iProfileId)
{
$oLink = new URP_UserProfile();
$oLink->Set('profileid', $iProfileId);
$oLink->Set('reason', $sOrigin);
$oProfilesSet->AddObject($oLink);
}
$oUser->Set('profile_list', $oProfilesSet);
$aExistingProfiles = self::SynchroniseProfiles($oUser, $aProfiles, $sOrigin);
if ($oUser->IsModified())
{
$oUser->DBWrite();
@@ -937,7 +960,7 @@ class LoginWebPage extends NiceWebPage
}
else
{
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
}
@@ -993,7 +1016,7 @@ class LoginWebPage extends NiceWebPage
{
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_MUSTBEADMIN;
}
@@ -1009,7 +1032,7 @@ class LoginWebPage extends NiceWebPage
}
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
}
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return $iRet;
}

View File

@@ -35,25 +35,19 @@ register_shutdown_function(function()
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
$sMessage = $err['message'];
if (strpos($sMessage, 'MySQLException') !== false) {
$iStackTracePos = strpos($sMessage, 'Stack trace:');
if ($iStackTracePos !== false) {
$sMessage = substr($sMessage, 0, $iStackTracePos);
}
}
// Log additional info but message from $err (since 2.7.6 N°4174)
$aErrToLog = $err;
unset($aErrToLog['message']);
IssueLog::error($sMessage, null, $aErrToLog);
if (strpos($err['message'], 'Allowed memory size of') !== false) {
IssueLog::error($err['message']);
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
$sLimit = ini_get('memory_limit');
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
} elseif (strpos($err['message'], 'Maximum execution time') !== false) {
}
elseif (strpos($err['message'], 'Maximum execution time') !== false)
{
$sLimit = ini_get('max_execution_time');
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
} else {
}
else
{
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
}
}
@@ -91,10 +85,4 @@ else
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
}
catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);

View File

@@ -57,10 +57,6 @@ class ThemeHandler
{
try
{
if (is_null(MetaModel::GetConfig()))
{
throw new CoreException('no config');
}
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
}
catch(CoreException $oCompileException)

View File

@@ -26,6 +26,8 @@
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class privUITransaction
{
/**
@@ -97,10 +99,9 @@ class privUITransaction
}
/**
* The original mechanism for storing transaction information as an array in the $_SESSION variable
* The original (and by default) mechanism for storing transaction information
* as an array in the $_SESSION variable
*
* Warning, since 2.6.0 the session is regenerated on each login (see PR #20) !
* Also, we saw some problems when using memcached as the PHP session implementation (see N°1835)
*/
class privUITransactionSession
{
@@ -193,35 +194,9 @@ class privUITransactionSession
*/
class privUITransactionFile
{
/** @var int Value to use when no user logged */
const UNAUTHENTICATED_USER_ID = -666;
/**
* @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
*/
private static function GetCurrentUserId()
{
$iCurrentUserId = UserRights::GetConnectedUserId();
if ('' === $iCurrentUserId) {
$iCurrentUserId = static::UNAUTHENTICATED_USER_ID;
}
return $iCurrentUserId;
}
/**
* Create a new transaction id, store it in the session and return its id
*
* @param void
*
* @return int The new transaction identifier
*
* @throws \SecurityException
* @throws \Exception
*
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
*/
public static function GetNewTransactionId()
{
@@ -238,115 +213,88 @@ class privUITransactionFile
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
$iCurrentUserId = static::GetCurrentUserId();
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
self::Info('GetNewTransactionId: Created transaction: '.$sTransactionIdFileName);
return $sTransactionIdFileName;
return (string)$id;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
*
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
*
* @return bool True if the transaction is valid, false otherwise
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
// Constraint the transaction file within APPROOT.'data/transactions'
$sTransactionDir = realpath(APPROOT.'data/transactions');
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
{
return false;
}
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
$bResult = file_exists($sFilepath);
if (false === $bResult) {
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
$iCurrentUserId = static::GetCurrentUserId();
$sTransactionIdUserId = file_get_contents($sFilepath);
if ($iCurrentUserId != $sTransactionIdUserId) {
self::Info("IsTransactionValid: Transaction '$id' not existing for current user. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
if ($bRemoveTransaction)
if ($bResult)
{
$bResult = @unlink($sFilepath);
if (!$bResult)
if ($bRemoveTransaction)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
$bResult = @unlink($sFilepath);
if (!$bResult)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
}
else
{
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
return $bResult;
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
* @return bool true if the token can be removed
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
* @return void
*/
public static function RemoveTransaction($id)
{
/** @noinspection PhpRedundantOptionalArgumentInspection */
$bResult = static::IsTransactionValid($id, true);
if (false === $bResult) {
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
.implode("\n", self::GetPendingTransactions()));
return false;
$bSuccess = true;
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
if(!file_exists($sFilepath))
{
$bSuccess = false;
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
return true;
$bSuccess = @unlink($sFilepath);
if (!$bSuccess)
{
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
}
else
{
self::Info('RemoveTransaction: OK '.$id);
}
return $bSuccess;
}
/**
* 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($sTransactionDir = null)
protected static function CleanupOldTransactions()
{
$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();
$iLimit = time() - 24*3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : APPROOT.'data/transactions/*';
$aTransactions = glob($sPattern);
clearstatcache();
$aTransactions = glob(APPROOT.'data/transactions/*-*');
foreach($aTransactions as $sFileName)
{
if (filemtime($sFileName) < $iLimit)
@@ -400,35 +348,22 @@ class privUITransactionFile
{
self::Write('Error | '.$sText);
}
protected static function IsLogEnabled() {
$oConfig = MetaModel::GetConfig();
if (is_null($oConfig)) {
return false;
}
$bLogTransactions = $oConfig->Get('log_transactions');
if (true === $bLogTransactions) {
return true;
}
return false;
}
protected static function Write($sText)
{
if (false === static::IsLogEnabled()) {
return;
}
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
if ($bLogEnabled)
{
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
if ($hLogFile !== false) {
if ($hLogFile !== false)
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
}

View File

@@ -5,6 +5,7 @@ namespace Combodo\iTop;
use AttributeDateTime;
use Dict;
use Exception;
use MetaModel;
use Twig_Environment;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
@@ -114,6 +115,14 @@ class TwigExtension
return utils::IsDevelopmentEnvironment();
}));
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_config_parameter', function($sParamName)
{
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
// Function to get the URL of a static page in a module
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)

View File

@@ -71,11 +71,7 @@ class UIExtKeyWidget
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
/**
* @since 2.7.7 3.0.1 3.1.0 N°3129 Add default value for $aArgs for PHP 8.0 compat
*/
public static function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs = [], $bSearchMode = false)
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
@@ -276,7 +272,7 @@ EOF
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
// the input for the auto-complete
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<input id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span>";
// another hidden input to store & pass the object's Id
@@ -285,18 +281,53 @@ EOF
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
<<<JS
$('#label_$this->iId').autocomplete({
source: function( request, response ) {
$.post( {
url: GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
dataType: "json",
data: {
q:request.term,
operation:'ac_extkey',
sTargetClass:'{$this->sTargetClass}',
sFilter:'$sFilter',
bSearchMode:$JSSearchMode,
sOutputFormat:'json',
json: function() { return $sWizHelperJSON; }
},
success: function( data ) {
response( data );
}
} );
},
autoFocus: true,
minLength:{$iMinChars},
select: function( event, ui ) {
$('#$this->iId').val( ui.item.value );
$('#label_$this->iId').val( ui.item.label );
$('#$this->iId').trigger('validate');
$('#$this->iId').trigger('extkeychange');
$('#$this->iId').trigger('change');
return false;
}
})
.autocomplete( "instance" )._renderItem = function( ul, item ) {
var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1");
var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
if (item.obsolete == 'yes'){
val = val + ' <b>old</b>';
}
return $( "<li>" )
.append( val )
.appendTo( ul );
};
if ($('#ac_dlg_{$this->iId}').length == 0)
{
$('body').append('<div id="ac_dlg_{$this->iId}"></div>');
}
EOF
JS
);
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
@@ -376,10 +407,10 @@ EOF
$sHTML .= "</form>\n";
$sHTML .= '</div></div>';
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
$sDialogTitle = addslashes($sTitle);
$oPage->add_ready_script(
<<<EOF
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitleSanitized', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
EOF
@@ -430,10 +461,8 @@ EOF
*
* @throws CoreException
* @throws OQLException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $oObj for PHP 8.0 compatibility
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null)
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null)
{
if (is_null($sFilter))
{
@@ -447,10 +476,12 @@ EOF
$iMax = 150;
$oValuesSet->SetLimit($iMax);
$oValuesSet->SetSort(false);
$aOrder = array('friendlyname'=>true);
$oValuesSet->SetOrderBy($aOrder);
$oValuesSet->SetSort(true);
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oValuesSet->SetLimit($iMax);
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
asort($aValuesContains);
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
$aValues = array();
foreach($aValuesContains as $sKey => $sFriendlyName)
{
@@ -459,6 +490,24 @@ EOF
$aValues[$sKey] = $sFriendlyName;
}
}
if (sizeof($aValuesContains) < $iMax)
{
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains,
'contains');
//asort($aValuesContains);
$iSize=sizeof($aValuesContains);
foreach($aValuesContains as $sKey => $sFriendlyName)
{
if (!isset($aValues[$sKey]))
{
$aValues[$sKey] = $sFriendlyName;
if (++$iSize >= $iMax)
{
break;
}
}
}
}
switch($sOutputFormat)
{

View File

@@ -85,15 +85,9 @@ class UILinksWidgetDirect
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (handling wrong values at method start)
*/
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
if (empty($aArgs)) {
$aArgs = [];
}
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
switch($oLinksetDef->GetEditMode())
{
@@ -143,10 +137,8 @@ class UILinksWidgetDirect
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param bool $bDisplayMenu
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, always called with default value)
*/
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $bDisplayMenu)
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sTargetClass = $oLinksetDef->GetLinkedClass();
@@ -247,10 +239,8 @@ class UILinksWidgetDirect
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param array $aButtons
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, caller already handles it)
*/
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();

File diff suppressed because it is too large Load Diff

View File

@@ -482,75 +482,13 @@ class WebPage implements Page
$this->a_headers[] = $s_header;
}
/**
* @param string|null $sXFrameOptionsHeaderValue passed to {@see add_xframe_options}
*
* @return void
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation, replace {@see add_xframe_options} consumers call
*/
public function add_http_headers($sXFrameOptionsHeaderValue = null)
{
$this->add_xframe_options($sXFrameOptionsHeaderValue);
$this->add_xcontent_type_options();
}
/**
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the `security_header_xframe` config parameter value.
*
* @since 2.7.3 3.0.0 N°3416
* @uses \utils::GetConfig()
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options HTTP header MDN documentation
*/
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);
}
/**
* Warning : this header will trigger the Cross-Origin Read Blocking (CORB) protection for some mime types (HTML, XML except SVG, JSON, text/plain)
* In consequence some children pages will override this method.
*
* Sending header can be disabled globally using the `security.enable_header_xcontent_type_options` optional config parameter.
*
* @return void
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options HTTP header MDN documentation
* @link https://chromium.googlesource.com/chromium/src/+/master/services/network/cross_origin_read_blocking_explainer.md#determining-whether-a-response-is-corb_protected "Determining whether a response is CORB-protected"
*/
public function add_xcontent_type_options()
{
try {
$oConfig = utils::GetConfig();
} catch (ConfigException|CoreException $e) {
$oConfig = null;
}
if (is_null($oConfig)) {
$bSendXContentTypeOptionsHttpHeader = true;
} else {
$bSendXContentTypeOptionsHttpHeader = $oConfig->Get('security.enable_header_xcontent_type_options');
}
if ($bSendXContentTypeOptionsHttpHeader === false) {
return;
}
$this->add_header('X-Content-Type-Options: nosniff');
}
/**
* Add needed headers to the page so that it will no be cached
*/
public function no_cache()
{
$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("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
$this->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
}
/**
@@ -1608,12 +1546,6 @@ class TabManager
{
unset($aTabs['tabs'][$sTabCode]);
}
// N°3320: Do not display empty tabs
if (empty($aTabData['html']) && empty($aTabData['url']))
{
unset($aTabs['tabs'][$sTabCode]);
}
}
// Render tabs

View File

@@ -43,20 +43,9 @@ class XMLPage extends WebPage
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_http_headers();
$this->add_header("Cache-control: no-cache");
$this->add_header("Content-location: export.xml");
}
/**
* Disabling sending the header so that resource won't be blocked by CORB. See parent method documentation.
* @return void
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
*/
public function add_xcontent_type_options()
{
// Nothing to do !
}
}
public function output()
{

View File

@@ -3,18 +3,4 @@
define('APPROOT', dirname(__FILE__).'/');
define('APPCONF', APPROOT.'conf/');
/**
* Constant containing the iTop core version, whatever application was built
*
* Note that in iTop 3.0.0 we used {@see ITOP_DESIGN_LATEST_VERSION} to get core version.
* When releasing, both constants should be updated : see `.make/release/update-versions.php` for that !
*
* @since 2.7.7 3.0.1 3.1.0 N°4714 constant creation
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '2.7.12');
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -22,8 +22,14 @@ define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
$fItopStarted = microtime(true);
$iItopInitialMemory = memory_get_usage(true);
if (function_exists('microtime'))
{
$fItopStarted = microtime(true);
}
else
{
$fItopStarted = 1000 * time();
}
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
{
@@ -48,7 +54,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
http_response_code(503);
// Display message depending on the request
include(APPROOT.'application/maintenancemsg.php');
$sSAPIName = strtoupper(trim(PHP_SAPI));
$sSAPIName = strtoupper(trim(php_sapi_name()));
switch (true)
{

View File

@@ -1,10 +1,8 @@
{
"name": "combodo/itop",
"description": "IT Operations Portal",
"type": "project",
"license": "AGPL-3.0-only",
"license": "AGPLv3",
"require": {
"php": ">=7.1.3",
"php": ">=5.6.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -12,29 +10,22 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"combodo/tcpdf": "~6.4.4",
"firebase/php-jwt": "~6.4.0",
"guzzlehttp/guzzle": "^6.5.8",
"guzzlehttp/psr7": "~1.9.1",
"laminas/laminas-mail": "^2.11",
"laminas/laminas-servicemanager": "^3.5",
"league/oauth2-google": "^3.0",
"nikic/php-parser": "~4.13.2",
"pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "~3.1.0",
"combodo/tcpdf": "6.3.5",
"nikic/php-parser": "^3.1",
"pear/archive_tar": "1.4.9",
"pelago/emogrifier": "2.1.0",
"scssphp/scssphp": "1.0.6",
"swiftmailer/swiftmailer": "~6.3.0",
"symfony/console": "~3.4.47",
"symfony/dotenv": "~3.4.47",
"symfony/framework-bundle": "~3.4.47",
"symfony/twig-bundle": "~3.4.47",
"symfony/yaml": "~3.4.47",
"thenetworg/oauth2-azure": "^2.0",
"twig/twig": "~1.42.5"
"swiftmailer/swiftmailer": "5.4.12",
"symfony/console": "3.4.*",
"symfony/dotenv": "3.4.*",
"symfony/framework-bundle": "3.4.*",
"symfony/polyfill-php70": "1.*",
"symfony/twig-bundle": "3.4.*",
"symfony/yaml": "3.4.*"
},
"require-dev": {
"symfony/stopwatch": "~3.4.47",
"symfony/web-profiler-bundle": "~3.4.47"
"symfony/stopwatch": "3.4.*",
"symfony/web-profiler-bundle": "3.4.*"
},
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
@@ -46,7 +37,7 @@
},
"config": {
"platform": {
"php": "7.1.3"
"php": "5.6.0"
},
"vendor-dir": "lib",
"preferred-install": {
@@ -60,10 +51,7 @@
"core",
"application",
"sources/application",
"sources/Composer",
"sources/Controller",
"sources/Service",
"sources/Core"
"sources/Composer"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",

2840
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -67,7 +67,8 @@ class MyHelpers
// format sss.mmmuuupppnnn
public static function getmicrotime()
{
return microtime(true);
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
/*
@@ -419,7 +420,6 @@ class MyHelpers
//}
return $sOutput;
}
}
/**
@@ -524,3 +524,5 @@ class Str
return (strtolower($sString) == $sString);
}
}
?>

View File

@@ -35,6 +35,10 @@ require_once(APPROOT.'/core/email.class.inc.php');
*/
abstract class Action extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -57,15 +61,32 @@ abstract class Action extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status'));
// Search criteria
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// - Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status'));
// - Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name'));
}
/**
* Encapsulate the execution of the action and handle failure & logging
*
* @param \Trigger $oTrigger
* @param array $aContextArgs
*
* @return mixed
*/
abstract public function DoExecute($oTrigger, $aContextArgs);
/**
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsActive()
{
switch($this->Get('status'))
@@ -79,6 +100,13 @@ abstract class Action extends cmdbAbstractObject
}
}
/**
* Return true if the current action status is set on "test"
*
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsBeingTested()
{
switch($this->Get('status'))
@@ -99,6 +127,10 @@ abstract class Action extends cmdbAbstractObject
*/
abstract class ActionNotification extends Action
{
/**
* @inheritDoc
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
@@ -117,11 +149,15 @@ abstract class ActionNotification extends Action
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status'));
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// - Criteria of the std search form
// MetaModel::Init_SetZListItems('standard_search', array('name'));
// - Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name'));
}
}
@@ -132,6 +168,9 @@ abstract class ActionNotification extends Action
*/
class ActionEmail extends ActionNotification
{
/**
* @inheritDoc
*/
public static function Init()
{
$aParams = array
@@ -161,11 +200,15 @@ class ActionEmail extends ActionNotification
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values"=>new ValueSetEnum('low,normal,high'), "sql"=>"importance", "default_value"=>'normal', "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject'));
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name','description', 'status', 'subject')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// - Criteria of the std search form
MetaModel::Init_SetZListItems('standard_search', array('name','description', 'status', 'subject'));
// - Criteria of the advanced search form
// MetaModel::Init_SetZListItems('advanced_search', array('name'));
}
// count the recipients found
@@ -175,11 +218,22 @@ class ActionEmail extends ActionNotification
// executed in the background, while making sure that any issue would be reported clearly
protected $m_aMailErrors; //array of strings explaining the issue
// returns a the list of emails as a string, or a detailed error description
/**
* Return a the list of emails as a string, or a detailed error description
*
* @param string $sRecipAttCode
* @param array $aArgs
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected function FindRecipients($sRecipAttCode, $aArgs)
{
$sOQL = $this->Get($sRecipAttCode);
if (strlen($sOQL) === 0) return '';
if (strlen($sOQL) == '') return '';
try
{
@@ -224,9 +278,7 @@ class ActionEmail extends ActionNotification
}
/**
* @param \Trigger $oTrigger
* @param array $aContextArgs
*
* @inheritDoc
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
@@ -306,6 +358,7 @@ class ActionEmail extends ActionNotification
*
* @return string
* @throws \CoreException
* @throws \Exception
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
@@ -314,58 +367,42 @@ class ActionEmail extends ActionNotification
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recicipients
$bRes = false; // until we do succeed in sending the email
// Determine recipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = sprintf('iTop_%s_%d_%F@%s.openitop.org',
get_class($oObj),
$oObj->GetKey(),
microtime(true /* get as float*/),
MetaModel::GetEnvironmentId()
);
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
$sReference = '<'.$sMessageId.'>';
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
}
if (!is_null($oLog)) {
catch(Exception $e)
{
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
throw $e;
}
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
if (!is_null($oLog))
{
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($sTo)) {
$oLog->Set('to', $sTo);
}
if (isset($sCC)) {
$oLog->Set('cc', $sCC);
}
if (isset($sBCC)) {
$oLog->Set('bcc', $sBCC);
}
if (isset($sFrom)) {
$oLog->Set('from', $sFrom);
}
if (isset($sSubject)) {
$oLog->Set('subject', $sSubject);
}
if (isset($sBody)) {
$oLog->Set('body', $sBody);
}
if (isset($sTo)) $oLog->Set('to', $sTo);
if (isset($sCC)) $oLog->Set('cc', $sCC);
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
if (isset($sFrom)) $oLog->Set('from', $sFrom);
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
if (isset($sBody)) $oLog->Set('body', $sBody);
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');

View File

@@ -1,42 +0,0 @@
<?php
/**
* Class ApcService
* @since 2.7.6 N°4125
*/
class ApcService {
public function __construct() {
}
/**
* @param string $function_name
* @return bool
* @see function_exists()
*/
public function function_exists($function_name) {
return function_exists($function_name);
}
/**
* @param string|array $key
* @return mixed
* @see apc_fetch()
*/
function apc_fetch($key)
{
return apc_fetch($key);
}
/**
* @param array|string $key
* @param $var
* @param int $ttl
* @return array|bool
* @see apc_store()
*/
function apc_store($key, $var = NULL, $ttl = 0)
{
return apc_store($key, $var, $ttl);
}
}
?>

View File

@@ -65,6 +65,10 @@ class ExecAsyncTask implements iBackgroundProcess
*/
abstract class AsyncTask extends DBObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
@@ -230,7 +234,7 @@ abstract class AsyncTask extends DBObject
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->SetTrim('last_error', $sErrorMessage);
$this->Set('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());
@@ -285,11 +289,13 @@ abstract class AsyncTask extends DBObject
/**
* Throws an exception (message and code)
*
* @return string
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
{
@@ -352,6 +358,11 @@ class AsyncSendEmail extends AsyncTask
$oNew->DBInsert();
}
/**
* @inheritDoc
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoProcess()
{
$sMessage = $this->Get('message');

View File

@@ -18,7 +18,6 @@
*/
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
@@ -103,14 +102,6 @@ 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.)
@@ -138,7 +129,7 @@ abstract class AttributeDefinition
protected $aCSSClasses;
public function GetType()
public function GetType()
{
return Dict::S('Core:'.get_class($this));
}
@@ -751,7 +742,7 @@ abstract class AttributeDefinition
*
* @return mixed a value out of suffix/value pairs, for SELECT result interpretation
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
return null;
}
@@ -2411,9 +2402,9 @@ class AttributeDBFieldVoid extends AttributeDefinition
return $aColumns;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
$value = $this->MakeRealValue($aCols[$sPrefix.''], $oHostObject);
return $value;
}
@@ -2695,11 +2686,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
return ($proposedValue == 0);
}
/**
* @inheritDoc
*
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -2712,6 +2698,7 @@ class AttributeObjectKey extends AttributeDBFieldVoid
}
if (MetaModel::IsValidObject($proposedValue))
{
/** @var \DBObject $proposedValue */
return $proposedValue->GetKey();
}
@@ -3773,9 +3760,9 @@ class AttributeFinalClass extends AttributeString
*
* @package iTopORM
*/
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
class AttributePassword extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
@@ -3849,9 +3836,9 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
*
* @package iTopORM
*/
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
class AttributeEncryptedString extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static $sKey = null; // Encryption key used for all encrypted fields
static $sLibrary = null; // Encryption library used for all encrypted fields
@@ -3926,7 +3913,7 @@ class AttributeEncryptedString extends AttributeString implements iAttributeNoGr
* @return string
* @throws \Exception
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$oSimpleCrypt = new SimpleCrypt(self::$sLibrary);
$sValue = $oSimpleCrypt->Decrypt(self::$sKey, $aCols[$sPrefix]);
@@ -4296,7 +4283,7 @@ class AttributeText extends AttributeString
*
* @return string
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$value = $aCols[$sPrefix.''];
if ($this->GetOptional('format', null) != null)
@@ -4590,7 +4577,7 @@ class AttributeCaseLog extends AttributeLongText
* @return \ormCaseLog
* @throws \MissingColumnException
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
if (!array_key_exists($sPrefix, $aCols))
{
@@ -5944,11 +5931,6 @@ class AttributeDateTime extends AttributeDBField
}
}
/**
* @inheritDoc
*
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -7396,18 +7378,18 @@ class AttributeExternalField extends AttributeDefinition
//public function GetSQLExpressions($sPrefix = '') {return array();}
// Here, we get the data...
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->FromSQLToValue($aCols, $sPrefix);
return $oExtAttDef->FromSQLToValue($aCols, $sPrefix, $oHostObject, $sHostAttCode);
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetAsHTML($value, null, $bLocalize);
return $oExtAttDef->GetAsHTML($value, $oHostObject, $bLocalize);
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
@@ -7467,12 +7449,6 @@ class AttributeExternalField extends AttributeDefinition
}
}
parent::MakeFormField($oObject, $oFormField);
if ($oFormField instanceof TextAreaField) {
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
$oFormField->SetFormat($oRemoteAttDef->GetFormat());
}
}
// Manually setting for remote ExternalKey, otherwise, the id would be displayed.
if ($oRemoteAttDef instanceof AttributeExternalKey)
@@ -7490,16 +7466,6 @@ class AttributeExternalField extends AttributeDefinition
{
return false;
}
public function GetFormat()
{
$oRemoteAttDef = $this->GetExtAttDef();
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
return $oRemoteAttDef->GetFormat();
}
return 'text';
}
}
@@ -7664,9 +7630,9 @@ class AttributeBlob extends AttributeDefinition
}
/**
* {@inheritDoc}
*
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
* Users can provide the document from an URL (including an URL on iTop itself)
* for CSV import. Administrators can even provide the path to a local file
* {@inheritDoc}
*
* @see AttributeDefinition::MakeRealValue()
*/
@@ -7679,6 +7645,10 @@ class AttributeBlob extends AttributeDefinition
if (is_object($proposedValue))
{
if (!$proposedValue instanceof ormDocument)
{
throw new Exception(__METHOD__.": unexpected class ".get_class($proposedValue));
}
$proposedValue = clone $proposedValue;
}
else
@@ -7695,6 +7665,7 @@ class AttributeBlob extends AttributeDefinition
}
}
$proposedValue->SetHostObject($oHostObj, $this->GetCode());
return $proposedValue;
}
@@ -7713,7 +7684,7 @@ class AttributeBlob extends AttributeDefinition
return $aColumns;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
if (!array_key_exists($sPrefix, $aCols))
{
@@ -7737,7 +7708,7 @@ class AttributeBlob extends AttributeDefinition
$sFileName = isset($aCols[$sPrefix.'_filename']) ? $aCols[$sPrefix.'_filename'] : '';
$value = new ormDocument($data, $sMimeType, $sFileName);
$value->SetHostObject($oHostObject, $sHostAttCode);
return $value;
}
@@ -7979,25 +7950,6 @@ class AttributeImage extends AttributeBlob
return "Image";
}
/**
* {@inheritDoc}
* @see AttributeBlob::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
if (($oDoc instanceof ormDocument)
&& (false === $oDoc->IsEmpty())
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
}
// The validation of the MIME Type is done by CheckFormat below
return $oDoc;
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}
@@ -8272,7 +8224,7 @@ class AttributeStopWatch extends AttributeDefinition
return date("Y-m-d H:i:s", $iSeconds);
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$aExpectedCols = array($sPrefix, $sPrefix.'_started', $sPrefix.'_laststart', $sPrefix.'_stopped');
foreach($this->ListThresholds() as $iThreshold => $aFoo)
@@ -9109,7 +9061,7 @@ class AttributeSubItem extends AttributeDefinition
//
// protected function ScalarToSQL($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
}
@@ -9241,9 +9193,9 @@ class AttributeSubItem extends AttributeDefinition
/**
* One way encrypted (hashed) password
*/
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
class AttributeOneWayPassword extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
@@ -9327,7 +9279,7 @@ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeN
return $aColumns;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
if (!array_key_exists($sPrefix, $aCols))
{
@@ -9524,7 +9476,7 @@ class AttributeTable extends AttributeDBField
return $proposedValue;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
try
{
@@ -9539,11 +9491,11 @@ class AttributeTable extends AttributeDBField
}
if ($value === false)
{
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
$value = $this->MakeRealValue($aCols[$sPrefix.''], $oHostObject);
}
} catch (Exception $e)
{
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
$value = $this->MakeRealValue($aCols[$sPrefix.''], $oHostObject);
}
return $value;
@@ -9938,11 +9890,11 @@ abstract class AttributeSet extends AttributeDBFieldVoid
* @return mixed
* @throws \Exception
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$sValue = $aCols["$sPrefix"];
return $this->MakeRealValue($sValue, null, true);
return $this->MakeRealValue($sValue, $oHostObject, true);
}
/**
@@ -11087,7 +11039,7 @@ class AttributeTagSet extends AttributeSet
* @throws \CoreException
* @throws \Exception
*/
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$sValue = $aCols["$sPrefix"];
@@ -11669,7 +11621,7 @@ class AttributeFriendlyName extends AttributeDefinition
return $sLabel;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
$sValue = $aCols[$sPrefix];

View File

@@ -26,24 +26,6 @@
*/
class BackgroundTask extends DBObject
{
protected $bDebug = false;
/**
* @return bool
*/
public function IsDebug()
{
return $this->bDebug;
}
/**
* @param bool $bDebug
*/
public function SetDebug($bDebug)
{
$this->bDebug = $bDebug;
}
public static function Init()
{
$aParams = array

View File

@@ -309,7 +309,7 @@ class BulkChange
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
@@ -363,7 +363,6 @@ class BulkChange
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Default reporting
// $aRowData[$iCol] is always null
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
if ($oExtKey->IsNullAllowed())
@@ -396,7 +395,7 @@ class BulkChange
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
@@ -466,7 +465,7 @@ class BulkChange
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
}
}
}
@@ -539,7 +538,7 @@ class BulkChange
{
if ($sAttCode == 'id')
{
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
else
{
@@ -555,7 +554,7 @@ class BulkChange
}
if (isset($aErrors[$sAttCode]))
{
$aResults[$iCol]= new CellStatus_Issue(utils::HtmlEntities($aRowData[$iCol]), $sOrigValue, $aErrors[$sAttCode]);
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
}
elseif (array_key_exists($sAttCode, $aChangedFields))
{
@@ -578,7 +577,7 @@ class BulkChange
}
else
{
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
}
}
@@ -925,7 +924,7 @@ class BulkChange
{
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, utils::HtmlEntities($this->m_aData[$iRow][$iCol]), Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
}
}
}
@@ -1083,7 +1082,7 @@ class BulkChange
{
if (!array_key_exists($iCol, $aResult[$iRow]))
{
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
}
foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
@@ -1097,7 +1096,7 @@ class BulkChange
if (!array_key_exists($iCol, $aResult[$iRow]))
{
// The foreign attribute is one of our reconciliation key
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
}
}

View File

@@ -149,9 +149,7 @@ abstract class BulkExport
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = [
'show_obsolete_data' => utils::ShowObsoleteData(),
];
$this->aStatusInfo = array();
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
@@ -205,17 +203,15 @@ abstract class BulkExport
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo($aStatusInfo);
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
@@ -293,7 +289,6 @@ abstract class BulkExport
*/
public function SetObjectList(DBSearch $oSearch)
{
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
$this->oSearch = $oSearch;
}
@@ -350,10 +345,10 @@ abstract class BulkExport
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
utils::PushArchiveMode(false);
$ret = $this->oBulkExportResult->DBWrite();
utils::PopArchiveMode();
@@ -425,11 +420,6 @@ abstract class BulkExport
public function GetStatistics()
{
}
public function SetFields($sFields)
{
}
public function GetDownloadFileName()

View File

@@ -63,30 +63,22 @@ class CMDBChangeOp extends DBObject
/**
* Describe (as a text string) the modifications corresponding to this change
*/
*/
public function GetDescription()
{
return '';
}
/**
* Safety net:
* * if change isn't persisted yet, use the current change and persist it if needed
* * in case the change is not given, let's guarantee that it will be set to the current ongoing change (or create a new one)
*
* @since 2.7.7 3.0.2 3.1.0 N°3717 do persist the current change if needed
*/
* Safety net: in case the change is not given, let's guarantee that it will
* be set to the current ongoing change (or create a new one)
*/
protected function OnInsert()
{
$iChange = $this->Get('change');
if (($iChange <= 0) || (is_null($iChange))) {
$oChange = CMDBObject::GetCurrentChange();
if ($oChange->IsNew()) {
$oChange->DBWrite();
}
$this->Set('change', $oChange);
if ($this->Get('change') <= 0)
{
$this->Set('change', CMDBObject::GetCurrentChange());
}
parent::OnInsert();
}
}

View File

@@ -95,45 +95,15 @@ abstract class CMDBObject extends DBObject
protected static $m_oCurrChange = null;
protected static $m_sInfo = null; // null => the information is built in a standard way
protected static $m_sOrigin = null; // null => the origin is 'interactive'
/**
* Specify the change to be used by the API to attach any CMDBChangeOp* object created
*
* @see SetTrackInfo if CurrentChange is null, then a new one will be create using trackinfo
*
* @param CMDBChange|null $oChange use null so that the API will recreate a new CMDBChange using TrackInfo & TrackOrigin
* If providing a CMDBChange, you should persist it first ! Indeed the API will automatically create CMDBChangeOp (see
* \CMDBObject::RecordObjCreation / RecordAttChange / RecordObjDeletion for example) and link them to the current change : in
* consequence this CMDBChange must have a key set !
*
* @since 2.7.2 N°3219 can now reset CMDBChange by passing null
* @since 2.7.2 N°3218 PHPDoc about persisting the $oChange parameter first
* Specify another change (this is mainly for backward compatibility)
*/
public static function SetCurrentChange($oChange)
public static function SetCurrentChange(CMDBChange $oChange)
{
self::$m_oCurrChange = $oChange;
}
/**
* @param string $sUserInfo
* @param string $sOrigin
* @param \DateTime $oDate
*
* @throws \CoreException
*
* @since 2.7.7 3.0.2 3.1.0 N°3717 new method to reset current change
*/
public static function SetCurrentChangeFromParams($sUserInfo, $sOrigin = null, $oDate = null)
{
static::SetTrackInfo($sUserInfo);
static::SetTrackOrigin($sOrigin);
static::CreateChange();
if (!is_null($oDate)) {
static::$m_oCurrChange->Set("date", $oDate);
}
}
//
// Todo: simplify the APIs and do not pass the current change as an argument anymore
// SetTrackInfo to be invoked in very few cases (UI.php, CSV import, Data synchro)
@@ -142,11 +112,7 @@ abstract class CMDBObject extends DBObject
// GetCurrentChange to create a default change if not already done in the current context
//
/**
* @param bool $bAutoCreate if true calls {@link CreateChange} to get a new persisted object
*
* @return \CMDBChange
*
* @uses CreateChange
* Get a change record (create it if not existing)
*/
public static function GetCurrentChange($bAutoCreate = true)
{
@@ -160,17 +126,11 @@ abstract class CMDBObject extends DBObject
/**
* Override the additional information (defaulting to user name)
* A call to this verb should replace every occurence of
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange->Set("date", time());
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
* $iChangeId = $oMyChange->DBInsert();
*
* **warning** : this will do nothing if current change already exists !
*
* @see SetCurrentChange to specify a CMDBObject instance instead
*
* @param string $sInfo
*/
*/
public static function SetTrackInfo($sInfo)
{
self::$m_sInfo = $sInfo;
@@ -178,28 +138,24 @@ abstract class CMDBObject extends DBObject
/**
* Provides information about the origin of the change
*
* **warning** : this will do nothing if current change already exists !
*
* @see SetTrackInfo
* @see SetCurrentChange to specify a CMDBObject instance instead
*
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source,
* email-processing, custom-extension
*/
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
*/
public static function SetTrackOrigin($sOrigin)
{
self::$m_sOrigin = $sOrigin;
}
/**
* Get the additional information (defaulting to user name)
*/
public static function GetTrackInfo()
*/
protected static function GetTrackInfo()
{
if (is_null(self::$m_sInfo)) {
if (is_null(self::$m_sInfo))
{
return CMDBChange::GetCurrentUserName();
} else {
}
else
{
return self::$m_sInfo;
}
}
@@ -218,19 +174,17 @@ abstract class CMDBObject extends DBObject
return self::$m_sOrigin;
}
}
/**
* Set to {@link $m_oCurrChange} a standard change record (done here 99% of the time, and nearly once per page)
*
* @since 2.7.7 3.0.2 3.1.0 N°3717 {@see CMDBChange} **will be persisted later** in {@see \CMDBChangeOp::OnInsert} (was done previously directly here)
* This will avoid creating in DB CMDBChange lines without any corresponding CMDBChangeOp
*/
* Create a standard change record (done here 99% of the time, and nearly once per page)
*/
protected static function CreateChange()
{
self::$m_oCurrChange = MetaModel::NewObject("CMDBChange");
self::$m_oCurrChange->Set("date", time());
self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo());
self::$m_oCurrChange->Set("origin", self::GetTrackOrigin());
self::$m_oCurrChange->DBInsert();
}
/**
@@ -559,83 +513,6 @@ abstract class CMDBObject extends DBObject
}
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBInsert} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal();
return $ret;
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBInsertNoReload} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal(true);
return $ret;
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBInsert} or {@link DBInsertNoReload} instead
*
* @param bool $bDoNotReload
*
* @return integer Identifier of the created object
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
if ($bDoNotReload)
{
$ret = $this->DBInsertNoReload();
}
else
{
$ret = $this->DBInsert();
}
return $ret;
}
public function DBClone($newKey = null)
{
return $this->DBCloneTracked_Internal();
@@ -668,24 +545,6 @@ abstract class CMDBObject extends DBObject
return $ret;
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBUpdate} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int|void
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \SecurityException
*/
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$this->DBUpdate();
}
/**
* @param null $oDeletionPlan
@@ -705,31 +564,6 @@ abstract class CMDBObject extends DBObject
return $this->DBDeleteTracked_Internal($oDeletionPlan);
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBDelete} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
* @param null $oDeletionPlan
*
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
$this->DBDeleteTracked_Internal($oDeletionPlan);
}
/**
* @param null $oDeletionPlan
*

View File

@@ -39,7 +39,6 @@ class MySQLException extends CoreException
*/
public function __construct($sIssue, $aContext, $oException = null, $oMysqli = null)
{
if ($oException != null)
{
$aContext['mysql_errno'] = $oException->getCode();
@@ -59,11 +58,6 @@ class MySQLException extends CoreException
$aContext['mysql_error'] = CMDBSource::GetError();
}
parent::__construct($sIssue, $aContext);
//if is connection error, don't log the default message with password in
if (mysqli_connect_errno()) {
error_log($this->message);
error_reporting(0);
}
}
}
@@ -95,7 +89,7 @@ class MySQLHasGoneAwayException extends MySQLException
{
return array(
2006,
2013,
2013
);
}
@@ -113,18 +107,10 @@ class MySQLNoTransactionException extends MySQLException
}
/**
* @since 2.7.8 3.0.3 3.1.0 N°5538
*/
class MySQLTransactionNotClosedException extends MySQLException
{
}
/**
* CMDBSource
* database access wrapper
* database access wrapper
*
* @package iTopORM
*/
@@ -134,12 +120,6 @@ class CMDBSource
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
const ENUM_DB_VENDOR_PERCONA = 'Percona';
/**
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
* @internal will be removed in a future version
*/
const MYSQL_DEFAULT_PORT = 3306;
/**
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
* Message: Lock wait timeout exceeded; try restarting transaction
@@ -168,17 +148,6 @@ class CMDBSource
/** @var mysqli $m_oMysqli */
protected static $m_oMysqli;
/**
* The mysqli object is really hard to mock ! This attribute is used only in certain methods, so that we can mock only a very little subset of the mysqli object.
* We are setting it in {@see Init}, by default it is a copy of {@see $m_oMysqli}
* The mock can be injected using the setter {@see SetMySQLiForQuery}
*
* @var mysqli $oMySQLiForQuery
* @see GetMySQLiForQuery
* @see SetMySQLiForQuery
* @since 2.7.5 N°3513 new var to allow mock in tests ({@see \Combodo\iTop\Test\UnitTest\Core\TransactionsTest})
*/
protected static $oMySQLiForQuery;
/**
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
@@ -245,7 +214,6 @@ class CMDBSource
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
self::SetMySQLiForQuery(self::$m_oMysqli);
}
/**
@@ -259,12 +227,12 @@ class CMDBSource
*
* @return \mysqli
* @throws \MySQLException
*
* @uses IsOpenedDbConnectionUsingTls when asking for a TLS connection, to check if it was really opened using TLS
*/
public static function GetMysqliInstance(
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
) {
$oMysqli = null;
$sServer = null;
$iPort = null;
self::InitServerAndPort($sDbHost, $sServer, $iPort);
@@ -294,7 +262,7 @@ class CMDBSource
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser),$e);
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
}
if ($bTlsEnabled
@@ -325,19 +293,16 @@ class CMDBSource
/**
* @param string $sDbHost initial value ("p:domain:port" syntax)
* @param string $sServer server variable to update
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
*
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
*
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
* @param int $iPort port variable to update
*/
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
{
$aConnectInfo = explode(':', $sDbHost);
$bUsePersistentConnection = false;
if (strcasecmp($aConnectInfo[0], 'p') === 0)
if (strcasecmp($aConnectInfo[0], 'p') == 0)
{
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
$bUsePersistentConnection = true;
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
}
@@ -355,6 +320,10 @@ class CMDBSource
{
$iPort = (int)($aConnectInfo[1]);
}
else
{
$iPort = 3306;
}
}
/**
@@ -362,41 +331,41 @@ class CMDBSource
* parameters were used.<br>
* This method can be called to ensure that the DB connection really uses TLS.
*
* <p>We're using our own mysqli instance to do the check as this check is done when creating the mysqli instance : the consumer
* might want a dedicated object, and if so we don't want to overwrite the one saved in CMDBSource !<br>
* This is the case for example with {@see \iTopMutex} !
* <p>We're using this object connection : {@link self::$m_oMysqli}
*
* @param \mysqli $oMysqli
*
* @return boolean true if the connection was really established using TLS, false otherwise
* @return boolean true if the connection was really established using TLS
* @throws \MySQLException
*
* @used-by GetMysqliInstance
* @uses IsMySqlVarNonEmpty
* @uses 'ssl_version' MySQL var
* @uses 'ssl_cipher' MySQL var
*/
private static function IsOpenedDbConnectionUsingTls($oMysqli)
{
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version', $oMysqli);
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher', $oMysqli);
if (self::$m_oMysqli == null)
{
self::$m_oMysqli = $oMysqli;
}
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
}
/**
* @param string $sVarName
* @param mysqli $oMysqli connection to use for the query
*
* @return bool
* @throws \MySQLException
* @uses 'SHOW SESSION STATUS' queries
*
* @uses SHOW STATUS queries
*/
private static function IsMySqlVarNonEmpty($sVarName, $oMysqli)
private static function IsMySqlVarNonEmpty($sVarName)
{
try
{
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1, $oMysqli);
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
}
catch (MySQLQueryHasNoResultException $e)
{
@@ -460,12 +429,6 @@ class CMDBSource
}
/**
* @return string
* @throws \MySQLException
*
* @uses \CMDBSource::QueryToCol() so needs a connection opened !
*/
public static function GetDBVersion()
{
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
@@ -483,10 +446,8 @@ class CMDBSource
/**
* Get the DB vendor between MySQL and its main forks
* @return string
*
* @uses \CMDBSource::GetServerVariable() so needs a connection opened !
*/
public static function GetDBVendor()
static public function GetDBVendor()
{
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
@@ -540,7 +501,6 @@ class CMDBSource
{
self::$m_sDBName = '';
}
self::_TablesInfoCacheReset(); // reset the table info cache!
}
public static function CreateTable($sQuery)
@@ -570,24 +530,6 @@ class CMDBSource
return self::$m_oMysqli;
}
/**
* @return
*/
private static function GetMySQLiForQuery()
{
return self::$oMySQLiForQuery;
}
/**
* Used for test purpose (mysqli mock)
* @param $oMySQLi
*/
private static function SetMySQLiForQuery($oMySQLi)
{
self::$oMySQLiForQuery = $oMySQLi;
}
public static function GetErrNo()
{
if (self::$m_oMysqli->errno != 0)
@@ -674,9 +616,10 @@ class CMDBSource
/**
* @param string $sSQLQuery
*
* @return mysqli_result|null
* @throws MySQLException
* @throws MySQLHasGoneAwayException
* @return \mysqli_result|null
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \CoreException
*
* @since 2.7.0 N°679 handles nested transactions
*/
@@ -722,28 +665,22 @@ class CMDBSource
*/
private static function DBQuery($sSql)
{
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
IssueLog::Trace("$sShortSQL", 'cmdbsource');
}
$oKPI = new ExecutionKPI();
try
{
$oResult = self::GetMySQLiForQuery()->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch (mysqli_sql_exception $e)
{
self::LogDeadLock($e);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
} finally {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
}
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false)
{
$aContext = array('query' => $sSql);
$iMySqlErrorNo = self::GetMySQLiForQuery()->errno;
$iMySqlErrorNo = self::$m_oMysqli->errno;
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
{
@@ -765,7 +702,7 @@ class CMDBSource
private static function LogDeadLock(Exception $e)
{
// checks MySQL error code
$iMySqlErrorNo = self::GetMySQLiForQuery()->errno;
$iMySqlErrorNo = self::$m_oMysqli->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK)))
{
return;
@@ -773,7 +710,7 @@ class CMDBSource
// Get error info
$sUser = UserRights::GetUser();
$oError = self::GetMySQLiForQuery()->query('SHOW ENGINE INNODB STATUS');
$oError = self::$m_oMysqli->query('SHOW ENGINE INNODB STATUS');
if ($oError !== false)
{
$aData = $oError->fetch_all(MYSQLI_ASSOC);
@@ -795,7 +732,7 @@ class CMDBSource
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
IssueLog::Error($sMessage, 'DeadLock', $e->getMessage());
}
/**
@@ -811,15 +748,10 @@ class CMDBSource
*/
private static function StartTransaction()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$bHasExistingTransactions = self::IsInsideTransaction();
if (!$bHasExistingTransactions)
{
IssueLog::Trace("START TRANSACTION was sent to the DB", LogChannels::CMDB_SOURCE, ['stacktrace' => $aStackTrace]);
self::DBQuery('START TRANSACTION');
} else {
IssueLog::Trace("START TRANSACTION ignored as a transaction is already opened", LogChannels::CMDB_SOURCE, ['stacktrace' => $aStackTrace]);
}
self::AddTransactionLevel();
@@ -837,12 +769,9 @@ class CMDBSource
*/
private static function Commit()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
if (!self::IsInsideTransaction())
{
// should not happen !
IssueLog::Error("No Transaction COMMIT $sCaller", 'cmdbsource');
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
@@ -850,10 +779,8 @@ class CMDBSource
if (self::IsInsideTransaction())
{
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", 'cmdbsource');
return;
}
IssueLog::Trace("COMMIT $sCaller", 'cmdbsource');
self::DBQuery('COMMIT');
}
@@ -872,22 +799,17 @@ class CMDBSource
*/
private static function Rollback()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
if (!self::IsInsideTransaction())
{
// should not happen !
IssueLog::Error("No Transaction ROLLBACK $sCaller", 'cmdbsource');
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
self::RemoveLastTransactionLevel();
if (self::IsInsideTransaction())
{
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", 'cmdbsource');
return;
}
IssueLog::Trace("ROLLBACK $sCaller", 'cmdbsource');
self::DBQuery('ROLLBACK');
}
@@ -937,36 +859,6 @@ class CMDBSource
self::$m_iTransactionLevel = 0;
}
public static function IsDeadlockException(Exception $e)
{
while ($e instanceof Exception) {
if (($e instanceof MySQLException) && ($e->getCode() == 1213)) {
return true;
}
$e = $e->getPrevious();
}
return false;
}
/**
*
* @deprecated 2.7.0 N°1627 use ItopCounter instead
*
* @param string $sTable
*
* @return int
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function GetNextInsertId($sTable)
{
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
$oResult = self::Query($sSQL);
$aRow = $oResult->fetch_assoc();
return $aRow['Auto_increment'];
}
public static function GetInsertId()
{
$iRes = self::$m_oMysqli->insert_id;
@@ -1001,21 +893,17 @@ class CMDBSource
/**
* @param string $sSql
* @param int $iCol beginning at 0
* @param mysqli $oMysqli if not null will query using this connection, otherwise will use {@see GetMySQLiForQuery}
*
* @return string corresponding cell content on the first line
* @throws \MySQLException
* @throws \MySQLQueryHasNoResultException
* @since 2.7.5-2 2.7.6 3.0.0 N°4215 new optional mysqli param
*/
public static function QueryToScalar($sSql, $iCol = 0, $oMysqli = null)
public static function QueryToScalar($sSql, $iCol = 0)
{
$oMysqliToQuery = (is_null($oMysqli)) ? self::GetMySQLiForQuery() : $oMysqli;
$oKPI = new ExecutionKPI();
try
{
$oResult = $oMysqliToQuery->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1055,7 +943,7 @@ class CMDBSource
$oKPI = new ExecutionKPI();
try
{
$oResult = self::GetMySQLiForQuery()->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1137,7 +1025,7 @@ class CMDBSource
{
try
{
$oResult = self::GetMySQLiForQuery()->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1274,30 +1162,23 @@ class CMDBSource
}
/**
* There may have some differences between DB ! For example in :
* * MySQL 5.7 we have `INT`
* * MariaDB >= 10.2 you get `int DEFAULT 'NULL'`
* 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'"
*
* We still need to do a case sensitive comparison for enum values !
* We still 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
*
* @see GetFieldDataTypeAndOptions extracts all info from the SQL field definition
*
* @param string $sItopGeneratedFieldType
* @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)
{
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
{
@@ -1339,68 +1220,24 @@ class CMDBSource
}
/**
* @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`
* @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`
*
* @throws \CoreException
* 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'
*/
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
@@ -1624,7 +1461,7 @@ class CMDBSource
$sSql = "SELECT * FROM `$sTable`";
try
{
$oResult = self::GetMySQLiForQuery()->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1734,20 +1571,8 @@ class CMDBSource
return false;
}
public static function GetClusterNb()
{
$result = 0;
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
$aRows = self::QueryToArray($sSql);
if (count($aRows) > 0)
{
$result = $aRows[0]['Value'];
}
return intval($result);
}
/**
* @return string query to upgrade database charset and collation if needed, null if not
/**
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5.0 N°1001 switch to utf8mb4

View File

@@ -22,15 +22,7 @@
define('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
/**
* Constant containing the application version
* Warning: this might be different from iTop core version!
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '2.7.0-dev');
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
define('ITOP_VERSION_FULL', ITOP_VERSION.'-'.ITOP_REVISION);
@@ -476,11 +468,11 @@ class Config
'show_in_conf_sample' => true,
),
'cron_max_execution_time' => array(
'type' => 'integer',
'description' => 'Duration (seconds) of the cron.php script : if exceeded the script will exit even if there are remaining tasks to process. Must be shorter than php max_execution_time setting (note than when using CLI, this is set to 0 by default which means unlimited). If cron.php is ran via web, it must be shorter than the web server response timeout.',
'default' => 600,
'value' => 600,
'source_of_value' => '',
'type' => 'integer',
'description' => 'Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout',
'default' => 600,
'value' => 600,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cron_sleep' => array(
@@ -509,7 +501,7 @@ class Config
),
'email_transport' => array(
'type' => 'string',
'description' => 'Mean to send emails: PHPMail (uses the function mail()), SMTP (implements the client protocol) or SMTP_OAuth (connect to the server using OAuth 2.0)',
'description' => 'Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)',
'default' => "PHPMail",
'value' => "PHPMail",
'source_of_value' => '',
@@ -555,22 +547,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_transport_smtp.allow_self_signed' => array(
'type' => 'bool',
'description' => 'Allow self signed peer certificates',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_transport_smtp.verify_peer' => array(
'type' => 'bool',
'description' => 'Verify peer certificate',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_css' => array(
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
@@ -619,13 +595,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
/**
* The timezone is automatically set using this parameter in \utils::InitTimeZone
* This method is called almost everywhere, cause it's called in \MetaModel::LoadConfig and exec.php... but you might
* need to get it yourself !
*
* @used-by utils::InitTimeZone()
*/
'timezone' => array(
'type' => 'string',
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP',
@@ -869,14 +838,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'impact_analysis_lazy_loading' => [
'type' => 'bool',
'description' => 'In the impact analysis view: display the analysis or filter before display',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'url_validation_pattern' => array(
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
@@ -963,14 +924,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_kpi_generate_legacy_report' => array(
'type' => 'bool',
'description' => 'Generate the legacy KPI report (kpi.html)',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'max_linkset_output' => array(
'type' => 'integer',
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
@@ -1112,14 +1065,6 @@ 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.',
@@ -1160,14 +1105,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'svg_sanitizer' => array(
'type' => 'string',
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
'default' => 'SVGDOMSanitizer',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'inline_image_max_display_width' => array(
'type' => 'integer',
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
@@ -1312,38 +1249,6 @@ 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,
],
'security.enable_header_xcontent_type_options' => [
'type' => 'bool',
'description' => 'If set to false, iTop will stop sending the X-Content-Type-Options HTTP header. This header could trigger CORB protection on certain resources (JSON, XML, HTML, text) therefore blocking them.',
'default' => true,
'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,
],
'security.disable_inline_documents_sandbox' => array(
'type' => 'bool',
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
@@ -1634,7 +1539,7 @@ class Config
}
if (strlen($sNoise) > 0)
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
throw new ConfigException('Syntax error in configuration file',
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
}
@@ -1909,24 +1814,6 @@ class Config
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
}
/**
* @since 2.7.11 N°7085
* Add login mode if not configured already
* @param string $sLoginMode
*
* @return void
*/
public function AddAllowedLoginTypes($sLoginMode)
{
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
if (in_array($sLoginMode, $aAllowedLoginTypes)){
return;
}
$aAllowedLoginTypes[] = $sLoginMode;
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
}
public function SetExternalAuthenticationVariable($sExtAuthVariable)
{
$this->m_sExtAuthVariable = $sExtAuthVariable;
@@ -2446,7 +2333,7 @@ class ConfigPlaceholdersResolver
}
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
{
return $rawValue;
@@ -2499,4 +2386,4 @@ class ConfigPlaceholdersResolver
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
throw new ConfigException($sErrorMessage);
}
}
}

View File

@@ -28,39 +28,31 @@
class CoreException extends Exception
{
/**
* CoreException constructor.
*
* @param string $sIssue error message
* @param array|null $aContextData key/value array, value MUST implements _toString
* @param string $sImpact
* @param Exception|null $oPrevious
*/
public function __construct($sIssue, $aContextData = null, $sImpact = '', $oPrevious = null)
public function __construct($sIssue, $aContextData = null, $sImpact = '')
{
$this->m_sIssue = $sIssue;
$this->m_sImpact = $sImpact;
if (is_array($aContextData)) {
$this->m_aContextData = $aContextData;
} else {
$this->m_aContextData = [];
}
$this->m_aContextData = $aContextData ? $aContextData : array();
$sMessage = $sIssue;
if (!empty($sImpact)) {
$sMessage .= "($sImpact)";
}
if (count($this->m_aContextData) > 0) {
if (!empty($sImpact)) $sMessage .= "($sImpact)";
if (count($this->m_aContextData) > 0)
{
$sMessage .= ": ";
$aContextItems = array();
foreach ($this->m_aContextData as $sKey => $value) {
if (is_array($value)) {
foreach($this->m_aContextData as $sKey => $value)
{
if (is_array($value))
{
$aPairs = array();
foreach ($value as $key => $val) {
if (is_array($val)) {
foreach($value as $key => $val)
{
if (is_array($val))
{
$aPairs[] = $key.'=>('.implode(', ', $val).')';
} else {
}
else
{
$aPairs[] = $key.'=>'.$val;
}
}
@@ -74,7 +66,7 @@ class CoreException extends Exception
}
$sMessage .= implode(', ', $aContextItems);
}
parent::__construct($sMessage, 0, $oPrevious);
parent::__construct($sMessage, 0);
}
/**
@@ -89,16 +81,6 @@ class CoreException extends Exception
return $this->getMessage();
}
/**
* getTraceAsString() cannot be overrided and it is limited as only current exception stack is returned.
* we need stack of all previous exceptions
* @uses __tostring() already does the work.
* @since 2.7.2/ 2.8.0
*/
public function getFullStackTraceAsString(){
return "" . $this;
}
public function getTraceAsHtml()
{
$aBackTrace = $this->getTrace();
@@ -149,14 +131,14 @@ class CoreCannotSaveObjectException extends CoreException
*
* @param array $aContextData containing at least those keys : issues, id, class
*/
public function __construct($aContextData, $oPrevious = null)
public function __construct($aContextData)
{
$this->aIssues = $aContextData['issues'];
$this->iObjectId = $aContextData['id'];
$this->sObjectClass = $aContextData['class'];
$sIssues = implode(', ', $this->aIssues);
parent::__construct($sIssues, $aContextData, '', $oPrevious);
parent::__construct($sIssues, $aContextData);
}
/**
@@ -165,15 +147,19 @@ class CoreCannotSaveObjectException extends CoreException
public function getHtmlMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = "<span><strong>".utils::HtmlEntities($sTitle)."</strong></span>";
$sContent = "<span><strong>{$sTitle}</strong></span>";
if (count($this->aIssues) == 1) {
if (count($this->aIssues) == 1)
{
$sIssue = reset($this->aIssues);
$sContent .= " <span>".utils::HtmlEntities($sIssue)."</span>";
} else {
$sContent .= " <span>{$sIssue}</span>";
}
else
{
$sContent .= '<ul>';
foreach ($this->aIssues as $sError) {
$sContent .= "<li>".utils::HtmlEntities($sError)."</li>";
foreach ($this->aIssues as $sError)
{
$sContent .= "<li>$sError</li>";
}
$sContent .= '</ul>';
}
@@ -229,47 +215,12 @@ class CoreUnexpectedValue extends CoreException
{
}
/**
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
*/
class InvalidExternalKeyValueException extends CoreUnexpectedValue
{
private const ENUM_PARAMS_OBJECT = 'current_object';
private const ENUM_PARAMS_ATTCODE = 'attcode';
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
private const ENUM_PARAMS_USER = 'current_user';
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
{
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
$oCurrentUser = UserRights::GetUserObject();
if (false === is_null($oCurrentUser)) {
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
}
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
}
public function GetAttCode(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
}
public function GetAttValue(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
}
}
class SecurityException extends CoreException
{
}
/**
* Thrown when querying on an object that exists in the database but is archived
* Throwned when querying on an object that exists in the database but is archived
*
* @see N.1108
* @since 2.5.1

View File

@@ -188,8 +188,8 @@ final class ItopCounter
if (!$hDBLink)
{
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
return $hDBLink;
}

View File

@@ -15,7 +15,6 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\Helper\ExportHelper;
/**
* Bulk export: CSV export
@@ -114,7 +113,6 @@ class CSVBulkExport extends TabularBulkExport
case 'csv_options':
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
$oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');

View File

@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<user_rights>
<profiles>
<profile id="1024" _delta="define">
<name>REST Services User</name>
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false
in the configuration file).
</description>
<groups/>
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false in the configuration file).</description>
<groups />
</profile>
</profiles>
</user_rights>

View File

@@ -461,7 +461,7 @@ abstract class DBObject implements iDisplay
if (array_key_exists($sAttRef, $aRow))
{
$value = $oAttDef->FromSQLToValue($aRow, $sAttRef);
$value = $oAttDef->FromSQLToValue($aRow, $sAttRef, $this, $sAttCode);
$bIsDefined = true;
}
}
@@ -532,10 +532,11 @@ abstract class DBObject implements iDisplay
* Attributes setter
*
* Set $sAttCode to $value.
* The value must be valid according to the type of attribute : see the different {@see AttributeDefinition::MakeRealValue()} implementations
* The value must be valid according to the type of attribute.
* The value will not be recorded into the DB until DBObject::DBWrite() is called.
*
* @api
* @see DBWrite()
*
* @param string $sAttCode
* @param mixed $value
@@ -543,8 +544,6 @@ abstract class DBObject implements iDisplay
* @return bool
* @throws CoreException
* @throws CoreUnexpectedValue
*
* @see DBWrite()
*/
public function Set($sAttCode, $value)
{
@@ -857,17 +856,13 @@ abstract class DBObject implements iDisplay
}
/**
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
*
* @param string $sAttCode
*
* @return mixed|null the value as it was before changed with {@see DBObject::Set()}.
* Returns null if the attribute wasn't changed.
* Values are reset during {@see DBObject::DBUpdate()}
*
* @see DBObject::$m_aOrigValues
* @throws CoreException if the attribute is unknown for the current object
* @uses DBObject::$m_aOrigValues
*/
public function GetOriginal($sAttCode)
{
@@ -1978,9 +1973,9 @@ abstract class DBObject implements iDisplay
/**
* check attributes together
*
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @return true|string true if successful, the error description otherwise
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @return bool
*/
public function CheckConsistency()
{
@@ -2085,17 +2080,16 @@ abstract class DBObject implements iDisplay
}
/**
* @param array $aUniquenessRuleProperties uniqueness rule properties
*
* @param string $sUniquenessRuleId uniqueness rule ID
* @return \DBSearch
* @throws \OQLException
* @throws \CoreException
*
* @internal
*
* @since 2.6.0 N°659 uniqueness constraint
* @since 2.7.11 3.1.2 3.2.0 N°4314 Fix Uniqueness rules not working with Silo
* @param string $sUniquenessRuleId uniqueness rule ID
* @param array $aUniquenessRuleProperties uniqueness rule properties
*
* @return \DBSearch
* @throws \CoreException
* @throws \OQLException
* @since 2.6.0 N°659 uniqueness constraint
* @api
*/
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
{
@@ -2125,10 +2119,8 @@ abstract class DBObject implements iDisplay
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
}
$oUniquenessQuery->AllowAllData();
return $oUniquenessQuery;
}
return $oUniquenessQuery;
}
/**
* Check integrity rules (before inserting or updating the object)
@@ -2144,6 +2136,7 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*
*/
public function DoCheckToWrite()
{
@@ -2198,6 +2191,7 @@ abstract class DBObject implements iDisplay
}
/**
*
* @api
* @api-advanced
*
@@ -2213,6 +2207,7 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
*
*/
final public function CheckToWrite()
{
@@ -2226,7 +2221,7 @@ abstract class DBObject implements iDisplay
$oKPI = new ExecutionKPI();
$this->DoCheckToWrite();
$oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
$oKPI->ComputeStats('CheckToWrite', get_class($this));
if (count($this->m_aCheckIssues) == 0)
{
$this->m_bCheckStatus = true;
@@ -2239,87 +2234,6 @@ abstract class DBObject implements iDisplay
return array($this->m_bCheckStatus, $this->m_aCheckIssues, $this->m_bSecurityIssue);
}
/**
* Checks for extkey attributes values. This will throw exception on non-existing as well as non-accessible objects (silo, scopes).
* That's why the test is done for all users including Administrators
*
* Note that due to perf issues, this isn't called directly by the ORM, but has to be called by consumers when possible.
*
* @param callable(string, string):bool|null $oIsObjectLoadableCallback Override to check if object is accessible.
* Parameters are object class and key
* Return value should be false if cannot access object, true otherwise
* @return void
*
* @throws ArchivedObjectException
* @throws CoreException if cannot get object attdef list
* @throws CoreUnexpectedValue
* @throws InvalidExternalKeyValueException
* @throws MySQLException
* @throws SecurityException if one extkey is pointing to an invalid value
*
* @link https://github.com/Combodo/iTop/security/advisories/GHSA-245j-66p9-pwmh
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458
*
* @see \RestUtils::FindObjectFromKey for the same check in the REST endpoint
*/
final public function CheckChangedExtKeysValues(callable $oIsObjectLoadableCallback = null)
{
if (is_null($oIsObjectLoadableCallback)) {
$oIsObjectLoadableCallback = function ($sClass, $sId) {
$oRemoteObject = MetaModel::GetObject($sClass, $sId, false);
if (is_null($oRemoteObject)) {
return false;
}
return true;
};
}
$aChanges = $this->ListChanges();
$aAttCodesChanged = array_keys($aChanges);
foreach ($aAttCodesChanged as $sAttDefCode) {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttDefCode);
if ($oAttDef instanceof AttributeLinkedSetIndirect) {
/** @var ormLinkSet $oOrmSet */
$oOrmSet = $this->Get($sAttDefCode);
while ($oLnk = $oOrmSet->Fetch()) {
$oLnk->CheckChangedExtKeysValues($oIsObjectLoadableCallback);
}
continue;
}
/** @noinspection PhpConditionCheckedByNextConditionInspection */
/** @noinspection NotOptimalIfConditionsInspection */
if (($oAttDef instanceof AttributeHierarchicalKey) || ($oAttDef instanceof AttributeExternalKey)) {
$sRemoteObjectClass = $oAttDef->GetTargetClass();
$sRemoteObjectKey = $this->Get($sAttDefCode);
} else if ($oAttDef instanceof AttributeObjectKey) {
$sRemoteObjectClassAttCode = $oAttDef->Get('class_attcode');
$sRemoteObjectClass = $this->Get($sRemoteObjectClassAttCode);
$sRemoteObjectKey = $this->Get($sAttDefCode);
} else {
continue;
}
if (utils::IsNullOrEmptyString($sRemoteObjectClass)
|| utils::IsNullOrEmptyString($sRemoteObjectKey)
) {
continue;
}
// 0 : Undefined ext. key (EG. non-mandatory and no value provided)
// < 0 : Non yet persisted object
/** @noinspection TypeUnsafeComparisonInspection Non-strict comparison as object ID can be string */
if ($sRemoteObjectKey <= 0) {
continue;
}
if (false === $oIsObjectLoadableCallback($sRemoteObjectClass, $sRemoteObjectKey)) {
throw new InvalidExternalKeyValueException($this, $sAttDefCode);
}
}
}
/**
* Check if it is allowed to delete the existing object from the database
*
@@ -2461,17 +2375,15 @@ abstract class DBObject implements iDisplay
return $aDelta;
}
/**
* @api
* @api-advanced
*
* @return array attcode => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
* Reset during {@see DBObject::DBUpdate()}
* @throws Exception
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
* @uses m_aCurrValues
*/
/**
* @api
* @api-advanced
*
* @return array attname => currentvalue List the attributes that have been changed using {@see DBObject::Set()}. Reset during {@see DBObject::DBUpdate()}
* @uses m_aCurrValues
* @see \DBObject::ListPreviousValuesForUpdatedAttributes()
* @throws Exception
*/
public function ListChanges()
{
if ($this->m_bIsInDB)
@@ -2490,10 +2402,10 @@ abstract class DBObject implements iDisplay
*
* To get values that were set to the changed fields, simply use {@link \DBObject::Get()}
*
* @see \DBObject::ListChanges() old method, but using data that are reset during DBObject::DBUpdate
* @return array attname => value : value that was present before the last {@see DBObject::Set()} call.
* This array is set at the beginning of {@see DBObject::DBpdate()} using {@see DBObject::InitPreviousValuesForUpdatedAttributes()}.
* @uses m_aPreviousValuesForUpdatedAttributes
* @see \DBObject::ListChanges()
* @since 2.7.0 N°2293
*/
public function ListPreviousValuesForUpdatedAttributes()
@@ -2762,6 +2674,7 @@ abstract class DBObject implements iDisplay
* @throws \Exception
*
* @internal
*
*/
public function DBInsertNoReload()
{
@@ -2774,12 +2687,8 @@ abstract class DBObject implements iDisplay
$sRootClass = MetaModel::GetRootClass($sClass);
// Ensure the update of the values (we are accessing the data directly)
$oKPI = new ExecutionKPI();
$this->DoComputeValues();
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
$oKPI = new ExecutionKPI();
$this->OnInsert();
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
if ($this->m_iKey < 0)
{
@@ -2797,7 +2706,7 @@ abstract class DBObject implements iDisplay
}
// Ultimate check - ensure DB integrity
[$bRes, $aIssues] = $this->CheckToWrite();
list($bRes, $aIssues) = $this->CheckToWrite();
if (!$bRes)
{
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
@@ -2824,72 +2733,50 @@ abstract class DBObject implements iDisplay
}
}
$iTransactionRetry = 1;
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled)
try
{
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iTransactionRetryCount;
}
while ($iTransactionRetry > 0) {
try {
$iTransactionRetry--;
if ($bIsTransactionEnabled) {
CMDBSource::Query('START TRANSACTION');
}
// First query built upon on the root class, because the ID must be created first
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
// Then do the leaf class, if different from the root class
if ($sClass != $sRootClass) {
$this->DBInsertSingleTable($sClass);
}
// Then do the other classes
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
if ($sParentClass == $sRootClass) {
continue;
}
$this->DBInsertSingleTable($sParentClass);
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
if ($bIsTransactionEnabled) {
CMDBSource::Query('COMMIT');
}
break;
if ($bIsTransactionEnabled)
{
CMDBSource::Query('START TRANSACTION');
}
catch (Exception $e) {
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
// First query built upon on the root class, because the ID must be created first
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
// Then do the leaf class, if different from the root class
if ($sClass != $sRootClass)
{
$this->DBInsertSingleTable($sClass);
}
// Then do the other classes
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if ($sParentClass == $sRootClass)
{
CMDBSource::Query('ROLLBACK');
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
{
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Insert TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
continue;
}
else
{
IssueLog::Error("Insert Deadlock TRANSACTION prevention failed.");
}
}
continue;
}
throw $e;
$this->DBInsertSingleTable($sParentClass);
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
if ($bIsTransactionEnabled)
{
CMDBSource::Query('COMMIT');
}
}
catch (Exception $e)
{
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
}
throw $e;
}
$this->m_bIsInDB = true;
@@ -2903,9 +2790,7 @@ abstract class DBObject implements iDisplay
$this->m_aOrigValues[$sAttCode] = $value;
}
$oKPI = new ExecutionKPI();
$this->AfterInsert();
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
// Activate any existing trigger
$sClass = get_class($this);
@@ -2913,16 +2798,8 @@ abstract class DBObject implements iDisplay
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectCreate AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \TriggerOnObjectCreate $oTrigger */
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e)
{
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
$this->RecordObjCreation();
@@ -3041,6 +2918,8 @@ abstract class DBObject implements iDisplay
* Persist an object to the DB, for the first time
*
* @api
* @see DBWrite
*
* @return int|null inserted object key
*
* @throws \ArchivedObjectException
@@ -3050,12 +2929,10 @@ abstract class DBObject implements iDisplay
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*
* @see DBWrite
*/
public function DBInsert()
{
$this->DBInsertNoReload();
$this->DBInsertNoReload();
if (MetaModel::DBIsReadOnly())
{
@@ -3070,46 +2947,6 @@ abstract class DBObject implements iDisplay
return $this->m_iKey;
}
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsert()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
* @return int|null
* @throws CoreException
*/
public function DBInsertTracked(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBInsert();
}
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsertNoReload()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
* @return int
* @throws ArchivedObjectException
* @throws CoreCannotSaveObjectException
* @throws CoreException
* @throws CoreUnexpectedValue
* @throws CoreWarning
* @throws MySQLException
* @throws OQLException
*/
public function DBInsertTrackedNoReload(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBInsertNoReload();
}
/**
* Creates a copy of the current object into the database
*
@@ -3154,13 +2991,13 @@ abstract class DBObject implements iDisplay
* Update an object in DB
*
* @api
* @see DBObject::DBWrite()
*
* @return int object key
*
* @throws \CoreException
* @throws \CoreCannotSaveObjectException if CheckToWrite() returns issues
* @throws \Exception
*
* @see DBObject::DBWrite()
*/
public function DBUpdate()
{
@@ -3168,7 +3005,6 @@ abstract class DBObject implements iDisplay
{
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
}
// Protect against reentrance (e.g. cascading the update of ticket logs)
static $aUpdateReentrance = array();
$sKey = get_class($this).'::'.$this->GetKey();
@@ -3182,11 +3018,8 @@ abstract class DBObject implements iDisplay
try
{
$oKPI = new ExecutionKPI();
$this->DoComputeValues();
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
// Stop watches
// Stop watches
$sState = $this->GetState();
if ($sState != '')
{
@@ -3205,9 +3038,7 @@ abstract class DBObject implements iDisplay
}
}
}
$oKPI = new ExecutionKPI();
$this->OnUpdate();
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
$this->OnUpdate();
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
@@ -3219,7 +3050,7 @@ abstract class DBObject implements iDisplay
}
// Ultimate check - ensure DB integrity
[$bRes, $aIssues] = $this->CheckToWrite();
list($bRes, $aIssues) = $this->CheckToWrite();
if (!$bRes)
{
throw new CoreCannotSaveObjectException(array(
@@ -3239,14 +3070,8 @@ abstract class DBObject implements iDisplay
array(), $aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \TriggerOnObjectUpdate $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
$bHasANewExternalKeyValue = false;
@@ -3273,11 +3098,9 @@ abstract class DBObject implements iDisplay
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled)
{
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iTransactionRetryCount;
$iTransactionRetry = $iIsTransactionRetryCount;
}
while ($iTransactionRetry > 0)
{
@@ -3345,6 +3168,13 @@ abstract class DBObject implements iDisplay
$this->DBWriteLinks();
$this->WriteExternalAttributes();
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
// new values are already in the object (call {@see DBObject::Get()} to get them)
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
$this->m_bDirty = false;
$this->m_aTouchedAtt = array();
$this->m_aModifiedAtt = array();
if (count($aChanges) != 0)
{
$this->RecordAttChanges($aChanges, $aOriginalValues);
@@ -3358,18 +3188,18 @@ abstract class DBObject implements iDisplay
}
catch (MySQLException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
if ($e->getCode() == 1213)
{
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
// Deadlock found when trying to get lock; try restarting transaction
IssueLog::Error($e->getMessage());
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Update TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
continue;
}
else
@@ -3383,11 +3213,10 @@ abstract class DBObject implements iDisplay
'id' => $this->GetKey(),
'class' => get_class($this),
'issues' => $aErrors
), $e);
));
}
catch (CoreCannotSaveObjectException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
@@ -3396,7 +3225,6 @@ abstract class DBObject implements iDisplay
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
@@ -3410,18 +3238,9 @@ abstract class DBObject implements iDisplay
}
}
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
// new values are already in the object (call {@see DBObject::Get()} to get them)
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
$this->m_bDirty = false;
$this->m_aTouchedAtt = array();
$this->m_aModifiedAtt = array();
try
{
$oKPI = new ExecutionKPI();
$this->AfterUpdate();
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
$this->AfterUpdate();
// Reload to get the external attributes
if ($bHasANewExternalKeyValue)
@@ -3479,39 +3298,15 @@ abstract class DBObject implements iDisplay
$this->m_aPreviousValuesForUpdatedAttributes = $aPreviousValuesForUpdatedAttributes;
}
/**
*
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBUpdate()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
* @return int
* @throws CoreCannotSaveObjectException
* @throws CoreException
*/
public function DBUpdateTracked(CMDBChange $oChange)
{
CMDBObject::SetCurrentChange($oChange);
return $this->DBUpdate();
}
/**
* Make the current changes persistent - clever wrapper for Insert or Update
*
* @api
*
* @api
*
* @return int
*
* @throws ArchivedObjectException
* @throws CoreCannotSaveObjectException
* @throws CoreException
* @throws CoreUnexpectedValue
* @throws CoreWarning
* @throws MySQLException
* @throws OQLException
*
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
*/
public function DBWrite()
{
@@ -3572,15 +3367,8 @@ abstract class DBObject implements iDisplay
$aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \TriggerOnObjectDelete $oTrigger */
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
@@ -3622,11 +3410,9 @@ abstract class DBObject implements iDisplay
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled)
{
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iTransactionRetryCount;
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iIsTransactionRetryCount;
}
while ($iTransactionRetry > 0)
{
@@ -3649,18 +3435,18 @@ abstract class DBObject implements iDisplay
}
catch (MySQLException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
if ($e->getCode() == 1213)
{
// Deadlock found when trying to get lock; try restarting transaction
IssueLog::Error($e->getMessage());
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Delete TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
continue;
}
else
@@ -3766,34 +3552,9 @@ abstract class DBObject implements iDisplay
return $oDeletionPlan;
}
/**
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBDelete()} instead.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
* @param boolean $bSkipStrongSecurity
* @param \DeletionPlan $oDeletionPlan
*
* @throws ArchivedObjectException
* @throws CoreCannotSaveObjectException
* @throws CoreException
* @throws CoreUnexpectedValue
* @throws DeleteException
* @throws MySQLException
* @throws MySQLHasGoneAwayException
* @throws OQLException
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
CMDBObject::SetCurrentChange($oChange);
$this->DBDelete($oDeletionPlan);
}
/**
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @return array
*
* @throws ArchivedObjectException
@@ -3837,22 +3598,16 @@ abstract class DBObject implements iDisplay
/**
* Apply a stimulus (workflow)
*
* @api
*
* @param string $sStimulusCode
* @param bool $bDoNotWrite if true we won't save the object !
*
*
* @api
*
* @param string $sStimulusCode
* @param bool $bDoNotWrite
*
* @return bool
*
*
* @throws CoreException
* @throws CoreUnexpectedValue
*
* @uses \AttributeStopWatch::Start
* @uses \AttributeStopWatch::Stop
* @uses \DBObject::DBWrite
* @uses \TriggerOnStateLeave::DoActivate
* @uses \TriggerOnStateEnter::DoActivate
*/
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
{
@@ -3875,14 +3630,16 @@ abstract class DBObject implements iDisplay
// save current object values in case of an action failure (in memory rollback)
$aBackupValues = array();
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
if (isset($this->m_aCurrValues[$sAttCode])) {
$value = $this->m_aCurrValues[$sAttCode];
if (is_object($value)) {
$aBackupValues[$sAttCode] = clone $value;
} else {
$aBackupValues[$sAttCode] = $value;
}
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
$value = $this->m_aCurrValues[$sAttCode];
if (is_object($value))
{
$aBackupValues[$sAttCode] = clone $value;
}
else
{
$aBackupValues[$sAttCode] = $value;
}
}
@@ -3976,7 +3733,8 @@ abstract class DBObject implements iDisplay
}
}
if (!$bDoNotWrite) {
if (!$bDoNotWrite)
{
$this->DBWrite();
}
@@ -3984,30 +3742,19 @@ abstract class DBObject implements iDisplay
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $sNewState,
);
'new_state' => $sNewState);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
}
else
@@ -4589,22 +4336,7 @@ abstract class DBObject implements iDisplay
}
/**
* implement relations
*
* Return an empty set for the parent of all
*
* this way of implementing the relations suffers limitations (not handling the redundancy)
* and you should consider defining those things in XML
*
* @internal
* @deprecated
*/
public static function GetRelationQueries($sRelCode)
{
return array();
}
/**
* Reserved: do not overload
*
@@ -4615,72 +4347,6 @@ abstract class DBObject implements iDisplay
return array();
}
/**
* Use GetRelatedObjectsDown/Up instead to take redundancy into account
*
* @internal
* @deprecated
*/
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
{
// Temporary patch: until the impact analysis GUI gets rewritten,
// let's consider that "depends on" is equivalent to "impacts/up"
// The current patch has been implemented in DBObject and MetaModel
$sHackedRelCode = $sRelCode;
$bDown = true;
if ($sRelCode == 'depends on')
{
$sHackedRelCode = 'impacts';
$bDown = false;
}
foreach (MetaModel::EnumRelationQueries(get_class($this), $sHackedRelCode, $bDown) as $sDummy => $aQueryInfo)
{
$sQuery = $bDown ? $aQueryInfo['sQueryDown'] : $aQueryInfo['sQueryUp'];
//$bPropagate = $aQueryInfo["bPropagate"];
//$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
$iDepth = $iMaxDepth - 1;
// Note: the loop over the result set has been written in an unusual way for error reporting purposes
// In the case of a wrong query parameter name, the error occurs on the first call to Fetch,
// thus we need to have this first call into the try/catch, but
// we do NOT want to nest the try/catch for the error message to be clear
try
{
$oFlt = DBObjectSearch::FromOQL($sQuery);
$oObjSet = new DBObjectSet($oFlt, array(), $this->ToArgsForQuery());
$oObj = $oObjSet->Fetch();
}
catch (Exception $e)
{
$sClassOfDefinition = $aQueryInfo['_legacy_'] ? get_class($this).'(or a parent)::GetRelationQueries()' : $aQueryInfo['sDefinedInClass'];
throw new Exception("Wrong query for the relation $sRelCode/$sClassOfDefinition/{$aQueryInfo['sNeighbour']}: ".$e->getMessage());
}
if ($oObj)
{
do
{
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
$sObjKey = $oObj->GetKey();
if (array_key_exists($sRootClass, $aResults))
{
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
{
continue; // already visited, skip
}
}
$aResults[$sRootClass][$sObjKey] = $oObj;
if ($iDepth > 0)
{
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
}
}
while ($oObj = $oObjSet->Fetch());
}
}
return $aResults;
}
/**
* Compute the "RelatedObjects" (forward or "down" direction) for the object
* for the specified relation

View File

@@ -63,15 +63,9 @@ class DBObjectSearch extends DBSearch
{
parent::__construct();
if (is_null($sClassAlias)) {
$sClassAlias = $sClass;
}
if (!is_string($sClass)) {
throw new Exception('DBObjectSearch::__construct called with a non-string parameter: $sClass = '.print_r($sClass, true));
}
if (!MetaModel::IsValidClass($sClass)) {
throw new Exception('DBObjectSearch::__construct called for an invalid class: "'.$sClass.'"');
}
if (is_null($sClassAlias)) $sClassAlias = $sClass;
if(!is_string($sClass)) throw new Exception('DBObjectSearch::__construct called with a non-string parameter: $sClass = '.print_r($sClass, true));
if(!MetaModel::IsValidClass($sClass)) throw new Exception('DBObjectSearch::__construct called for an invalid class: "'.$sClass.'"');
$this->m_aSelectedClasses = array($sClassAlias => $sClass);
$this->m_aClasses = array($sClassAlias => $sClass);
@@ -81,43 +75,30 @@ class DBObjectSearch extends DBSearch
$this->m_aReferencedBy = array();
}
public function AllowAllData($bAllowAllData = true) {
$this->m_bAllowAllData = $bAllowAllData;
$this->m_oSearchCondition->Browse(function ($oThisExpression) use ($bAllowAllData) {
ExpressionHelper::ExpressionAllowAllDataCallback($oThisExpression, $bAllowAllData);
});
}
public function IsAllDataAllowed() {
return $this->m_bAllowAllData;
}
protected function IsDataFiltered() {
return $this->m_bDataFiltered;
}
protected function SetDataFiltered() {
$this->m_bDataFiltered = true;
}
public function AllowAllData($bAllowAllData = true) {$this->m_bAllowAllData = $bAllowAllData;}
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
protected function IsDataFiltered() {return $this->m_bDataFiltered; }
protected function SetDataFiltered() {$this->m_bDataFiltered = true;}
// Create a search definition that leads to 0 result, still a valid search object
public static function FromEmptySet($sClass) {
static public function FromEmptySet($sClass)
{
$oResultFilter = new DBObjectSearch($sClass);
$oResultFilter->m_oSearchCondition = new FalseExpression;
return $oResultFilter;
}
public function GetJoinedClasses() {
return $this->m_aClasses;
}
public function GetJoinedClasses() {return $this->m_aClasses;}
public function GetClassName($sAlias) {
if (array_key_exists($sAlias, $this->m_aSelectedClasses)) {
public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->m_aSelectedClasses))
{
return $this->m_aSelectedClasses[$sAlias];
} else {
}
else
{
throw new CoreException("Invalid class alias '$sAlias'");
}
}
@@ -377,35 +358,37 @@ class DBObjectSearch extends DBSearch
}
foreach($this->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator) {
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters) {
foreach ($aFilters as $oForeignFilter) {
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $oForeignFilter)
{
$oForeignFilter->RenameParam($sOldName, $sNewName);
}
}
}
}
}
public function ResetCondition() {
public function ResetCondition()
{
$this->m_oSearchCondition = new TrueExpression();
// ? is that usefull/enough, do I need to rebuild the list after the subqueries ?
}
public function MergeConditionExpression($oExpression) {
$this->m_oSearchCondition = $this->m_oSearchCondition->LogOr($oExpression);
public function MergeConditionExpression($oExpression)
{
$this->m_oSearchCondition = $this->m_oSearchCondition->LogOr($oExpression);
}
public function AddConditionExpression($oExpression) {
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
$bRootSearchAllowAllData = $this->IsAllDataAllowed();
$oExpression->Browse(function ($oThisExpression) use ($bRootSearchAllowAllData) {
ExpressionHelper::ExpressionAllowAllDataCallback($oThisExpression, $bRootSearchAllowAllData);
});
public function AddConditionExpression($oExpression)
{
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
}
public function AddNameCondition($sName) {
public function AddNameCondition($sName)
{
$oValueExpr = new ScalarExpression($sName);
$oNameExpr = new FieldExpression('friendlyname', $this->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr);
@@ -416,10 +399,6 @@ class DBObjectSearch extends DBSearch
* @param string $sFilterCode
* @param mixed $value
* @param string $sOpCode operator to use : 'IN', 'NOT IN', 'Contains',' Begins with', 'Finishes with', ...
* If no operator is specified then :
* * for id field we will use "="
* * for other fields we will call the corresponding {@link AttributeDefinition::GetSmartConditionExpression} method impl
* to generate the expression
* @param bool $bParseSearchString
*
* @throws \CoreException
@@ -464,6 +443,7 @@ class DBObjectSearch extends DBSearch
case '<|':
case '=|':
throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
break;
case 'IN':
if (!is_array($value)) $value = array($value);
@@ -645,32 +625,17 @@ class DBObjectSearch extends DBSearch
public function AddCondition_FullText($sNeedle)
{
// Transform the full text condition into additional condition expression
$aAttCodes = [];
foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef) {
$aFullTextFields = array();
foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef)
{
if (!$oAttDef->IsScalar()) continue;
if ($oAttDef->IsExternalKey()) continue;
if (!$oAttDef->IsSearchable()) continue;
$aAttCodes[] = $sAttCode;
}
$this->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle);
}
/**
* @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)
{
$aFullTextFields = [];
foreach ($aAttCodes as $sAttCode) {
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
}
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
$sQueryParam = str_replace('.', '', uniqid('needle_', true));
$sQueryParam = 'needle';
$oFlexNeedle = new CharConcatExpression(array(new ScalarExpression('%'), new VariableExpression($sQueryParam), new ScalarExpression('%')));
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', $oFlexNeedle);
@@ -827,11 +792,10 @@ class DBObjectSearch extends DBSearch
* Helper to
* - convert a translation table (format optimized for the translation in an expression tree) into simple hash
* - compile over an eventually existing map
* - accept multiple translations for the same alias for unions
*
* @param array $aRealiasingMap Map to update
* @param array $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
* @return void of [old-alias][] => new-alias (@since 2.7.2)
* @return void of <old-alias> => <new-alias>
*/
protected function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
{
@@ -839,33 +803,17 @@ class DBObjectSearch extends DBSearch
{
foreach ($aAliasTranslation as $sPrevAlias => $aRules)
{
if (!isset($aRules['*']))
if (isset($aRules['*']))
{
continue;
}
$sNewAlias = $aRules['*'];
$bOriginalFound = false;
$iIndex = 0;
foreach ($aRealiasingMap as $sOriginalAlias => $aAliases)
{
$iIndex = array_search($sPrevAlias, $aAliases);
if ($iIndex !== false)
$sNewAlias = $aRules['*'];
$sOriginalAlias = array_search($sPrevAlias, $aRealiasingMap);
if ($sOriginalAlias !== false)
{
$bOriginalFound = true;
break;
$aRealiasingMap[$sOriginalAlias] = $sNewAlias;
}
}
if ($bOriginalFound)
{
$aRealiasingMap[$sOriginalAlias][$iIndex] = $sNewAlias;
}
else
{
if (!isset($aRealiasingMap[$sPrevAlias]) || !in_array($sNewAlias, $aRealiasingMap[$sPrevAlias]))
else
{
$aRealiasingMap[$sPrevAlias][] = $sNewAlias;
$aRealiasingMap[$sPrevAlias] = $sNewAlias;
}
}
}
@@ -911,7 +859,7 @@ class DBObjectSearch extends DBSearch
}
/**
* @param DBObjectSearch $oFilter (can be modified)
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
@@ -1003,7 +951,7 @@ class DBObjectSearch extends DBSearch
}
/**
* @param DBObjectSearch $oFilter (can be modified)
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
@@ -1232,7 +1180,7 @@ class DBObjectSearch extends DBSearch
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
{
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
$oRightFilter->ChangeClass($oLeftFilter->GetClass());
}
else
{
@@ -1376,7 +1324,7 @@ class DBObjectSearch extends DBSearch
// Make the list of acceptable arguments... could be factorized with run_query, into oSearch->GetQueryParams($bExclude magic params)
$aNakedMagicArguments = array();
foreach (MetaModel::PrepareQueryArguments(array()) as $sArgName => $value)
foreach (MetaModel::PrepareQueryArguments(array(),array(), $this->GetExpectedArguments()) as $sArgName => $value)
{
$iPos = strpos($sArgName, '->object()');
if ($iPos === false)
@@ -1439,7 +1387,7 @@ class DBObjectSearch extends DBSearch
{
$aParams = array_merge($aContextParams, $this->m_aParams);
}
$aParams = MetaModel::PrepareQueryArguments($aParams);
$aParams = MetaModel::PrepareQueryArguments($aParams,array(), $this->GetExpectedArguments());
}
else
{
@@ -1632,7 +1580,7 @@ class DBObjectSearch extends DBSearch
$aRet = array('selects' => array(), 'joins' => array(), 'where' => array());
$aParams = array_merge($this->m_aParams);
$aParams = MetaModel::PrepareQueryArguments($aParams);
$aParams = MetaModel::PrepareQueryArguments($aParams, array(), $this->GetExpectedArguments());
foreach ($this->m_aSelectedClasses as $sAlias => $sClass)
{
@@ -1839,7 +1787,7 @@ class DBObjectSearch extends DBSearch
{
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectDeleteQuery();
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments());
$sRet = $oSQLQuery->RenderDelete($aScalarArgs);
return $sRet;
}
@@ -1855,7 +1803,7 @@ class DBObjectSearch extends DBSearch
{
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments());
$sRet = $oSQLQuery->RenderUpdate($aScalarArgs);
return $sRet;
}
@@ -1874,7 +1822,7 @@ class DBObjectSearch extends DBSearch
{
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments());
$sRet = $oSQLQuery->RenderInsert($aScalarArgs);
return $sRet;
}

View File

@@ -763,10 +763,7 @@ class DBObjectSet implements iDBObjectSetIterator
try
{
$oKPI = new ExecutionKPI();
$this->m_oSQLResult = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
} catch (MySQLException $e)
{
// 1116 = ER_TOO_MANY_TABLES
@@ -846,11 +843,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery);
@@ -861,42 +855,6 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
}
/**
* @param \DBSearch $oFilter
* @param array $aOrder
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bCount
*
* @return string
*/
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
{
$sOQL = '';
if ($bCount) {
$sOQL .= 'COUNT ';
}
$sOQL .= $oFilter->ToOQL();
if ($iLimitCount > 0) {
$sOQL .= ' LIMIT ';
if ($iLimitStart > 0) {
$sOQL .= "$iLimitStart, ";
}
$sOQL .= "$iLimitCount";
}
if (count($aOrder) > 0) {
$sOQL .= ' ORDER BY ';
$aOrderBy = [];
foreach ($aOrder as $sAttCode => $bAsc) {
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
}
$sOQL .= implode(', ', $aOrderBy);
}
return $sOQL;
}
/**
* Check if the count exceeds a given limit
*
@@ -913,11 +871,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -928,7 +883,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -953,11 +908,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -968,7 +920,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -1360,34 +1312,6 @@ class DBObjectSet implements iDBObjectSetIterator
return $oNewSet;
}
/**
* Will be deprecated soon - use MetaModel::GetRelatedObjectsDown/Up instead to take redundancy into account
*
* @throws \Exception
*/
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
{
$aRelatedObjs = array();
$aVisited = array(); // optimization for consecutive calls of MetaModel::GetRelatedObjects
$this->Seek(0);
while ($oObject = $this->Fetch())
{
$aMore = $oObject->GetRelatedObjects($sRelCode, $iMaxDepth, $aVisited);
foreach ($aMore as $sClass => $aRelated)
{
foreach ($aRelated as $iObj => $oObj)
{
if (!isset($aRelatedObjs[$sClass][$iObj]))
{
$aRelatedObjs[$sClass][$iObj] = $oObj;
}
}
}
}
return $aRelatedObjs;
}
/**
* Compute the "RelatedObjects" (forward or "down" direction) for the set
* for the specified relation
@@ -1544,7 +1468,7 @@ class DBObjectSet implements iDBObjectSetIterator
public function ListConstantFields()
{
// The complete list of arguments will include magic arguments (e.g. current_user->attcode)
$aScalarArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs);
$aScalarArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs, $this->m_oFilter->ListParameters());
$aConst = $this->m_oFilter->ListConstantFields();
foreach($aConst as $sClassAlias => $aVals)
@@ -1563,7 +1487,7 @@ class DBObjectSet implements iDBObjectSetIterator
public function ApplyParameters()
{
$aAllArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs);
$aAllArgs = MetaModel::PrepareQueryArguments($this->m_oFilter->GetInternalParams(), $this->m_aArgs, $this->m_oFilter->GetExpectedArguments());
$this->m_oFilter->ApplyParameters($aAllArgs);
}
}

View File

@@ -97,40 +97,42 @@ abstract class DBSearch
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
*
* @internal
*
* @internal
*
* @return \DBSearch
**/
**/
public function DeepClone()
{
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
}
/**
* @api
* @see IsAllDataAllowed()
*
* @param bool $bAllowAllData whether or not some information should be hidden to the current user.
*/
abstract public function AllowAllData($bAllowAllData = true);
/**
* whether or not some information should be hidden to the current user.
*
* @api
* @see IsAllDataAllowed()
*
* @return mixed
*/
abstract public function AllowAllData();
/**
* Current state of AllowAllData
*
* @internal
* @see AllowAllData()
*
* @return mixed
*/
/**
* Current state of AllowAllData
*
* @internal
* @see AllowAllData()
*
* @return mixed
*/
abstract public function IsAllDataAllowed();
/**
* Should the archives be fetched
*
* @internal
*
* @param $bEnable
*/
/**
* Should the archives be fetched
*
* @internal
*
* @param $bEnable
*/
public function SetArchiveMode($bEnable)
{
$this->m_bArchiveMode = $bEnable;
@@ -402,9 +404,7 @@ abstract class DBSearch
*/
abstract public function AddCondition_FullText($sFullText);
abstract public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle);
/**
/**
* Perform a join, the remote class being matched by the mean of its primary key
*
* The join is performed
@@ -631,7 +631,7 @@ abstract class DBSearch
}
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return urlencode(json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties)));
return json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties));
}
/**
@@ -648,7 +648,7 @@ abstract class DBSearch
*/
static public function unserialize($sValue)
{
$aData = json_decode(urldecode($sValue), true);
$aData = json_decode($sValue, true);
if (is_null($aData))
{
throw new CoreException("Invalid filter parameter");
@@ -754,14 +754,14 @@ abstract class DBSearch
* @see DBSearch::ToOQL()
*
* @param string $sQuery The OQL to convert to a DBSearch
* @param array $aParams array of <mixed> params index by <string> name
* @param mixed[string] $aParams array of <mixed> params index by <string> name
* @param ModelReflection|null $oMetaModel The MetaModel to use when checking the consistency of the OQL
*
* @return DBObjectSearch|DBUnionSearch
*
* @throws OQLException
*/
public static function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel=null)
static public function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel=null)
{
if (empty($sQuery))
{
@@ -866,11 +866,11 @@ abstract class DBSearch
return;
}
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
}
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
@@ -900,55 +900,6 @@ abstract class DBSearch
return $aRes;
}
/**
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
* @param string $sAttCode
* @param string|null $sClassAlias
*
* @return array
* @throws ConfigException
* @throws CoreException
* @throws MissingQueryArgument
* @throws MySQLException
* @throws MySQLHasGoneAwayException
*/
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
{
if(is_null($sClassAlias)) {
$sClassAlias = $this->GetClassAlias();
}
$sClass = $this->GetClass();
if($sAttCode === 'id'){
$aAttToLoad[$sClassAlias]=[];
} else {
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
}
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery)
{
return [];
}
$sColName = $sClassAlias.$sAttCode;
$aRes = [];
while ($aRow = CMDBSource::FetchArray($resQuery))
{
$aMappedRow = array();
if($sAttCode === 'id') {
$aMappedRow[$sAttCode] = $aRow[$sColName];
} else {
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
}
$aRes[] = $aMappedRow;
}
CMDBSource::FreeResult($resQuery);
return $aRes;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries
@@ -1053,8 +1004,10 @@ abstract class DBSearch
}
/**
* Generate a SQL query from the current search
*
* Generate a SQL query from the current search
*
* @internal
*
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
* @param null $aAttToLoad
@@ -1062,16 +1015,12 @@ abstract class DBSearch
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bGetCount
* @param bool $bBeautifulSQL
*
* @return string
* @throws \ConfigException
* @throws \CoreException
* @throws \MissingQueryArgument
* @internal
*
* @throws CoreException
* @throws Exception
* @throws MissingQueryArgument
*/
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
{
// Check the order by specification, and prefix with the class alias
// and make sure that the ordering columns are going to be selected
@@ -1136,7 +1085,8 @@ abstract class DBSearch
}
try
{
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
if ($sClassAlias == '_itop_')
{
IssueLog::Info('SQL Query (_itop_): '.$sRes);
@@ -1162,8 +1112,6 @@ abstract class DBSearch
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*
* @since 2.7.0 N°2555
*/
public function GetFirstResult($bMustHaveOneResultMax = true, $aOrderBy = array(), $aSearchParams = array())
{
@@ -1236,30 +1184,6 @@ 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());
@@ -1672,7 +1596,7 @@ abstract class DBSearch
$oSet = new DBObjectSet($this);
if (MetaModel::IsStandaloneClass($sClass))
{
$oSet->OptimizeColumnLoad(array($this->GetClassAlias() => array()));
$oSet->OptimizeColumnLoad(array($this->GetClassAlias() => array('')));
$aIds = array($sClass => $oSet->GetColumnAsArray('id'));
}
else

View File

@@ -65,7 +65,9 @@ class DBUnionSearch extends DBSearch
{
$this->aSearches[] = $oSubSearch->DeepClone();
}
} else {
}
else
{
$this->aSearches[] = $oSearch->DeepClone();
}
}
@@ -73,16 +75,17 @@ class DBUnionSearch extends DBSearch
$this->ComputeSelectedClasses();
}
public function AllowAllData($bAllowAllData = true)
public function AllowAllData()
{
foreach ($this->aSearches as $oSearch) {
foreach ($this->aSearches as $oSearch)
{
$oSearch->AllowAllData();
}
}
public function IsAllDataAllowed()
{
foreach ($this->aSearches as $oSearch) {
foreach ($this->aSearches as $oSearch)
{
if ($oSearch->IsAllDataAllowed() === false) return false;
}
return true;
@@ -373,26 +376,19 @@ class DBUnionSearch extends DBSearch
}
}
public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle);
}
}
/**
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oConditionFilter = $oFilter->DeepClone();
$oSearch->AddCondition_PointingTo($oConditionFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
@@ -400,14 +396,13 @@ class DBUnionSearch extends DBSearch
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oConditionFilter = $oFilter->DeepClone();
$oSearch->AddCondition_ReferencedBy($oConditionFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
@@ -416,11 +411,7 @@ class DBUnionSearch extends DBSearch
$aSearches = array();
foreach ($this->aSearches as $oSearch)
{
if (!$oSearch->IsAllDataAllowed()) {
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
} else {
$aSearches[] = $oSearch;
}
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
}
return new DBUnionSearch($aSearches);
}

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -18,7 +18,7 @@
/**
* Class Dict
* Management of localizable strings
* Management of localizable strings
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -64,8 +64,6 @@ class Dict
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
protected static $m_aData = array();
protected static $m_sApplicationPrefix = null;
/** @var \ApcService $m_oApcService */
protected static $m_oApcService = null;
/**
* @param $sLanguageCode
@@ -144,69 +142,50 @@ class Dict
* @return string
*/
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
return $aInfo['label'];
}
/**
* Returns a localised string from the dictonary with its associated lang code
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
*
* @return array{
* lang: string, label: string
* } with localized label string and used lang code
*/
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
// Attempt to find the string in the user language
//
$sLangCode = self::GetUserLanguage();
self::InitLangIfNeeded($sLangCode);
self::InitLangIfNeeded(self::GetUserLanguage());
if (! array_key_exists($sLangCode, self::$m_aData))
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
{
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
// It may happen, when something happens before the dictionaries get loaded
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[$sLangCode];
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
if (array_key_exists($sStringCode, $aCurrentDictionary))
{
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
return $aCurrentDictionary[$sStringCode];
}
if (!$bUserLanguageOnly)
{
// Attempt to find the string in the default language
//
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
self::InitLangIfNeeded('EN US');
$aDefaultDictionary = self::$m_aData['EN US'];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
return $aDefaultDictionary[$sStringCode];
}
}
// Could not find the string...
//
if (is_null($sDefault))
{
return [ 'label' => $sStringCode, 'lang' => null ];
return $sStringCode;
}
return [ 'label' => $sDefault, 'lang' => null ];
return $sDefault;
}
@@ -222,25 +201,19 @@ class Dict
*/
public static function Format($sFormatCode /*, ... arguments ....*/)
{
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
$sLocalizedFormat = self::S($sFormatCode);
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
try{
return vsprintf($sLocalizedFormat, $aArguments);
} catch(\Throwable $e){
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
}
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
@@ -250,7 +223,7 @@ class Dict
{
self::$m_aData[$sLanguageCode] = $aEntries;
}
/**
* Set the list of available languages
* @param hash $aLanguagesList
@@ -259,26 +232,7 @@ class Dict
{
self::$m_aLanguages = $aLanguagesList;
}
/**
* @since 2.7.6 N°4125
* @return \ApcService
*/
public static function GetApcService() {
if (self::$m_oApcService === null){
self::$m_oApcService = new ApcService();
}
return self::$m_oApcService;
}
/**
* @since 2.7.6 N°4125
* @param \ApcService $m_oApcService
*/
public static function SetApcService($oApcService) {
self::$m_oApcService = $oApcService;
}
/**
* Load a language from the language dictionary, if not already loaded
* @param string $sLangCode Language code
@@ -287,23 +241,20 @@ class Dict
public static function InitLangIfNeeded($sLangCode)
{
if (array_key_exists($sLangCode, self::$m_aData)) return true;
$bResult = false;
if (self::GetApcService()->function_exists('apc_fetch')
&& (self::$m_sApplicationPrefix !== null))
if (function_exists('apc_fetch') && (self::$m_sApplicationPrefix !== null))
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
if (self::$m_aData[$sLangCode] === false) {
self::$m_aData[$sLangCode] = apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
if (self::$m_aData[$sLangCode] === false)
{
unset(self::$m_aData[$sLangCode]);
} else if (! is_array(self::$m_aData[$sLangCode])) {
// N°4125: we dont fix dictionnary corrupted cache (on iTop side).
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
$bResult = true;
} else {
}
else
{
$bResult = true;
}
}
@@ -311,17 +262,16 @@ class Dict
{
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
require_once($sDictFile);
if (self::GetApcService()->function_exists('apc_store')
&& (self::$m_sApplicationPrefix !== null))
if (function_exists('apc_store') && (self::$m_sApplicationPrefix !== null))
{
self::GetApcService()->apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
}
$bResult = true;
}
return $bResult;
}
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
@@ -365,14 +315,14 @@ class Dict
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
@@ -380,7 +330,7 @@ class Dict
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
@@ -403,7 +353,7 @@ class Dict
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
@@ -426,7 +376,7 @@ class Dict
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
}
/**
* Export all the dictionary entries - of the given language - whose code matches the given prefix
* missing entries in the current language will be replaced by entries in the default language
@@ -439,7 +389,7 @@ class Dict
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
{
@@ -448,7 +398,7 @@ class Dict
$aEntries[$sCode] = $sEntry;
}
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
{

View File

@@ -1203,10 +1203,8 @@ class DisplayableGraph extends SimpleGraph
* @param float $xMax Right coordinate of the bounding box to display the graph
* @param float $yMin Top coordinate of the bounding box to display the graph
* @param float $yMax Bottom coordinate of the bounding box to display the graph
*
* @since 2.7.7 3.0.2 3.1.0 N°4985 $sComments param is no longer optional
*/
function RenderAsPDF(PDFPage $oPage, $sComments, $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
function RenderAsPDF(PDFPage $oPage, $sComments = '', $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
{
$aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters
$oPdf = $oPage->get_tcpdf();
@@ -1433,25 +1431,83 @@ class DisplayableGraph extends SimpleGraph
* @param int $iObjKey
* @param string $sContextKey
* @param array $aContextParams
* @param bool $bLazyLoading since 2.7.7 3.0.1
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), $bLazyLoading = false)
{
list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading);
$iGroupingThreshold = utils::ReadParam('g', 5);
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array())
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach($aExcludedObjects as $oObj)
{
if (!array_key_exists(get_class($oObj), $aExcludedByClass))
{
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$sSftShort = Dict::S('UI:ElementsDisplayed');
$sSearchToggle = Dict::S('UI:Search:Toggle');
$oP->add("<div class=\"not-printable\">\n");
$oP->add(
<<<EOF
<div id="ds_flash" class="search_box">
<form id="dh_flash" class="search_form_handler closed">
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fas fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
EOF
);
$oP->add_ready_script(
<<<EOF
$("#dh_flash > .sf_title").click( function() {
$("#dh_flash").toggleClass('closed');
});
$('#ReloadMovieBtn').button().button('disable');
EOF
);
$aSortedElements = array();
foreach($aResults as $sClassIdx => $aObjects)
{
foreach($aObjects as $oCurrObj)
{
$sSubClass = get_class($oCurrObj);
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
asort($aSortedElements);
$idx = 0;
foreach($aSortedElements as $sSubClass => $sClassName)
{
$oP->add("<span style=\"padding-right:2em; white-space:nowrap;\"><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">&nbsp;".MetaModel::GetClassIcon($sSubClass)."&nbsp;$sClassName</label></span> ");
$idx++;
}
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></p>");
$oP->add("</div></div></form>");
$oP->add("</div>\n");
$oP->add("</div>\n"); // class="not-printable"
$aAdditionalContexts = array();
foreach($aContextDefs as $sKey => $aDefinition)
{
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
}
$sDirection = utils::ReadParam('d', 'horizontal');
$iGroupingThreshold = utils::ReadParam('g', 5);
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/fraphael.js');
$oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/jquery.contextMenu.css');
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.contextMenu.js');
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/simple_graph.js');
try
{
$this->InitFromGraphviz();
$sExportAsPdfURL = '';
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$oAppcontext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
@@ -1530,14 +1586,7 @@ class DisplayableGraph extends SimpleGraph
// Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple
unset($aParams['export_as_attachment']);
}
if ($oP->IsPrintableVersion() || !$bLazyLoading) {
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
} else {
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
$oP->add_ready_script("$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
}
$oP->add_ready_script("$('#$sId').simple_graph(".json_encode($aParams).");");
}
catch(Exception $e)
{
@@ -1569,86 +1618,5 @@ class DisplayableGraph extends SimpleGraph
EOF
);
}
/**
* @param $sContextKey
* @param array $aContextParams
* @param array $aExcludedObjects
* @param \WebPage $oP
* @param array $aResults
* @param bool $bLazyLoading
*
* @return array
* @throws \CoreException
* @throws \DictExceptionMissingString
* @since 2.7.7 & 3.0.1
*/
protected function DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading)
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$sSftShort = Dict::S('UI:ElementsDisplayed');
$sSearchToggle = Dict::S('UI:Search:Toggle');
$oP->add("<div class=\"not-printable\">\n");
$oP->add(
<<<EOF
<div id="ds_flash" class="search_box">
<form id="dh_flash" class="search_form_handler">
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fas fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
EOF
);
$oP->add_ready_script(
<<<EOF
$("#dh_flash > .sf_title").click( function() {
$("#dh_flash").toggleClass('closed');
});
$('#ReloadMovieBtn').button().button('disable');
EOF
);
if ($bLazyLoading) {
$oP->add_ready_script("$('#ReloadMovieBtn').button('enable');");
} else {
$oP->add_ready_script("$('#dh_flash').addClass('closed');");
}
$aSortedElements = array();
foreach ($aResults as $sClassIdx => $aObjects) {
foreach ($aObjects as $oCurrObj) {
$sSubClass = get_class($oCurrObj);
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
asort($aSortedElements);
$idx = 0;
foreach ($aSortedElements as $sSubClass => $sClassName) {
$oP->add("<span style=\"padding-right:2em; white-space:nowrap;\"><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">&nbsp;".MetaModel::GetClassIcon($sSubClass)."&nbsp;$sClassName</label></span> ");
$idx++;
}
if ($bLazyLoading) {
$sOnCLick = "Load(); $('#ReloadMovieBtn').attr('onclick','DoReload()');$('#ReloadMovieBtn').html('".Dict::S('UI:Button:Refresh')."');";
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"$sOnCLick\">".Dict::S('Relation:impacts/LoadData')."</button></p>");
} else {
$sOnCLick = "DoReload()";
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></p>");
}
$oP->add("</div></div></form>");
$oP->add("</div>\n");
$oP->add("</div>\n"); // class="not-printable"
$aAdditionalContexts = array();
foreach ($aContextDefs as $sKey => $aDefinition) {
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
}
return array($aExcludedByClass, $aAdditionalContexts);
}
}

View File

@@ -24,69 +24,38 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Core\Email\EmailFactory;
use Combodo\iTop\Core\Email\iEMail;
Swift_Preferences::getInstance()->setCharset('UTF-8');
define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
class EMail implements iEMail
class EMail
{
/**
* @see self::LoadConfig()
* @var Config
* @since 2.7.7 3.0.2 3.1.0 N°3169 N°5102 Move attribute to children classes
* @since 2.7.8 3.0.3 3.1.0 N°4947 pull up the attribute back to the Email class as config init is done here
*/
protected static $m_oConfig = null;
protected $oMailer;
// Serialization formats
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
protected static $m_oConfig = null;
protected $m_aData; // For storing data to serialize
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
{
if (is_null(self::$m_oConfig))
{
self::$m_oConfig = new Config($sConfigFile);
}
}
protected $m_oMessage;
public function __construct()
{
$this->oMailer = EmailFactory::GetMailer();
}
/**
* Sets {@see m_oConfig} if current attribute is null
*
* @returns \Config the current {@see m_oConfig} value
* @throws \ConfigException
* @throws \CoreException
*
* @uses utils::GetConfig()
*
* @since 2.7.7 3.0.2 3.1.0 N°3169 N°5102 Move method to children classes
* @since 2.7.8 3.0.3 3.1.0 N°4947 Pull up to the parent class, and remove `$sConfigFile` param
*/
public function LoadConfig()
{
if (is_null(static::$m_oConfig)) {
static::$m_oConfig = utils::GetConfig();
}
return static::$m_oConfig;
}
/**
* @return void
* @throws \ConfigException
* @throws \CoreException
* @since 2.7.8 3.0.3 3.1.0 N°4947 Method creation, to factorize same code in children classes
*/
protected function InitRecipientFrom()
{
$oConfig = $this->LoadConfig();
$this->SetRecipientFrom(
$oConfig->Get('email_default_sender_address'),
$oConfig->Get('email_default_sender_label')
);
$this->m_aData = array();
$this->m_oMessage = Swift_Message::newInstance();
$this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label'));
}
/**
@@ -97,96 +66,485 @@ class EMail implements iEMail
*/
public function SerializeV2()
{
return $this->oMailer->SerializeV2();
return serialize($this->m_aData);
}
/**
* Custom de-serialization method
*
* @param string $sSerializedMessage The serialized representation of the message
*
* @return \Email
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
*/
static public function UnSerializeV2($sSerializedMessage)
{
return EmailFactory::GetMailer()::UnSerializeV2($sSerializedMessage);
$aData = unserialize($sSerializedMessage);
$oMessage = new Email();
if (array_key_exists('body', $aData))
{
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
}
if (array_key_exists('message_id', $aData))
{
$oMessage->SetMessageId($aData['message_id']);
}
if (array_key_exists('bcc', $aData))
{
$oMessage->SetRecipientBCC($aData['bcc']);
}
if (array_key_exists('cc', $aData))
{
$oMessage->SetRecipientCC($aData['cc']);
}
if (array_key_exists('from', $aData))
{
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
}
if (array_key_exists('reply_to', $aData))
{
$oMessage->SetRecipientReplyTo($aData['reply_to']);
}
if (array_key_exists('to', $aData))
{
$oMessage->SetRecipientTO($aData['to']);
}
if (array_key_exists('subject', $aData))
{
$oMessage->SetSubject($aData['subject']);
}
if (array_key_exists('headers', $aData))
{
foreach($aData['headers'] as $sKey => $sValue)
{
$oMessage->AddToHeader($sKey, $sValue);
}
}
if (array_key_exists('parts', $aData))
{
foreach($aData['parts'] as $aPart)
{
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
}
}
if (array_key_exists('attachments', $aData))
{
foreach($aData['attachments'] as $aAttachment)
{
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
}
}
return $oMessage;
}
protected function SendAsynchronous(&$aIssues, $oLog = null)
{
try
{
AsyncSendEmail::AddToQueue($this, $oLog);
}
catch(Exception $e)
{
$aIssues = array($e->GetMessage());
return EMAIL_SEND_ERROR;
}
$aIssues = array();
return EMAIL_SEND_PENDING;
}
protected function SendSynchronous(&$aIssues, $oLog = null)
{
// If the body of the message is in HTML, embed all images based on attachments
$this->EmbedInlineImages();
$this->LoadConfig();
$sTransport = self::$m_oConfig->Get('email_transport');
switch ($sTransport)
{
case 'SMTP':
$sHost = self::$m_oConfig->Get('email_transport_smtp.host');
$sPort = self::$m_oConfig->Get('email_transport_smtp.port');
$sEncryption = self::$m_oConfig->Get('email_transport_smtp.encryption');
$sUserName = self::$m_oConfig->Get('email_transport_smtp.username');
$sPassword = self::$m_oConfig->Get('email_transport_smtp.password');
$oTransport = Swift_SmtpTransport::newInstance($sHost, $sPort, $sEncryption);
if (strlen($sUserName) > 0)
{
$oTransport->setUsername($sUserName);
$oTransport->setPassword($sPassword);
}
break;
case 'Null':
$oTransport = Swift_NullTransport::newInstance();
break;
case 'LogFile':
$oTransport = Swift_LogFileTransport::newInstance();
$oTransport->setLogFile(APPROOT.'log/mail.log');
break;
case 'PHPMail':
default:
$oTransport = Swift_MailTransport::newInstance();
}
$oMailer = Swift_Mailer::newInstance($oTransport);
$aFailedRecipients = array();
$this->m_oMessage->setMaxLineLength(0);
$oKPI = new ExecutionKPI();
try
{
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
$oKPI->ComputeStats('Email Sent', 'Error received');
return EMAIL_SEND_ERROR;
}
else
{
$aIssues = array();
$oKPI->ComputeStats('Email Sent', 'Succeded');
return EMAIL_SEND_OK;
}
}
catch (Exception $e)
{
$oKPI->ComputeStats('Email Sent', 'Error received');
throw $e;
}
}
/**
* Reprocess the body of the message (if it is an HTML message)
* to replace the URL of images based on attachments by a link
* to an embedded image (i.e. cid:....)
*/
protected function EmbedInlineImages()
{
if ($this->m_aData['body']['mimeType'] == 'text/html')
{
$oDOMDoc = new DOMDocument();
$oDOMDoc->preserveWhitespace = true;
@$oDOMDoc->loadHTML('<?xml encoding="UTF-8"?>'.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified
$oXPath = new DOMXPath($oDOMDoc);
$sXPath = '//img[@'.InlineImage::DOM_ATTR_ID.']';
$oImagesList = $oXPath->query($sXPath);
if ($oImagesList->length != 0)
{
foreach($oImagesList as $oImg)
{
$iAttId = $oImg->getAttribute(InlineImage::DOM_ATTR_ID);
$oAttachment = MetaModel::GetObject('InlineImage', $iAttId, false, true /* Allow All Data */);
if ($oAttachment)
{
$sImageSecret = $oImg->getAttribute('data-img-secret');
$sAttachmentSecret = $oAttachment->Get('secret');
if ($sImageSecret !== $sAttachmentSecret)
{
// @see N°1921
// If copying from another iTop we could get an IMG pointing to an InlineImage with wrong secret
continue;
}
$oDoc = $oAttachment->Get('contents');
$oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType());
$sCid = $this->m_oMessage->embed($oSwiftImage);
$oImg->setAttribute('src', $sCid);
}
}
}
$sHtmlBody = $oDOMDoc->saveHTML();
$this->m_oMessage->setBody($sHtmlBody, 'text/html', 'UTF-8');
}
}
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{
return $this->oMailer->Send($aIssues, $bForceSynchronous, $oLog);
//select a default sender if none is provided.
if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){
$this->SetRecipientFrom($this->m_aData['to']);
}
if ($bForceSynchronous)
{
return $this->SendSynchronous($aIssues, $oLog);
}
else
{
$bConfigASYNC = MetaModel::GetConfig()->Get('email_asynchronous');
if ($bConfigASYNC)
{
return $this->SendAsynchronous($aIssues, $oLog);
}
else
{
return $this->SendSynchronous($aIssues, $oLog);
}
}
}
public function AddToHeader($sKey, $sValue)
{
$this->oMailer->AddToHeader($sKey, $sValue);
if (!array_key_exists('headers', $this->m_aData))
{
$this->m_aData['headers'] = array();
}
$this->m_aData['headers'][$sKey] = $sValue;
if (strlen($sValue) > 0)
{
$oHeaders = $this->m_oMessage->getHeaders();
switch(strtolower($sKey))
{
case 'return-path':
$this->m_oMessage->setReturnPath($sValue);
break;
default:
$oHeaders->addTextHeader($sKey, $sValue);
}
}
}
public function SetMessageId($sId)
{
$this->oMailer->SetMessageId($sId);
$this->m_aData['message_id'] = $sId;
// Note: Swift will add the angle brackets for you
// so let's remove the angle brackets if present, for historical reasons
$sId = str_replace(array('<', '>'), '', $sId);
$oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID');
$oMsgId->SetId($sId);
}
public function SetReferences($sReferences)
{
$this->oMailer->SetReferences($sReferences);
$this->AddToHeader('References', $sReferences);
}
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
{
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
{
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
}
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
$this->m_oMessage->setBody($sBody, $sMimeType);
}
public function AddPart($sText, $sMimeType = 'text/html')
{
$this->oMailer->AddPart($sText, $sMimeType);
if (!array_key_exists('parts', $this->m_aData))
{
$this->m_aData['parts'] = array();
}
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
$this->m_oMessage->addPart($sText, $sMimeType);
}
public function AddAttachment($data, $sFileName, $sMimeType)
{
$this->oMailer->AddAttachment($data, $sFileName, $sMimeType);
if (!array_key_exists('attachments', $this->m_aData))
{
$this->m_aData['attachments'] = array();
}
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
}
public function SetSubject($sSubject)
{
$this->oMailer->SetSubject($sSubject);
$this->m_aData['subject'] = $sSubject;
$this->m_oMessage->setSubject($sSubject);
}
public function GetSubject()
{
return $this->oMailer->GetSubject();
return $this->m_oMessage->getSubject();
}
/**
* Helper to transform and sanitize addresses
* - get rid of empty addresses
*/
protected function AddressStringToArray($sAddressCSVList)
{
$aAddresses = array();
foreach(explode(',', $sAddressCSVList) as $sAddress)
{
$sAddress = trim($sAddress);
if (strlen($sAddress) > 0)
{
$aAddresses[] = $sAddress;
}
}
return $aAddresses;
}
public function SetRecipientTO($sAddress)
{
$this->oMailer->SetRecipientTO($sAddress);
$this->m_aData['to'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setTo($aAddresses);
}
}
public function GetRecipientTO($bAsString = false)
{
return $this->oMailer->GetRecipientTO($bAsString);
$aRes = $this->m_oMessage->getTo();
if ($aRes === null)
{
// There is no "To" header field
$aRes = array();
}
if ($bAsString)
{
$aStrings = array();
foreach ($aRes as $sEmail => $sName)
{
if (is_null($sName))
{
$aStrings[] = $sEmail;
}
else
{
$sName = str_replace(array('<', '>'), '', $sName);
$aStrings[] = "$sName <$sEmail>";
}
}
return implode(', ', $aStrings);
}
else
{
return $aRes;
}
}
public function SetRecipientCC($sAddress)
{
$this->oMailer->SetRecipientCC($sAddress);
$this->m_aData['cc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setCc($aAddresses);
}
}
public function SetRecipientBCC($sAddress)
{
$this->oMailer->SetRecipientBCC($sAddress);
$this->m_aData['bcc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setBcc($aAddresses);
}
}
public function SetRecipientFrom($sAddress, $sLabel = '')
{
$this->oMailer->SetRecipientFrom($sAddress, $sLabel);
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
if ($sLabel != '')
{
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
}
else if (!empty($sAddress))
{
$this->m_oMessage->setFrom($sAddress);
}
}
public function SetRecipientReplyTo($sAddress)
{
$this->oMailer->SetRecipientReplyTo($sAddress);
$this->m_aData['reply_to'] = $sAddress;
if (!empty($sAddress))
{
$this->m_oMessage->setReplyTo($sAddress);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
/**
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
* but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
{
protected $sLogFile;
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$hFile = @fopen($this->sLogFile, 'a');
if ($hFile)
{
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
$sTxt .= $message->toString()."\n";
@fwrite($hFile, $sTxt);
@fclose($hFile);
}
return parent::send($message, $failedRecipients);
}
public function setLogFile($sFilename)
{
$this->sLogFile = $sFilename;
}
}
/**
* Pretends messages have been sent, but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
{
/**
* Create a new LogFileTransport.
*/
public function __construct()
{
call_user_func_array(
array($this, 'Swift_Transport_LogFileTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.null')
);
}
/**
* Create a new LogFileTransport instance.
*
* @return Swift_LogFileTransport
*/
public static function newInstance()
{
return new self();
}
}

View File

@@ -23,8 +23,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\ExportHelper;
require_once(APPROOT.'application/xlsxwriter.class.php');
class ExcelBulkExport extends TabularBulkExport
@@ -91,7 +89,6 @@ class ExcelBulkExport extends TabularBulkExport
case 'xlsx_options':
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:XLSXOptions').'</legend>');
$oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
@@ -356,8 +353,7 @@ EOF
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$sDateFormat = isset($this->aStatusInfo['date_format']) ? $this->aStatusInfo['date_format'] : (string)AttributeDateTime::GetFormat();
$oDateTimeFormat = new DateTimeFormat($sDateFormat);
$oDateTimeFormat = new DateTimeFormat($this->aStatusInfo['date_format']);
$writer->setDateTimeFormat($oDateTimeFormat->ToExcel());
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
$writer->setDateFormat($oDateFormat->ToExcel());

View File

@@ -24,59 +24,52 @@ abstract class HTMLSanitizer
{
// Do nothing..
}
/**
* Sanitizes the given HTML document
* @param string $sHTML
* @return string
*/
abstract public function DoSanitize($sHTML);
/**
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
*
* @param string $sHTML
* @param string $sConfigKey
*
* @return string
* @noinspection SelfClassReferencingInspection
*/
public static function Sanitize($sHTML, $sConfigKey = 'html_sanitizer')
public static function Sanitize($sHTML)
{
$sSanitizerClass = MetaModel::GetConfig()->Get($sConfigKey);
if (!class_exists($sSanitizerClass)) {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
if(!class_exists($sSanitizerClass))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
}
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else if (false === is_subclass_of($sSanitizerClass, HTMLSanitizer::class)) {
if ($sConfigKey === 'html_sanitizer') {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else {
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
return $sHTML;
}
}
try {
try
{
$oSanitizer = new $sSanitizerClass();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
catch(Exception $e) {
if ($sConfigKey === 'html_sanitizer') {
if ($sSanitizerClass !== HTMLDOMSanitizer::class) {
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer
$oSanitizer = new HTMLDOMSanitizer();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
} else {
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
IssueLog::Error('The HTML will NOT be sanitized.');
$sCleanHTML = $sHTML;
}
} else {
IssueLog::Error('Failed to sanitize string with "'.$sSanitizerClass.'", will return original value ! Exception: '.$e->getMessage());
catch(Exception $e)
{
if($sSanitizerClass != 'HTMLDOMSanitizer')
{
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer
$oSanitizer = new HTMLDOMSanitizer();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
else
{
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
IssueLog::Error('The HTML will NOT be sanitized.');
$sCleanHTML = $sHTML;
}
}
@@ -107,176 +100,64 @@ class HTMLNullSanitizer extends HTMLSanitizer
}
/**
* Common implementation for sanitizer using DOM parsing
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
* Complete but quite slow
* http://htmlpurifier.org
*/
abstract class DOMSanitizer extends HTMLSanitizer
/*
class HTMLPurifierSanitizer extends HTMLSanitizer
{
/** @var DOMDocument */
protected $oDoc;
/**
* @var string Class to use for InlineImage static method calls
* @used-by \Combodo\iTop\Test\UnitTest\Core\Sanitizer\HTMLDOMSanitizerTest::testDoSanitizeCallInlineImageProcessImageTag
*/
protected $sInlineImageClassName;
protected static $oPurifier = null;
public function __construct($sInlineImageClassName = InlineImage::class)
public function __construct()
{
parent::__construct();
if (self::$oPurifier == null)
{
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
if (!file_exists($sLibPath))
{
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
}
require_once($sLibPath);
$this->sInlineImageClassName = $sInlineImageClassName;
$oPurifierConfig = HTMLPurifier_Config::createDefault();
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
$oPurifierConfig->set('URI.AllowedSchemes', array (
'http' => true,
'https' => true,
'data' => true, // This one is not present by default
));
$sPurifierCache = APPROOT.'data/HTMLPurifier';
if (!is_dir($sPurifierCache))
{
mkdir($sPurifierCache);
}
if (!is_dir($sPurifierCache))
{
throw new Exception("Could not create the cache directory '$sPurifierCache'");
}
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
}
}
abstract public function GetTagsWhiteList();
abstract public function GetTagsBlackList();
abstract public function GetAttrsWhiteList();
abstract public function GetAttrsBlackList();
abstract public function GetStylesWhiteList();
public function DoSanitize($sHTML)
{
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
$this->LoadDoc($sHTML);
$this->CleanNode($this->oDoc);
$sCleanHtml = $this->PrintDoc();
$sCleanHtml = self::$oPurifier->purify($sHTML);
return $sCleanHtml;
}
abstract public function LoadDoc($sHTML);
/**
* @return string cleaned source
* @uses \DOMSanitizer::oDoc
*/
abstract public function PrintDoc();
protected function CleanNode(DOMNode $oElement)
{
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes()) {
foreach ($oElement->attributes as $oAttr) {
$sAttr = strtolower($oAttr->name);
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
$aAttrToRemove[] = $oAttr->name;
} else if ((false === empty($this->GetTagsWhiteList()))
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
$aAttrToRemove[] = $oAttr->name;
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else if ($sAttr == 'style') {
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '') {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else {
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode) {
if ($oNode instanceof DOMElement) {
$sNodeTagName = strtolower($oNode->tagName);
}
if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsBlackList()))
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
$aChildElementsToRemove[] = $oNode;
} else if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsWhiteList()))
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
$aChildElementsToRemove[] = $oNode;
} else if ($oNode instanceof DOMComment) {
$aChildElementsToRemove[] = $oNode;
} else {
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
$this->sInlineImageClassName::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
}
protected function IsValidAttributeContent($sAttributeName, $sValue)
{
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttributeName, $this->GetAttrsBlackList(), true))) {
return true;
}
if (array_key_exists($sAttributeName, $this->GetAttrsWhiteList())) {
return preg_match($this->GetAttrsWhiteList()[$sAttributeName], $sValue);
}
return true;
}
protected function CleanStyle($sStyle)
{
if (empty($this->GetStylesWhiteList())) {
return $sStyle;
}
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach ($aItems as $sItem) {
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), $this->GetStylesWhiteList())) {
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
}
}
*/
class HTMLDOMSanitizer extends DOMSanitizer
class HTMLDOMSanitizer extends HTMLSanitizer
{
protected $oDoc;
/**
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aTagsWhiteList = array(
'html' => array(),
@@ -333,8 +214,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
);
/**
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aStylesWhiteList = array(
'background-color',
@@ -358,200 +239,164 @@ class HTMLDOMSanitizer extends DOMSanitizer
'white-space',
);
public function __construct($sInlineImageClassName = InlineImage::class)
public function __construct()
{
parent::__construct($sInlineImageClassName);
parent::__construct();
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
if (!array_key_exists('href', self::$aAttrsWhiteList)) {
if (!array_key_exists('href', self::$aAttrsWhiteList))
{
// Regular urls
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
// Mailto urls
$sMailtoPattern = '(mailto:('.utils::GetConfig()->Get('email_validation_pattern').')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
// Notification placeholders
// eg. $this->caller_id$, $this->hyperlink()$, $this->hyperlink(portal)$, $APP_URL$, $MODULES_URL$, ...
// Note: Authorize both $xxx$ and %24xxx%24 as the latter one is encoded when used in HTML attributes (eg. a[href])
$sPlaceholderPattern = '(\$|%24)[\w-]*(->[\w]*(\([\w-]*?\))?)?(\$|%24)';
$sPattern = $sUrlPattern.'|'.$sMailtoPattern.'|'.$sPlaceholderPattern;
$sPattern = $sUrlPattern . '|' . $sMailtoPattern . '|' . $sPlaceholderPattern;
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}
public function GetTagsWhiteList()
public function DoSanitize($sHTML)
{
return static::$aTagsWhiteList;
}
public function GetTagsBlackList()
{
return [];
}
public function GetAttrsWhiteList()
{
return static::$aAttrsWhiteList;
}
public function GetAttrsBlackList()
{
return [];
}
public function GetStylesWhiteList()
{
return static::$aStylesWhiteList;
}
public function LoadDoc($sHTML)
{
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
}
public function PrintDoc()
{
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
// Replace badly encoded non breaking space
$sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML);
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
$this->CleanNode($this->oDoc);
$oXPath = new DOMXPath($this->oDoc);
$sXPath = "//body";
$oNodesList = $oXPath->query($sXPath);
if ($oNodesList->length == 0) {
if ($oNodesList->length == 0)
{
// No body, save the whole document
$sCleanHtml = $this->oDoc->saveHTML();
} else {
}
else
{
// Export only the content of the body tag
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
// remove the body tag itself
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
}
return $sCleanHtml;
}
}
/**
* @since 2.6.5 2.7.6 3.0.0 N°4360
*/
class SVGDOMSanitizer extends DOMSanitizer
{
public function GetTagsWhiteList()
protected function CleanNode(DOMNode $oElement)
{
return [];
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes())
{
foreach($oElement->attributes as $oAttr)
{
$sAttr = strtolower($oAttr->name);
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
{
// Forbidden (or unknown) attribute
$aAttrToRemove[] = $oAttr->name;
}
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else if ($sAttr == 'style')
{
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '')
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else
{
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode)
{
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
{
$aChildElementsToRemove[] = $oNode;
}
else if ($oNode instanceof DOMComment)
{
$aChildElementsToRemove[] = $oNode;
}
else
{
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
{
InlineImage::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
}
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
*/
public function GetTagsBlackList()
protected function CleanStyle($sStyle)
{
return [
'script',
];
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach($aItems as $sItem)
{
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
{
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
}
public function GetAttrsWhiteList()
protected function IsValidAttributeContent($sAttributeName, $sValue)
{
return [];
}
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
*/
public function GetAttrsBlackList()
{
return [
'onbegin',
'onbegin',
'onrepeat',
'onabort',
'onerror',
'onerror',
'onscroll',
'onunload',
'oncopy',
'oncut',
'onpaste',
'oncancel',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncuechange',
'ondblclick',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onpause',
'onplay',
'onplaying',
'onprogress',
'onratechange',
'onreset',
'onresize',
'onscroll',
'onseeked',
'onseeking',
'onselect',
'onshow',
'onstalled',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'onvolumechange',
'onwaiting',
'onactivate',
'onfocusin',
'onfocusout',
];
}
public function GetStylesWhiteList()
{
return [];
}
public function LoadDoc($sHTML)
{
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
}
public function PrintDoc()
{
return $this->oDoc->saveXML();
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
{
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
}
return true;
}
}

View File

@@ -6,8 +6,6 @@
*/
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Variable;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
@@ -82,49 +80,38 @@ class iTopConfigParser
* @param \PhpParser\Parser $oParser
* @param $sConfig
*
* @return void
* @return \Combodo\iTop\Config\Validator\ConfigNodesVisitor
*/
private function BrowseFile(Parser $oParser, $sConfig)
private function BrowseFile(\PhpParser\Parser $oParser, $sConfig)
{
$prettyPrinter = new Standard();
try {
try
{
$aNodes = $oParser->parse($sConfig);
}
catch (\Error $e) {
catch (\Error $e)
{
$sMessage = Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
$this->oException = new \Exception($sMessage, 0, $e);
}
foreach ($aNodes as $sKey => $oNode) {
// With PhpParser 3 we had an Assign node at root
// In PhpParser 4 the root node is now an Expression
if (false === ($oNode instanceof \PhpParser\Node\Stmt\Expression)) {
continue;
}
/** @var \PhpParser\Node\Stmt\Expression $oNode */
if (false === ($oNode->expr instanceof Assign)) {
continue;
}
/** @var Assign $oAssignation */
$oAssignation = $oNode->expr;
if (false === ($oAssignation->var instanceof Variable)) {
continue;
}
if (false === ($oAssignation->expr instanceof PhpParser\Node\Expr\Array_)) {
foreach ($aNodes as $oAssignation)
{
if (! $oAssignation instanceof Assign)
{
continue;
}
$sCurrentRootVar = $oAssignation->var->name;
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap)) {
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap))
{
continue;
}
$aCurrentRootVarMap =& $this->aVarsMap[$sCurrentRootVar];
foreach ($oAssignation->expr->items as $oItem) {
foreach ($oAssignation->expr->items as $oItem)
{
$sValue = $prettyPrinter->prettyPrintExpr($oItem->value);
$aCurrentRootVarMap[$oItem->key->value] = $sValue;
}

View File

@@ -176,29 +176,31 @@ class InlineImage extends DBObject
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
$aInlineImagesId = array();
while ($oInlineImage = $oSet->Fetch()) {
$aInlineImagesId[] = $oInlineImage->GetKey();
$aInlineImagesId = array();
while($oInlineImage = $oSet->Fetch())
{
$aInlineImagesId[] = $oInlineImage->GetKey();
$oInlineImage->SetItem($oObject);
$oInlineImage->Set('temp_id', '');
$oInlineImage->DBUpdate();
}
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', LogChannels::INLINE_IMAGE, array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', 'InlineImage', array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
else {
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', LogChannels::INLINE_IMAGE, array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
else
{
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', 'InlineImage', array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
}
@@ -218,12 +220,12 @@ class InlineImage extends DBObject
$aInlineImagesId[] = $oInlineImage->GetKey();
$oInlineImage->DBDelete();
}
IssueLog::Trace('OnFormCancel', LogChannels::INLINE_IMAGE, array(
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
IssueLog::Trace('OnFormCancel', 'InlineImage', array(
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
/**
@@ -563,17 +565,17 @@ JS
protected function AfterInsert()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
IssueLog::Trace(__METHOD__, 'InlineImage', array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterInsert();
@@ -581,17 +583,17 @@ JS
protected function AfterUpdate()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
IssueLog::Trace(__METHOD__, 'InlineImage', array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterUpdate();
@@ -599,17 +601,17 @@ JS
protected function AfterDelete()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
IssueLog::Trace(__METHOD__, 'InlineImage', array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterDelete();

View File

@@ -15,8 +15,6 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Core\Kpi\KpiLogData;
use Combodo\iTop\Service\Module\ModuleService;
/**
@@ -32,8 +30,6 @@ class ExecutionKPI
static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*';
static protected $m_bGenerateLegacyReport = true;
static protected $m_fSlowQueries = 0;
static protected $m_aStats = array(); // Recurrent operations
static protected $m_aExecData = array(); // One shot operations
@@ -81,39 +77,14 @@ class ExecutionKPI
return false;
}
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
{
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
}
static public function SetSlowQueries($fSlowQueries)
{
self::$m_fSlowQueries = $fSlowQueries;
}
static public function GetDescription()
{
$aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
$sFeatures = implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
$sSlowQueries = '';
if (self::$m_fSlowQueries > 0) {
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
}
$aExtensions = [];
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
}
$sExtensions = '';
if (count($aExtensions) > 0) {
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
}
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
return "KPI logging is active for $sFor. Measures: $sFeatures";
}
static public function ReportStats()
@@ -121,28 +92,7 @@ class ExecutionKPI
if (!self::IsEnabled()) return;
global $fItopStarted;
global $iItopInitialMemory;
$sExecId = microtime(); // id to differentiate the hrefs!
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
if (isset($_POST['operation'])) {
$sRequest .= ' operation: '.$_POST['operation'];
}
$fStop = MyHelpers::getmicrotime();
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!self::$m_bGenerateLegacyReport) {
return;
}
$aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats)
@@ -155,7 +105,7 @@ class ExecutionKPI
self::Report("<hr/>");
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>");
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>");
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>");
self::Report("<div>");
@@ -250,6 +200,8 @@ class ExecutionKPI
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
$fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries');
// Report operation details
foreach (self::$m_aStats as $sOperation => $aOpStats)
{
@@ -293,7 +245,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= self::$m_fSlowQueries))
if (($fTotalInter >= $fSlowQueries))
{
if ($bDisplayHeader)
{
@@ -319,19 +271,11 @@ class ExecutionKPI
self::Report('<a name="end-'.md5($sExecId).'">&nbsp;</a>');
}
public static function InitStats()
{
// Invoke extensions to initialize the KPI statistics
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oExtensionInstance->InitStats();
}
}
public function __construct()
{
$this->ResetCounters();
}
}
// Get the duration since startup, and reset the counter for the next measure
//
@@ -339,14 +283,8 @@ class ExecutionKPI
{
global $fItopStarted;
if (!self::IsEnabled()) {
return;
}
$aNewEntry = null;
$fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration)
{
$fStopped = MyHelpers::getmicrotime();
@@ -359,9 +297,6 @@ class ExecutionKPI
$this->m_fStarted = $fStopped;
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
@@ -371,102 +306,41 @@ class ExecutionKPI
}
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory;
$iPeakMemory = self::memory_get_peak_usage();
$aNewEntry['mem_peak'] = $iPeakMemory;
if (function_exists('memory_get_peak_usage'))
{
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory;
}
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
{
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_REPORT,
'Step',
$sOperationDesc,
$fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
if (!is_null($aNewEntry))
{
self::$m_aExecData[] = $aNewEntry;
}
$this->ResetCounters();
}
public function ComputeStatsForExtension($object, $sMethod)
{
if (!self::IsEnabled()) {
return;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', $sSignature);
}
}
public function ComputeStats($sOperation, $sArguments)
{
if (!self::IsEnabled()) {
return;
}
if (self::$m_bEnabled_Duration)
{
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
$aCallstack = [];
if (self::$m_bGenerateLegacyReport) {
if (self::$m_bBlameCaller) {
$aCallstack = MyHelpers::get_callstack(1);
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration,
'callers' => $aCallstack,
];
} else {
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration
];
}
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
}
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,
$sArguments,
$this->m_fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory,
$aCallstack);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (self::$m_bBlameCaller)
{
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration,
'callers' => MyHelpers::get_callstack(1),
);
}
else
{
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration
);
}
}
}
protected function ResetCounters()
@@ -496,7 +370,35 @@ class ExecutionKPI
static protected function memory_get_usage()
{
return memory_get_usage(true);
if (function_exists('memory_get_usage'))
{
return memory_get_usage(true);
}
// Copied from the PHP manual
//
//If its Windows
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
//Doesn't work for 2000
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
if (substr(PHP_OS,0,3) == 'WIN')
{
$output = array();
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
}
else
{
//We now assume the OS is UNIX
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
//This should work on most UNIX systems
$pid = getmypid();
exec("ps -eo%mem,rss,pid | grep $pid", $output);
$output = explode(" ", $output[0]);
//rss is given in 1024 byte units
return $output[1] * 1024;
}
}
static public function memory_get_peak_usage($bRealUsage = false)

View File

@@ -2602,18 +2602,5 @@ 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();
}
}

View File

@@ -398,10 +398,10 @@ class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
*/
protected function GetFileSuffix($oDate)
{
$sMonthYear = $oDate->format('o');
$sMonthNumber = $oDate->format('m');
$sWeekYear = $oDate->format('o');
$sWeekNumber = $oDate->format('m');
return $sMonthYear.'-month'.$sMonthNumber;
return $sWeekYear.'-month'.$sWeekNumber;
}
/**
@@ -502,7 +502,6 @@ class FileLog
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
{
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
@@ -517,9 +516,12 @@ class FileLog
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
if (empty($aContext)) {
if (empty($aContext))
{
fwrite($hLogFile, "$sDate | $sText\n");
} else {
}
else
{
$sContext = var_export($aContext, true);
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
}
@@ -530,44 +532,6 @@ class FileLog
}
}
/**
* Simple enum like class to factorize channels values as constants
* Channels are used especially as parameters in {@see \LogAPI} methods
*
* @since 2.7.5 3.0.0 N°4012
*/
class LogChannels
{
const APC = 'apc';
/**
* @var string
* @since 2.7.7 N°4558 use this new channel when logging DB transactions
*/
const CMDB_SOURCE = 'cmdbsource';
const DEADLOCK = 'DeadLock';
/**
* @var string
* @since 2.7.9
*/
const EXPORT = 'export';
const INLINE_IMAGE = 'InlineImage';
/**
* @var string
* @since 3.0.1 N°4849
* @since 2.7.7 N°4635
*/
const NOTIFICATIONS = 'notifications';
const PORTAL = 'portal';
}
abstract class LogAPI
{
const CHANNEL_DEFAULT = '';
@@ -579,11 +543,11 @@ abstract class LogAPI
const LEVEL_DEBUG = 'Debug';
const LEVEL_TRACE = 'Trace';
/**
* @see GetMinLogLevel
* @var string default log level, can be overrided
* @see GetMinLogLevel
* @since 2.7.1 N°2977
*/
const LEVEL_DEFAULT = self::LEVEL_OK;
const LEVEL_DEFAULT = self::LEVEL_OK;
protected static $aLevelsPriority = array(
self::LEVEL_ERROR => 400,
@@ -640,29 +604,36 @@ abstract class LogAPI
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
{
if (!static::$m_oFileLog) {
if (! static::$m_oFileLog)
{
return;
}
if (!isset(self::$aLevelsPriority[$sLevel])) {
if (! isset(self::$aLevelsPriority[$sLevel]))
{
IssueLog::Error("invalid log level '{$sLevel}'");
return;
}
if (is_null($sChannel)) {
if (is_null($sChannel))
{
$sChannel = static::CHANNEL_DEFAULT;
}
$sMinLogLevel = self::GetMinLogLevel($sChannel);
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
{
return;
}
if (is_string($sMinLogLevel)) {
if (!isset(self::$aLevelsPriority[$sMinLogLevel])) {
if (is_string($sMinLogLevel))
{
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
{
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
} elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel]) {
}
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
{
//priority too low regarding the conf, do not log this
return;
}
@@ -704,7 +675,7 @@ abstract class LogAPI
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
{
return $sLogLevelMin[static::CHANNEL_DEFAULT];
return $sLogLevelMin[$sChannel];
}
return static::LEVEL_DEFAULT;
@@ -741,9 +712,7 @@ class ToolsLog extends LogAPI
/**
* @see \CMDBSource::LogDeadLock()
* @since 2.7.1 PR #139
*
* @link https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
* @since 2.7.1
*/
class DeadLockLog extends LogAPI
{
@@ -763,16 +732,15 @@ class DeadLockLog extends LogAPI
parent::Enable($sTargetFile);
}
/** @noinspection PhpUnreachableStatementInspection */
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
{
switch ($iMysqlErrorNo)
{
case CMDBSource::MYSQL_ERRNO_WAIT_TIMEOUT:
case 1205:
return self::CHANNEL_WAIT_TIMEOUT;
break;
case CMDBSource::MYSQL_ERRNO_DEADLOCK:
return self::CHANNEL_DEADLOCK_FOUND;
case 1213:
return self::CHANNEL_DEADLOCK_FOUND;
break;
default:
return self::CHANNEL_DEFAULT;
@@ -781,21 +749,17 @@ class DeadLockLog extends LogAPI
}
/**
* @param string $sLevel
* @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo}
* @param string $sMessage
* @param int $iMysqlErrorNumber will be converted to channel using {@link GetChannelFromMysqlErrorNo}
* @param null $iMysqlErroNo
* @param array $aContext
*
* @throws \Exception
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
*
* @since 2.7.1 method creation
* @since 2.7.5 3.0.0 rename param names and fix phpdoc (thanks Hipska !)
*/
public static function Log($sLevel, $sMessage, $iMysqlErrorNumber = null, $aContext = array())
public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array())
{
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErrorNumber);
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo);
parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,9 +22,7 @@
* A class to serialize the execution of some code sections
* Emulates the API of PECL Mutex class
* Relies on MySQL locks because the API sem_get is not always present in the
* installed PHP.
*
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
* installed PHP.
*
* @copyright Copyright (C) 2013-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -244,8 +242,6 @@ class iTopMutex
*
* @throws \Exception
* @throws \MySQLException
*
* @since 2.7.5 3.0.0 N°3968 specify `wait_timeout` for the mutex dedicated connection
*/
public function InitMySQLSession()
{
@@ -258,35 +254,9 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink) {
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
// since the lock will be released if/when the connection times out.
// Source https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html :
// > A lock obtained with GET_LOCK() is released explicitly by executing RELEASE_LOCK() or implicitly when your session terminates
//
// BEWARE: If you want to check the value of this variable, when run from an interactive console `SHOW VARIABLES LIKE 'wait_timeout'`
// will actually returns the value of the variable `interactive_timeout` which may be quite different.
$sSql = "SHOW VARIABLES LIKE 'wait_timeout'";
$result = mysqli_query($this->hDBLink, $sSql);
if (!$result) {
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
}
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH)) {
$iTimeout = (int)$aRow[1];
} else {
mysqli_free_result($result);
throw new Exception("No result for query '".$sSql."'");
}
mysqli_free_result($result);
if ($iTimeout < 86400) {
$result = mysqli_query($this->hDBLink, 'SET SESSION wait_timeout=86400');
if ($result === false) {
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
}
if (!$this->hDBLink)
{
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
}

View File

@@ -1,52 +1,32 @@
<?php
/*
* Copyright (C) 2010-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
// Copyright (c) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
class MissingQueryArgument extends CoreException {
}
class ExpressionHelper {
/**
* Callback to be used with {@link Expression::Browse}, to update the AllowAllData attribute in the NestedQueryExpression that are
* present in the Expression tree
*
* @param \Expression $oExpression
* @param boolean $bAllowAllData
*
* @uses \DBSearch::AllowAllData()
*
* @since 2.7.2 2.8.0 N°3324
*/
public static function ExpressionAllowAllDataCallback($oExpression, $bAllowAllData) {
if (!($oExpression instanceof NestedQueryExpression)) {
return;
}
$oExpression->AllowAllData($bAllowAllData);
}
class MissingQueryArgument extends CoreException
{
}
/**
* @method Check($oModelReflection, array $aAliases, $sSourceQuery)
*/
abstract class Expression {
abstract class Expression
{
const OPERATOR_BINARY = 'binary';
const OPERATOR_BOOLEAN = 'boolean_binary';
const OPERATOR_FIELD = 'field';
@@ -159,13 +139,9 @@ abstract class Expression {
}
/**
* Recursively browse the expression tree.
*
* To access variables, specify them using the `use` keyword and the `&` to pass by reference if necessary
*
* @see https://www.php.net/manual/fr/functions.anonymous.php
*
* @param Closure $callback with current expression as parameter
* Recursively browse the expression tree
* @param Closure $callback
* @return mixed
*/
abstract public function Browse(Closure $callback);
@@ -177,9 +153,6 @@ abstract class Expression {
// recursively list field parents ($aTable = array of sParent => dummy)
abstract public function CollectUsedParents(&$aTable);
/**
* @return boolean true if the expression's value is constant and evaluates to true, false otherwise
*/
abstract public function IsTrue();
// recursively builds an array of [classAlias][fieldName] => value
@@ -188,7 +161,8 @@ abstract class Expression {
// recursively builds an array of parameters to give to current request
abstract public function ListParameters();
public function RequiresField($sClass, $sFieldName) {
public function RequiresField($sClass, $sFieldName)
{
// #@# todo - optimize : this is called quite often when building a single query !
$aRequired = $this->ListRequiredFields();
if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
@@ -865,15 +839,12 @@ class MatchExpression extends BinaryExpression
* MatchExpression constructor.
*
* @param \FieldExpression $oLeftExpr
* @param \Expression $oRightExpr
* @param \ScalarExpression $oRightExpr
*
* @throws \CoreException
*/
public function __construct(FieldExpression $oLeftExpr, Expression $oRightExpr)
public function __construct(FieldExpression $oLeftExpr, ScalarExpression $oRightExpr)
{
if (!$oRightExpr instanceof ScalarExpression && !$oRightExpr instanceof VariableExpression) {
throw new CoreException('Only instance of ScalarExpression or VariableExpression are allowed in MATCHES '.get_class($oRightExpr).' found');
}
parent::__construct($oLeftExpr, 'MATCHES', $oRightExpr);
}
@@ -1696,10 +1667,6 @@ class FieldExpressionResolved extends FieldExpression
parent::__construct($sName, $sParent);
}
/**
* @return array of additional expressions for muti-column attributes
* @since 2.7.4
*/
public function AdditionalExpressions()
{
return $this->m_aAdditionalExpressions;
@@ -2034,15 +2001,7 @@ class ListExpression extends Expression
{
if ($oExpr instanceof VariableExpression)
{
$oVarExpr = $oExpr->GetAsScalar($aArgs);
if ($oVarExpr instanceof ListExpression)
{
$this->m_aExpressions = $oVarExpr->GetItems();
}
else
{
$this->m_aExpressions[$idx] = $oVarExpr;
}
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{
@@ -2216,62 +2175,60 @@ class NestedQueryExpression extends Expression
}
/**/
public function ApplyParameters($aArgs) {
public function ApplyParameters($aArgs)
{
$this->m_oNestedQuery->ApplyParameters($aArgs);
}
/**/
public function GetUnresolvedFields($sAlias, &$aUnresolved) {
public function GetUnresolvedFields($sAlias, &$aUnresolved)
{
}
/**/
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true) {
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
{
// Check and prepare the select information
$this->m_oNestedQuery->TranslateConditions($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
$this->m_oNestedQuery->TranslateConditions($aTranslationData, $bMatchAll , $bMarkFieldsAsResolved );
return clone $this;
}
public function ListRequiredFields() {
public function ListRequiredFields()
{
return array();
}
public function CollectUsedParents(&$aTable) {
public function CollectUsedParents(&$aTable)
{
}
public function ListConstantFields() {
public function ListConstantFields()
{
return $this->m_oNestedQuery->ListConstantFields();
}
public function ListParameters() {
public function ListParameters()
{
return $this->m_oNestedQuery->ListParameters();
}
public function RenameParam($sOldName, $sNewName) {
public function RenameParam($sOldName, $sNewName)
{
$this->m_oNestedQuery->RenameParam($sOldName, $sNewName);
}
public function RenameAlias($sOldName, $sNewName) {
public function RenameAlias($sOldName, $sNewName)
{
$this->m_oNestedQuery->RenameAlias($sOldName, $sNewName);
}
/**
* @inheritDoc
*/
public function ToJSON(&$aArgs = null, $bRetrofitParams = false) {
public function ToJSON(&$aArgs = null, $bRetrofitParams = false)
{
return $this->m_oNestedQuery->ToJSON();
}
/**
* Simple indirection to {@link \DBObjectSearch::AllowAllData()}
*
* @param bool $bAllowAllData
*
* @uses \DBSearch::AllowAllData()
*/
public function AllowAllData($bAllowAllData = true) {
$this->m_oNestedQuery->AllowAllData($bAllowAllData);
}
}
class FunctionExpression extends Expression

View File

@@ -597,7 +597,7 @@ static public $yy_action = array(
** defined, then do no error processing.
*/
const YYNOCODE = 119;
const YYSTACKDEPTH = 1000;
const YYSTACKDEPTH = 100;
const YYNSTATE = 175;
const YYNRULE = 125;
const YYERRORSYMBOL = 76;
@@ -1175,10 +1175,6 @@ static public $yy_action = array(
}
/* Here code is inserted which will execute if the parser
** stack ever overflows */
#line 30 "..\oql-parser.y"
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
#line 1186 "..\oql-parser.php"
return;
}
$yytos = new OQLParser_yyStackEntry;
@@ -1478,116 +1474,116 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
** function yy_r0($yymsp){ ... } // User supplied code
** #line <lineno> <thisfile>
*/
#line 37 "..\oql-parser.y"
#line 29 "..\oql-parser.y"
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
#line 1488 "..\oql-parser.php"
#line 41 "..\oql-parser.y"
#line 1483 "..\oql-parser.php"
#line 33 "..\oql-parser.y"
function yy_r3(){
$this->_retvalue = new OqlUnionQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1493 "..\oql-parser.php"
#line 48 "..\oql-parser.y"
#line 1488 "..\oql-parser.php"
#line 40 "..\oql-parser.y"
function yy_r5(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
}
#line 1498 "..\oql-parser.php"
#line 51 "..\oql-parser.y"
#line 1493 "..\oql-parser.php"
#line 43 "..\oql-parser.y"
function yy_r6(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
}
#line 1503 "..\oql-parser.php"
#line 55 "..\oql-parser.y"
#line 1498 "..\oql-parser.php"
#line 47 "..\oql-parser.y"
function yy_r7(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor);
}
#line 1508 "..\oql-parser.php"
#line 58 "..\oql-parser.y"
#line 1503 "..\oql-parser.php"
#line 50 "..\oql-parser.y"
function yy_r8(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor);
}
#line 1513 "..\oql-parser.php"
#line 63 "..\oql-parser.y"
#line 1508 "..\oql-parser.php"
#line 55 "..\oql-parser.y"
function yy_r9(){
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
}
#line 1518 "..\oql-parser.php"
#line 66 "..\oql-parser.y"
#line 1513 "..\oql-parser.php"
#line 58 "..\oql-parser.y"
function yy_r10(){
array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
$this->_retvalue = $this->yystack[$this->yyidx + -2]->minor;
}
#line 1524 "..\oql-parser.php"
#line 71 "..\oql-parser.y"
#line 1519 "..\oql-parser.php"
#line 63 "..\oql-parser.y"
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
#line 1527 "..\oql-parser.php"
#line 72 "..\oql-parser.y"
#line 1522 "..\oql-parser.php"
#line 64 "..\oql-parser.y"
function yy_r12(){ $this->_retvalue = null; }
#line 1530 "..\oql-parser.php"
#line 74 "..\oql-parser.y"
#line 1525 "..\oql-parser.php"
#line 66 "..\oql-parser.y"
function yy_r13(){
// insert the join statement on top of the existing list
array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor);
// and return the updated array
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
}
#line 1538 "..\oql-parser.php"
#line 80 "..\oql-parser.y"
#line 1533 "..\oql-parser.php"
#line 72 "..\oql-parser.y"
function yy_r14(){
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
}
#line 1543 "..\oql-parser.php"
#line 86 "..\oql-parser.y"
#line 1538 "..\oql-parser.php"
#line 78 "..\oql-parser.y"
function yy_r16(){
// create an array with one single item
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1549 "..\oql-parser.php"
#line 91 "..\oql-parser.y"
#line 1544 "..\oql-parser.php"
#line 83 "..\oql-parser.y"
function yy_r17(){
// create an array with one single item
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1555 "..\oql-parser.php"
#line 96 "..\oql-parser.y"
#line 1550 "..\oql-parser.php"
#line 88 "..\oql-parser.y"
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
#line 1558 "..\oql-parser.php"
#line 97 "..\oql-parser.y"
#line 1553 "..\oql-parser.php"
#line 89 "..\oql-parser.y"
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1561 "..\oql-parser.php"
#line 98 "..\oql-parser.y"
#line 1556 "..\oql-parser.php"
#line 90 "..\oql-parser.y"
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1564 "..\oql-parser.php"
#line 99 "..\oql-parser.y"
#line 1559 "..\oql-parser.php"
#line 91 "..\oql-parser.y"
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1567 "..\oql-parser.php"
#line 100 "..\oql-parser.y"
#line 1562 "..\oql-parser.php"
#line 92 "..\oql-parser.y"
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1570 "..\oql-parser.php"
#line 101 "..\oql-parser.y"
#line 1565 "..\oql-parser.php"
#line 93 "..\oql-parser.y"
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1573 "..\oql-parser.php"
#line 102 "..\oql-parser.y"
#line 1568 "..\oql-parser.php"
#line 94 "..\oql-parser.y"
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1576 "..\oql-parser.php"
#line 103 "..\oql-parser.y"
#line 1571 "..\oql-parser.php"
#line 95 "..\oql-parser.y"
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1579 "..\oql-parser.php"
#line 104 "..\oql-parser.y"
#line 1574 "..\oql-parser.php"
#line 96 "..\oql-parser.y"
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1582 "..\oql-parser.php"
#line 106 "..\oql-parser.y"
#line 1577 "..\oql-parser.php"
#line 98 "..\oql-parser.y"
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
#line 1585 "..\oql-parser.php"
#line 111 "..\oql-parser.y"
#line 1580 "..\oql-parser.php"
#line 103 "..\oql-parser.y"
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
#line 1588 "..\oql-parser.php"
#line 112 "..\oql-parser.y"
#line 1583 "..\oql-parser.php"
#line 104 "..\oql-parser.y"
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
#line 1591 "..\oql-parser.php"
#line 113 "..\oql-parser.y"
#line 1586 "..\oql-parser.php"
#line 105 "..\oql-parser.y"
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
#line 1594 "..\oql-parser.php"
#line 119 "..\oql-parser.y"
#line 1589 "..\oql-parser.php"
#line 111 "..\oql-parser.y"
function yy_r37(){
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
{
@@ -1598,44 +1594,44 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
}
#line 1606 "..\oql-parser.php"
#line 136 "..\oql-parser.y"
#line 1601 "..\oql-parser.php"
#line 128 "..\oql-parser.y"
function yy_r42(){
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
}
#line 1611 "..\oql-parser.php"
#line 139 "..\oql-parser.y"
#line 1606 "..\oql-parser.php"
#line 132 "..\oql-parser.y"
function yy_r43(){
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
}
#line 1616 "..\oql-parser.php"
#line 154 "..\oql-parser.y"
#line 1611 "..\oql-parser.php"
#line 147 "..\oql-parser.y"
function yy_r47(){
$this->_retvalue = array();
}
#line 1621 "..\oql-parser.php"
#line 165 "..\oql-parser.y"
#line 1616 "..\oql-parser.php"
#line 158 "..\oql-parser.y"
function yy_r51(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
#line 1624 "..\oql-parser.php"
#line 178 "..\oql-parser.y"
#line 1619 "..\oql-parser.php"
#line 171 "..\oql-parser.y"
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
#line 1627 "..\oql-parser.php"
#line 180 "..\oql-parser.y"
#line 1622 "..\oql-parser.php"
#line 173 "..\oql-parser.y"
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
#line 1630 "..\oql-parser.php"
#line 182 "..\oql-parser.y"
#line 1625 "..\oql-parser.php"
#line 175 "..\oql-parser.y"
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
#line 1633 "..\oql-parser.php"
#line 183 "..\oql-parser.y"
#line 1628 "..\oql-parser.php"
#line 176 "..\oql-parser.y"
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
#line 1636 "..\oql-parser.php"
#line 184 "..\oql-parser.y"
#line 1631 "..\oql-parser.php"
#line 177 "..\oql-parser.y"
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
#line 1639 "..\oql-parser.php"
#line 187 "..\oql-parser.y"
#line 1634 "..\oql-parser.php"
#line 180 "..\oql-parser.y"
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
#line 1642 "..\oql-parser.php"
#line 189 "..\oql-parser.y"
#line 1637 "..\oql-parser.php"
#line 182 "..\oql-parser.y"
function yy_r68(){
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
{
@@ -1647,22 +1643,22 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
}
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
}
#line 1655 "..\oql-parser.php"
#line 200 "..\oql-parser.y"
#line 1650 "..\oql-parser.php"
#line 193 "..\oql-parser.y"
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
#line 1658 "..\oql-parser.php"
#line 201 "..\oql-parser.y"
#line 1653 "..\oql-parser.php"
#line 194 "..\oql-parser.y"
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
#line 1661 "..\oql-parser.php"
#line 202 "..\oql-parser.y"
#line 1656 "..\oql-parser.php"
#line 195 "..\oql-parser.y"
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
#line 1664 "..\oql-parser.php"
#line 203 "..\oql-parser.y"
#line 1659 "..\oql-parser.php"
#line 196 "..\oql-parser.y"
function yy_r72(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); }
#line 1667 "..\oql-parser.php"
#line 206 "..\oql-parser.y"
#line 1662 "..\oql-parser.php"
#line 199 "..\oql-parser.y"
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
#line 1670 "..\oql-parser.php"
#line 1665 "..\oql-parser.php"
/**
* placeholder for the left hand side in a reduce operation.
@@ -1763,10 +1759,6 @@ throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine,
}
/* Here code is inserted which will be executed whenever the
** parser fails */
#line 33 "..\oql-parser.y"
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
#line 1775 "..\oql-parser.php"
}
/**
@@ -1780,8 +1772,8 @@ throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine,
{
#line 25 "..\oql-parser.y"
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
#line 1791 "..\oql-parser.php"
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
#line 1781 "..\oql-parser.php"
}
/**
@@ -1948,47 +1940,19 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
}
}
#line 271 "..\oql-parser.y"
#line 264 "..\oql-parser.y"
class OQLParserException extends OQLException
{
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
{
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserSyntaxErrorException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserStackOverFlowException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Stack overflow";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParserParseFailureException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParser extends OQLParserRaw
{
// dirty, but working for us (no other mean to get the final result :-(
@@ -2041,4 +2005,4 @@ class OQLParser extends OQLParserRaw
}
}
#line 2052 "..\oql-parser.php"
#line 2014 "..\oql-parser.php"

Some files were not shown because too many files have changed in this diff Show More