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
2322 changed files with 39804 additions and 211291 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/

17
.gitignore vendored
View File

@@ -6,6 +6,12 @@
# 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/*
test/vendor/*
@@ -13,7 +19,6 @@ test/vendor/*
# all conf but listing prevention
/conf/**
!/conf/.htaccess
!/conf/index.php
!/conf/web.config
# all datas but listing prevention
@@ -32,13 +37,14 @@ test/vendor/*
!/log/index.php
!/log/web.config
# PHPUnit cache file
/test/.phpunit.result.cache
# Jetbrains
/.idea/**
!/.idea/IntelliLang.xml
!/.idea/encodings.xml
!/.idea/codeStyles
!/.idea/codeStyles/*
!/.idea/inspectionProfiles
!/.idea/inspectionProfiles/*
# doc. generation
/.doc/vendor
@@ -133,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

@@ -17,15 +17,7 @@ $MySettings = array(
// default: 3
'access_mode' => 3,
// activity_panel.entry_form_opened_by_default: Whether or not the new entry form will be automatically opened when viewing an object.
// default: false
'activity_panel.entry_form_opened_by_default' => false,
// activity_panel.show_author_name_below_entries: Whether or not to show the author friendlyname next to the date on the last entry.
// default: false
'activity_panel.show_author_name_below_entries' => false,
'allowed_login_types' => 'form|external|basic',
'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
@@ -37,11 +29,15 @@ $MySettings = array(
// 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://localhost/itop-dev/',
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
// behind_reverse_proxy: 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
'behind_reverse_proxy' => false,
// 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
@@ -68,23 +64,29 @@ $MySettings = array(
// ),
// )
'date_and_time_format' => array (
'default' =>
array (
'date' => 'Y-m-d',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
'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' => 'localhost',
'db_host' => '',
'db_name' => 'itop_dev',
'db_name' => 'itop_ci',
'db_pwd' => '',
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
'db_subname' => '',
'db_user' => 'root',
'db_user' => 'jenkins_itop',
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
// default: '$difference$'
@@ -92,6 +94,10 @@ $MySettings = array(
'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,
@@ -104,39 +110,47 @@ $MySettings = array(
// default: ''
'email_default_sender_label' => '',
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocol)
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
// default: 'PHPMail'
'email_transport' => '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' => '061c5b88c47ed64664618cf37b91a442b5550704aacecc5f58f96fc98087333b',
'encryption_library' => 'OpenSSL',
'encryption_key' => '@iT0pEncr1pti0n!',
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
'fast_reload_interval' => 60,
'fast_reload_interval' => '60',
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
// default: '/usr/bin/dot'
'graphviz_path' => 'C:\\Program Files (x86)\\Graphviz2.38\\bin\\dot.exe',
// high_cardinality_classes: List of classes with high cardinality (Force manual submit of search)
// default: array (
// )
'high_cardinality_classes' => array (
),
'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_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',
'inline_image_max_storage_width' => 1600,
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
// default: '\''
@@ -158,45 +172,41 @@ $MySettings = array(
'log_issue' => true,
'log_kpi_duration' => 0,
'log_notification' => true,
'log_web_service' => true,
'max_display_limit' => 30,
// 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,
// mentions.allowed_classes: Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value can be either a DM class or a valid OQL (eg. "@" => "Person", "?" => "SELECT FAQ WHERE status = 'published'")
// default: array (
// '@' => 'SELECT Person WHERE status = \'active\'',
// )
'mentions.allowed_classes' => array (
'@' => 'SELECT Person WHERE status = \'active\'',
),
'min_display_limit' => 20,
'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',
// optimize_requests_for_join_count: Optimize request joins to minimize the count (default is true, try to set it to false in case of performance issues)
// default: true
'optimize_requests_for_join_count' => true,
'password_hash_algo' => '2y',
// php_path: Path to the php executable in CLI mode
// default: 'php'
'php_path' => 'php',
// search_manual_submit: Force manual submit of search all requests
// 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' => false,
'search_manual_submit' => array (
'Person' => true,
),
'secure_connection_required' => false,
@@ -212,21 +222,17 @@ $MySettings = array(
// default: ''
'source_dir' => 'datamodels/2.x/',
'standard_reload_interval' => 300,
'standard_reload_interval' => '300',
// synchro_trace: Synchronization details: none, display, save (includes 'display')
// default: 'none'
'synchro_trace' => 'none',
// tag_set_item_separator: Tag set from string: tag label separator
// default: '|'
'tag_set_item_separator' => '|',
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP
// 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 explicitly 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)
// 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,
@@ -241,34 +247,15 @@ $MySettings = array(
*
*/
$MyModuleSettings = array(
'authent-cas' => array (
'cas_debug' => false,
'cas_host' => '',
'cas_port' => '',
'cas_context' => '',
'cas_version' => '',
),
'authent-ldap' => array (
'host' => 'localhost',
'port' => 389,
'default_user' => '',
'default_pwd' => '',
'base_dn' => 'dc=yourcompany,dc=com',
'user_query' => '(&(uid=%1$s)(inetuserstatus=ACTIVE))',
'options' => array (
17 => 3,
8 => 0,
),
'start_tls' => false,
'debug' => false,
'authent-local' => array (
'password_validation.pattern' => '',
),
'itop-attachments' => array (
'allowed_classes' => array (
0 => 'Ticket',
0 => 'Ticket',
),
'position' => 'relations',
'preview_max_width' => 290,
'icon_preview_max_size' => 500000,
),
'itop-backup' => array (
'mysql_bindir' => '',
@@ -276,105 +263,12 @@ $MyModuleSettings = array(
'time' => '23:30',
'retention_count' => 5,
'enabled' => true,
'itop_backup_incident' => '',
'debug' => false,
),
'molkobain-console-tooltips' => array (
'decoration_class' => 'fas fa-question',
'enabled' => true,
),
'itop-time-tracking' => array (
'allowed_classes' => array (
'UserRequest' =>
array (
'calendar-tab' => 'SELECT UserRequest WHERE status != "closed"',
'stopwatch' => 'SELECT UserRequest WHERE status != "closed"',
'report-tab' => 'SELECT UserRequest WHERE status IN ("resolved", "closed")',
),
'Incident' =>
array (
'calendar-tab' => 'SELECT Incident WHERE status != "closed"',
'stopwatch' => 'SELECT Incident WHERE status != "closed"',
'report-tab' => 'SELECT Incident WHERE status IN ("resolved", "closed")',
),
'CustomerContract' =>
array (
'calendar-page' => 'SELECT CustomerContract WHERE status = "production"',
'report-tab' => 'SELECT CustomerContract',
),
),
'colors' => array (
'default_stopwatch' =>
array (
'text' => '#ffffff',
'background' => '#a6a6a6',
),
'default_calendar' =>
array (
'text' => '#ffffff',
'background' => '#FFCC80',
),
'classes' =>
array (
'UserRequest' =>
array (
'text' => '#ffffff',
'background' => 'shadeof:blue',
),
'Incident' =>
array (
'text' => '#ffffff',
'background' => 'shadeof:green',
),
'CustomerContract' =>
array (
'text' => '#ffffff',
'background' => 'shadeof:grey',
),
),
),
'clone_events' => false,
'default_event_duration' => '00:30:00',
'day_start_time' => '06:00:00',
'day_end_time' => '22:00:00',
'excluded_days' => array (
0 => 'Saturday',
1 => 'Sunday',
),
'first_day' => 1,
'business_hours' => array (
'days_of_week' =>
array (
0 => '1',
1 => '2',
2 => '3',
3 => '4',
4 => '5',
),
'start' => '08:00:00',
'end' => '18:00:00',
),
'minimum_event_duration_display' => '00:30:00',
'stopwatch_clean_periodicity' => 1,
'stopwatch_max_time' => 4,
'delete_max_event_age' => 30,
'default_report_query' => 'SELECT TimeSpent WHERE contact_id = :contact_id AND start_date >= :start_date AND end_date < :end_date',
'manager_report_query' => 'SELECT TimeSpent WHERE start_date >= :start_date AND end_date < :end_date',
'manager_report_silo' => 'SELECT Person',
'weekly_report_time_spent_attribute' => '',
'weekly_report_time_spent_default' => '30hrs',
'report_charts_definition' => array (
0 =>
array (
'group_by_attribute' => 'contact_id',
'label' => 'TimeTracking:ReportActivityPerUser',
),
1 =>
array (
'group_by_attribute' => 'org_id',
'label' => 'TimeTracking:ReportActivityPerCustomer',
),
),
),
);
/**
@@ -387,4 +281,4 @@ $MyModules = array(
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
),
);
?>
?>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<installation>
<!-- On manual installs, this file is generated in setup/install-*.xml -->
<mode>install</mode>
<mode>upgrade</mode>
<preinstall>
<copies type="array"/>
</preinstall>
@@ -20,7 +20,7 @@
<db_tls_ca></db_tls_ca>
<prefix></prefix>
</database>
<url>http://localhost/iTop/</url>
<url>http://127.0.0.1/itop/svn/trunk/</url>
<graphviz_path>/usr/bin/dot</graphviz_path>
<admin_account>
<user>admin</user>

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_name() !== '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

@@ -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,22 +1,7 @@
<?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__ . "/../../" ;
@@ -66,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.

78
Jenkinsfile vendored
View File

@@ -1,11 +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(){
checkout scm
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'
}
}
}
}
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
stage('unattended_install') {
parallel {
stage('unattended_install default env') {
steps {
sh './.jenkins/bin/unattended_install/default_env.sh'
}
}
}
}
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)
@@ -434,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();
@@ -442,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();
}
@@ -521,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())
{
@@ -557,7 +561,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return bool
* @return array
*/
public function IsAdministrator($oUser)
{
@@ -567,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)
{
@@ -646,10 +644,8 @@ 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];
@@ -815,8 +811,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
* 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();
@@ -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,32 +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_xframe_options();
$this->add_header("Cache-control: no-cache");
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
}
/**
* @inheritDoc

View File

@@ -72,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);
@@ -1083,11 +1084,11 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
/**
* Implement this interface to add content to any enhanced portal page
*
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
*
* @api
* @package Extensibility
*
* @since 2.4.0 interface creation
* @since 2.7.0 change method signatures due to Silex to Symfony migration
* @since 2.4.0
*/
interface iPortalUIExtension
{
@@ -1160,11 +1161,7 @@ interface iPortalUIExtension
}
/**
* Extend this class instead of iPortalUIExtension if you don't need to overload all methods
*
* @api
* @package Extensibility
* @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
{

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())
{
@@ -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':
@@ -4735,15 +4729,11 @@ 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))

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_xframe_options();
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

@@ -852,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)
@@ -882,7 +881,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
$oDashboard->SetDefinitionFile($sDashboardFile);
}
else
{
@@ -1478,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

@@ -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]
@@ -1420,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;
@@ -1577,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
{

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_xframe_options();
$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');

View File

@@ -59,8 +59,6 @@ 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;
}
@@ -69,7 +67,7 @@ class LoginBasic extends AbstractLoginFSMExtension
{
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;

View File

@@ -43,8 +43,6 @@ 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;
}
@@ -53,7 +51,7 @@ class LoginExternal extends AbstractLoginFSMExtension
{
if ($_SESSION['login_mode'] == 'external')
{
$sAuthUser = $_SESSION['auth_user'];
$sAuthUser = $this->GetAuthUser();
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -71,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;
}
@@ -84,7 +82,15 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
{
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']);
}

View File

@@ -58,8 +58,6 @@ 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;
}
@@ -68,7 +66,7 @@ class LoginURL extends AbstractLoginFSMExtension
{
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;

View File

@@ -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_xframe_options();
$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);
@@ -685,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
@@ -886,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();

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";
}
}

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();

View File

@@ -283,7 +283,6 @@ class utils
*
* @since 2.5.2 2.6.0 new 'transaction_id' filter
* @since 2.7.0 new 'element_identifier' filter
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
@@ -308,7 +307,6 @@ class utils
case 'context_param':
case 'parameter':
case 'field_name':
case 'transaction_id':
if (is_array($value))
{
$retValue = array();
@@ -359,11 +357,6 @@ class utils
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
// For URL
case 'url':
$retValue = filter_var($value, FILTER_SANITIZE_URL);
break;
default:
case 'raw_data':
$retValue = $value;
@@ -569,93 +562,48 @@ class utils
public static function ReadFromFile($sFileName)
{
if (!file_exists($sFileName)) {
return false;
}
if (!file_exists($sFileName)) return false;
return file_get_contents($sFileName);
}
/**
* @param mixed $value The value as read from php.ini (eg 256k, 2M, 1G etc.)
*
* @return int conversion to number of bytes
*
* @since 2.7.5 3.0.0 convert to int numeric values
*
* @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes Shorthand bytes value reference in PHP.net FAQ
* Helper function to convert a value expressed in a 'user friendly format'
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
* @param mixed $value The value as read from php.ini
* @return number
*/
public static function ConvertToBytes($value)
public static function ConvertToBytes( $value )
{
if (!is_numeric($value)) {
$iLength = strlen($value);
$iReturn = substr($value, 0, $iLength - 1);
$sUnit = strtoupper(substr($value, $iLength - 1));
switch ($sUnit) {
case 'G':
$iReturn *= 1024;
case 'M':
$iReturn *= 1024;
case 'K':
$iReturn *= 1024;
}
} else {
$iReturn = (int)$value;
}
return $iReturn;
}
/**
* Checks if the memory limit is at least what is required
*
* @param int $iMemoryLimit set limit in bytes, use {@link utils::ConvertToBytes()} to convert current php.ini value
* @param int $iRequiredLimit required limit in bytes
*
* @return bool
*/
public static function IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)
{
if ($iMemoryLimit === -1) {
// -1 means : no limit (see https://www.php.net/manual/fr/ini.core.php#ini.memory-limit)
return true;
}
return ($iMemoryLimit >= $iRequiredLimit);
}
/**
* Set memory_limit to required value
*
* @param string $sRequiredLimit required limit, for example '512M'
*
* @return bool|null null if nothing was done, true if modifying memory_limit was successful, false otherwise
*
* @uses utils::ConvertToBytes()
* @uses \ini_get('memory_limit')
* @uses \ini_set()
* @uses utils::ConvertToBytes()
*
* @since 2.7.5 N°3806
*/
public static function SetMinMemoryLimit($sRequiredLimit)
{
$iRequiredLimit = static::ConvertToBytes($sRequiredLimit);
$sMemoryLimit = trim(ini_get('memory_limit'));
if (empty($sMemoryLimit)) {
// On some PHP installations, memory_limit does not exist as a PHP setting!
// (encountered on a 5.2.0 under Windows)
// In that case, ini_set will not work
return false;
}
$iMemoryLimit = static::ConvertToBytes($sMemoryLimit);
if (static::IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)) {
return null;
}
return ini_set('memory_limit', $iRequiredLimit);
}
$iReturn = $value;
if ( !is_numeric( $value ) )
{
$iLength = strlen( $value );
$iReturn = substr( $value, 0, $iLength - 1 );
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
switch ( $sUnit )
{
case 'G':
$iReturn *= 1024;
case 'M':
$iReturn *= 1024;
case 'K':
$iReturn *= 1024;
}
}
return $iReturn;
}
/**
* Checks if the memory limit is at least what is required
*
* @param int $memoryLimit set limit in bytes
* @param int $requiredLimit required limit in bytes
* @return bool
*/
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
{
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
}
/**
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
@@ -784,7 +732,7 @@ class utils
* @throws \ConfigException
* @throws \CoreException
*
* @since 2.7.0 N°2478 this method will now always call {@link MetaModel::GetConfig} first, and cache in this class is only set when loading from disk
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
*/
public static function GetConfig()
{
@@ -833,42 +781,22 @@ class utils
}
}
/**
* @return bool The boolean value of the conf. "behind_reverse_proxy" (except if there is no REMOTE_ADDR int his case, it return false)
*
* @since 2.7.4
*/
public static function IsProxyTrusted()
{
if (empty($_SERVER['REMOTE_ADDR'])) {
return false;
}
$bTrustProxies = (bool) self::GetConfig()->Get('behind_reverse_proxy');
return $bTrustProxies;
}
/**
* Returns the absolute URL to the application root path
*
* @param bool $bForceTrustProxy
*
* @return string The absolute URL to the application root, without the first slash
*
* @throws \Exception
*
* @since 2.7.4 $bForceTrustProxy param added
*/
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
public static function GetAbsoluteUrlAppRoot()
{
static $sUrl = null;
if ($sUrl === null || $bForceTrustProxy)
if ($sUrl === null)
{
$sUrl = self::GetConfig()->Get('app_root_url');
if ($sUrl == '')
{
$sUrl = self::GetDefaultUrlAppRoot($bForceTrustProxy);
$sUrl = self::GetDefaultUrlAppRoot();
}
elseif (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
{
@@ -892,116 +820,31 @@ class utils
* For most usages, when an root url is needed, use utils::GetAbsoluteUrlAppRoot() instead as uses this only as a fallback when the
* app_root_url conf parameter is not defined.
*
* @param bool $bForceTrustProxy
*
* @return string
*
* @throws \Exception
*
* @since 2.7.4 $bForceTrustProxy param added
*/
public static function GetDefaultUrlAppRoot($bForceTrustProxy = false)
{
$sAbsoluteUrl = self::GetCurrentAbsoluteUrl($bForceTrustProxy, true);
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
$sAppRoot = realpath(APPROOT);
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
}
/**
* Build the current absolute URL from the server's variables.
*
* For almost every usage, you should use the more secure utils::GetAbsoluteUrlAppRoot() : instead of reading the current uri, it provide you the configured application's root URL (this is done during the setup and chn be changed in the configuration file)
*
* @see utils::GetAbsoluteUrlAppRoot
*
* @param bool $bForceTrustProxy
* @param bool $bTrimQueryString
*
* @return string
*
* @since 2.7.4
*/
public static function GetCurrentAbsoluteUrl($bForceTrustProxy = false, $bTrimQueryString = false)
*/
public static function GetDefaultUrlAppRoot()
{
// Build an absolute URL to this page on this server/port
$sServerName = self::GetServerName($bForceTrustProxy);
$bIsSecure = self::IsConnectionSecure($bForceTrustProxy);
$sProtocol = $bIsSecure ? 'https' : 'http';
$iPort = self::GetServerPort($bForceTrustProxy);
if ($bIsSecure) {
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
} else {
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
$sProtocol = self::IsConnectionSecure() ? 'https' : 'http';
$iPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
if ($sProtocol == 'http')
{
$sPort = ($iPort == 80) ? '' : ':'.$iPort;
}
$sPath = self::GetRequestUri($bForceTrustProxy);
if ($bTrimQueryString) {
// remove all the parameters from the query string
$iQuestionMarkPos = strpos($sPath, '?');
if ($iQuestionMarkPos !== false) {
$sPath = substr($sPath, 0, $iQuestionMarkPos);
}
else
{
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
}
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
return $sAbsoluteUrl;
}
/**
* @param bool $bForceTrustProxy
*
* @return string
*
* @since 2.7.4
*/
public static function GetServerName($bForceTrustProxy = false)
{
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
if ($bTrustProxy) {
$sServerName = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $sServerName;
}
return $sServerName;
}
/**
* @param bool $bForceTrustProxy
*
* @return int|mixed
* @since 2.7.4
*/
public static function GetServerPort($bForceTrustProxy = false)
{
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
$sServerPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
if ($bTrustProxy) {
$sServerPort = isset($_SERVER['HTTP_X_FORWARDED_PORT']) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $sServerPort;
}
return $sServerPort;
}
/**
* @return string
*
* @since 2.7.4
*/
public static function GetRequestUri()
{
// $_SERVER['REQUEST_URI'] is empty when running on IIS
// Let's use Ivan Tcholakov's fix (found on www.dokeos.com)
if (empty($_SERVER['REQUEST_URI']))
if (!empty($_SERVER['REQUEST_URI']))
{
$sPath = $_SERVER['REQUEST_URI'];
}
else
{
$sPath = $_SERVER['SCRIPT_NAME'];
if (!empty($_SERVER['QUERY_STRING']))
@@ -1012,7 +855,18 @@ class utils
}
$sPath = $_SERVER['REQUEST_URI'];
return $sPath;
// remove all the parameters from the query string
$iQuestionMarkPos = strpos($sPath, '?');
if ($iQuestionMarkPos !== false)
{
$sPath = substr($sPath, 0, $iQuestionMarkPos);
}
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
$sAppRoot = realpath(APPROOT);
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
}
/**
@@ -1056,36 +910,19 @@ class utils
/**
* Helper to handle the variety of HTTP servers
* See N°286 (fixed in [896]), and N°634 (this fix)
*
*
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
* nginx set it to an empty string
* Others might leave it unset (no array entry)
*
* @param bool $bForceTrustProxy
*
* @return bool
*
* @since 2.7.4 reverse proxies handling
*/
public static function IsConnectionSecure($bForceTrustProxy = false)
* Others might leave it unset (no array entry)
*/
public static function IsConnectionSecure()
{
$bSecured = false;
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
if ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
{
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
}
elseif ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL']))
{
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
}
elseif ((!empty($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) != 'off'))
if (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
{
$bSecured = true;
}
return $bSecured;
}
@@ -1329,19 +1166,19 @@ class utils
$oDashboard = $param;
$sDashboardId = $oDashboard->GetId();
$sDashboardFile = $oDashboard->GetDefinitionFile();
$sDashboardFileRelative = utils::LocalPath($sDashboardFile);
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
$sDashboardFileJS = addslashes($sDashboardFileRelative);
$sDashboardFileURL = urlencode($sDashboardFileRelative);
$sDashboardFileJS = addslashes($sDashboardFile);
$sDashboardFileURL = urlencode($sDashboardFile);
$sUploadDashboardTransactId = utils::GetNewTransactionId();
$aResult = array(
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL),
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
);
if ($oDashboard->GetReloadURL()) {
if ($oDashboard->GetReloadURL())
{
$aResult[] = new SeparatorPopupMenuItem();
$aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank');
}
@@ -1966,15 +1803,11 @@ class utils
}
/**
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
*
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
*
* @return string the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
* this function is made
* or an empty string if no such module is found (or not called within a module file)
*
* @uses \debug_backtrace()
* Returns the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
* this function is made
* or an empty string if no such module is found (or not called within a module file)
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
* @return string
*/
public static function GetCurrentModuleDir($iCallDepth)
{
@@ -1999,14 +1832,9 @@ class utils
}
/**
* **Warning** : as this method uses {@see GetCurrentModuleDir} it produces hazardous results.
* You should better uses directly {@see GetAbsoluteUrlModulesRoot} and add the module dir name yourself ! See N°4573
*
* @return string the base URL for all files in the current module from which this method is called
* or an empty string if no such module is found (or not called within a module file)
* @throws \Exception
*
* @uses GetCurrentModuleDir
*/
public static function GetCurrentModuleUrl()
{
@@ -2260,22 +2088,6 @@ class utils
return COMPILATION_TIMESTAMP;
}
/**
* @return string eg : '2_7_0' if iTop core version is '2.7.5-2'
* @throws \ApplicationException if constant value is invalid
* @uses ITOP_CORE_VERSION
*/
public static function GetItopVersionWikiSyntax($sItopVersion = ITOP_CORE_VERSION)
{
$aExplodedVersion = explode('.', $sItopVersion);
if ((false === isset($aExplodedVersion[0])) || (false === isset($aExplodedVersion[1]))) {
throw new ApplicationException('iTop version is wrongfully configured!');
}
return "{$aExplodedVersion[0]}_{$aExplodedVersion[1]}_0";
}
/**
* Check if the given class if configured as a high cardinality class.
*
@@ -2373,22 +2185,16 @@ class utils
}
/**
* @param string $sPath for example `/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz`
* **Warning**, if path is a symlink, it will be resolved !
* So `C:\Dev\wamp64\www\itop-dev/env-production/itop-hub-connector/land.php`
* Will become `C:\Dev\wamp64\www\itop-dev\datamodels\2.x\itop-hub-connector\land.php`
* @param string $sBasePath for example `/var/www/html/itop/data/`
* @param string $sPath for example '/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz'
* @param string $sBasePath for example '/var/www/html/itop/data/'
*
* @return bool|string false if path :
* @return bool false if path :
* * invalid
* * not allowed
* * not contained in base path
* Otherwise return the real path (see realpath())
*
* @uses \realpath()
* @uses static::StartsWith
* @since 2.6.5 2.7.0 N°2538
* @since 2.7.5 details in PHPDoc about symlink resolution
*/
final public static function RealPath($sPath, $sBasePath)
{
@@ -2473,35 +2279,4 @@ class utils
{
return str_replace(' ', '', ucwords(strtr($sInput, '_-', ' ')));
}
/**
* @param \cmdbAbstractObject $oCmdbAbstract
* @param \Exception $oException
*
* @throws \Exception
* @since 2.7.2/ 2.8.0
*/
public static function EnrichRaisedException($oCmdbAbstract, $oException)
{
if (is_null($oCmdbAbstract) ||
! is_a($oCmdbAbstract, \cmdbAbstractObject::class))
{
throw $oException;
}
$sCmdbAbstractInfo = str_replace("\n", '', "" . $oCmdbAbstract);
$sMessage = $oException->getMessage() . " (" . $sCmdbAbstractInfo . ")";
$e = new CoreException($sMessage, null, '', $oException);
throw $e;
}
/**
* @return bool : indicate whether we run under a windows environnement or not
* @since 2.7.4 : N°3412
*/
public static function IsWindowsEnvironment(){
return (substr(PHP_OS,0,3) === 'WIN');
}
}

View File

@@ -482,31 +482,13 @@ class WebPage implements Page
$this->a_headers[] = $s_header;
}
/**
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the config parameter value.
*
* @since 2.7.3 3.0.0 N°3416
* @uses security_header_xframe config parameter
* @uses \utils::GetConfig()
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
*/
public function add_xframe_options($sHeaderValue = null)
{
if (is_null($sHeaderValue)) {
$sHeaderValue = utils::GetConfig()->Get('security_header_xframe');
}
$this->add_header('X-Frame-Options: '.$sHeaderValue);
}
/**
* Add needed headers to the page so that it will no be cached
*/
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
}
/**
@@ -1564,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,10 +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_xframe_options();
$this->add_header("Cache-control: no-cache");
$this->add_header("Content-location: export.xml");
}
}
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.8');
require_once APPROOT.'bootstrap.inc.php';

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.0.8",
"php": ">=5.6.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -12,27 +10,22 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"combodo/tcpdf": "~6.4.4",
"guzzlehttp/guzzle": "^6.5.8",
"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.",
@@ -44,7 +37,7 @@
},
"config": {
"platform": {
"php": "7.0.8"
"php": "5.6.0"
},
"vendor-dir": "lib",
"preferred-install": {
@@ -58,9 +51,7 @@
"core",
"application",
"sources/application",
"sources/Composer",
"sources/Controller",
"sources/Core"
"sources/Composer"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",

2837
composer.lock generated

File diff suppressed because it is too large Load Diff

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.)
@@ -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;
}
@@ -3769,7 +3760,7 @@ 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;
@@ -3845,7 +3836,7 @@ 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;
@@ -3922,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]);
@@ -4292,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)
@@ -4586,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))
{
@@ -7387,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)
@@ -7458,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)
@@ -7481,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';
}
}
@@ -7670,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
@@ -7686,6 +7665,7 @@ class AttributeBlob extends AttributeDefinition
}
}
$proposedValue->SetHostObject($oHostObj, $this->GetCode());
return $proposedValue;
}
@@ -7704,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))
{
@@ -7728,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;
}
@@ -7970,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}
@@ -8263,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)
@@ -9100,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)
{
}
@@ -9232,7 +9193,7 @@ 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;
@@ -9318,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))
{
@@ -9515,7 +9476,7 @@ class AttributeTable extends AttributeDBField
return $proposedValue;
}
public function FromSQLToValue($aCols, $sPrefix = '')
public function FromSQLToValue($aCols, $sPrefix = '', $oHostObject = null, $sHostAttCode = null)
{
try
{
@@ -9530,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;
@@ -9929,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);
}
/**
@@ -11078,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"];
@@ -11660,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

@@ -345,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();
@@ -420,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);
}
}
}
@@ -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
*/
@@ -162,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
@@ -239,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);
}
/**
@@ -253,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);
@@ -288,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
@@ -357,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)
{
@@ -455,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');
@@ -478,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;
@@ -564,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)
@@ -717,15 +665,10 @@ 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)
{
@@ -737,7 +680,7 @@ class CMDBSource
{
$aContext = array('query' => $sSql);
$iMySqlErrorNo = self::GetMySQLiForQuery()->errno;
$iMySqlErrorNo = self::$m_oMysqli->errno;
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
{
@@ -759,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;
@@ -767,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);
@@ -789,7 +732,7 @@ class CMDBSource
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
IssueLog::Error($sMessage, 'DeadLock', $e->getMessage());
}
/**
@@ -805,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();
@@ -831,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);
}
@@ -844,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');
}
@@ -866,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');
}
@@ -931,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;
@@ -995,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)
{
@@ -1049,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)
{
@@ -1131,7 +1025,7 @@ class CMDBSource
{
try
{
$oResult = self::GetMySQLiForQuery()->query($sSql);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1268,24 +1162,17 @@ 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)
@@ -1333,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
@@ -1618,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)
{

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)',
@@ -1104,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.',
@@ -1152,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.',
@@ -1304,30 +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,
],
'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,
),
);

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>';
}

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;
}
}
@@ -856,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)
{
@@ -1977,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()
{
@@ -2379,16 +2375,14 @@ abstract class DBObject implements iDisplay
return $aDelta;
}
/**
* @api
* @api-advanced
*
* @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
* @return array attname => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
* Reset during {@see DBObject::DBUpdate()}
* @throws Exception
* @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()
{
@@ -2408,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()
@@ -2739,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;
@@ -2827,14 +2799,7 @@ abstract class DBObject implements iDisplay
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e)
{
utils::EnrichRaisedException($oTrigger, $e);
}
$oTrigger->DoActivate($this->ToArgs('this'));
}
$this->RecordObjCreation();
@@ -2982,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
*
@@ -3145,13 +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) {
utils::EnrichRaisedException($oTrigger, $e);
}
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
$bHasANewExternalKeyValue = false;
@@ -3178,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)
{
@@ -3250,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);
@@ -3263,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
@@ -3288,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');
@@ -3301,7 +3225,6 @@ abstract class DBObject implements iDisplay
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
@@ -3315,13 +3238,6 @@ 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
{
$this->AfterUpdate();
@@ -3382,25 +3298,6 @@ 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
*
@@ -3471,14 +3368,7 @@ abstract class DBObject implements iDisplay
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e)
{
utils::EnrichRaisedException($oTrigger, $e);
}
$oTrigger->DoActivate($this->ToArgs('this'));
}
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
@@ -3520,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)
{
@@ -3547,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
@@ -3664,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
@@ -3735,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)
{
@@ -3773,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;
}
}
@@ -3874,7 +3733,8 @@ abstract class DBObject implements iDisplay
}
}
if (!$bDoNotWrite) {
if (!$bDoNotWrite)
{
$this->DBWrite();
}
@@ -3882,28 +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) {
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) {
utils::EnrichRaisedException($oTrigger, $e);
}
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
}
}
else
@@ -4485,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
*
@@ -4511,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

@@ -1312,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
@@ -1496,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)
@@ -1515,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");
@@ -1004,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
@@ -1013,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
@@ -1087,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);
@@ -1113,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())
{
@@ -1187,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());
@@ -1623,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

@@ -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
@@ -147,17 +145,15 @@ class Dict
{
// 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 dictionnaries. default labels displayed");
// It may happen, when something happens before the dictionaries get loaded
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 $aCurrentDictionary[$sStringCode];
}
@@ -168,7 +164,7 @@ class Dict
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 $aDefaultDictionary[$sStringCode];
}
@@ -177,7 +173,7 @@ class Dict
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 $aDefaultDictionary[$sStringCode];
}
@@ -236,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
@@ -264,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;
}
}
@@ -289,10 +263,9 @@ 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;
}

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

@@ -353,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,60 +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. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
}
if ($sConfigKey === 'svg_sanitizer') {
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;
}
}
@@ -108,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(),
@@ -334,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',
@@ -359,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

@@ -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,38 +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';
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 = '';
@@ -573,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,
@@ -634,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;
}
@@ -698,7 +675,7 @@ abstract class LogAPI
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
{
return $sLogLevelMin[static::CHANNEL_DEFAULT];
return $sLogLevelMin[$sChannel];
}
return static::LEVEL_DEFAULT;
@@ -763,7 +740,7 @@ class DeadLockLog extends LogAPI
return self::CHANNEL_WAIT_TIMEOUT;
break;
case 1213:
return self::CHANNEL_DEADLOCK_FOUND;
return self::CHANNEL_DEADLOCK_FOUND;
break;
default:
return self::CHANNEL_DEFAULT;
@@ -772,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

@@ -242,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()
{
@@ -256,36 +254,10 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$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().')');
}
// 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).')');
}
}
}

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"

View File

@@ -23,15 +23,7 @@ later : solve the 2 remaining shift-reduce conflicts (JOIN)
%name OQLParser_
%declare_class {class OQLParserRaw}
%syntax_error {
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
}
/* Bug N°4052 Parser stack size too small for huge OQL requests */
%stack_size 1000
%stack_overflow {
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
}
%parse_failure {
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
}
result ::= union(X). { $this->my_result = X; }
@@ -271,43 +263,15 @@ func_name(A) ::= F_INET_NTOA(X). { A=X; }
%code {
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 :-(

View File

@@ -173,7 +173,7 @@ class MatchOqlExpression extends MatchExpression implements CheckableExpression
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oLeftExpr->RenderExpression(true), 0));
}
// Only field MATCHES scalar is allowed
if (!$this->m_oRightExpr instanceof ScalarExpression && !$this->m_oRightExpr instanceof VariableOqlExpression)
if (!$this->m_oRightExpr instanceof ScalarExpression)
{
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oRightExpr->RenderExpression(true), 0));
}

View File

@@ -1 +1 @@
2021-06-03
2019-12-03

View File

@@ -331,12 +331,4 @@ class OQLJoin
return $this->sRightField;
}
/**
* @return string
*/
public function GetLeftField()
{
return $this->sLeftField;
}
}

View File

@@ -50,15 +50,8 @@ class OQLClassTreeOptimizer
{
if ($oJoin->IsOutbound())
{
// If joined class in not the same class than the external key target class
// then the join cannot be removed because it is used to filter the request
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
$sExtKeyAttCode = $oJoin->GetLeftField();
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
if (($oExtKeyAttDef instanceof AttributeExternalKey) && ($sJoinedClass == $oExtKeyAttDef->GetTargetClass())) {
// The join is not used, remove from tree
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
}
// The join is not used, remove from tree
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
}
else
{

View File

@@ -38,6 +38,8 @@ class ormDocument
protected $m_data;
protected $m_sMimeType;
protected $m_sFileName;
protected $m_oHostObject;
protected $m_sHostObjectAttcode;
/**
* Constructor
@@ -47,6 +49,15 @@ class ormDocument
$this->m_data = $data;
$this->m_sMimeType = $sMimeType;
$this->m_sFileName = $sFileName;
$this->m_oHostObject = null;
$this->m_sHostObjectAttcode = null;
}
public function SetHostObject(DBObject $oHostObject, $sAttCode)
{
$this->m_oHostObject = $oHostObject;
$this->m_sHostObjectAttcode = $sAttCode;
}
public function __toString()
@@ -138,6 +149,12 @@ class ormDocument
*/
public function GetDownloadLink($sClass, $Id, $sAttCode)
{
if ($this->m_oHostObject)
{
$sClass = get_class($this->m_oHostObject);
$Id = $this->m_oHostObject->GetKey();
$sAttCode = $this->m_sHostObjectAttcode;
}
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
}
@@ -147,6 +164,12 @@ class ormDocument
*/
public function GetDisplayURL($sClass, $Id, $sAttCode)
{
if ($this->m_oHostObject)
{
$sClass = get_class($this->m_oHostObject);
$Id = $this->m_oHostObject->GetKey();
$sAttCode = $this->m_sHostObjectAttcode;
}
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
}
@@ -157,6 +180,12 @@ class ormDocument
*/
public function GetDownloadURL($sClass, $Id, $sAttCode)
{
if ($this->m_oHostObject)
{
$sClass = get_class($this->m_oHostObject);
$Id = $this->m_oHostObject->GetKey();
$sAttCode = $this->m_sHostObjectAttcode;
}
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
$sSignature = md5($this->GetData());
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)

View File

@@ -814,17 +814,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$oNotObsoleteRemote->AddConditionExpression($oNotObsolete);
$oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote);
}
if (!utils::IsArchiveMode() && MetaModel::IsArchivable($sTargetClass))
{
$oNotArchived = new BinaryExpression(
new FieldExpression('archive_flag', $sTargetClass),
'=',
new ScalarExpression(0)
);
$oNotArchivedRemote = new DBObjectSearch($sTargetClass);
$oNotArchivedRemote->AddConditionExpression($oNotArchived);
$oLinkSearch->AddCondition_PointingTo($oNotArchivedRemote, $sExtKeyToRemote);
}
}
$oLinkSet = new DBObjectSet($oLinkSearch);
$oLinkSet->SetShowObsoleteData($bShowObsolete);
@@ -835,4 +824,4 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
return $oLinkSet;
}
}
}

View File

@@ -607,22 +607,14 @@ class CheckStopWatchThresholds implements iBackgroundProcess
// Activate any existing trigger
//
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oTriggerSet = new DBObjectSet(
DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('$sClassList') AND stop_watch_code MATCHES :stop_watch_code AND threshold_index = :threshold_index"),
DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('$sClassList') AND stop_watch_code=:stop_watch_code AND threshold_index = :threshold_index"),
array(), // order by
array('stop_watch_code' => $sAttCode, 'threshold_index' => $iThreshold)
);
while ($oTrigger = $oTriggerSet->Fetch())
{
try
{
$oTrigger->DoActivate($oObj->ToArgs('this'));
}
catch(Exception $e)
{
utils::EnrichRaisedException($oTrigger, $e);
}
$oTrigger->DoActivate($oObj->ToArgs('this'));
}
}
}

View File

@@ -138,7 +138,7 @@ final class ormTagSet extends ormSet
}
/**
* @return array index: code, value: corresponding {@see \TagSetFieldData}
* @return array of tags indexed by code
*/
public function GetTags()
{

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